👨‍🏫 Nexora Teacher Panel

Secure School OS teacher dashboard for assigned classes, attendance, online tests, notes, messages, class links, results and support.

🔐 Teacher Login

Login with Gmail first, then enter the Secret Teacher ID generated by School Manager. No demo login and no localStorage is used.
Gmail login required.
Only School Manager approved teachers can open this dashboard.

👤 My Teacher Profile

👨‍🎓 Assigned Students

These students are loaded from Firestore based on your assigned school, class and section.

📅 Daily Attendance

Select attendance status for each assigned student and submit daily attendance report.

📝 Tests / Class Tasks

📚 Notes / Homework / Assignments

💻 Online Class Link

📢 Messages

Send Class Message

🛠 Teacher Support

function renderProfile(){ if(!requireTeacher()) return; el("profileBox").innerHTML = `

${esc(currentTeacher.teacherName)}

School: ${esc(currentTeacher.schoolName)}

Teacher Gmail: ${esc(currentTeacher.teacherEmail)}

Contact: ${esc(currentTeacher.teacherContact || "-")}

Assigned Class: ${esc(currentTeacher.className)}

Section: ${esc(currentTeacher.section || "-")}

Subject: ${esc(currentTeacher.subject)}

Status: ${esc(currentTeacher.status)}

Teacher ID: ${esc(currentTeacher.teacherId)}

`; } window.loadAssignedStudents = async function(){ if(!requireTeacher()) return; const q = query( collection(db,"schoolStudentAccess"), where("schoolId","==",currentTeacher.schoolId), where("className","==",currentTeacher.className), where("status","==","Active") ); const snap = await getDocs(q); assignedStudents = []; snap.forEach((item)=>{ const data = item.data(); if(!currentTeacher.section || !data.section || data.section === currentTeacher.section){ assignedStudents.push({ id:item.id, ...data }); } }); assignedStudents.sort((a,b)=>String(a.studentName || "").localeCompare(String(b.studentName || ""))); const box = el("studentsListBox"); if(assignedStudents.length === 0){ box.innerHTML = `
No assigned students found for your class.
`; return; } let html = `
`; assignedStudents.forEach((s)=>{ html += ` `; }); html += `
Name Father Reg No Gender Class Section Parent Contact
${esc(s.studentName)} ${esc(s.fatherName)} ${esc(s.regNo)} ${esc(s.gender)} ${esc(s.className)} ${esc(s.section || "-")} ${esc(s.parentContact || "-")}
`; box.innerHTML = html; }; window.loadAttendanceStudents = async function(){ if(!requireTeacher()) return; await loadAssignedStudents(); const box = el("attendanceStudentsBox"); if(assignedStudents.length === 0){ box.innerHTML = `
No students found for attendance.
`; return; } let html = ""; assignedStudents.forEach((s)=>{ html += `
${esc(s.studentName)}
Reg No: ${esc(s.regNo)} | ${esc(s.gender || "-")}
`; }); box.innerHTML = html; }; window.submitDailyAttendance = async function(){ if(!requireTeacher()) return; const dateValue = el("attendanceDate").value; if(!dateValue){ alert("Attendance date is required."); return; } if(assignedStudents.length === 0){ await loadAssignedStudents(); } if(assignedStudents.length === 0){ alert("No students found."); return; } const attendanceItems = []; let presentCount = 0; let absentCount = 0; let leaveCount = 0; let boysPresent = 0; let girlsPresent = 0; assignedStudents.forEach((s)=>{ const status = el("att_" + s.id)?.value || "Present"; const note = el("attNote_" + s.id)?.value || ""; if(status === "Present"){ presentCount++; if(s.gender === "Boy") boysPresent++; if(s.gender === "Girl") girlsPresent++; } if(status === "Absent") absentCount++; if(status === "Leave") leaveCount++; attendanceItems.push({ studentDocId:s.id, studentId:s.studentId || "", studentName:s.studentName || "", regNo:s.regNo || "", gender:s.gender || "", status, note }); }); await addDoc(collection(db,"schoolDailyAttendance"),{ attendanceId:makeId("ATT"), schoolId:currentTeacher.schoolId, schoolName:currentTeacher.schoolName, teacherId:currentTeacher.teacherId, teacherName:currentTeacher.teacherName, teacherEmail:currentTeacher.teacherEmail, className:currentTeacher.className, section:currentTeacher.section || "", subject:currentTeacher.subject, attendanceDate:dateValue, students:attendanceItems, presentCount, absentCount, leaveCount, boysPresent, girlsPresent, totalStudents:assignedStudents.length, createdAt:Date.now(), updatedAt:serverTimestamp() }); alert("Daily attendance submitted ✅"); await loadAttendanceHistory(); await refreshAll(); }; async function loadAttendanceHistory(){ if(!requireTeacher()) return; const rows = await getTeacherRows("schoolDailyAttendance","createdAt"); const box = el("attendanceHistoryBox"); if(rows.length === 0){ box.innerHTML = `
No attendance history found.
`; return; } let html = `

Attendance History

`; rows.slice(0,20).forEach((a)=>{ html += `

${esc(a.attendanceDate || formatDate(a.createdAt))}

Class: ${esc(a.className)} ${esc(a.section || "")}

Total: ${esc(a.totalStudents || 0)}

Present: ${esc(a.presentCount || 0)}

Absent: ${esc(a.absentCount || 0)}

Leave: ${esc(a.leaveCount || 0)}

Boys Present: ${esc(a.boysPresent || 0)}

Girls Present: ${esc(a.girlsPresent || 0)}

`; }); box.innerHTML = html; } async function countTeacherRows(collectionName){ try{ const rows = await getTeacherRows(collectionName); return rows.length; }catch(error){ return 0; } } window.refreshAll = async function(){ if(!requireTeacher()) return; await loadAssignedStudents(); const attendance = await countTeacherRows("schoolDailyAttendance"); const tests = await countTeacherRows("schoolClassTasks"); const schoolMessages = await getClassRows("schoolMessages","createdAt"); const teacherMessages = await getTeacherRows("schoolTeacherMessages","createdAt"); el("assignedStudentCount").textContent = assignedStudents.length; el("teacherAttendanceCount").textContent = attendance; el("teacherTestCount").textContent = tests; el("teacherMessageCount").textContent = schoolMessages.length + teacherMessages.length; }; window.createTeacherTest = async function(){ if(!requireTeacher()) return; const test = { testId:makeId("TTEST"), schoolId:currentTeacher.schoolId, schoolName:currentTeacher.schoolName, teacherId:currentTeacher.teacherId, teacherName:currentTeacher.teacherName, teacherEmail:currentTeacher.teacherEmail, className:currentTeacher.className, section:currentTeacher.section || "", subject:currentTeacher.subject, title:el("testTitle").value.trim(), chapter:el("testChapter").value.trim(), testCode:el("testCode").value.trim(), questions:el("testQuestions").value.trim(), status:"Active", createdBy:currentUser.email, createdAt:Date.now(), updatedAt:serverTimestamp() }; if(!test.title || !test.testCode || !test.questions){ alert("Test title, test code and questions are required."); return; } await addDoc(collection(db,"schoolClassTasks"),test); el("testTitle").value = ""; el("testChapter").value = ""; el("testCode").value = ""; el("testQuestions").value = ""; alert("Test / task created successfully ✅"); await loadTeacherTests(); await refreshAll(); }; window.loadTeacherTests = async function(){ if(!requireTeacher()) return; const tests = await getTeacherRows("schoolClassTasks","createdAt"); const box = el("testsListBox"); if(tests.length === 0){ box.innerHTML = `
No tests or class tasks found.
`; return; } let html = ""; tests.forEach((t)=>{ html += `

${esc(t.title)}

Class: ${esc(t.className)} ${esc(t.section || "")}

Subject: ${esc(t.subject)}

Chapter: ${esc(t.chapter || "-")}

Test Code: ${esc(t.testCode)}

Status: ${esc(t.status)}

Questions / Instructions:

${esc(t.questions)}

Date: ${formatDate(t.createdAt)}

`; }); box.innerHTML = html; }; window.toggleTeacherTestStatus = async function(id,status){ if(!requireTeacher()) return; const newStatus = status === "Active" ? "Disabled" : "Active"; await updateDoc(doc(db,"schoolClassTasks",id),{ status:newStatus, updatedBy:currentUser.email, updatedAt:serverTimestamp() }); alert("Test status updated ✅"); await loadTeacherTests(); }; window.deleteTeacherTest = async function(id){ if(!requireTeacher()) return; if(!confirm("Delete this test / task?")){ return; } await deleteDoc(doc(db,"schoolClassTasks",id)); alert("Test deleted ✅"); await loadTeacherTests(); await refreshAll(); }; window.sendClassNote = async function(){ if(!requireTeacher()) return; const note = { noteId:makeId("NOTE"), schoolId:currentTeacher.schoolId, schoolName:currentTeacher.schoolName, teacherId:currentTeacher.teacherId, teacherName:currentTeacher.teacherName, teacherEmail:currentTeacher.teacherEmail, className:currentTeacher.className, section:currentTeacher.section || "", subject:currentTeacher.subject, title:el("noteTitle").value.trim(), type:el("noteType").value, details:el("noteDetails").value.trim(), status:"Active", createdBy:currentUser.email, createdAt:Date.now(), updatedAt:serverTimestamp() }; if(!note.title || !note.details){ alert("Note title and details are required."); return; } await addDoc(collection(db,"schoolClassNotes"),note); el("noteTitle").value = ""; el("noteDetails").value = ""; el("noteType").value = "Homework"; alert("Class note sent ✅"); await loadClassNotes(); }; window.loadClassNotes = async function(){ if(!requireTeacher()) return; const notes = await getTeacherRows("schoolClassNotes","createdAt"); const box = el("notesListBox"); if(notes.length === 0){ box.innerHTML = `
No class notes found.
`; return; } let html = ""; notes.forEach((n)=>{ html += `

${esc(n.title)}

Type: ${esc(n.type)}

Class: ${esc(n.className)} ${esc(n.section || "")}

Subject: ${esc(n.subject)}

${esc(n.details)}

Status: ${esc(n.status)}

Date: ${formatDate(n.createdAt)}

`; }); box.innerHTML = html; }; window.toggleClassNoteStatus = async function(id,status){ if(!requireTeacher()) return; const newStatus = status === "Active" ? "Disabled" : "Active"; await updateDoc(doc(db,"schoolClassNotes",id),{ status:newStatus, updatedBy:currentUser.email, updatedAt:serverTimestamp() }); alert("Note status updated ✅"); await loadClassNotes(); }; window.deleteClassNote = async function(id){ if(!requireTeacher()) return; if(!confirm("Delete this note?")){ return; } await deleteDoc(doc(db,"schoolClassNotes",id)); alert("Note deleted ✅"); await loadClassNotes(); }; window.createOnlineClassLink = async function(){ if(!requireTeacher()) return; const onlineClass = { classLinkId:makeId("ONLINE"), schoolId:currentTeacher.schoolId, schoolName:currentTeacher.schoolName, teacherId:currentTeacher.teacherId, teacherName:currentTeacher.teacherName, teacherEmail:currentTeacher.teacherEmail, className:currentTeacher.className, section:currentTeacher.section || "", subject:currentTeacher.subject, title:el("onlineTitle").value.trim(), platform:el("onlinePlatform").value, dateTime:el("onlineDateTime").value.trim(), classLink:el("onlineLink").value.trim(), instructions:el("onlineInstructions").value.trim(), status:"Active", createdBy:currentUser.email, createdAt:Date.now(), updatedAt:serverTimestamp() }; if(!onlineClass.title || !onlineClass.dateTime || !onlineClass.classLink){ alert("Class title, date/time and class link are required."); return; } await addDoc(collection(db,"schoolOnlineClasses"),onlineClass); el("onlineTitle").value = ""; el("onlineDateTime").value = ""; el("onlineLink").value = ""; el("onlineInstructions").value = ""; el("onlinePlatform").value = "Google Meet"; alert("Online class notice created ✅"); await loadOnlineClassLinks(); }; window.loadOnlineClassLinks = async function(){ if(!requireTeacher()) return; const classes = await getTeacherRows("schoolOnlineClasses","createdAt"); const box = el("onlineClassListBox"); if(classes.length === 0){ box.innerHTML = `
No online class links found.
`; return; } let html = ""; classes.forEach((c)=>{ html += `

${esc(c.title)}

Platform: ${esc(c.platform)}

Class: ${esc(c.className)} ${esc(c.section || "")}

Subject: ${esc(c.subject)}

Date/Time: ${esc(c.dateTime)}

Link: ${esc(c.classLink)}

Instructions:

${esc(c.instructions || "-")}

Status: ${esc(c.status)}

`; }); box.innerHTML = html; }; window.toggleOnlineClassStatus = async function(id,status){ if(!requireTeacher()) return; const newStatus = status === "Active" ? "Disabled" : "Active"; await updateDoc(doc(db,"schoolOnlineClasses",id),{ status:newStatus, updatedBy:currentUser.email, updatedAt:serverTimestamp() }); alert("Online class status updated ✅"); await loadOnlineClassLinks(); }; window.deleteOnlineClass = async function(id){ if(!requireTeacher()) return; if(!confirm("Delete this online class link?")){ return; } await deleteDoc(doc(db,"schoolOnlineClasses",id)); alert("Online class link deleted ✅"); await loadOnlineClassLinks(); }; window.sendTeacherMessage = async function(){ if(!requireTeacher()) return; const message = { messageId:makeId("TMSG"), schoolId:currentTeacher.schoolId, schoolName:currentTeacher.schoolName, teacherId:currentTeacher.teacherId, teacherName:currentTeacher.teacherName, teacherEmail:currentTeacher.teacherEmail, className:currentTeacher.className, section:currentTeacher.section || "", subject:currentTeacher.subject, title:el("messageTitle").value.trim(), body:el("messageBody").value.trim(), target:"students", status:"Active", createdBy:currentUser.email, createdAt:Date.now(), updatedAt:serverTimestamp() }; if(!message.title || !message.body){ alert("Message title and body are required."); return; } await addDoc(collection(db,"schoolTeacherMessages"),message); el("messageTitle").value = ""; el("messageBody").value = ""; alert("Class message sent ✅"); await loadMessages(); await refreshAll(); }; window.loadMessages = async function(){ if(!requireTeacher()) return; const schoolMessages = await getClassRows("schoolMessages","createdAt"); const teacherMessages = await getTeacherRows("schoolTeacherMessages","createdAt"); let allMessages = []; schoolMessages.forEach((m)=>allMessages.push({...m,source:"School Manager"})); teacherMessages.forEach((m)=>allMessages.push({...m,source:"Teacher"})); allMessages.sort((a,b)=>Number(b.createdAt || 0) - Number(a.createdAt || 0)); const box = el("messagesListBox"); if(allMessages.length === 0){ box.innerHTML = `
No messages found.
`; return; } let html = ""; allMessages.forEach((m)=>{ html += `

${esc(m.title)}

Source: ${esc(m.source)}

Target: ${esc(m.target || "-")}

${esc(m.body)}

Status: ${esc(m.status || "Active")}

Date: ${formatDate(m.createdAt)}

`; }); box.innerHTML = html; }; window.submitTeacherSupport = async function(){ if(!requireTeacher()) return; const title = el("supportTitle").value.trim(); const details = el("supportDetails").value.trim(); if(!title || !details){ alert("Support title and details are required."); return; } await addDoc(collection(db,"schoolSupportTickets"),{ ticketId:makeId("TSUP"), schoolId:currentTeacher.schoolId, schoolName:currentTeacher.schoolName, teacherId:currentTeacher.teacherId, teacherName:currentTeacher.teacherName, teacherEmail:currentTeacher.teacherEmail, title, details, source:"Teacher Panel", status:"Open", createdBy:currentUser.email, createdAt:Date.now(), updatedAt:serverTimestamp() }); el("supportTitle").value = ""; el("supportDetails").value = ""; alert("Support ticket submitted ✅"); await loadTeacherSupport(); }; window.loadTeacherSupport = async function(){ if(!requireTeacher()) return; const q = query( collection(db,"schoolSupportTickets"), where("schoolId","==",currentTeacher.schoolId), where("teacherId","==",currentTeacher.teacherId) ); const snap = await getDocs(q); let tickets = []; snap.forEach((item)=>{ tickets.push({ id:item.id, ...item.data() }); }); tickets.sort((a,b)=>Number(b.createdAt || 0) - Number(a.createdAt || 0)); const box = el("supportListBox"); if(tickets.length === 0){ box.innerHTML = `
No support ticket found.
`; return; } let html = ""; tickets.forEach((s)=>{ html += `

${esc(s.title)}

${esc(s.details)}

Status: ${esc(s.status || "Open")}

Reply: ${esc(s.reply || "No reply yet")}

Date: ${formatDate(s.createdAt)}

`; }); box.innerHTML = html; }; window.downloadTeacherBackup = async function(){ if(!requireTeacher()) return; const backup = { teacher:currentTeacher, students:assignedStudents, data:{}, exportedAt:new Date().toLocaleString() }; const teacherCollections = [ "schoolDailyAttendance", "schoolClassTasks", "schoolClassNotes", "schoolOnlineClasses", "schoolTeacherMessages", "schoolSupportTickets" ]; for(const colName of teacherCollections){ let rows = []; try{ const q = query( collection(db,colName), where("schoolId","==",currentTeacher.schoolId), where("teacherId","==",currentTeacher.teacherId) ); const snap = await getDocs(q); snap.forEach((item)=>{ rows.push({ id:item.id, ...item.data() }); }); }catch(error){ console.warn("Backup collection failed:",colName,error); } backup.data[colName] = rows; } downloadTextFile( "teacher-panel-backup.json", JSON.stringify(backup,null,2) ); };