// Subject Materials Section Functions
function showSection(sectionId, forceOrSubjectId = null) {
console.log('Showing section:', sectionId, 'force/subjectId:', forceOrSubjectId);
// Special handling for subject materials section using the toggle function
if (sectionId === 'subjectMaterialsSection') {
const subjectId = forceOrSubjectId;
toggleSubjectView('materials');
// Get the section
const section = document.getElementById(sectionId);
if (section) {
// Make sure the header div is visible and properly styled
const headerDiv = section.querySelector('.d-flex.justify-content-between.align-items-center');
if (headerDiv) {
// Ensure header is visible with !important
headerDiv.style.cssText = "display: flex !important; opacity: 1; visibility: visible;";
// Make the heading more prominent
const heading = headerDiv.querySelector('h3');
if (heading) {
heading.style.fontWeight = 'bold';
heading.style.color = '#3F51B5';
}
}
// Now load the content
loadSubjectMaterials(subjectId);
}
return;
}
// Special handling for subjects section using the toggle function
if (sectionId === 'subjectsSection') {
toggleSubjectView('subjects');
return;
}
// Handle the case where we're being called from the back button
const forceDisplay = forceOrSubjectId === true;
// If not a boolean, treat as subjectId
const subjectId = typeof forceOrSubjectId === 'boolean' ? null : forceOrSubjectId;
// Special handling for forced navigation to subjects
if (sectionId === 'subjectsSection' && forceDisplay) {
console.log('FORCE NAVIGATION to subjects section');
// Hide all other sections first
document.querySelectorAll('.content-section').forEach(sect => {
if (sect.id !== 'subjectsSection') {
sect.style.display = 'none';
sect.classList.remove('active');
}
});
// Activate subjects section with high specificity
const subjectsSection = document.getElementById('subjectsSection');
if (subjectsSection) {
// Force display using multiple methods
subjectsSection.style.cssText = 'display: block !important; opacity: 1; visibility: visible;';
subjectsSection.classList.add('active');
// Force a redraw
void subjectsSection.offsetHeight;
// Add active class to subjects nav
document.querySelectorAll('.nav-item').forEach(item => {
const section = item.getAttribute('data-section');
if (section === 'subjects') {
item.classList.add('active');
} else {
item.classList.remove('active');
}
});
// Update URL hash
window.location.hash = 'subjectsSection';
// Scroll to top
window.scrollTo(0, 0);
console.log('Force navigation complete.');
return;
}
}
// Special handling for subjects section to correctly set the active nav item
if (sectionId === 'subjectsSection') {
// Update URL hash to track the section
window.location.hash = 'subjectsSection';
console.log('Setting hash to subjectsSection');
}
// Debug output - list all sections in DOM
const allSections = document.querySelectorAll('.content-section');
console.log('Available sections:');
allSections.forEach(section => {
console.log('- Section ID:', section.id, 'Display:', section.style.display, 'Classes:', section.className);
});
// Hide all sections
document.querySelectorAll('.content-section').forEach(section => {
section.classList.remove('active');
section.style.display = 'none';
console.log('Hiding section:', section.id);
});
// First remove active class from all nav items
document.querySelectorAll('.nav-item').forEach(item => {
item.classList.remove('active');
});
// Then add active class to the correct nav item
document.querySelectorAll('.nav-item').forEach(item => {
const navSection = item.getAttribute('data-section');
console.log('Nav item section:', navSection, 'comparing with:', sectionId);
if (navSection === sectionId ||
(sectionId === 'subjectMaterialsSection' && navSection === 'subjects') ||
(sectionId === 'subjectsSection' && navSection === 'subjects')) {
item.classList.add('active');
console.log('Activated nav item:', navSection);
}
});
// Show the selected section
const selectedSection = document.getElementById(sectionId);
if (selectedSection) {
console.log('Found section to show:', sectionId);
selectedSection.classList.add('active');
selectedSection.style.display = 'block';
// Force browser to recognize the display change
setTimeout(() => {
selectedSection.style.opacity = '0.99';
setTimeout(() => {
selectedSection.style.opacity = '1';
}, 10);
}, 10);
} else {
console.error('Could not find section with ID:', sectionId);
}
// Special handling for subjects section to ensure it's visible
if (sectionId === 'subjectsSection') {
const subjectsSection = document.getElementById('subjectsSection');
if (subjectsSection) {
console.log('Forcing subjects section to display');
subjectsSection.style.display = 'block';
subjectsSection.classList.add('active');
// The following forces a reflow
void subjectsSection.offsetWidth;
} else {
console.error('Subjects section element not found in DOM');
}
}
// Scroll to top
window.scrollTo(0, 0);
}
// Custom API function to get subject materials directly
function getSubjectMaterials(subjectId = null) {
return new Promise((resolve, reject) => {
// Get the CSRF token
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
// Build the URL for the API endpoint
let url = '/app/get-teacher-subjects/';
console.log('Fetching teacher subjects from:', url);
// First fetch the subjects
fetch(url, {
headers: {
'X-CSRFToken': csrftoken
}
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(subjectsData => {
console.log('Teacher subjects data:', subjectsData);
if (!subjectsData.success || !subjectsData.subjects || subjectsData.subjects.length === 0) {
console.log('No subjects found or empty response');
resolve({ subjects: [] });
return;
}
let subjects = subjectsData.subjects;
// If a specific subject is requested, filter the subjects
if (subjectId) {
console.log('Filtering subjects by ID:', subjectId);
subjects = subjects.filter(subject => subject.id.toString() === subjectId.toString());
if (subjects.length === 0) {
console.log('No matching subject found for ID:', subjectId);
resolve({ subjects: [] });
return;
}
}
// Now we need to get the files for each subject
// We'll use the manage_subject_files endpoint with a GET request
// and parse the HTML response to extract the files
// Create a result object to store the subjects with their files
const result = {
subjects: []
};
// Process each subject to get its files
const promises = subjects.map(subject => {
const filesUrl = `/app/manage-subject-files/?subject=${subject.id}`;
console.log(`Fetching files for subject ${subject.name} (ID: ${subject.id}) from: ${filesUrl}`);
return fetch(filesUrl, {
headers: {
'X-CSRFToken': csrftoken
}
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.text();
})
.then(html => {
// Parse the HTML to extract the files
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
console.log(`Parsing HTML response for subject ${subject.name}`);
// Try different ways to find the subject card
let subjectCard = null;
let cardBody = null;
// Method 1: Find by exact heading text
const headings = Array.from(doc.querySelectorAll('.card-header h5, .card-title'));
subjectCard = headings.find(h5 => h5.textContent.trim() === subject.name);
if (subjectCard) {
console.log(`Found subject card for ${subject.name} using heading match`);
cardBody = subjectCard.closest('.card')?.querySelector('.card-body');
} else {
// Method 2: Find by card with subject ID in attributes
const cards = doc.querySelectorAll('.card[data-subject-id="' + subject.id + '"], .card[id*="subject-' + subject.id + '"]');
if (cards.length > 0) {
console.log(`Found subject card for ${subject.name} using ID attribute`);
subjectCard = cards[0];
cardBody = subjectCard.querySelector('.card-body');
} else {
// Method 3: Just take the first card if there's only one subject requested
if (subjectId) {
const allCards = doc.querySelectorAll('.card');
if (allCards.length > 0) {
console.log(`Using first card for ${subject.name} as fallback`);
subjectCard = allCards[0];
cardBody = subjectCard.querySelector('.card-body');
}
}
}
}
const files = [];
let hasSyllabus = false;
// If we found a card body with a table, extract the files
if (cardBody) {
// Look for table rows
const fileRows = cardBody.querySelectorAll('table tbody tr');
console.log(`Found ${fileRows.length} file rows for subject ${subject.name}`);
fileRows.forEach(row => {
const columns = row.querySelectorAll('td');
if (columns.length >= 3) { // Need at least 3 columns
const title = columns[0].textContent.trim();
const description = columns.length > 1 ? columns[1].textContent.trim() : '';
const date = columns.length > 2 ? columns[2].textContent.trim() : '';
// Extract file ID from the delete button
const deleteBtn = row.querySelector('button[onclick*="deleteFile"], button[data-file-id]');
let fileId = null;
if (deleteBtn) {
// Try onclick attribute
const onClickAttr = deleteBtn.getAttribute('onclick');
if (onClickAttr) {
const match = onClickAttr.match(/deleteFile\(['"]([^'"]*)['"]\)/);
if (match && match[1]) {
fileId = match[1];
}
}
// Try data attribute if onclick failed
if (!fileId) {
fileId = deleteBtn.getAttribute('data-file-id');
}
}
// Extract file URL from the download link
const actionCell = columns[columns.length - 1]; // Last column usually has actions
const downloadLink = actionCell.querySelector('a[href]:not([href="#"])');
let fileUrl = null;
if (downloadLink) {
fileUrl = downloadLink.getAttribute('href');
}
// Check if this is a syllabus file based on title or other indicators
const isSyllabus = title.toLowerCase().includes('syllabus');
// Set the syllabus flag for the subject if this file is a syllabus
if (isSyllabus) {
hasSyllabus = true;
}
files.push({
id: fileId,
title: title,
description: description,
uploaded_on: date,
file_url: fileUrl,
file_type: isSyllabus ? 'syllabus' : 'material'
});
console.log(`Added file: ${title} for subject ${subject.name}`);
}
});
} else {
console.log(`Could not find card body for subject ${subject.name}`);
}
// Check if the subject has a syllabus PDF using the View Syllabus button existence
const syllabusBtn = doc.querySelector(`a[href*="/subject/${subject.id}/syllabus/"], button[onclick*="viewSyllabus('${subject.id}')"]`);
if (syllabusBtn) {
console.log(`Found syllabus button for subject ${subject.name}`);
hasSyllabus = true;
}
// Add this subject with its files to the result
result.subjects.push({
...subject,
materials: files,
has_syllabus: hasSyllabus
});
console.log(`Finished processing subject ${subject.name}, found ${files.length} files`);
});
});
// Wait for all promises to resolve
Promise.all(promises)
.then(() => {
console.log('Final result:', result);
resolve(result);
})
.catch(error => {
console.error('Error processing subjects:', error);
reject(error);
});
})
.catch(error => {
console.error('Error fetching subjects:', error);
reject(error);
});
});
}
function loadSubjectMaterials(subjectId = null) {
const contentDiv = document.getElementById('subjectMaterialsContent');
if (!contentDiv) {
console.error('Subject materials content container not found');
return;
}
// Show loading state in content area only
contentDiv.innerHTML = `
Loading...
Loading subject materials...
`;
console.log('==== LOADING SUBJECT MATERIALS ====');
console.log('Subject ID:', subjectId);
// Build the URL with optional subject filter
let url = '/app/manage-subject-files/';
if (subjectId) {
url += `?subject=${subjectId}`;
}
console.log('Fetching from URL:', url);
// Create a direct link to the materials as a backup
const directLink = document.createElement('div');
directLink.className = 'text-center mt-3';
directLink.innerHTML = `
If materials don't load automatically, try:
Open Subject Materials in New Tab
`;
// Fetch the materials page directly
fetch(url, {
headers: {
'Accept': 'text/html',
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => {
console.log('Response status:', response.status);
console.log('Response type:', response.headers.get('content-type'));
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.text();
})
.then(html => {
console.log('Received HTML content of length:', html.length);
if (!html || html.trim().length === 0) {
console.error('Empty response received');
contentDiv.innerHTML = `
Empty response received from server
`;
contentDiv.appendChild(directLink);
return;
}
// Create a temporary div to hold the HTML content
const tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
console.log('HTML parsed, searching for content...');
// Clear the content div
contentDiv.innerHTML = '';
// Add back button at the top of the content for mobile
const mobileBackNav = document.createElement('div');
mobileBackNav.className = 'd-block d-md-none text-center mb-3';
mobileBackNav.innerHTML = `
`;
contentDiv.appendChild(mobileBackNav);
// Look for subjects and display them, trying multiple selectors
let contentFound = false;
// First approach: find the row of cards
const subjectsRow = tempDiv.querySelector('.content-section .row, .content-card .row, .row');
if (subjectsRow) {
console.log('Found subjects row with', subjectsRow.children.length, 'children');
contentDiv.innerHTML = '';
const newRow = document.createElement('div');
newRow.className = 'row';
newRow.style.display = 'flex';
newRow.style.flexWrap = 'wrap';
newRow.style.margin = '0 -12px'; // Negative margin to offset the padding
// Copy each card individually to avoid any parent context issues
Array.from(subjectsRow.querySelectorAll('.col-md-6, .col-lg-4, .col')).forEach(col => {
newRow.appendChild(col.cloneNode(true));
});
if (newRow.children.length > 0) {
contentDiv.appendChild(newRow);
contentFound = true;
console.log('Added', newRow.children.length, 'subject cards to the content');
}
}
// Second approach: find individual cards if no row found
if (!contentFound) {
const cards = tempDiv.querySelectorAll('.card');
if (cards && cards.length > 0) {
console.log('Found', cards.length, 'individual cards');
contentDiv.innerHTML = '';
const newRow = document.createElement('div');
newRow.className = 'row';
newRow.style.display = 'flex';
newRow.style.flexWrap = 'wrap';
newRow.style.margin = '0 -12px'; // Negative margin to offset the padding
cards.forEach(card => {
const col = document.createElement('div');
col.className = 'col-md-6 col-lg-4 mb-4';
// Add explicit styling to ensure visibility
col.style.display = 'block';
col.style.padding = '0 12px';
col.style.marginBottom = '24px';
col.style.width = '33.333%';
col.style.minWidth = '300px';
// Clone the card and add explicit styles
const cardClone = card.cloneNode(true);
cardClone.style.display = 'flex';
cardClone.style.flexDirection = 'column';
cardClone.style.height = '100%';
cardClone.style.border = '1px solid rgba(0,0,0,0.125)';
cardClone.style.borderRadius = '0.25rem';
cardClone.style.overflow = 'hidden';
cardClone.style.backgroundColor = '#fff';
col.appendChild(cardClone);
newRow.appendChild(col);
});
// Clear and append with explicit styles
contentDiv.style.display = 'block';
contentDiv.style.width = '100%';
contentDiv.style.visibility = 'visible';
contentDiv.appendChild(newRow);
contentFound = true;
console.log('Added cards to content as individual items with explicit styles');
// Force redraw
setTimeout(() => {
contentDiv.style.opacity = '0.99';
setTimeout(() => {
contentDiv.style.opacity = '1';
}, 50);
}, 50);
}
}
// Third approach: try to find any content card
if (!contentFound) {
const contentCard = tempDiv.querySelector('.content-card');
if (contentCard) {
console.log('Found content card, using its content');
// Remove header if present to avoid duplicate headers
const header = contentCard.querySelector('.d-flex.justify-content-between.align-items-center');
if (header) {
console.log('Removing header from content card');
header.remove();
}
contentDiv.innerHTML = contentCard.innerHTML;
contentFound = true;
}
}
// Fourth approach: direct render of subjects
if (!contentFound) {
console.log('No content found in HTML, trying to manually render subjects');
// Directly call the API to get subjects
fetch('/app/get-teacher-subjects/')
.then(response => response.json())
.then(data => {
if (data.success && data.subjects && data.subjects.length > 0) {
console.log('Got', data.subjects.length, 'subjects from API');
// Filter by subject ID if provided
let subjects = data.subjects;
if (subjectId) {
subjects = subjects.filter(s => s.id == subjectId);
}
if (subjects.length === 0) {
contentDiv.innerHTML = `
`;
contentDiv.appendChild(directLink);
});
}
// If all else fails, show a simple message
if (!contentFound) {
console.log('All content extraction methods failed');
contentDiv.innerHTML = `
Unable to display subject materials automatically.
`;
contentDiv.appendChild(directLink);
}
// Re-attach event handlers for the buttons after a short delay
setTimeout(() => {
console.log('Attaching event handlers to buttons');
// Add event listeners for upload buttons
contentDiv.querySelectorAll('[onclick*="openUploadModal"]').forEach(button => {
const match = button.getAttribute('onclick')?.match(/openUploadModal\(['"]([^'"]*)['"]\)/);
if (match) {
const id = match[1];
console.log('Found upload button for subject ID:', id);
button.setAttribute('onclick', `openUploadModal('${id}')`);
}
});
// Add event listeners for delete buttons
contentDiv.querySelectorAll('[onclick*="deleteFile"]').forEach(button => {
const match = button.getAttribute('onclick')?.match(/deleteFile\(['"]([^'"]*)['"]\)/);
if (match) {
const id = match[1];
console.log('Found delete button for file ID:', id);
button.setAttribute('onclick', `deleteFile('${id}')`);
}
});
// Add upload button if none exists
if (!contentDiv.querySelector('[onclick*="openUploadModal"]')) {
console.log('No upload buttons found, adding one');
const uploadBtn = document.createElement('div');
uploadBtn.className = 'text-center mt-4';
uploadBtn.innerHTML = `
`;
contentDiv.appendChild(uploadBtn);
}
}, 200);
})
.catch(error => {
console.error('Error loading subject materials:', error);
contentDiv.innerHTML = `