Welcome back, Admin. Here's what's happening today.

Total Students

1,245

12% vs last term

Teaching Staff

84

Fee Collection

$428K

85% Paid

Today's Attendance

94.2%

Attendance Trends

Upcoming Exams

FEB
12

Mathematics Final

Grade 10 � 09:00 AM

FEB
14

Physics Theory

Grade 12 � 10:30 AM

FEB
15

History Mid-term

Grade 8 � 01:00 PM

Recent Admissions

View All
Student Name ID Grade Guardian Status Date
John Smith John Smith
ST-2024-001 Grade 10-A Robert Smith Admitted Feb 09, 2026
Emily Adams Emily Adams
ST-2024-002 Grade 5-B Sarah Adams Pending Docs Feb 09, 2026
Michael King Michael King
ST-2024-003 Grade 8-C Diana King Admitted Feb 08, 2026

Manage detailed records of all students.

All Students

Name ID Grade Date of birth Guardian Phone Notes Status Actions

Teacher directory and assignments � manage staff records, subjects and allocations.

All Faculty

Name Employee ID Department Subjects Grades Phone Email Status Actions

Daily Attendance

Present: 0
Absent: 0
Late: 0
Total: 0
StudentIDGradeStatusNotes

Daily Report

Create and manage weekly class timetables. Drag lessons between slots, auto-generate, export or print.

Drag lessons to move; hold Ctrl or enable "Copy" to duplicate � conflicts highlighted in red

Schedule exams, manage rooms and generate hall tickets for students.

Exam Grade Date Time Room Max Marks Actions
Hall tickets - '+esc(ex.name)+''+styles+' ' + tickets + '<\/scr'+'ipt>\n<\/body><\/html>'); win.document.close(); setTimeout(()=> win.print(), 450); }catch(err){ alert('Failed to generate hall tickets: '+err.message); } } async function downloadHallTicketsPDF(examId){ const ex = load().find(x=> x.id === examId); if(!ex){ alert('Exam not found'); return; } try{ const studs = JSON.parse(localStorage.getItem('school_students_v1')||'[]').filter(s=> s.grade === ex.grade); if(!studs.length){ alert('No students found for ' + ex.grade); return; } // create temporary container const container = document.getElementById('exam-halltickets'); container.innerHTML = ''; studs.forEach((s,i)=>{ const el = document.createElement('div'); el.className = 'hall-ticket'; el.innerHTML = `

${esc(ex.name)}

