School Performance Dashboard

Total Enrollment

0

Avg. Daily Attendance

0%

Student-Teacher Ratio

0:1

Graduation Rate (8th Grade)

0%

Overall Proficiency vs. District

Performance by Subject

Math Proficiency by Grade

ELA Proficiency by Grade

Attendance Rate by Month

Student Demographics

Add Student Record

Student Roster

IDGradeMathELAAttendanceAction

Error: A required library is missing.

'; return; } // --- DATA MANAGEMENT --- let students = JSON.parse(localStorage.getItem('spd_students')) || []; const TOTAL_TEACHERS = 25; const getSampleData = () => { const sample = []; const demos = ['Hispanic', 'White', 'Black', 'Asian', 'Other']; for (let i = 1; i <= 500; i++) { const grade = Math.floor(Math.random() * 8) + 1; sample.push({ id: 'SID' + (1000 + i), grade: grade, mathScore: Math.floor(Math.random() * 60) + 40, // 40-100 elaScore: Math.floor(Math.random() * 60) + 40, attendance: Math.floor(Math.random() * 15) + 85, // 85-100 demographic: demos[Math.floor(Math.random() * demos.length)] }); } students = sample; }; if (students.length === 0) getSampleData(); const saveState = () => localStorage.setItem('spd_students', JSON.stringify(students)); // --- CHART INSTANCES & UTILITIES --- let proficiencyChart, subjectChart, mathGradeChart, elaGradeChart, attendanceChart, demoChart; const tabButtons = document.querySelectorAll('.spd-tab-button'); const tabContents = document.querySelectorAll('.spd-tab-content'); const nextBtn = document.getElementById('spd-next-btn'); const prevBtn = document.getElementById('spd-prev-btn'); // --- RENDER FUNCTIONS --- const renderAll = () => { try { renderKPIs(); renderProficiencyChart(); renderSubjectChart(); renderMathGradeChart(); renderElaGradeChart(); renderAttendanceTrendChart(); renderDemographicsChart(); renderManageTable(); updateNavButtons(); } catch(error) { console.error("Dashboard rendering failed:", error); } }; const renderKPIs = () => { document.getElementById('spd-enrollment-kpi').textContent = students.length; const avgAttendance = students.reduce((sum, s) => sum + s.attendance, 0) / students.length; document.getElementById('spd-attendance-kpi').textContent = `${avgAttendance.toFixed(1)}%`; document.getElementById('spd-ratio-kpi').textContent = `${Math.round(students.length / TOTAL_TEACHERS)}:1`; document.getElementById('spd-graduation-kpi').textContent = '98%'; // Static for demo }; const renderProficiencyChart = () => { const schoolMathProf = (students.filter(s=>s.mathScore >= 70).length / students.length) * 100; const schoolElaProf = (students.filter(s=>s.elaScore >= 70).length / students.length) * 100; const options = { chart: { type: 'bar', height: 350 }, series: [ { name: 'Our School', data: [schoolMathProf.toFixed(1), schoolElaProf.toFixed(1)] }, { name: 'District Average', data: [78, 82] } ], xaxis: { categories: ['Math', 'ELA'] }, yaxis: { max: 100, labels: { formatter: val => `${val}%` } }, plotOptions: { bar: { horizontal: false, columnWidth: '50%'} }, colors: ['var(--spd-primary-color)', '#bdc3c7'], dataLabels: { enabled: true, formatter: val => `${val}%` }, }; if(proficiencyChart) proficiencyChart.destroy(); document.querySelector("#spd-proficiency-chart").innerHTML = ''; proficiencyChart = new ApexCharts(document.querySelector("#spd-proficiency-chart"), options); proficiencyChart.render(); }; const renderSubjectChart = () => { const getProficiency = (subject) => (students.filter(s => s[subject] >= 70).length / students.length) * 100; const options = { chart: { type: 'donut', height: 350 }, series: [getProficiency('mathScore'), getProficiency('elaScore')], labels: ['Math Proficient', 'ELA Proficient'], legend: { position: 'bottom' } }; if(subjectChart) subjectChart.destroy(); document.querySelector("#spd-subject-chart").innerHTML = ''; subjectChart = new ApexCharts(document.querySelector("#spd-subject-chart"), options); subjectChart.render(); }; const renderGradeLevelChart = (subject, chartId) => { const grades = [...new Set(students.map(s => s.grade))].sort((a,b)=>a-b); const proficientData = grades.map(g => { const gradeStudents = students.filter(s => s.grade === g); return ((gradeStudents.filter(s => s[subject] >= 70).length / gradeStudents.length) * 100).toFixed(1); }); const options = { chart: { type: 'bar', height: 350 }, series: [{ name: 'Proficiency', data: proficientData }], xaxis: { categories: grades.map(g => `Grade ${g}`) }, yaxis: { max: 100, labels: { formatter: val => `${val}%` } }, colors: [subject === 'mathScore' ? '#2980b9' : '#8e44ad'] }; let chartInstance = subject === 'mathScore' ? mathGradeChart : elaGradeChart; if(chartInstance) chartInstance.destroy(); document.querySelector(`#${chartId}`).innerHTML = ''; chartInstance = new ApexCharts(document.querySelector(`#${chartId}`), options); chartInstance.render(); if (subject === 'mathScore') mathGradeChart = chartInstance; else elaGradeChart = chartInstance; }; const renderMathGradeChart = () => renderGradeLevelChart('mathScore', 'spd-math-grade-chart'); const renderElaGradeChart = () => renderGradeLevelChart('elaScore', 'spd-ela-grade-chart'); const renderAttendanceTrendChart = () => { const options = { chart: { type: 'line', height: 350, toolbar: { show: false } }, series: [{ name: 'Attendance', data: [96.5, 96.8, 97.2, 96.9, 95.8, 95.5, 94.8, 95.1, 96.3, 97.0] }], xaxis: { categories: ['Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Jan', 'Feb', 'Mar', 'Apr', 'May'] }, yaxis: { min: 90, max: 100, labels: { formatter: val => `${val.toFixed(1)}%` } }, stroke: { curve: 'smooth' } }; if(attendanceChart) attendanceChart.destroy(); document.querySelector("#spd-attendance-trend-chart").innerHTML = ''; attendanceChart = new ApexCharts(document.querySelector("#spd-attendance-trend-chart"), options); attendanceChart.render(); }; const renderDemographicsChart = () => { const byDemo = students.reduce((acc, s) => { acc[s.demographic] = (acc[s.demographic] || 0) + 1; return acc; }, {}); const options = { chart: { type: 'donut', height: 350 }, series: Object.values(byDemo), labels: Object.keys(byDemo), legend: { position: 'bottom' } }; if(demoChart) demoChart.destroy(); document.querySelector("#spd-demographics-chart").innerHTML = ''; demoChart = new ApexCharts(document.querySelector("#spd-demographics-chart"), options); demoChart.render(); }; const renderManageTable = () => { const tbody = document.getElementById('spd-roster-tbody'); tbody.innerHTML = ''; students.slice(0, 100).forEach(s => { // Show first 100 for performance tbody.innerHTML += `${s.id}${s.grade}${s.mathScore}%${s.elaScore}%${s.attendance}%`; }); }; // --- EVENT HANDLING --- const switchTab = (tabId) => { tabContents.forEach(c => c.style.display = 'none'); tabButtons.forEach(b => b.classList.remove('active')); const activeContent = document.getElementById(tabId); const activeButton = document.querySelector(`.spd-tab-button[data-tab="${tabId}"]`); if (activeContent && activeButton) { activeContent.style.display = 'block'; activeButton.classList.add('active'); } updateNavButtons(); }; const updateNavButtons = () => { const i = [...tabButtons].findIndex(b => b.classList.contains('active')); prevBtn.disabled = i === 0; nextBtn.disabled = i === tabButtons.length - 1; }; tabButtons.forEach(b => b.addEventListener('click', () => switchTab(b.dataset.tab))); nextBtn.addEventListener('click', () => { const i = [...tabButtons].findIndex(b=>b.classList.contains('active')); if (i < tabButtons.length - 1) switchTab(tabButtons[i+1].dataset.tab); }); prevBtn.addEventListener('click', () => { const i = [...tabButtons].findIndex(b=>b.classList.contains('active')); if (i > 0) switchTab(tabButtons[i-1].dataset.tab); }); document.getElementById('spd-student-form').addEventListener('submit', e => { e.preventDefault(); students.push({ id: 'SID' + (1500 + students.length), grade: parseInt(document.getElementById('spd-student-grade').value), mathScore: parseInt(document.getElementById('spd-student-math').value), elaScore: parseInt(document.getElementById('spd-student-ela').value), attendance: parseInt(document.getElementById('spd-student-attendance').value), demographic: document.getElementById('spd-student-demo').value, }); saveState(); renderAll(); e.target.reset(); }); document.getElementById('spd-roster-tbody').addEventListener('click', e => { if(e.target.tagName === 'BUTTON') { const id = e.target.dataset.id; if(confirm('Are you sure you want to delete this student record?')) { students = students.filter(s => s.id !== id); saveState(); renderAll(); } } }); // --- PDF EXPORT --- document.getElementById('spd-download-pdf-btn').addEventListener('click', function() { const btn = this; const activeTab = document.querySelector('.spd-tab-content.active'); const pdfCaptureAreaId = activeTab.querySelector('div[id^="spd-pdf-capture-"]').id; const content = document.getElementById(pdfCaptureAreaId); btn.textContent = 'Generating...'; btn.disabled = true; html2canvas(content, { scale: 2 }).then(canvas => { const doc = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); const imgData = canvas.toDataURL('image/png'); const pdfWidth = doc.internal.pageSize.getWidth(); const imgHeight = (canvas.height * pdfWidth) / canvas.width; doc.setFontSize(18); doc.text('School Performance Report', 14, 22); doc.addImage(imgData, 'PNG', 0, 30, pdfWidth, imgHeight); doc.save('School_Performance_Report.pdf'); }).finally(() => { btn.textContent = 'Download Current Tab as Report'; btn.disabled = false; }); }); // --- INITIALIZATION --- renderAll(); });
Scroll to Top