${esc(s.name)} � ${esc(s.studentId||'')}
Grade: ${esc(s.grade)}
Date: ${esc(ex.date)}
Time: ${esc(ex.start)} ${ex.end?('– '+esc(ex.end)) : ''}
Room: ${esc(ex.room||'')}
Max Marks: ${ex.max||''}
Notes: ${esc(ex.notes||'')}
`; container.appendChild(el); }); // render whole container as canvas if(!window.html2canvas || !window.jspdf){ alert('PDF libraries not available'); return; } const canvas = await html2canvas(container, { scale:2, backgroundColor:'#ffffff' }); const imgData = canvas.toDataURL('image/png'); const pdf = new window.jspdf.jsPDF('portrait', 'pt', 'a4'); const pageW = pdf.internal.pageSize.getWidth(); const pageH = pdf.internal.pageSize.getHeight(); const ratio = canvas.width / canvas.height; const imgW = pageW - 40; const imgH = imgW / ratio; pdf.addImage(imgData, 'PNG', 20, 20, imgW, imgH); pdf.save((ex.grade+'_'+ex.name).replace(/\s+/g,'_') + '.pdf'); container.innerHTML = ''; }catch(err){ alert('PDF generation failed: '+err.message); } } // bootstrap modal interactions finished; init ensureGradeOptions(); render(); })();

Generate, print or email student report cards for any exam or term.

Student ID Grade Guardian Email Latest Marks
Report Cards'+styles+' ' + headerHtml; studs.forEach(s=>{ const res = findResult(examId, s.studentId); html += buildReportCardHTML(s, exam, res); }); html += '<\/scr'+'ipt>\n<\/body><\/html>'; w.document.open(); w.document.write(html); w.document.close(); setTimeout(()=> w.print(), 500); } function openEmailModal(examId, studentIds){ const exam = loadExams().find(e=> e.id === examId); const studs = loadStudents().filter(s=> studentIds.indexOf(String(s.studentId))>=0); if(!studs.length) return alert('No students selected'); rcEmailList.innerHTML = studs.map(s=>{ const email = (s.guardian && s.guardian.includes('@')) ? s.guardian : (s.studentId? s.studentId + '@parent.local' : 'parent@example.com'); return `
${esc(s.name)} � ${esc(email)}
`; }).join(''); rcEmailSummary.textContent = `Sending ${studs.length} emails for ${exam ? (exam.name + ' � ' + exam.date) : 'selected students' }`; rcEmailModal && rcEmailModal.show(); rcEmailSend.onclick = function(){ // simulate send const sent = loadSentRecord(); sent[examId || 'manual'] = sent[examId || 'manual'] || []; studs.forEach(s=> { if(!sent[examId || 'manual'].includes(s.studentId)) sent[examId || 'manual'].push(s.studentId); }); saveSentRecord(sent); rcEmailModal && rcEmailModal.hide(); alert('Emails queued (simulated) to ' + studs.length + ' parents'); } } // button handlers downloadPdfBtn && downloadPdfBtn.addEventListener('click', function(){ const examId = examSel.value; const ids = getSelectedStudentIds(); if(!examId) return alert('Pick an exam first'); if(!ids.length) return alert('Select at least one student'); generatePDFForStudents(examId, ids, (examSel.options[examSel.selectedIndex].text || 'reportcards')); }); exportCsvBtn && exportCsvBtn.addEventListener('click', function(){ const examId = examSel.value; const ids = getSelectedStudentIds(); if(!examId) return alert('Pick an exam first'); if(!ids.length) return alert('Select at least one student'); exportCSVForStudents(examId, ids); }); printLastBtn && printLastBtn.addEventListener('click', function(){ const grade = gradeSel.value; const ex = getLatestExamForGrade(grade); if(!ex) return alert('No exams found for this grade'); const studs = loadStudents().filter(s=> s.grade === grade).map(s=> s.studentId); printReportCards(ex.id, studs); }); emailParentsBtn && emailParentsBtn.addEventListener('click', function(){ const grade = gradeSel.value; const ex = getLatestExamForGrade(grade); if(!ex) return alert('No exams found for this grade'); const studs = loadStudents().filter(s=> s.grade === grade).map(s=> String(s.studentId)); openEmailModal(ex.id, studs); }); generateBtn && generateBtn.addEventListener('click', function(){ const examId = examSel.value; const ids = getSelectedStudentIds(); if(!examId) return alert('Select exam'); const students = ids.length ? ids : loadStudents().filter(s=> s.grade === gradeSel.value).map(s=> String(s.studentId)); generatePDFForStudents(examId, students, 'batch_reportcards'); }); generateSelectedBtn && generateSelectedBtn.addEventListener('click', function(){ const examId = examSel.value; const ids = getSelectedStudentIds(); if(!examId) return alert('Select exam'); if(!ids.length) return alert('Select students'); generatePDFForStudents(examId, ids); }); printSelectedBtn && printSelectedBtn.addEventListener('click', function(){ const examId = examSel.value; const ids = getSelectedStudentIds(); if(!examId) return alert('Select exam'); if(!ids.length) return alert('Select students'); printReportCards(examId, ids); }); emailSelectedBtn && emailSelectedBtn.addEventListener('click', function(){ const examId = examSel.value; const ids = getSelectedStudentIds(); if(!ids.length) return alert('Select students'); openEmailModal(examId || 'manual', ids); }); // Transcript functions const rcTranscriptModalEl = document.getElementById('rcTranscriptModal'); const rcTranscriptModal = (window.bootstrap && rcTranscriptModalEl) ? new bootstrap.Modal(rcTranscriptModalEl) : null; const transcriptTableBody = document.querySelector('#transcript-table tbody'); const transcriptStudentInfo = document.getElementById('transcript-student-info'); const transcriptSummary = document.getElementById('transcript-summary'); const transcriptPdfBtn = document.getElementById('transcript-download-pdf'); const transcriptCsvBtn = document.getElementById('transcript-export-csv'); function gradeFromPctLocal(p){ if(p >= 90) return 'A'; if(p >= 80) return 'B'; if(p >= 70) return 'C'; if(p >= 60) return 'D'; return 'F'; } function computeTranscript(studentId, yearLabel){ const exams = loadExams().filter(e=> { if(yearLabel && yearLabel !== '') return getAcademicYearLabel(e.date) === yearLabel; return true; }).sort((a,b)=> (a.date||'').localeCompare(b.date||'')); const resultsMap = loadResults(); const rows = []; let totalObt = 0, totalMax = 0, countWithMarks = 0; exams.forEach(ex=>{ const arr = resultsMap[ex.id] || []; const r = arr.find(rr=> String(rr.studentId) === String(studentId)); if(r && (r.marks !== null && r.marks !== undefined)){ const marks = Number(r.marks); const max = Number(ex.max||100); const pct = max ? (marks / max * 100) : 0; rows.push({ exam: ex.name, date: ex.date, marks, max, pct, letter: gradeFromPctLocal(pct), remarks: r.remarks || '' }); totalObt += marks; totalMax += max; countWithMarks++; } else { rows.push({ exam: ex.name, date: ex.date, marks: null, max: ex.max||100, pct: null, letter: '-', remarks: '' }); } }); const overallPct = totalMax > 0 ? (totalObt / totalMax * 100) : 0; const overallLetter = gradeFromPctLocal(overallPct); return { rows, summary: { totalObt, totalMax, overallPct, overallLetter, examsCount: exams.length, gradedCount: countWithMarks } }; } function openTranscriptModalFor(studentId){ const studs = loadStudents(); const s = studs.find(x=> String(x.studentId) === String(studentId)); if(!s) return alert('Student not found'); const yearFilter = (document.getElementById('transcript-year') && document.getElementById('transcript-year').value) || ''; const data = computeTranscript(studentId, yearFilter); if(rcTranscriptModalEl) rcTranscriptModalEl.dataset.currentStudent = String(studentId); const tLogoImg = document.getElementById('transcript-logo-img'); const gLogo = (typeof getGlobalLogo === 'function' ? getGlobalLogo() : (localStorage.getItem('elinom_erp_logo_v1') || null)); if(tLogoImg){ if(gLogo){ tLogoImg.src = gLogo; tLogoImg.parentElement.style.display = 'block'; } else { tLogoImg.src = ''; tLogoImg.parentElement.style.display = 'none'; } } transcriptStudentInfo.innerHTML = `${esc(s.name)} � ${esc(s.studentId)}   ${esc(s.grade||'')}`; transcriptTableBody.innerHTML = data.rows.map(r=> `${esc(r.exam)}${esc(r.date||'')}${r.marks !== null ? r.marks : '-'}${r.max}${r.pct !== null ? r.pct.toFixed(1)+'%' : '-'}${esc(r.letter)}${esc(r.remarks||'')}`).join(''); transcriptSummary.textContent = `Total: ${data.summary.totalObt} / ${data.summary.totalMax} � ${data.summary.overallPct.toFixed(1)}% (${data.summary.overallLetter}) � ${data.summary.gradedCount}/${data.summary.examsCount} exams graded`; transcriptPdfBtn.onclick = async function(){ if(!window.html2canvas || !window.jspdf) return alert('PDF libraries not available'); const container = rcTranscriptModalEl.querySelector('.modal-body'); const canvas = await html2canvas(container, { scale: 2, backgroundColor: '#ffffff' }); const imgData = canvas.toDataURL('image/png'); const pdf = new window.jspdf.jsPDF('portrait','pt','a4'); const pageW = pdf.internal.pageSize.getWidth(); const ratio = canvas.width / canvas.height; const imgW = pageW - 40; const imgH = imgW / ratio; pdf.addImage(imgData, 'PNG', 20, 20, imgW, imgH); pdf.save((s.studentId + '_transcript').replace(/\s+/g,'_') + '.pdf'); }; transcriptCsvBtn.onclick = function(){ const rows = [['Exam','Date','Marks','Max','Percentage','Letter','Remarks']]; data.rows.forEach(r=> rows.push([r.exam,r.date, r.marks!==null? r.marks:'', r.max, r.pct!==null? r.pct.toFixed(1):'', r.letter, r.remarks||''])); const csv = rows.map(r=> r.map(c=> '"'+String(c||'').replace(/"/g,'""')+'"').join(',')).join('\n'); const blob = new Blob([csv], { type:'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = (s.studentId + '_transcript.csv').replace(/\s+/g,'_'); document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); }; rcTranscriptModal && rcTranscriptModal.show(); } document.addEventListener('click', function(e){ const t = e.target.closest('.rc-transcript-row'); if(t){ const sid = t.dataset.studentid; openTranscriptModalFor(sid); } }); const rcTranscriptSelectedBtn = document.getElementById('rc-transcript-selected'); rcTranscriptSelectedBtn && rcTranscriptSelectedBtn.addEventListener('click', function(){ const ids = getSelectedStudentIds(); if(!ids.length) return alert('Select students to generate transcripts'); (async function(){ const container = document.createElement('div'); container.style.padding='18px'; container.style.background='#fff'; const gLogo = (typeof getGlobalLogo === 'function' ? getGlobalLogo() : (localStorage.getItem('elinom_erp_logo_v1') || null)); if(gLogo) container.innerHTML = `
Transcripts
`; ids.forEach(stid=>{ const s = loadStudents().find(x=> String(x.studentId) === String(stid)); const yearFilter = (document.getElementById('rc-year') && document.getElementById('rc-year').value) || ''; const data = computeTranscript(stid, yearFilter); const html = `

${esc(s.name)} � ${esc(s.studentId)}

${data.summary.overallPct.toFixed(1)}% (${data.summary.overallLetter}) � ${data.summary.gradedCount}/${data.summary.examsCount} exams
` + data.rows.map(r=> `
${esc(r.exam)} � ${r.marks!==null?r.marks:'-'} / ${r.max} � ${r.pct!==null? r.pct.toFixed(1)+'%':''} � ${esc(r.letter)}
`).join('') + `
`; container.innerHTML += html; }); document.body.appendChild(container); try{ const canvas = await html2canvas(container, { scale:2, backgroundColor:'#ffffff' }); const img = canvas.toDataURL('image/png'); const pdf = new window.jspdf.jsPDF('portrait','pt','a4'); const pageW = pdf.internal.pageSize.getWidth(); const ratio = canvas.width / canvas.height; const imgW = pageW - 40; const imgH = imgW / ratio; pdf.addImage(img, 'PNG', 20, 20, imgW, imgH); pdf.save('transcripts_batch.pdf'); }catch(err){ alert('PDF generation failed: '+ err.message); } finally{ container.remove(); } })(); }); // init ensureGradeOptions(); populateYearOptions(); populateExamOptions(); renderList(); ensureGradeOptions(); populateExamOptions(); renderList(); })();

Track tuition fees, payments and outstanding balances per student.

$

Collected

$0

Outstanding

$0

Click student Actions ? Record payment or View history
StudentIDGradeAssigned FeePaidBalanceLast PaymentActions

Manage parent / guardian contacts, link to students, and keep communication logs.

Select a parent to view logs or send messages
NameRelationEmailPhoneLinked StudentsLast ContactActions

Parent Portal

Manage parent portal accounts, link children, and monitor parent engagement.

0
Total Accounts
0
Active Parents
0
Linked Children
0
Unread Messages
Portal Accounts
Parent Name Email Phone Linked Children Last Seen Status Actions
Link Child to Parent
School Announcements

No announcements yet.

Parent Messages

No messages.

Consent Requests

No consent requests.

Shared Documents

No shared documents.

Behaviour Reports

Behaviour logs marked as parent-visible will appear in connected parents' dashboards.

No behaviour reports.

School Settings

Manage your school's configuration, branding and preferences.

School Profile

Academic Year

Grading System

Fee & Currency

Notifications

Access & Roles

Data & Storage