🏫
School ERP Login
Secure Cloud Access
Encrypted & stored in Google Cloud
Sara Azam Memorial Education
Dashboard
🔍
P
Good morning, Principal 🙏
Sara Azam Memorial Education
📅 Session:
● LIVE
👨‍🎓
STUDENTS
0
Total Students
👨‍🏫
STAFF
0
Teaching Staff
📊
SCORE
Average Score
💳
FEES
Fee Collection
Fee Status
0
✓ PAID
0
✗ DUE
💰 TODAY COLLECTION
₹0
Categories
No data yet.
Examination Schedule
'; var win = window.open('', '_blank', 'width=900,height=700'); if(!win){ showToast("Popup blocked! Browser settings mein allow karo","error"); return; } win.document.write(fullHtml); win.document.close(); win.focus(); if(autoprint) setTimeout(function(){ win.print(); }, 400); } // ── EDIT STUDENT ── var editingStudentId = null; function openEditStudent(id) { var s = students.find(function(x){ return x.id===id; }); if(!s) return; editingStudentId = id; // Subtitle var sub = document.getElementById("edit-student-subtitle"); if(sub) sub.textContent = s.name + " · " + displayClass(s.class); // Fill fields var setVal = function(id, val){ var el=document.getElementById(id); if(el) el.value=val||""; }; setVal("edit-name", s.name); // Admission status var admOpen = document.getElementById("edit-adm-open"); var admClosed = document.getElementById("edit-adm-closed"); if(admOpen && admClosed) { admOpen.checked = !s.admissionClosed; admClosed.checked = !!s.admissionClosed; highlightAdmStatus(); } setVal("edit-dob", s.dob); setVal("edit-gender", s.gender); setVal("edit-category", s.category); setVal("edit-father", s.father); setVal("edit-mother", s.mother); setVal("edit-phone", s.phone); setVal("edit-addr1", s.addr1); setVal("edit-city", s.city); setVal("edit-dist", s.dist); setVal("edit-state", s.state); setVal("edit-pin", s.pin); // Bus radio var busYes = document.getElementById("edit-bus-yes"); var editFareEl = document.getElementById("edit-bus-fare"); var editRouteEl = document.getElementById("edit-bus-route"); var busNo = document.getElementById("edit-bus-no"); // Fill bus fare fields var editBusFare = document.getElementById("edit-bus-fare"); var editBusRoute = document.getElementById("edit-bus-route"); if(editBusFare) editBusFare.value = s.busFare || ""; if(editBusRoute) editBusRoute.value = s.busRoute || ""; // Show/hide bus fare box based on current bus setting setTimeout(editHighlightBus, 50); if(busYes && busNo) { busYes.checked = s.bus === "Yes"; busNo.checked = s.bus !== "Yes"; editHighlightBus(); } // Photo var img = document.getElementById("edit-photo-img"); var icon = document.getElementById("edit-photo-icon"); var txt = document.getElementById("edit-photo-txt"); var prev = document.getElementById("edit-photo-preview"); if(img && s.photo) { img.src = s.photo; img.style.display = "block"; if(icon) icon.style.display = "none"; if(txt) txt.style.display = "none"; if(prev) { prev.style.border = "2px solid #BBF7D0"; prev.style.background = "#F0FDF4"; } } else if(img) { img.src = ""; img.style.display = "none"; if(icon) icon.style.display = "block"; if(txt) txt.style.display = "block"; if(prev) { prev.style.border = "2px dashed #DDE3EC"; prev.style.background = "#F8FAFC"; } } // Clear PIN status var ps = document.getElementById("edit-pin-status"); if(ps) ps.textContent = ""; // Close detail modal, open edit modal closeModal("student-detail"); g("modal-edit-student").classList.remove("hidden"); } function highlightAdmStatus() { var openEl = document.getElementById("edit-adm-open"); var closedEl = document.getElementById("edit-adm-closed"); var openLbl = document.getElementById("edit-adm-open-lbl"); var closedLbl= document.getElementById("edit-adm-closed-lbl"); if(!openEl || !closedEl) return; if(openEl.checked) { if(openLbl) { openLbl.style.border="2px solid #22C55E"; openLbl.style.background="#F0FDF4"; openLbl.style.color="#15803D"; } if(closedLbl){ closedLbl.style.border="2px solid #DDE3EC"; closedLbl.style.background="#fff"; closedLbl.style.color="#374151"; } } else { if(closedLbl){ closedLbl.style.border="2px solid #DC2626"; closedLbl.style.background="#FEF2F2"; closedLbl.style.color="#DC2626"; } if(openLbl) { openLbl.style.border="2px solid #DDE3EC"; openLbl.style.background="#fff"; openLbl.style.color="#374151"; } } } function editHighlightBus() { var yes = document.getElementById("edit-bus-yes"); var yLbl = document.getElementById("edit-bus-yes-lbl"); var nLbl = document.getElementById("edit-bus-no-lbl"); var fareBox = document.getElementById("edit-bus-fare-box"); if(!yes) return; var isYes = yes.checked; if(isYes) { if(yLbl) { yLbl.style.border="2px solid #15803D"; yLbl.style.background="#F0FDF4"; yLbl.style.color="#15803D"; } if(nLbl) { nLbl.style.border="2px solid #DDE3EC"; nLbl.style.background="#fff"; nLbl.style.color="#374151"; } if(fareBox) fareBox.style.display="block"; // Focus on fare input setTimeout(function(){ var f=document.getElementById("edit-bus-fare"); if(f&&!f.value) f.focus(); },100); } else { if(nLbl) { nLbl.style.border="2px solid #DC2626"; nLbl.style.background="#FEF2F2"; nLbl.style.color="#DC2626"; } if(yLbl) { yLbl.style.border="2px solid #DDE3EC"; yLbl.style.background="#fff"; yLbl.style.color="#374151"; } if(fareBox) fareBox.style.display="none"; } } function editPreviewPhoto(input) { if(!input||!input.files||!input.files[0]) return; var reader = new FileReader(); reader.onload = function(e) { var img = document.getElementById("edit-photo-img"); var icon = document.getElementById("edit-photo-icon"); var txt = document.getElementById("edit-photo-txt"); var prev = document.getElementById("edit-photo-preview"); if(img) { img.src=e.target.result; img.style.display="block"; } if(icon) icon.style.display = "none"; if(txt) txt.style.display = "none"; if(prev) { prev.style.border="2px solid #BBF7D0"; prev.style.background="#F0FDF4"; } }; reader.readAsDataURL(input.files[0]); } function editLookupPin(pin) { if(!pin || pin.length !== 6) return; var st = document.getElementById("edit-pin-status"); if(st) { st.textContent="⏳"; st.style.color="#B45309"; } fetch("https://api.postalpincode.in/pincode/"+pin) .then(function(r){ return r.json(); }) .then(function(data){ if(data&&data[0]&&data[0].Status==="Success"&&data[0].PostOffice&&data[0].PostOffice.length) { var po=data[0].PostOffice[0]; var distEl=document.getElementById("edit-dist"); if(distEl) distEl.value=po.District||""; var stEl=document.getElementById("edit-state"); if(stEl) stEl.value=po.State||""; if(st) { st.textContent="✓"; st.style.color="#15803D"; } } else { if(st) { st.textContent="✗"; st.style.color="#B91C1C"; } } }).catch(function(){ if(st) { st.textContent="✗"; st.style.color="#B91C1C"; } }); } function saveEditStudent() { var s = students.find(function(x){ return x.id===editingStudentId; }); if(!s) return; var name = (document.getElementById("edit-name")||{}).value||""; var city = (document.getElementById("edit-city")||{}).value||""; if(!name.trim()){ showToast("Name is required","error"); return; } if(!city.trim()){ showToast("Village/City is required","error"); return; } var getVal = function(id){ var el=document.getElementById(id); return el?el.value.trim():""; }; s.name = getVal("edit-name"); s.dob = getVal("edit-dob"); s.gender = getVal("edit-gender"); s.category= getVal("edit-category"); s.father = getVal("edit-father"); s.mother = getVal("edit-mother"); s.phone = getVal("edit-phone"); s.addr1 = getVal("edit-addr1"); s.city = getVal("edit-city"); s.dist = getVal("edit-dist"); s.state = getVal("edit-state"); s.pin = getVal("edit-pin"); var busYes = document.getElementById("edit-bus-yes"); s.bus = (busYes && busYes.checked) ? "Yes" : "No"; // Save bus fare from edit form var busFareEl = document.getElementById("edit-bus-fare"); var busRouteEl = document.getElementById("edit-bus-route"); if(s.bus === "Yes") { s.busFare = busFareEl ? (parseInt(busFareEl.value) || 0) : (s.busFare || 0); s.busRoute = busRouteEl ? (busRouteEl.value.trim()) : (s.busRoute || ""); } else { s.busFare = 0; s.busRoute = ""; } // Admission status save var admClosed = document.getElementById("edit-adm-closed"); s.admissionClosed = !!(admClosed && admClosed.checked); // Photo var img = document.getElementById("edit-photo-img"); if(img && img.src && img.src.startsWith("data:")) s.photo = img.src; saveData(); closeModal("edit-student"); showToast(s.name + " — Profile updated!"); renderStudents(); updateDash(); // Set current month/year in all-attendance selectors var nowD=new Date(); var sessionM=nowD.getMonth()>=3?(nowD.getMonth()-3):(nowD.getMonth()+9); var allMon=document.getElementById("all-att-month"); var allYr=document.getElementById("all-att-year"); if(allMon) allMon.value=String(sessionM); if(allYr) allYr.value=String(nowD.getFullYear()); // Re-open student detail setTimeout(function(){ openStudentDetail(editingStudentId); }, 300); } // ── SIDEBAR TOGGLE (mobile) ── function toggleSidebar() { var sb = g("sidebar"); var overlay = g("sidebar-overlay"); if(!sb) return; var isOpen = sb.classList.contains("open"); if(isOpen) { sb.classList.remove("open"); sb.style.left = "-240px"; if(overlay) overlay.style.display = "none"; } else { sb.classList.add("open"); sb.style.left = "0"; if(overlay) overlay.style.display = "block"; } } function closeSidebar() { var sb = g("sidebar"); var overlay = g("sidebar-overlay"); if(sb) { sb.classList.remove("open"); sb.style.left = "-240px"; } if(overlay) overlay.style.display = "none"; } // ── toast ── function showToast(msg,type){ var el=g("toast"); el.textContent=(type==="error"?"✗ ":"✓ ")+msg; el.style.background=type==="error"?"#FEF2F2":"#F0FDF4"; el.style.color=type==="error"?"#B91C1C":"#15803D"; el.style.border="1px solid "+(type==="error"?"#FECACA":"#BBF7D0"); el.classList.remove("hidden"); setTimeout(function(){el.classList.add("hidden");},3000); } // ── tabs ── function showTab(id){ closeSidebar(); document.querySelectorAll("[id^='tab-']").forEach(function(el){el.classList.add("hidden");}); g("tab-"+id).classList.remove("hidden"); document.querySelectorAll(".sb-btn").forEach(function(b){b.classList.remove("active");}); document.querySelectorAll(".sb-btn").forEach(function(b){ if(b.textContent.trim()&&b.getAttribute("onclick")==="showTab('"+id+"')") b.classList.add("active"); }); g("page-title").textContent=TABS_LABEL[id]||id; if(id==="dashboard")updateDash(); if(id==="students")renderStudents(); if(id==="attendance")initStudentAtt(); if(id==="teachers"){renderTeachers(); populateClassDropdown("tcl");} if(id==="fees")renderFees(); if(id==="attendance")renderAtt(); if(id==="exams")renderExams(); if(id==="reportcard")renderRC(); if(id==="tc")renderTC(); if(id==="tcy")renderTCY(); if(id==="feesettings"){renderFeeSettings(); loadExamConfig();} if(id==="classroutine") renderClassRoutine(); if(id==="enquiry") renderEnquiry(); if(id==="notice") renderNoticeBoard(); if(id==="website") renderWebsite(); if(id==="inventory") renderInventory(); if(id==="cloud") renderCloud(); } // ── modal ── var _openModalCount=0; // ── Admission Session Dropdown ── function populateAdmSessionDropdown() { var sel = document.getElementById("adm-session-select"); if(!sel) return; sel.innerHTML = ""; var now3 = new Date(); var curSY = now3.getMonth() >= 3 ? now3.getFullYear() : now3.getFullYear()-1; [curSY, curSY+1].forEach(function(sy){ var opt = document.createElement("option"); opt.value = sy; opt.textContent = sy+"-"+(sy+1).toString().slice(-2); if(sy === curSY) opt.selected = true; sel.appendChild(opt); }); // Immediately refresh class dropdown with selected session year var selSY = parseInt(sel.value) || curSY; populateClassDropdown("scl", selSY); } function updateAdmSessionDisplay() { // Called on change — update roll number preview var clsSel = document.getElementById("sclass"); if(clsSel && clsSel.value) { var rollEl = document.getElementById("sroll"); if(rollEl) rollEl.value = generateRollNumber(clsSel.value); } } function getAdmissionSessionYear() { var sel = document.getElementById("adm-session-select"); if(sel && sel.value) return parseInt(sel.value)||SESSION_START_YEAR; var n=new Date(); return n.getMonth()>=3?n.getFullYear():n.getFullYear()-1; } function openModal(id){ g("modal-"+id).classList.remove("hidden"); _openModalCount++; document.body.style.overflow="hidden"; // lock background scroll if(id==="student") { populateAdmSessionDropdown(); // populates dropdown AND calls populateClassDropdown internally updateSectionInfo(); var ms = document.getElementById("modal-session"); if(ms) ms.textContent = SESSION_SHORT; } if(id==="teacher") { populateClassDropdown("tcl"); } } function closeModal(id){ g("modal-"+id).classList.add("hidden"); _openModalCount=Math.max(0,_openModalCount-1); if(_openModalCount===0) document.body.style.overflow=""; // restore scroll } function closeDoc(id){g("doc-"+id).classList.add("hidden");} // ── add student ── function addStudent(){ var name=val("sn").trim(), cls=val("scl"); if(!name){showToast("Please enter student name","error");return;} if(!cls){showToast("Please select class","error");return;} var village=val("s-city").trim(); if(!village){showToast("Please enter Village / City","error");return;} // Phone validation var phone=val("sph").trim().replace(/\D/g,""); if(phone && phone.length!==10){showToast("Mobile number 10 digits ka hona chahiye!","error");var phEl=g("sph");if(phEl){phEl.style.borderColor="#DC2626";phEl.focus();}return;} // Bus fare validation var busYesEl=document.getElementById("sbus-yes"); var isBusYes=busYesEl&&busYesEl.checked; var busFare=0; if(isBusYes){var bfEl=g("s-bus-fare");busFare=bfEl?parseInt(bfEl.value)||0:0;if(busFare<=0){showToast("Bus fare bharna zaroori hai!","error");if(bfEl){bfEl.style.borderColor="#DC2626";bfEl.focus();}return;}} // Auto generate roll number based on admission sequence var roll = generateRollNumber(cls); students.push({ id:Date.now(),name:name,class:cls,roll:roll, category:val("sct"),marks:0,gender:val("sgender"),photo:getPhotoData(), bus:isBusYes?"Yes":"No",busFare:busFare,busRoute:(g("s-bus-route")||{}).value||"", phone:phone||val("sph").trim(),dob:val("sdb").trim(), father:val("sfa").trim(), mother:val("smo").trim(),fee:"Unpaid", addr1:val("s-addr1").trim(),city:val("s-city").trim(), dist:val("s-dist").trim(),state:val("s-state"),pin:val("s-pin").trim() }); // clear form safely ["sn","sph","sdb","sfa","smo","s-addr1","s-city","s-dist","s-pin","s-bus-fare","s-bus-route"].forEach(function(id){ var el=g(id); if(el) el.value=""; }); var fareBox=g("bus-fare-box");if(fareBox)fareBox.style.display="none"; var farePrev=g("bus-fare-preview");if(farePrev)farePrev.style.display="none"; var phSt=g("sph-status");if(phSt)phSt.style.display="none"; var phMsg=g("sph-msg");if(phMsg)phMsg.style.display="none"; var phEl2=g("sph");if(phEl2)phEl2.style.borderColor=""; var ps=g("pin-status"); if(ps) ps.textContent=""; var scl=g("scl"); if(scl) scl.value=""; var sct=g("sct"); if(sct) sct.value="General"; var srl=g("srl"); if(srl) srl.value=""; var si=g("section-info"); if(si) si.innerHTML=""; var sgender=g("sgender"); if(sgender) sgender.value=""; var busNo=g("sbus-no"); if(busNo){busNo.checked=true; highlightBus();} g("s-state") && (g("s-state").value=""); resetPhotoField(); closeModal("student"); // Save admission date var lastS=students[students.length-1]; if(lastS&&!lastS.admDate){ var now2=new Date(); lastS.admDate=String(now2.getDate()).padStart(2,"0")+"."+String(now2.getMonth()+1).padStart(2,"0")+"."+now2.getFullYear(); } showToast(name+" admitted! Roll No: "+roll); saveData(); renderStudents(); updateDash(); // Directly open student profile var newStudent = students[students.length-1]; if(newStudent) setTimeout(function(){ openStudentDetail(newStudent.id); }, 300); } // ── add teacher ── var PRE_PRIMARY_CLASSES = ["Play","Nursery","L.K.G","U.K.G"]; var PRIMARY_CLASSES = ["I","II","III","IV","V"]; var UPPER_PRIMARY_CLASSES = ["VI","VII","VIII","IX","X"]; function filterClassByTeacherCategory(selectId, catType){ var sel = document.getElementById(selectId); if(!sel) return; var classes = []; if(catType==="Pre-Primary") classes = PRE_PRIMARY_CLASSES; else if(catType==="Primary") classes = PRIMARY_CLASSES; else if(catType==="Upper Primary") classes = UPPER_PRIMARY_CLASSES; else classes = BASE_CLASSES; sel.innerHTML = ''; classes.forEach(function(bc){ // Add sections A, B etc ["A","B","C"].forEach(function(sec){ var hasStudents = students.some(function(s){ return s.class === bc+"-"+sec; }); if(hasStudents || sec==="A"){ var opt = document.createElement("option"); opt.value = bc+"-"+sec; opt.textContent = bc+"-"+sec; sel.appendChild(opt); } }); }); } function getTeacherCategoryFromClass(cls){ if(!cls) return ""; var base = cls.split("-")[0]; if(PRE_PRIMARY_CLASSES.indexOf(base)>=0) return "Pre-Primary"; if(PRIMARY_CLASSES.indexOf(base)>=0) return "Primary"; if(UPPER_PRIMARY_CLASSES.indexOf(base)>=0) return "Upper Primary"; return ""; } function addTeacher(){ var name=val("tn").trim(), subj=val("ts").trim(); if(!name){showToast("Please enter teacher name","error");return;} if(!subj){showToast("Please enter subject","error");return;} var tPhotoEl=document.getElementById("t-photo-preview"); var tPhoto=tPhotoEl&&tPhotoEl._photoData?tPhotoEl._photoData:""; var tCatType = val("t-category-type"); teachers.push({ id:Date.now(),name:name,subject:subj,class:val("tcl"), category_type:tCatType, qual:val("tq").trim(),exp:val("te").trim(),phone:val("tp").trim(), salary:val("tsal").trim(),gender:val("t-gender"),dob:val("t-dob").trim(), address:val("t-addr").trim(),joining:fixDateFormat(val("t-joining").trim()), photo:tPhoto, attendance:{},salaryLedger:{} }); ["tn","ts","tq","te","tp","tsal","t-dob","t-addr","t-joining"].forEach(function(id){ var el=g(id); if(el) el.value=""; }); var tg=g("t-gender");if(tg)tg.value=""; g("tcl").value=""; var tct=g("t-category-type");if(tct)tct.value=""; if(tPhotoEl){tPhotoEl.innerHTML="👤";tPhotoEl.style.fontSize="28px";delete tPhotoEl._photoData;} var tclear=g("t-photo-clear");if(tclear)tclear.style.display="none"; var tinp=g("t-photo-input");if(tinp)tinp.value=""; closeModal("teacher"); showToast(name+" added to staff!"); renderTeachers(); updateDash(); saveData(); } function removeStudent(id){students=students.filter(function(s){return s.id!==id;});saveData();showToast("Student removed","error");renderStudents();updateDash();} // ── STUDENT DETAIL ── var currentDetailId = null; var currentReceiptStudent = null; // ── STUDENT PROFILE MINIMIZE / TASKBAR ── var minimizedProfiles = {}; // {id: {name, class, color}} function minimizeStudentProfile() { var id = currentDetailId; if(!id) return; var s = students.find(function(x){ return x.id===id; }); if(!s) return; // Save to minimized list var i = students.indexOf(s); var a = avt(s.name, i); minimizedProfiles[id] = { id: id, name: s.name, cls: displayClass(s.class), color: a.bg, photo: s.photo || "" }; // Hide modal (but keep data) document.getElementById("modal-student-detail").classList.add("hidden"); _openModalCount=Math.max(0,_openModalCount-1); if(_openModalCount===0) document.body.style.overflow=""; // Render taskbar renderTaskbar(); } function renderTaskbar() { var bar = document.getElementById("student-taskbar"); if(!bar) return; var keys = Object.keys(minimizedProfiles); if(!keys.length) { bar.innerHTML = ""; return; } bar.innerHTML = keys.map(function(id) { var p = minimizedProfiles[id]; var photoHtml = p.photo ? '' : '
'+p.name.split(" ").map(function(w){return w[0]||"";}).join("").substring(0,2).toUpperCase()+'
'; return '
' + photoHtml + '
' + '
'+p.name+'
' + '
'+p.cls+'
' + '
' + '' + '
'; }).join(""); } function restoreStudentProfile(id) { // Remove from minimized delete minimizedProfiles[id]; renderTaskbar(); // Open profile openStudentDetail(id); } function closeMinimizedProfile(e, id) { e.stopPropagation(); delete minimizedProfiles[id]; renderTaskbar(); } function sdTab(name) { var tabs = ["info","admform","fee","idcard","rc","tc","att","diary","edit"]; tabs.forEach(function(t) { var panel = document.getElementById("sdp-"+t); var menu = document.getElementById("sdm-"+t); if(panel) panel.style.display = "none"; if(menu) { menu.style.background = "transparent"; menu.style.borderColor = "transparent"; menu.style.color = "#374151"; menu.style.fontWeight = "600"; } }); var activePanel = document.getElementById("sdp-"+name); var activeMenu = document.getElementById("sdm-"+name); if(activePanel) { activePanel.style.display = activePanel.id==="sdp-fee" ? "flex" : "block"; } if(activeMenu) { activeMenu.style.background = "#EFF6FF"; activeMenu.style.borderColor = "#BFDBFE"; activeMenu.style.color = "#1D4ED8"; activeMenu.style.fontWeight = "700"; } if(name==="fee"){renderFeeLedger();setTimeout(function(){feeTab('adm');},60);} if(name==="admform") { /* form shown inline */ } if(name==="att"){ renderAttendancePanel(); } if(name==="diary"){ renderStudentDiary(); } renderDuesFlash(); } function openStudentDetail(id) { var s = students.find(function(x){return x.id===id;}); if(!s) return; currentDetailId = id; var i = students.indexOf(s); var a = avt(s.name, i); // Header var avEl = g("sd-av"); if(s.photo) { avEl.style.background = "transparent"; avEl.style.padding = "0"; avEl.innerHTML = ''; } else { avEl.style.background = a.bg; avEl.style.fontSize = "28px"; avEl.innerHTML = a.init; } g("sd-name").textContent = s.name; var sdClosedBadge = document.getElementById("sd-closed-badge"); if(!sdClosedBadge) { sdClosedBadge = document.createElement("span"); sdClosedBadge.id = "sd-closed-badge"; sdClosedBadge.style.cssText = "font-size:10px;font-weight:700;background:rgba(220,38,38,.25);color:#FCA5A5;border-radius:5px;padding:2px 8px;margin-left:8px;vertical-align:middle;"; g("sd-name").parentNode.insertBefore(sdClosedBadge, g("sd-name").nextSibling); } sdClosedBadge.textContent = s.admissionClosed ? "🔒 Closed" : ""; sdClosedBadge.style.display = s.admissionClosed ? "inline" : "none"; g("sd-class").textContent = displayClass(s.class); var rollBadge = g("sd-roll-badge"); if(rollBadge) rollBadge.textContent = s.roll || ""; // Info g("sd-roll").textContent = s.roll || "—"; g("sd-dob").textContent = s.dob || "—"; g("sd-cat").innerHTML = ''+s.category+''; var genderEl = g("sd-gender"); if(genderEl) genderEl.textContent = s.gender || "—"; // fee field removed g("sd-phone").textContent = s.phone || "—"; var feeInfoEl = g("sd-fee-info"); var busel = g("sd-bus"); var busbtn = g("sd-bus-btn"); if(busel) { busel.textContent = s.bus==="Yes" ? "✓ Yes — Enabled" : "✗ No — Disabled"; busel.style.color = s.bus==="Yes" ? "#15803D" : "#B91C1C"; } if(busbtn) { if(s.bus==="Yes") { busbtn.textContent = "Disable"; busbtn.style.background = "#FEE2E2"; busbtn.style.color = "#B91C1C"; } else { busbtn.textContent = "Enable"; busbtn.style.background = "#DCFCE7"; busbtn.style.color = "#15803D"; } } // Parents g("sd-father").textContent = s.father || "—"; g("sd-mother").textContent = s.mother || "—"; // Address var addrParts = []; if(s.addr1) addrParts.push(s.addr1); if(s.city) addrParts.push("Village/City: " + s.city); if(s.dist) addrParts.push("District: " + s.dist); if(s.state) addrParts.push("State: " + s.state); if(s.pin) addrParts.push("PIN: " + s.pin); g("sd-address").textContent = addrParts.length ? addrParts.join(", ") : "Address not provided"; renderStudentAttendanceBox(s); renderNoticeFlash("sd-notice-flash","Students"); openModal("student-detail"); renderDuesFlash(); sdTab("info"); } // (duplicate removed) function renderAttendancePanel(){ var s=students.find(function(x){return x.id===currentDetailId;}); if(!s) return; var att=s.attendance||{}; // att structure: att["2026-05"]["01"] = "P"/"A"/"H"/"L" // Detect all sessions from attendance keys // Session = Apr(04) to Mar(03). Key "2026-05" = session 2026 (2026-27) // Key "2026-01" (Jan) = session 2025 (2025-26) because Jan is before March var sessionSet={}; Object.keys(att).forEach(function(mk){ var parts=mk.split("-"); var yr=parseInt(parts[0]),mon=parseInt(parts[1]); var sessYear = mon>=4 ? yr : yr-1; // Apr-Dec = same year session, Jan-Mar = prev year session sessionSet[sessYear]=true; }); // Also add current session var curSessYear = new Date().getMonth()>=3 ? new Date().getFullYear() : new Date().getFullYear()-1; sessionSet[curSessYear]=true; var sessionYears=Object.keys(sessionSet).map(Number).sort(function(a,b){return b-a;}); // Populate session dropdown var selEl=document.getElementById("sd-att-session"); if(selEl){ var curSelVal=selEl.value?parseInt(selEl.value):curSessYear; selEl.innerHTML=sessionYears.map(function(sy){ var label=sy+"–"+String(sy+1).slice(-2); return ''; }).join(""); if(!selEl.value) selEl.value=curSessYear; } var selectedSession=selEl?parseInt(selEl.value):curSessYear; // Filter months for this session: Apr(04) of selectedSession to Mar(03) of selectedSession+1 var sessionMonths=[]; // Apr to Dec = selectedSession for(var m=4;m<=12;m++) sessionMonths.push(selectedSession+"-"+String(m).padStart(2,"0")); // Jan to Mar = selectedSession+1 for(var m2=1;m2<=3;m2++) sessionMonths.push((selectedSession+1)+"-"+String(m2).padStart(2,"0")); var totalD=0,present=0,absent=0,holiday=0,leave=0; var activeMonthKeys=[]; sessionMonths.forEach(function(mk){ if(!att[mk]) return; activeMonthKeys.push(mk); var days=att[mk]; Object.keys(days).forEach(function(dk){ var v=days[dk]; totalD++; if(v==="P") present++; else if(v==="A") absent++; else if(v==="H") holiday++; else if(v==="L") leave++; }); }); var workingDays=present+absent+leave; var pct=workingDays?Math.round(present/workingDays*100):0; var barCol=pct>=75?"#059669":pct>=50?"#D97706":"#DC2626"; var sessLabel=selectedSession+"–"+String(selectedSession+1).slice(-2); // Summary var sumEl=document.getElementById("sd-att-summary"); if(sumEl) sumEl.innerHTML='
' +'
' +'
'+present+'
' +'
✅ Present
' +'
' +'
'+absent+'
' +'
❌ Absent
' +'
' +'
'+pct+'%
' +'
📊 Rate
' +(holiday||leave?'
' +'
'+holiday+' 🟡 Holiday
' +'
'+leave+' 🔵 Leave
' +'
':'') +'
' +'
Session '+sessLabel+': '+present+'/'+workingDays+' working days'+(holiday?' ('+holiday+' holidays excl.)':'')+''+pct+'%
' +'
'; // Monthly breakdown var mNames=["","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; var dayNames=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]; var mEl=document.getElementById("sd-att-monthly"); if(!mEl) return; if(!activeMonthKeys.length){ mEl.innerHTML='
Session '+sessLabel+' mein koi attendance record nahi hai
'; return; } var html='
📅 Session '+sessLabel+' — Month-wise
'; // Show all 12 months of session (Apr to Mar), even if no data sessionMonths.forEach(function(mk){ var mParts=mk.split("-"); var yr=parseInt(mParts[0]),mon=parseInt(mParts[1]); var daysInMonth=new Date(yr,mon,0).getDate(); var days=att[mk]||{}; var hasDays=Object.keys(days).length>0; var mP=0,mA=0,mH=0,mL=0; for(var d=1;d<=daysInMonth;d++){ var dk=String(d).padStart(2,"0"); var st=days[dk]||""; if(st==="P")mP++;else if(st==="A")mA++;else if(st==="H")mH++;else if(st==="L")mL++; } var mWD=mP+mA+mL; var mPct=mWD?Math.round(mP/mWD*100):0; var mLabel=mNames[mon]+" "+yr; if(!hasDays){ // Show empty month as collapsed html+='
' +'📅 '+mLabel+'' +'No data
'; return; } html+='
' +'
' +'📅 '+mLabel+'' +'P:'+mP+' A:'+mA+(mH?' H:'+mH:'')+(mL?' L:'+mL:'')+' · '+mPct+'%' +'
' +'
'; for(var d2=1;d2<=daysInMonth;d2++){ var dk2=String(d2).padStart(2,"0"); var status=days[dk2]||""; var dayDate=new Date(yr,mon-1,d2); var dayName=dayNames[dayDate.getDay()]; var isSunday=dayDate.getDay()===0; var bg,col,icon; if(status==="P"){ bg="#F0FDF4"; col="#15803D"; icon="✅"; } else if(status==="A"){ bg="#FEF2F2"; col="#DC2626"; icon="❌"; } else if(status==="H"){ bg="#FFFBEB"; col="#B45309"; icon="🟡"; } else if(status==="L"){ bg="#EFF6FF"; col="#1D4ED8"; icon="🔵"; } else if(isSunday){ bg="#F8FAFC"; col="#94A3B8"; icon=d2; } else { bg="#fff"; col="#CBD5E1"; icon=d2; } var bdr=status==="P"?"#86EFAC":status==="A"?"#FECACA":status==="H"?"#FDE68A":status==="L"?"#93C5FD":"#E2E8F0"; html+='
' +'
'+icon+'
' +'
'+(status?d2:dayName.charAt(0))+'
' +'
'; } html+='
'; }); mEl.innerHTML=html; } function renderStudentAttendanceBox(s){ // stub - element removed from info panel var box=document.getElementById("sd-attendance-box"); if(!box) return; } function renderDuesFlash(){ var el=document.getElementById("sd-dues-flash"); if(!el) return; var s=students.find(function(x){return x.id===currentDetailId;}); if(!s){el.innerHTML="";return;} try{ var feeData=calculateMonthlyFees(s); var now9=new Date(); var realMM9={April:3,May:4,June:5,July:6,August:7,September:8,October:9,November:10,December:11,January:0,February:1,March:2}; var mNames9=["January","February","March","April","May","June","July","August","September","October","November","December"]; var curMonthName=mNames9[now9.getMonth()]; var sessStart9=now9.getMonth()>=3?now9.getFullYear():now9.getFullYear()-1; if(ACTIVE_SESSION_START) sessStart9=ACTIVE_SESSION_START; // Find current month in feeData var totalDue=0, dueMonths=[], covered=0, elapsed=0; feeData.forEach(function(r){ if(r.type!=="regular") return; var rm9=realMM9[r.month]; if(rm9===undefined) return; var yr9=rm9>=3?sessStart9:sessStart9+1; if(yr9>now9.getFullYear()||(yr9===now9.getFullYear()&&rm9>now9.getMonth())) return; elapsed++; if(r.paidAmt>0||r.cfCovered||r.hasCF||r.netPayable<=0){ covered++; } else { totalDue+=Math.max(0,r.balance); dueMonths.push(r.month); } }); if(totalDue<=0){ // All clear! el.innerHTML='
' +'' +'All fees clear till '+curMonthName+'! '+covered+'/'+elapsed+' months paid.' +'
'; } else { // Has dues var dueList=dueMonths.length<=3?dueMonths.join(", "):dueMonths.slice(0,3).join(", ")+" +"+( dueMonths.length-3)+" more"; el.innerHTML='
' +'
' +'💰' +'
' +'
₹'+totalDue.toLocaleString("en-IN")+' Due
' +'
'+dueMonths.length+' month'+(dueMonths.length>1?"s":"")+' unpaid: '+dueList+'
' +'
' +'
' +'' +'
'; } }catch(e){el.innerHTML="";} } function printAdmissionReceiptFromProfile() { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s){ showToast("Student not found","error"); return; } // Profile modal band mat karo showAdmissionReceipt(s); } function toggleBusService() { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; s.bus = s.bus==="Yes" ? "No" : "Yes"; // Refresh display var busel = g("sd-bus"); var busbtn = g("sd-bus-btn"); if(busel) { busel.textContent = s.bus==="Yes" ? "✓ Yes — Enabled" : "✗ No — Disabled"; busel.style.color = s.bus==="Yes" ? "#15803D" : "#B91C1C"; } if(busbtn) { busbtn.textContent = s.bus==="Yes" ? "Disable" : "Enable"; busbtn.style.background = s.bus==="Yes" ? "#FEE2E2" : "#DCFCE7"; busbtn.style.color = s.bus==="Yes" ? "#B91C1C" : "#15803D"; } saveData(); showToast("Bus service " + (s.bus==="Yes"?"enabled":"disabled") + " for " + s.name); renderFees(); } function confirmDeleteStudent() { if(!currentDetailId) return; var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; // Create confirmation popup var popup = document.createElement("div"); popup.id = "delete-confirm-popup"; popup.style.cssText = "position:fixed;inset:0;background:rgba(15,23,42,.6);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);"; popup.innerHTML = '
' + '
🗑️
' + '
Student Delete Karen?
' + '
'+s.name+' ko permanently delete kiya jayega.
' + '
Yeh action undo nahi ho sakta. Kya aap sure hain?
' + '
' + '' + '' + '
' + '
'; document.body.appendChild(popup); } function doDeleteStudent() { closeDeleteConfirm(); if(!currentDetailId) return; removeStudent(currentDetailId); closeModal("student-detail"); currentDetailId = null; } function closeDeleteConfirm() { var el = document.getElementById("delete-confirm-popup"); if(el) el.remove(); } function removeFromDetail() { confirmDeleteStudent(); } function viewRCFromDetail() { showTab("reportcard"); setTimeout(function(){ viewRC(currentDetailId); }, 200); } function viewTCFromDetail() { showTab("tc"); setTimeout(function(){ viewTC(currentDetailId); }, 200); } function removeTeacher(id){ if(!confirm("Remove this teacher?")) return; teachers=teachers.filter(function(t){return t.id!==id;}); saveData();showToast("Teacher removed","error");renderTeachers();updateDash(); } var currentTeacherId = null; var minimizedTeachers = {}; // {id: {name, subject, color}} var _teacherMaximized = false; function toggleTeacherMaximize() { var box = document.getElementById("tp-modal-box"); var btn = document.getElementById("tp-maximize-btn"); if(!box) return; _teacherMaximized = !_teacherMaximized; if(_teacherMaximized) { box.style.width = "99vw"; box.style.maxWidth = "99vw"; box.style.maxHeight = "98vh"; box.style.borderRadius = "8px"; if(btn) btn.textContent = "❐"; btn.title = "Restore"; } else { box.style.width = "820px"; box.style.maxWidth = "97vw"; box.style.maxHeight = "95vh"; box.style.borderRadius = "18px"; if(btn) btn.textContent = "□"; btn.title = "Maximize"; } } var _studentMaximized = false; function toggleStudentMaximize() { var box = document.getElementById("sd-modal-box"); var btn = document.getElementById("sd-maximize-btn"); if(!box) return; _studentMaximized = !_studentMaximized; if(_studentMaximized) { box.style.width = "99vw"; box.style.maxWidth = "99vw"; box.style.maxHeight = "98vh"; box.style.borderRadius = "8px"; if(btn) btn.textContent = "❐"; btn.title = "Restore"; } else { box.style.width = "820px"; box.style.maxWidth = "97vw"; box.style.maxHeight = "95vh"; box.style.borderRadius = "18px"; if(btn) btn.textContent = "□"; btn.title = "Maximize"; } } function minimizeTeacherProfile() { if(!currentTeacherId) return; var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var i=teachers.indexOf(t); var a=avt(t.name, i+2); minimizedTeachers[currentTeacherId]={ id:currentTeacherId, name:t.name, subject:t.subject||"", color:a.bg, photo:t.photo||"" }; document.getElementById("modal-teacher-profile").classList.add("hidden"); _openModalCount=Math.max(0,_openModalCount-1); if(_openModalCount===0) document.body.style.overflow=""; // Reset maximize state _teacherMaximized = false; var box=document.getElementById("tp-modal-box"); if(box){box.style.width="820px";box.style.maxWidth="97vw";box.style.maxHeight="95vh";box.style.borderRadius="18px";} var btn=document.getElementById("tp-maximize-btn"); if(btn){btn.textContent="□";} renderTeacherTaskbar(); } function renderTeacherTaskbar() { var bar=document.getElementById("teacher-taskbar"); if(!bar) return; var keys=Object.keys(minimizedTeachers); if(!keys.length){bar.innerHTML="";return;} bar.innerHTML=keys.map(function(id){ var p=minimizedTeachers[id]; var photoHtml=p.photo ?'' :'
'+p.name.split(" ").map(function(w){return w[0]||"";}).join("").substring(0,2).toUpperCase()+'
'; return '
' +photoHtml +'
' +'
'+p.name+'
' +'
'+p.subject+'
' +'
' +'' +'
'; }).join(""); } function restoreTeacherProfile(id) { delete minimizedTeachers[id]; renderTeacherTaskbar(); openTeacherProfile(id); } function closeMinimizedTeacher(e,id) { e.stopPropagation(); delete minimizedTeachers[id]; renderTeacherTaskbar(); } function removeTeacherFromProfile() { if(!currentTeacherId) return; var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var popup=document.createElement("div"); popup.id="tp-delete-popup"; popup.style.cssText="position:fixed;inset:0;background:rgba(15,23,42,.6);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);"; popup.innerHTML='
' +'
🗑️
' +'
Teacher Remove Karen?
' +'
'+t.name+' ko permanently remove kiya jayega.
' +'
' +'' +'' +'
'; document.body.appendChild(popup); } function closeTeacherDeletePopup() { var el=document.getElementById("tp-delete-popup"); if(el) el.remove(); } function doRemoveTeacher() { var el=document.getElementById("tp-delete-popup"); if(el) el.remove(); teachers=teachers.filter(function(t){return t.id!==currentTeacherId;}); saveData(); showToast("Teacher removed","error"); closeModal("teacher-profile"); renderTeachers(); updateDash(); } function editTeacherProfile() { tpTab("edit"); } function editTeacherProfileOld() { var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; // Fill edit form var setV=function(id,v){var el=document.getElementById(id);if(el)el.value=v||"";}; setV("edit-tn",t.name); setV("edit-ts",t.subject); setV("edit-tq",t.qual); setV("edit-te",t.exp); setV("edit-tp",t.phone); setV("edit-tsal",t.salary); setV("edit-t-dob",fixDateFormat(t.dob)); setV("edit-t-addr",t.address); setV("edit-t-joining",fixDateFormat(t.joining)); setV("edit-t-gender",t.gender); setV("edit-tcl",t.class||""); // Set category type var catType = t.category_type || getTeacherCategoryFromClass(t.class); setV("edit-t-category-type", catType); // Populate edit class dropdown based on category var ecl=document.getElementById("edit-tcl"); if(catType){ filterClassByTeacherCategory("edit-tcl", catType); } else if(ecl){ ecl.innerHTML=''; BASE_CLASSES.forEach(function(bc){ var o=document.createElement("option"); o.value=bc+"-A"; o.text=bc+"-A"; ecl.appendChild(o); }); } // Set current class value if(ecl){ ecl.value=t.class||""; if(t.class && ecl.value!==t.class){ var ex=document.createElement("option"); ex.value=t.class; ex.text=t.class; ecl.appendChild(ex); ecl.value=t.class; } } // Photo preview var prev=document.getElementById("edit-t-photo-preview"); var clearBtn=document.getElementById("edit-t-photo-clear"); if(prev){ if(t.photo){ prev.innerHTML=''; if(clearBtn) clearBtn.style.display="inline"; } else { prev.innerHTML="👤"; prev.style.fontSize="28px"; if(clearBtn) clearBtn.style.display="none"; } } // Store current photo document.getElementById("edit-t-photo-preview")._currentPhoto = t.photo||""; openModal("teacher-edit"); } function saveEditTeacher() { var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var getV=function(id){var el=document.getElementById(id);return el?el.value.trim():"";}; var name=getV("edit-tn"); if(!name){showToast("Name required","error");return;} t.name=name; t.subject=getV("edit-ts"); t.qual=getV("edit-tq"); t.exp=getV("edit-te"); t.phone=getV("edit-tp"); t.salary=getV("edit-tsal"); t.dob=fixDateFormat(getV("edit-t-dob")); t.address=getV("edit-t-addr"); t.joining=fixDateFormat(getV("edit-t-joining")); t.gender=getV("edit-t-gender"); t.class=getV("edit-tcl"); t.category_type=getV("edit-t-category-type"); // Photo var prev=document.getElementById("edit-t-photo-preview"); if(prev && prev._newPhoto !== undefined) { t.photo = prev._newPhoto; } saveData(); closeModal("teacher-edit"); // Small delay to ensure modal is closed before reopening profile var savedId = currentTeacherId; setTimeout(function(){ renderTeachers(); openTeacherProfile(savedId); }, 150); showToast("Profile updated — "+name); } function editTeacherPhotoPreview(input) { if(!input.files||!input.files[0]) return; var file=input.files[0]; if(file.size>2*1024*1024){showToast("Photo 2MB se chhota hona chahiye","error");return;} var reader=new FileReader(); reader.onload=function(e){ var prev=document.getElementById("edit-t-photo-preview"); if(prev){ prev.innerHTML=''; prev._newPhoto=e.target.result; } var clearBtn=document.getElementById("edit-t-photo-clear"); if(clearBtn) clearBtn.style.display="inline"; }; reader.readAsDataURL(file); } function clearEditTeacherPhoto() { var prev=document.getElementById("edit-t-photo-preview"); if(prev){prev.innerHTML="👤";prev.style.fontSize="28px";prev._newPhoto="";} var clearBtn=document.getElementById("edit-t-photo-clear"); if(clearBtn) clearBtn.style.display="none"; var inp=document.getElementById("edit-t-photo-input"); if(inp) inp.value=""; } // ── TEACHER ATTENDANCE ── var SESSION_MONTH_NAMES=["April","May","June","July","August","September","October","November","December","January","February","March"]; var SESSION_TO_REAL=[3,4,5,6,7,8,9,10,11,0,1,2]; function renderTpAttendance() { var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; if(!t.attendance) t.attendance={}; var monthSel=g("tp-att-month"), yearSel=g("tp-att-year"); var sessionMonthIdx=monthSel?parseInt(monthSel.value):0; var year=yearSel?parseInt(yearSel.value):new Date().getFullYear(); var realMonth=SESSION_TO_REAL[sessionMonthIdx]; var monthName=SESSION_MONTH_NAMES[sessionMonthIdx]; var daysInMonth=new Date(year,realMonth+1,0).getDate(); var firstDay=new Date(year,realMonth,1).getDay(); // 0=Sun var key=year+"-"+String(realMonth+1).padStart(2,"0"); if(!t.attendance[key]) t.attendance[key]={}; // Summary var present=0,absent=0,holiday=0,leave=0; for(var d=1;d<=daysInMonth;d++){ var dk=String(d).padStart(2,"0"); var status=t.attendance[key][dk]||""; if(status==="P") present++; else if(status==="A") absent++; else if(status==="H") holiday++; else if(status==="L") leave++; } var summaryEl=g("tp-att-summary"); summaryEl.innerHTML=[ ["Present",present,"#065F46","#ECFDF5","#6EE7B7"], ["Absent",absent,"#B91C1C","#FEF2F2","#FECACA"], ["Holiday",holiday,"#B45309","#FFFBEB","#FDE68A"], ["Leave",leave,"#1D4ED8","#EFF6FF","#BFDBFE"] ].map(function(s){ return '
' +'
'+s[1]+'
' +'
'+s[0]+'
' +'
'; }).join(""); // Calendar grid var gridEl=g("tp-att-grid"); var days=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]; var grid=days.map(function(d){ return '
'+d+'
'; }).join(""); // Empty cells before first day for(var e=0;etodayD; var bg=isFuture?"#F5F5F5":status==="P"?"#DCFCE7":status==="A"?"#FEE2E2":status==="H"?"#FEF3C7":status==="L"?"#DBEAFE":isWeekend?"#F1F5F9":"#fff"; var col=isFuture?"#D1D5DB":status==="P"?"#15803D":status==="A"?"#B91C1C":status==="H"?"#B45309":status==="L"?"#1D4ED8":isWeekend?"#CBD5E1":"#374151"; var cursor=isFuture?"not-allowed":"pointer"; var onclick=isFuture?"":"cycleAttendance(this)"; var titleTxt=isFuture?"Future date — attendance nahi lag sakti":"Click to change"; grid+='
' +'
'+d+'
' +'
'+(isFuture?"":status||"—")+'
' +'
'; } gridEl.innerHTML=grid; } function cycleAttendance(el) { if(el.getAttribute("data-future")==="1") return; var key=el.getAttribute("data-key"), day=el.getAttribute("data-day"); var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t||!key||!day) return; if(!t.attendance) t.attendance={}; if(!t.attendance[key]) t.attendance[key]={}; var seq=["","P","A","H","L"]; var cur=t.attendance[key][day]||""; var next=seq[(seq.indexOf(cur)+1)%seq.length]; // Check if this is today or back date var now2=new Date(); var parts=key.split("-"); var cellDate=new Date(parseInt(parts[0]),parseInt(parts[1])-1,parseInt(day)); var isToday=(cellDate.toDateString()===now2.toDateString()); var isBackDate=cellDate📅' +'
Back Date Change?
' +'
Date: '+dateStr+'   '+(cur||"blank")+''+(next||"blank")+'
' +'
' +'' +'' +'
'; document.body.appendChild(popup); popup.querySelector("#atc-yes").onclick=function(){popup.remove();t.attendance[key][day]=next;saveData();renderTpAttendance();}; popup.querySelector("#atc-no").onclick=function(){popup.remove();}; popup.onclick=function(e){if(e.target===popup)popup.remove();}; } else { // Today or blank cell — direct change t.attendance[key][day]=next; saveData(); renderTpAttendance(); } } // ── TEACHER KHATA / SALARY BOOK ── // Data: t.salaryLedger[monthKey] = [{amt, date, note}, ...] function getKhataEntries(t, k) { if(!t.salaryLedger) t.salaryLedger={}; if(!t.salaryLedger[k]) { // migrate old boolean/number data if(t.salaryPaid&&t.salaryPaid[k]) { var oldAmt=(t.salaryPaid[k]===true)?parseInt(t.salary||0):t.salaryPaid[k]; t.salaryLedger[k]=[{amt:oldAmt,date:k+"-01",note:"Migrated"}]; delete t.salaryPaid[k]; } else { t.salaryLedger[k]=[]; } } return t.salaryLedger[k]; } function renderTpKhata() { var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; if(!t.salaryLedger) t.salaryLedger={}; var yearSel=g("tp-khata-year"); var year=yearSel?parseInt(yearSel.value):new Date().getFullYear(); var monthlySal=parseInt(t.salary||0); var annual=monthlySal*12; var now=new Date(); var freeLeaveEl=document.getElementById("tp-free-leave"); var workDayEl=document.getElementById("sal-working-days"); var freeLeave=freeLeaveEl?parseInt(freeLeaveEl.value)||1:1; var workingDays=workDayEl?parseInt(workDayEl.value)||26:26; function calcEarned(k,realMon,yr) { var dim=new Date(yr,realMon+1,0).getDate(); var perDay=monthlySal/dim; var att=(t.attendance&&t.attendance[k])||{}; var P=0,A=0,H=0,L=0; for(var d=1;d<=dim;d++){var dk=String(d).padStart(2,"0");var st=att[dk]||"";if(st==="P")P++;else if(st==="A")A++;else if(st==="H")H++;else if(st==="L")L++;} var freeUsed=Math.min(L,freeLeave); var effectiveDays=P+freeUsed; var cutDays=A+Math.max(0,L-freeLeave); var deduction=Math.round(cutDays*perDay); var overtimeDays=Math.max(0,effectiveDays-workingDays); var bonus=Math.round(overtimeDays*perDay); var earned=Math.max(0,monthlySal-deduction+bonus); return {earned:Math.round(earned),deduction:deduction,bonus:bonus,cutDays:cutDays, overtimeDays:overtimeDays,perDay:Math.round(perDay),dim:dim,P:P,A:A,L:L,H:H}; } // Build months: only show month AFTER its last date has passed var carryBalance=0; var paidTotal=0; var months=[]; SESSION_MONTH_NAMES.forEach(function(mn,idx){ var realMon=SESSION_TO_REAL[idx]; var yr=realMon>=3?year:year+1; var k=yr+"-"+String(realMon+1).padStart(2,"0"); var lastDateOfMonth=new Date(yr,realMon+1,0); // last day of that month // Show salary ONLY after month ends (last date has passed) var monthEnded=now>lastDateOfMonth; if(!monthEnded) return; // skip current and future months // Skip months before teacher's joining date if(t.joining){ var jd=fixDateFormat(t.joining); var jp=jd.split("."); if(jp.length>=3){ var jDay=parseInt(jp[0]),jMon=parseInt(jp[1])-1,jYr=parseInt(jp[2]); var joinDate=new Date(jYr,jMon,jDay); var monthStart=new Date(yr,realMon,1); if(monthStart 0 and not "Earned:" entries) var paidAmt=entries.reduce(function(s,e){ if(e.note && e.note.indexOf("Earned:")===0) return s; return s+(e.amt||0); },0); var openingCarry=carryBalance; var creditFromPrev=Math.abs(Math.min(0,openingCarry)); var netDue=Math.max(0,earnedSal-creditFromPrev); var totalOwed=netDue+Math.max(0,openingCarry); var balance=totalOwed-paidAmt; carryBalance=balance; paidTotal+=paidAmt; months.push({mn:mn,yr:yr,k:k,entries:entries,paidAmt:paidAmt, monthlySal:monthlySal,earnedSal:earnedSal,totalOwed:totalOwed, balance:balance,openingCarry:openingCarry,calc:calc}); }); // Summary g("tp-khata-annual").textContent="Rs."+annual.toLocaleString("en-IN"); g("tp-khata-paid").textContent="Rs."+paidTotal.toLocaleString("en-IN"); var lastBal=months.length>0?months[months.length-1].balance:0; var pendEl=g("tp-khata-pending"); if(pendEl){ pendEl.textContent=lastBal>0?"Rs."+lastBal.toLocaleString("en-IN"):lastBal<0?"Rs."+Math.abs(lastBal).toLocaleString("en-IN")+" (Credit)":"Rs.0 ✓"; pendEl.style.color=lastBal>0?"#FCA5A5":lastBal<0?"#6EE7B7":"#6EE7B7"; } if(!months.length){ g("tp-khata-table").innerHTML='
' +'
📅
' +'
Koi month complete nahi hua
' +'
Month end hone ke baad salary generate hogi.
' +'
'; return; } var todayStr=now.getFullYear()+"-"+String(now.getMonth()+1).padStart(2,"0")+"-"+String(now.getDate()).padStart(2,"0"); var tableRows=months.map(function(md){ var status=md.paidAmt===0?"pending":md.balance<=0?"full":"partial"; var rowBg=status==="full"?"#F9FFFE":status==="partial"?"#FFFDF5":"#FFFAFA"; // Payment history var histLines=md.entries.length ? md.entries.map(function(e,ei){ return '
' +'Rs.'+e.amt.toLocaleString("en-IN")+'' +'📅 '+e.date+'' +(e.note?' '+e.note+'':'') +'' +'
'; }).join('') : ''; // Partial payment form — always show if not fully paid var suggested=md.balance>0?md.balance:md.totalOwed; var payForm=''; if(status!=="full"){ payForm='
' +'
+ Add Payment
' +'
' +'
' +'Rs.' +'' +'
' +'' +'' +'' +'
' +(md.balance>0?'
Balance: Rs.'+md.balance.toLocaleString("en-IN")+' — next month carry hoga
':'') +'
'; } else { payForm=''; } // Status badge var badge=status==="full" ?'✓ Paid' :status==="partial" ?'⚡ Partial' :'✗ Pending'; var balColor=md.balance>0?"#DC2626":md.balance<0?"#065F46":"#15803D"; var balText=md.balance>0?"Rs."+md.balance.toLocaleString("en-IN"):md.balance<0?"Credit Rs."+Math.abs(md.balance).toLocaleString("en-IN"):"✓ Clear"; return '' +'' +'
'+md.mn+'
' +'
'+md.yr+'
' +'
'+badge+'
' +'' +'' +'
Rs.'+md.monthlySal.toLocaleString("en-IN")+'
' +'
÷'+md.calc.dim+' days
' +(md.calc.P||md.calc.A||md.calc.L?'
' +(md.calc.P?'P:'+md.calc.P+' ':'')+(md.calc.A?'A:'+md.calc.A+' ':'')+(md.calc.L?'L:'+md.calc.L:'')+'
':'') +'' +'' +(md.calc.deduction>0 ?'
-Rs.'+md.calc.deduction.toLocaleString("en-IN")+'
'+md.calc.cutDays+' day(s)
' :'
') +'' +'' +(md.calc.bonus>0 ?'
+Rs.'+md.calc.bonus.toLocaleString("en-IN")+'
'+md.calc.overtimeDays+' day(s)
' :'
') +(md.openingCarry>0?'
+carry Rs.'+md.openingCarry.toLocaleString("en-IN")+'
':'') +(md.openingCarry<0?'
credit Rs.'+Math.abs(md.openingCarry).toLocaleString("en-IN")+'
':'') +'' +'' +'
Rs.'+md.totalOwed.toLocaleString("en-IN")+'
' +'' +'' +histLines +payForm +'' +'' +'
'+balText+'
' +(md.balance>0?'
→ carry next
':'') +'' +'' +'' +'' +''; }).join(""); var totDed=months.reduce(function(s,m){return s+m.calc.deduction;},0); var totOT=months.reduce(function(s,m){return s+m.calc.bonus;},0); var totEarned=months.reduce(function(s,m){return s+m.earnedSal;},0); var totPaid=months.reduce(function(s,m){return s+m.paidAmt;},0); var totSal=months.reduce(function(s,m){return s+m.monthlySal;},0); var finalBal=months[months.length-1].balance; var footerRow='' +'TOTAL ('+months.length+' mo.)' +'Rs.'+totSal.toLocaleString("en-IN")+'' +''+(totDed>0?'-Rs.'+totDed.toLocaleString("en-IN"):'—')+'' +''+(totOT>0?'+Rs.'+totOT.toLocaleString("en-IN"):'—')+'' +'Rs.'+totEarned.toLocaleString("en-IN")+'' +'Rs.'+totPaid.toLocaleString("en-IN")+'' +'' +(finalBal>0?"Rs."+finalBal.toLocaleString("en-IN"):finalBal<0?"Cr Rs."+Math.abs(finalBal).toLocaleString("en-IN"):"✓ Clear")+'' +'' +''; g("tp-khata-table").innerHTML= '
' +'' +'' +'' +'' +'' +'' +'' +'' +'' +'' +'' +''+tableRows+'' +''+footerRow+'' +'
MonthSalaryDeductionOvertime / CarryNet SalaryPayment ReceivedBalancePrint
'; } function addKhataEntry(el) { var key=el.getAttribute("data-key"); var amtEl=document.getElementById("kamt-"+key); var dateEl=document.getElementById("kdate-"+key); var noteEl=document.getElementById("knote-"+key); var amt=amtEl?parseInt(amtEl.value)||0:0; var dateVal=dateEl?dateEl.value:""; var note=noteEl?noteEl.value.trim():""; if(amt<=0){showToast("Amount enter karo","error");if(amtEl)amtEl.focus();return;} if(!dateVal){showToast("Date select karo","error");if(dateEl)dateEl.focus();return;} var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var parts=dateVal.split("-"); var dispDate=parts.length===3?(parts[2]+"."+parts[1]+"."+parts[0]):dateVal; if(!t.salaryLedger) t.salaryLedger={}; var yearSel=g("tp-khata-year"); var year=yearSel?parseInt(yearSel.value):new Date().getFullYear(); var freeLeaveEl=document.getElementById("tp-free-leave"); var freeLeave=freeLeaveEl?parseInt(freeLeaveEl.value)||1:1; var workDayEl=document.getElementById("sal-working-days"); var workingDays=workDayEl?parseInt(workDayEl.value)||26:26; var monthlySalBase=parseInt(t.salary||0); var now=new Date(); function calcEarnedForMonth(k2,realMon2,yr2){ var dim=new Date(yr2,realMon2+1,0).getDate(); var perDay=monthlySalBase/dim; var att=(t.attendance&&t.attendance[k2])||{}; var P=0,A=0,H=0,L=0; for(var d=1;d<=dim;d++){var dk=String(d).padStart(2,"0");var st=att[dk]||"";if(st==="P")P++;else if(st==="A")A++;else if(st==="H")H++;else if(st==="L")L++;} var freeUsed=Math.min(L,freeLeave); var cutDays=A+Math.max(0,L-freeLeave); var deduction=Math.round(cutDays*perDay); var otDays=Math.max(0,(P+freeUsed)-workingDays); var bonus=Math.round(otDays*perDay); return Math.max(0,monthlySalBase-deduction+bonus); } // Build month list up to and including key var monthList=[]; var foundCurrent=false; SESSION_MONTH_NAMES.forEach(function(mn,idx){ if(foundCurrent) return; var realMon=SESSION_TO_REAL[idx]; var yr=realMon>=3?year:year+1; var k=yr+"-"+String(realMon+1).padStart(2,"0"); var isPast=(yr=monthLastDay); if(!isPast&&!isCurrentComplete&&!isCurrentMonth) return; monthList.push({mn:mn,yr:yr,k:k,realMon:realMon}); if(k===key) foundCurrent=true; }); // ── Core: Distribute amt across months oldest-first ── // Step 1: Calculate each month's REMAINING BALANCE (after existing payments) var carry=0; var monthBalances=monthList.map(function(m){ var earned=calcEarnedForMonth(m.k,m.realMon,m.yr); var creditFromPrev=Math.abs(Math.min(0,carry)); var netDue=Math.max(0,earned-creditFromPrev); var totalOwed=netDue+Math.max(0,carry); if(!t.salaryLedger[m.k]) t.salaryLedger[m.k]=[]; var alreadyPaid=t.salaryLedger[m.k].reduce(function(s,e){return s+(e.amt||0);},0); var balance=totalOwed-alreadyPaid; carry=balance; return {mn:m.mn,yr:m.yr,k:m.k,balance:Math.max(0,balance),totalOwed:totalOwed,alreadyPaid:alreadyPaid}; }); // Step 2: Distribute entered amount across months from oldest to newest var remaining=amt; var paidLog=[]; monthBalances.forEach(function(mb){ if(remaining<=0) return; if(mb.balance<=0) return; // already fully paid var toPayThisMonth=Math.min(remaining, mb.balance); t.salaryLedger[mb.k].push({ amt:toPayThisMonth, date:dispDate, note: mb.k===key ? (note||"") : "Auto: "+mb.mn+" "+mb.yr }); paidLog.push(mb.mn+" "+mb.yr+": Rs."+toPayThisMonth.toLocaleString("en-IN")); remaining-=toPayThisMonth; }); saveData(); renderTpKhata(); var msg="Rs."+amt.toLocaleString("en-IN")+" distributed"; if(paidLog.length>1) showToast(msg+" across "+paidLog.length+" months"); else showToast("Rs."+amt.toLocaleString("en-IN")+" saved — "+dispDate); } function deleteKhataEntry(el) { var key=el.getAttribute("data-key"); var idx=parseInt(el.getAttribute("data-idx")||"-1"); if(idx<0) return; var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var entries=getKhataEntries(t,key); if(!confirm("Yeh payment entry delete karo?")) return; entries.splice(idx,1); saveData(); renderTpKhata(); showToast("Entry removed","error"); } function payFullMonth(el) { var key=el.getAttribute("data-key"); var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var monthlySal=parseInt(t.salary||0); var entries=getKhataEntries(t,key); var already=entries.reduce(function(s,e){return s+(e.amt||0);},0); var remaining=monthlySal-already; if(remaining<=0){showToast("Already fully paid!");return;} var now=new Date(); var dispDate=String(now.getDate()).padStart(2,"0")+"."+String(now.getMonth()+1).padStart(2,"0")+"."+now.getFullYear(); entries.push({amt:remaining,date:dispDate,note:"Full payment"}); saveData(); renderTpKhata(); showToast("Full salary paid — Rs."+remaining.toLocaleString("en-IN")); } function printSingleMonthSlip(el) { var tid=parseInt(el.getAttribute("data-tid")); var t=teachers.find(function(x){return x.id===tid;}); if(!t) return; var mn=el.getAttribute("data-month"); var yr=el.getAttribute("data-year"); var monthlySal=parseInt(el.getAttribute("data-sal")||0); var earnedSal=parseInt(el.getAttribute("data-earned")||0); var paidAmt=parseInt(el.getAttribute("data-paid")||0); var balance=parseInt(el.getAttribute("data-bal")||0); var deduction=parseInt(el.getAttribute("data-ded")||0); var bonus=parseInt(el.getAttribute("data-ot")||0); var P=parseInt(el.getAttribute("data-p")||0); var A=parseInt(el.getAttribute("data-a")||0); var L=parseInt(el.getAttribute("data-l")||0); var H=parseInt(el.getAttribute("data-h")||0); var today=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var W=''; W+='
Sara Azam Memorial Education
Salary Slip · '+mn+' '+yr+'
'+today+'
'; W+='
'; [["Name",t.name],["Designation",t.subject||""],["Class",t.class||""],["EMP ID","EMP-"+String(tid).slice(-4)]].forEach(function(f){W+='
'+f[0]+'
'+f[1]+'
';}); W+='
'; W+='
Attendance
'; [["P",P,"#15803D"],["A",A,"#DC2626"],["L",L,"#1D4ED8"],["H",H,"#B45309"]].forEach(function(s){W+='
'+s[1]+'
'+s[0]+'
';}); W+='
'; W+=''; var rows=[["Monthly Salary","Rs."+monthlySal.toLocaleString("en-IN"),"#0F172A"]]; if(deduction>0) rows.push(["Deduction","-Rs."+deduction.toLocaleString("en-IN"),"#DC2626"]); if(bonus>0) rows.push(["Overtime","+Rs."+bonus.toLocaleString("en-IN"),"#065F46"]); rows.forEach(function(r,i){W+='';}); W+='
Salary Details
'+r[0]+''+r[1]+'
NET PAYABLERs.'+earnedSal.toLocaleString("en-IN")+'
'; if(paidAmt>0||balance!==0){W+='
Payment Status
Received
Rs.'+paidAmt.toLocaleString("en-IN")+'
Balance
'+(balance>0?"Rs."+balance.toLocaleString("en-IN"):balance<0?"-Rs."+Math.abs(balance).toLocaleString("en-IN"):"Clear")+'
';} W+='
Declaration
मैं '+t.name+' घोषणा करता/करती हूँ कि मुझे माह '+mn+' '+yr+' का वेतन Rs.'+earnedSal.toLocaleString("en-IN")+' प्राप्त हुआ। I hereby declare salary received.
'; W+='
'+t.name+' (Employee)
Signature & Date
Principal
'+today+'
'; W+=''; var win=window.open("","_blank","width=700,height=600"); if(!win){showToast("Popup blocked!","error");return;} win.document.write(W); win.document.close(); win.focus(); setTimeout(function(){win.print();},400); } function unpayMonth(el) { var key=el.getAttribute("data-key"); var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t||!t.salaryLedger) return; if(!confirm("Is mahine ki saari payments delete karo?")) return; t.salaryLedger[key]=[]; saveData(); renderTpKhata(); showToast("All payments removed","error"); } function payPartialMonth(el) { addKhataEntry(el); } function toggleSalaryBtn(el) { payFullMonth(el); } function restoreStudentProfile(id) { // Remove from minimized delete minimizedProfiles[id]; renderTaskbar(); // Open profile openStudentDetail(id); } function closeMinimizedProfile(e, id) { e.stopPropagation(); delete minimizedProfiles[id]; renderTaskbar(); } function sdTab(name) { var tabs = ["info","admform","fee","idcard","rc","tc","att","diary","edit"]; tabs.forEach(function(t) { var panel = document.getElementById("sdp-"+t); var menu = document.getElementById("sdm-"+t); if(panel) panel.style.display = "none"; if(menu) { menu.style.background = "transparent"; menu.style.borderColor = "transparent"; menu.style.color = "#374151"; menu.style.fontWeight = "600"; } }); var activePanel = document.getElementById("sdp-"+name); var activeMenu = document.getElementById("sdm-"+name); if(activePanel) { activePanel.style.display = activePanel.id==="sdp-fee" ? "flex" : "block"; } if(activeMenu) { activeMenu.style.background = "#EFF6FF"; activeMenu.style.borderColor = "#BFDBFE"; activeMenu.style.color = "#1D4ED8"; activeMenu.style.fontWeight = "700"; } if(name==="fee"){setTimeout(renderFeeLedger,50);setTimeout(function(){feeTab('adm');},60);} if(name==="att"){ renderAttendancePanel(); } if(name==="diary"){ renderStudentDiary(); } renderDuesFlash(); } function openStudentDetail(id) { var s = students.find(function(x){return x.id===id;}); if(!s) return; currentDetailId = id; var i = students.indexOf(s); var a = avt(s.name, i); // Header var avEl = g("sd-av"); if(s.photo) { avEl.style.background = "transparent"; avEl.style.padding = "0"; avEl.innerHTML = ''; } else { avEl.style.background = a.bg; avEl.style.fontSize = "28px"; avEl.innerHTML = a.init; } g("sd-name").textContent = s.name; var sdClosedBadge = document.getElementById("sd-closed-badge"); if(!sdClosedBadge) { sdClosedBadge = document.createElement("span"); sdClosedBadge.id = "sd-closed-badge"; sdClosedBadge.style.cssText = "font-size:10px;font-weight:700;background:rgba(220,38,38,.25);color:#FCA5A5;border-radius:5px;padding:2px 8px;margin-left:8px;vertical-align:middle;"; g("sd-name").parentNode.insertBefore(sdClosedBadge, g("sd-name").nextSibling); } sdClosedBadge.textContent = s.admissionClosed ? "🔒 Closed" : ""; sdClosedBadge.style.display = s.admissionClosed ? "inline" : "none"; g("sd-class").textContent = displayClass(s.class); var rollBadge = g("sd-roll-badge"); if(rollBadge) rollBadge.textContent = s.roll || ""; // Info g("sd-roll").textContent = s.roll || "—"; g("sd-dob").textContent = s.dob || "—"; g("sd-cat").innerHTML = ''+s.category+''; var genderEl = g("sd-gender"); if(genderEl) genderEl.textContent = s.gender || "—"; // fee field removed g("sd-phone").textContent = s.phone || "—"; // Fee calculation - smart session-aware dues var feeInfoEl = g("sd-fee-info"); if(feeInfoEl) { var sBase = s.class ? s.class.split("-")[0] : ""; var sf = CLASS_FEES[sBase] || {admission:0, tuition:0, annual:0, exam:0, bus:0}; var monthlyFee = sf.tuition + (s.bus==="Yes" ? (s.busFare||sf.bus||0) : 0); var paidMonths = s.paidMonths || 0; if(!s.paidMonths) s.paidMonths = 0; // Session months: Apr=0, May=1, ... Mar=11 var SESSION_MTH_LABELS = ["Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","Jan","Feb","Mar"]; // Map session month index to real month (0=Jan) var SESSION_TO_REAL = [3,4,5,6,7,8,9,10,11,0,1,2]; // Figure out how many months have elapsed in current session var now = new Date(); var nowMonth = now.getMonth(); // 0=Jan var nowYear = now.getFullYear(); var sessionStart = ACTIVE_SESSION_START || 2025; var elapsedMonths = 0; for(var mi=0; mi<12; mi++) { var realMonth = SESSION_TO_REAL[mi]; var realYear = realMonth >= 3 ? sessionStart : sessionStart + 1; if(realYear < nowYear || (realYear === nowYear && realMonth <= nowMonth)) { elapsedMonths = mi + 1; } } // Dues = elapsed months - paid months (only count months that have started) var dueMonths = Math.max(0, elapsedMonths - paidMonths); var totalDues = dueMonths * monthlyFee; var feeHtml = ""; // Top cards feeHtml += '
'; feeHtml += '
'; feeHtml += '
Monthly Fee
'; feeHtml += '
₹'+monthlyFee.toLocaleString("en-IN")+'
'; feeHtml += (s.bus==="Yes" ? '
Incl. 🚌 Bus ₹'+(sf.bus||0)+'
' : ''); feeHtml += '
'; feeHtml += '
'; feeHtml += '
Current Dues
'; feeHtml += '
'+(totalDues>0?"₹"+totalDues.toLocaleString("en-IN"):"✓ Clear")+'
'; feeHtml += '
'+(totalDues>0?dueMonths+" month(s) due":"Up to date ✓")+'
'; feeHtml += '
'; feeHtml += '
'; // Month tracker feeHtml += '
'; feeHtml += '
'+CURRENT_SESSION+' · Fee Payment Tracker
'; feeHtml += '
'; SESSION_MTH_LABELS.forEach(function(m, idx) { var realMonth = SESSION_TO_REAL[idx]; var realYear = realMonth >= 3 ? sessionStart : sessionStart + 1; var hasStarted = (realYear < nowYear || (realYear === nowYear && realMonth <= nowMonth)); var isPaid = paidMonths > idx; var isDue = hasStarted && !isPaid; var bg, color, border, label; if(isPaid) { bg="#DCFCE7"; color="#15803D"; border="#BBF7D0"; label=m; } else if(isDue) { bg="#FEE2E2"; color="#B91C1C"; border="#FECACA"; label=m+"!"; } else { bg="#F1F5F9"; color="#CBD5E1"; border="#E2E8F0"; label=m; } feeHtml += '' + label+''; }); feeHtml += '
'; feeHtml += '
'; feeHtml += 'Paid'; feeHtml += 'Due'; feeHtml += 'Upcoming'; feeHtml += 'Tap to toggle'; feeHtml += '
'; feeHtml += '
'; feeInfoEl.innerHTML = feeHtml; } var busel = g("sd-bus"); var busbtn = g("sd-bus-btn"); if(busel) { busel.textContent = s.bus==="Yes" ? "✓ Yes — Enabled" : "✗ No — Disabled"; busel.style.color = s.bus==="Yes" ? "#15803D" : "#B91C1C"; } if(busbtn) { if(s.bus==="Yes") { busbtn.textContent = "Disable"; busbtn.style.background = "#FEE2E2"; busbtn.style.color = "#B91C1C"; } else { busbtn.textContent = "Enable"; busbtn.style.background = "#DCFCE7"; busbtn.style.color = "#15803D"; } } // Parents g("sd-father").textContent = s.father || "—"; g("sd-mother").textContent = s.mother || "—"; // Address var addrParts = []; if(s.addr1) addrParts.push(s.addr1); if(s.city) addrParts.push("Village/City: " + s.city); if(s.dist) addrParts.push("District: " + s.dist); if(s.state) addrParts.push("State: " + s.state); if(s.pin) addrParts.push("PIN: " + s.pin); g("sd-address").textContent = addrParts.length ? addrParts.join(", ") : "Address not provided"; renderStudentAttendanceBox(s); renderNoticeFlash("sd-notice-flash","Students"); openModal("student-detail"); renderDuesFlash(); sdTab("info"); } function closeFeeConfirm() { var el = document.getElementById("fee-paid-confirm"); if(el) el.remove(); } function printPendingReceipt() { closeFeeConfirm(); if(!window._pendingReceipt) return; var p = window._pendingReceipt; printMonthlyReceiptDirect(p.id, p.monthIdx, p.monthName, p.year); window._pendingReceipt = null; } function toggleMonthPaid(id, monthIdx) { var s = students.find(function(x){ return x.id===id; }); if(!s) return; if(!s.paidMonths && s.paidMonths !== 0) s.paidMonths = 0; var wasPaid = s.paidMonths > monthIdx; // If clicking on already paid month - unpay from that month if(wasPaid) { s.paidMonths = monthIdx; // reduce to this month saveData(); openStudentDetail(id); } else { // Mark as paid - show receipt option s.paidMonths = monthIdx + 1; saveData(); openStudentDetail(id); // Ask to print receipt var SESSION_MTH_LABELS = ["Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","Jan","Feb","Mar"]; var MONTHS_FULL = ["April","May","June","July","August","September","October","November","December","January","February","March"]; var monthName = MONTHS_FULL[monthIdx] || SESSION_MTH_LABELS[monthIdx]; var sessionStart = ACTIVE_SESSION_START || 2025; var SESSION_TO_REAL = [3,4,5,6,7,8,9,10,11,0,1,2]; var realMonth = SESSION_TO_REAL[monthIdx]; var realYear = realMonth >= 3 ? sessionStart : sessionStart + 1; // Show mini confirmation with print option setTimeout(function(){ var base = s.class ? s.class.split("-")[0] : ""; var sf = CLASS_FEES[base] || {tuition:0, bus:0}; var monthlyFee = sf.tuition + (s.bus==="Yes" ? (s.busFare||sf.bus||0) : 0); var confirm_el = document.createElement("div"); confirm_el.id = "fee-paid-confirm"; confirm_el.style.cssText = "position:fixed;bottom:28px;left:50%;transform:translateX(-50%);background:#0F172A;color:#fff;border-radius:14px;padding:16px 20px;z-index:99999;box-shadow:0 8px 32px rgba(0,0,0,.3);display:flex;align-items:center;gap:14px;min-width:300px;max-width:95vw;"; confirm_el.innerHTML = '
'+ '
'+ '
'+monthName+' '+realYear+' — Fee Paid
'+ '
₹'+monthlyFee.toLocaleString("en-IN")+' received
'+ '
'+ ''+ ''; document.body.appendChild(confirm_el); // Auto remove after 8 seconds setTimeout(function(){ var el = document.getElementById("fee-paid-confirm"); if(el) el.remove(); }, 8000); }, 300); } } function printMonthlyReceiptDirect(id, monthIdx, monthName, year) { // Close the confirm bar var el = document.getElementById("fee-paid-confirm"); if(el) el.remove(); var s = students.find(function(x){ return x.id===id; }); if(!s) return; var base = s.class ? s.class.split("-")[0] : ""; var sf = CLASS_FEES[base] || {admission:0, tuition:0, annual:0, exam:0, bus:0}; var noPrefix = ["Play","Nursery","L.K.G","U.K.G"]; var clsLabel = (noPrefix.indexOf(base)>=0?"":("Class "))+base+ (s.class&&s.class.split("-")[1]?" - Sec "+s.class.split("-")[1]:""); var tuition = sf.tuition; var busFee = s.bus==="Yes" ? (sf.bus||0) : 0; var total = tuition + busFee; var receiptNo = "SAME/MR/"+year+"/"+monthName.substring(0,3).toUpperCase()+"/"+String(s.id).slice(-4).padStart(4,"0"); var today = new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var addr = [s.city, s.dist, s.state].filter(Boolean).join(", ") || "—"; var html = '
'; html += '
'; html += '
'; html += '
Sara Azam Memorial Education
'; html += '
CBSE Affiliated · '+SESSION_SHORT+'
'; html += '
MONTHLY FEE RECEIPT
'; html += '
'; html += '
'; html += '
Receipt No: '+receiptNo+'
'; html += '
Date: '+today+'
'; html += '
'; html += '
'; html += '
👨‍🎓 Student Details
'; html += '
'; [["Name",s.name],["Class",clsLabel],["Roll No.",s.roll],["Father",s.father||"—"],["Phone",s.phone||"—"],["Address",addr]].forEach(function(f){ html += '
'+f[0]+'
'+f[1]+'
'; }); html += '
'; html += '
'; html += '
Fee Period
'; html += '
'+monthName+' '+year+'
'; html += '
'; html += '
'; html += '
💳 Fee Details
'; [["Monthly Tuition Fee",tuition]].concat(busFee>0?[["🚌 Bus Fee",busFee]]:[]).forEach(function(item,i){ html += '
'+item[0]+'₹'+item[1].toLocaleString("en-IN")+'
'; }); html += '
Total Amount₹'+total.toLocaleString("en-IN")+'
'; html += '
'; html += '
'; ["Parent/Guardian","Cashier","Principal"].forEach(function(sig){ html += '
'+sig+'
'; }); html += '
'; html += '
⚠️ Computer generated receipt. Keep for your records.
'; html += '
'; var origBody = document.body.innerHTML; var origTitle = document.title; document.title = "Fee Receipt - "+s.name+" - "+monthName+" "+year; document.body.innerHTML = '
'+html+'
'; var st = document.createElement("style"); st.innerHTML = "@media print{body{-webkit-print-color-adjust:exact;print-color-adjust:exact;}@page{margin:6mm;size:A5;}}"; document.head.appendChild(st); window.print(); setTimeout(function(){ document.body.innerHTML = origBody; document.title = origTitle; updateDash(); renderStudents(); openStudentDetail(id); }, 600); } function printAdmissionFormFromProfile() { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s){ showToast("Student not found","error"); return; } currentReceiptStudent = s; printAdmissionForm(); } function printAdmissionReceiptFromProfile() { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s){ showToast("Student not found","error"); return; } showAdmissionReceipt(s); } function toggleBusService() { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; s.bus = s.bus==="Yes" ? "No" : "Yes"; // Refresh display var busel = g("sd-bus"); var busbtn = g("sd-bus-btn"); if(busel) { busel.textContent = s.bus==="Yes" ? "✓ Yes — Enabled" : "✗ No — Disabled"; busel.style.color = s.bus==="Yes" ? "#15803D" : "#B91C1C"; } if(busbtn) { busbtn.textContent = s.bus==="Yes" ? "Disable" : "Enable"; busbtn.style.background = s.bus==="Yes" ? "#FEE2E2" : "#DCFCE7"; busbtn.style.color = s.bus==="Yes" ? "#B91C1C" : "#15803D"; } saveData(); showToast("Bus service " + (s.bus==="Yes"?"enabled":"disabled") + " for " + s.name); renderFees(); } function confirmDeleteStudent() { if(!currentDetailId) return; var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; // Create confirmation popup var popup = document.createElement("div"); popup.id = "delete-confirm-popup"; popup.style.cssText = "position:fixed;inset:0;background:rgba(15,23,42,.6);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);"; popup.innerHTML = '
' + '
🗑️
' + '
Student Delete Karen?
' + '
'+s.name+' ko permanently delete kiya jayega.
' + '
Yeh action undo nahi ho sakta. Kya aap sure hain?
' + '
' + '' + '' + '
' + '
'; document.body.appendChild(popup); } function doDeleteStudent() { closeDeleteConfirm(); if(!currentDetailId) return; removeStudent(currentDetailId); closeModal("student-detail"); currentDetailId = null; } function closeDeleteConfirm() { var el = document.getElementById("delete-confirm-popup"); if(el) el.remove(); } function removeFromDetail() { confirmDeleteStudent(); } function viewRCFromDetail() { // profile stays open showTab("reportcard"); setTimeout(function(){ viewRC(currentDetailId); }, 200); } function viewTCFromDetail() { // profile stays open showTab("tc"); setTimeout(function(){ viewTC(currentDetailId); }, 200); } function removeTeacher(id){ if(!confirm("Remove this teacher?")) return; teachers=teachers.filter(function(t){return t.id!==id;}); saveData();showToast("Teacher removed","error");renderTeachers();updateDash(); } var currentTeacherId = null; function openTeacherProfile(id) { var t = teachers.find(function(x){return x.id===id;}); if(!t) return; if(!t.attendance) t.attendance = {}; if(!t.salaryLedger) t.salaryLedger = {}; // Migrate old salaryPaid to ledger if(t.salaryPaid && Object.keys(t.salaryPaid).length>0) { var msal=parseInt(t.salary||0); Object.keys(t.salaryPaid).forEach(function(k){ if(!t.salaryLedger[k]||!t.salaryLedger[k].length) { var e=t.salaryPaid[k]; var a=(e===true)?msal:(typeof e==="number"?e:0); if(a>0){if(!t.salaryLedger[k])t.salaryLedger[k]=[];t.salaryLedger[k].push({amt:a,date:k+"-01",note:"Migrated"});} } }); t.salaryPaid={}; saveData(); } currentTeacherId = id; if(!t.attendance) t.attendance = {}; if(!t.salaryPaid) t.salaryPaid = {}; var i = teachers.indexOf(t); var a = avt(t.name, i+2); var noPrefix=["Play","Nursery","L.K.G","U.K.G"]; var clsBase=t.class?t.class.split("-")[0]:""; var clsLabel=t.class?((noPrefix.indexOf(clsBase)>=0?"":("Class "))+t.class):"—"; // Header var avEl=g("tp-av"); if(t.photo) { avEl.style.background="transparent"; avEl.style.overflow="hidden"; avEl.innerHTML=''; } else { avEl.style.background=a.bg; avEl.innerHTML=a.init; } g("tp-name").textContent=t.name; g("tp-subject").textContent=t.subject+(t.qual?" · "+t.qual:"")+(t.category_type?" · "+t.category_type:""); g("tp-class-badge").textContent="📚 "+clsLabel; g("tp-exp-badge").textContent="⏱ "+(t.exp||"—"); // Info grid var fields=[ ["Gender",t.gender||"—"],["Date of Birth",fixDateFormat(t.dob)],["Phone",t.phone||"—"], ["Joining Date",fixDateFormat(t.joining)],["Qualification",t.qual||"—"],["Salary","₹"+parseInt(t.salary||0).toLocaleString("en-IN")+"/month"] ]; g("tp-info-grid").innerHTML=fields.map(function(f){ return '
' +'
'+f[0]+'
' +'
'+f[1]+'
' +'
'; }).join(""); // Class info var studCount=students.filter(function(s){return s.class&&s.class.split("-")[0]===clsBase;}).length; g("tp-class-info").innerHTML='
'+clsLabel+'
' +'
'+studCount+' students enrolled in this class
' +(t.address?'
📍 '+t.address+'
':""); // Set default month/year for attendance var now=new Date(); var attMonth=g("tp-att-month"), attYear=g("tp-att-year"); // Map session month (Apr=0) to option value var sessionMonthIdx=now.getMonth()>=3?(now.getMonth()-3):(now.getMonth()+9); if(attMonth) attMonth.value=String(sessionMonthIdx); if(attYear) attYear.value=String(now.getFullYear()); if(g("tp-khata-year")) g("tp-khata-year").value=String(now.getFullYear()); renderNoticeFlash("tp-notice-flash","Teachers"); openModal("teacher-profile"); tpTab("profile"); renderTpAttendance(); renderTpKhata(); } // ════════════════════════════════════════ // LESSON PLAN SYSTEM // ════════════════════════════════════════ var SESSION_MONTHS_LP = ["April","May","June","July","August","September","October","November","December","January","February","March"]; function getLpKey() { var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return null; var subj=document.getElementById("lp-subject")?document.getElementById("lp-subject").value:""; var cls=document.getElementById("lp-class")?document.getElementById("lp-class").value:""; if(!subj||!cls) return null; return "lp_"+t.id+"_"+cls.replace(/\s/g,"_")+"_"+subj.replace(/\s/g,"_"); } function renderLessonPlan() { var wrap=document.getElementById("lp-table-wrap"); if(!wrap) return; var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var subj=document.getElementById("lp-subject")?document.getElementById("lp-subject").value:""; var cls=document.getElementById("lp-class")?document.getElementById("lp-class").value:""; if(!subj||!cls){ wrap.innerHTML='
Subject aur Class select karo
'; return; } // Load saved data var key=getLpKey(); var saved=key&&t.lessonPlans&&t.lessonPlans[key]?t.lessonPlans[key]:{}; var rows=SESSION_MONTHS_LP.map(function(month,idx){ var d=saved[month]||{topics:"",periods:"",method:"",remarks:""}; return '' +''+month+'' +'' +'' +'' +'' +'' +'' +'' +'' +'' +'' +'' +'' +''; }).join(""); wrap.innerHTML='
' +'📋 '+t.name+'' +''+cls+' · '+subj+'' +'Session: '+CURRENT_SESSION+'' +'
' +'' +'' +'' +'' +'' +'' +'' +'' +''+rows+'' +'
MonthTopics / ChaptersPeriodsMethodRemarks
'; } function saveLessonPlan() { var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var key=getLpKey(); if(!key){showToast("Subject aur Class select karo","error");return;} if(!t.lessonPlans) t.lessonPlans={}; var data={}; SESSION_MONTHS_LP.forEach(function(month,idx){ var topics=document.getElementById("lp-topics-"+idx); var periods=document.getElementById("lp-periods-"+idx); var method=document.getElementById("lp-method-"+idx); var remarks=document.getElementById("lp-remarks-"+idx); data[month]={ topics:topics?topics.value.trim():"", periods:periods?periods.value:"", method:method?method.value:"", remarks:remarks?remarks.value.trim():"" }; }); t.lessonPlans[key]=data; saveData(); showToast("Lesson Plan saved!"); } function printLessonPlan() { var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var subj=document.getElementById("lp-subject")?document.getElementById("lp-subject").value:""; var cls=document.getElementById("lp-class")?document.getElementById("lp-class").value:""; if(!subj||!cls){showToast("Subject aur Class select karo","error");return;} var key=getLpKey(); var saved=key&&t.lessonPlans&&t.lessonPlans[key]?t.lessonPlans[key]:{}; var today=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var totalPeriods=0; SESSION_MONTHS_LP.forEach(function(m){if(saved[m]&&saved[m].periods)totalPeriods+=parseInt(saved[m].periods)||0;}); var rows=SESSION_MONTHS_LP.map(function(month,idx){ var d=saved[month]||{topics:"",periods:"",method:"",remarks:""}; return '' +''+month+'' +''+(d.topics||'')+'' +''+(d.periods||'—')+'' +''+(d.method||'—')+'' +''+(d.remarks||'—')+'' +''; }).join(""); var W='' +''; // Header W+='
'; W+='
'; W+='
'; W+='
Sara Azam Memorial Education
'; W+='
Yearly Lesson Plan · '+cls+' · '+subj+'
'; W+='
Session: '+CURRENT_SESSION+'
'; W+='
'; W+='
Date: '+today+'
'; W+='
Total Periods: '+totalPeriods+'
'; W+='
'; // Teacher info W+='
'; [["Teacher",t.name],["Subject",subj],["Class",cls],["Qualification",t.qual||"—"]].forEach(function(f){ W+='
'; W+='
'+f[0]+'
'; W+='
'+f[1]+'
'; }); W+='
'; // Table W+=''; W+=''; [["Month","12%"],["Topics / Chapters","35%"],["Periods","8%"],["Teaching Method","15%"],["Remarks","30%"]].forEach(function(h){ W+=''; }); W+=''+rows+'
'+h[0]+'
'; // Signature W+='
'; [["Prepared By\n"+t.name,""],["Checked By",""],["Principal / HM",""]].forEach(function(s){ W+='
'; W+='
'; W+='
'+s[0]+'
'; }); W+='
'; W+=''; var win=window.open("","_blank","width=900,height=700"); if(!win){showToast("Popup blocked","error");return;} win.document.write(W); win.document.close(); win.focus(); setTimeout(function(){win.print();},400); } function tpTab(name) { ["profile","attendance","khata","edit","salary-settings","lesson-plan","diary"].forEach(function(t) { var p=document.getElementById("tpp-"+t), m=document.getElementById("tpm-"+t); if(p) p.style.display="none"; if(m){m.style.background="transparent";m.style.borderColor="transparent";m.style.color="#374151";m.style.fontWeight="600";} }); var ap=document.getElementById("tpp-"+name), am=document.getElementById("tpm-"+name); if(ap) ap.style.display="block"; if(am){am.style.background="#ECFDF5";am.style.borderColor="#6EE7B7";am.style.color="#065F46";am.style.fontWeight="700";} if(name==="edit") populateTpEdit(); if(name==="khata") renderTpKhata(); if(name==="lesson-plan") renderLessonPlan(); if(name==="diary") renderTeacherDiary(); } function populateTpEdit() { var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var sv=function(id,v){var el=document.getElementById(id);if(el)el.value=v||"";}; sv("tpe-name",t.name); sv("tpe-subject",t.subject); sv("tpe-qual",t.qual); sv("tpe-exp",t.exp); sv("tpe-phone",t.phone); sv("tpe-salary",t.salary); sv("tpe-dob",fixDateFormat(t.dob)); sv("tpe-address",t.address); sv("tpe-joining",fixDateFormat(t.joining)); sv("tpe-gender",t.gender); // Category type var catType = t.category_type || getTeacherCategoryFromClass(t.class); sv("tpe-category-type", catType); // Class dropdown filtered by category if(catType){ filterClassByTeacherCategory("tpe-class", catType); } else { var ecl=document.getElementById("tpe-class"); if(ecl){ ecl.innerHTML=''; BASE_CLASSES.forEach(function(bc){ var o=document.createElement("option"); o.value=bc+"-A"; o.text=bc+"-A"; ecl.appendChild(o); }); } } var ecl=document.getElementById("tpe-class"); if(ecl){ ecl.value=t.class||""; if(t.class&&ecl.value!==t.class){ var ex=document.createElement("option");ex.value=t.class;ex.text=t.class;ecl.appendChild(ex);ecl.value=t.class; } } // Photo var prev=document.getElementById("tpe-photo-prev"); var clearBtn=document.getElementById("tpe-photo-clear"); if(prev){ if(t.photo){prev.innerHTML='';if(clearBtn)clearBtn.style.display="inline";} else{prev.innerHTML="👤";prev.style.fontSize="22px";if(clearBtn)clearBtn.style.display="none";} prev._newPhoto=undefined; } } function saveTpEdit() { var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var gv=function(id){var el=document.getElementById(id);return el?el.value.trim():"";}; var name=gv("tpe-name"); if(!name){showToast("Name required","error");return;} t.name=name; t.subject=gv("tpe-subject"); t.qual=gv("tpe-qual"); t.exp=gv("tpe-exp"); t.phone=gv("tpe-phone"); t.salary=gv("tpe-salary"); t.dob=fixDateFormat(gv("tpe-dob")); t.address=gv("tpe-address"); t.joining=fixDateFormat(gv("tpe-joining")); t.gender=gv("tpe-gender"); t.class=gv("tpe-class"); t.category_type=gv("tpe-category-type"); // Photo var prev=document.getElementById("tpe-photo-prev"); if(prev&&prev._newPhoto!==undefined) t.photo=prev._newPhoto; saveData(); renderTeachers(); // Refresh header in profile var i=teachers.indexOf(t); var a=avt(t.name,i+2); var avEl=g("tp-av"); if(avEl){ if(t.photo){avEl.style.background="transparent";avEl.style.overflow="hidden";avEl.innerHTML='';} else{avEl.style.background=a.bg;avEl.innerHTML=a.init;} } if(g("tp-name")) g("tp-name").textContent=t.name; if(g("tp-subject")) g("tp-subject").textContent=t.subject+(t.qual?" · "+t.qual:""); if(g("tp-class-badge")) g("tp-class-badge").textContent="📚 "+t.class; if(g("tp-exp-badge")) g("tp-exp-badge").textContent="⏱ "+(t.exp||"—"); showToast("Profile updated — "+name); tpTab("profile"); } function tpePhotoPreview(input) { if(!input.files||!input.files[0]) return; var file=input.files[0]; if(file.size>2*1024*1024){showToast("Photo 2MB se chhota hona chahiye","error");return;} var reader=new FileReader(); reader.onload=function(e){ var prev=document.getElementById("tpe-photo-prev"); if(prev){prev.innerHTML='';prev._newPhoto=e.target.result;} var cb=document.getElementById("tpe-photo-clear"); if(cb) cb.style.display="inline"; }; reader.readAsDataURL(file); } function tpeClearPhoto() { var prev=document.getElementById("tpe-photo-prev"); if(prev){prev.innerHTML="👤";prev.style.fontSize="22px";prev._newPhoto="";} var cb=document.getElementById("tpe-photo-clear"); if(cb) cb.style.display="none"; var inp=document.getElementById("tpe-photo-inp"); if(inp) inp.value=""; } function removeTeacherFromProfile() { if(!currentTeacherId) return; var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var popup=document.createElement("div"); popup.id="tp-delete-popup"; popup.style.cssText="position:fixed;inset:0;background:rgba(15,23,42,.6);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);"; popup.innerHTML='
' +'
🗑️
' +'
Teacher Remove Karen?
' +'
'+t.name+' ko permanently remove kiya jayega.
' +'
' +'' +'' +'
'; document.body.appendChild(popup); } function closeTeacherDeletePopup() { var el=document.getElementById("tp-delete-popup"); if(el) el.remove(); } function doRemoveTeacher() { var el=document.getElementById("tp-delete-popup"); if(el) el.remove(); teachers=teachers.filter(function(t){return t.id!==currentTeacherId;}); saveData(); showToast("Teacher removed","error"); closeModal("teacher-profile"); renderTeachers(); updateDash(); } // ── TEACHER ATTENDANCE ── var SESSION_MONTH_NAMES=["April","May","June","July","August","September","October","November","December","January","February","March"]; var SESSION_TO_REAL=[3,4,5,6,7,8,9,10,11,0,1,2]; function cycleAttendance(el) { if(el.getAttribute("data-future")==="1") return; var key=el.getAttribute("data-key"), day=el.getAttribute("data-day"); var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t||!key||!day) return; if(!t.attendance) t.attendance={}; if(!t.attendance[key]) t.attendance[key]={}; var seq=["","P","A","H","L"]; var cur=t.attendance[key][day]||""; var next=seq[(seq.indexOf(cur)+1)%seq.length]; // Check if this is today or back date var now2=new Date(); var parts=key.split("-"); var cellDate=new Date(parseInt(parts[0]),parseInt(parts[1])-1,parseInt(day)); var isToday=(cellDate.toDateString()===now2.toDateString()); var isBackDate=cellDate📅' +'
Back Date Change?
' +'
Date: '+dateStr+'   '+(cur||"blank")+''+(next||"blank")+'
' +'
' +'' +'' +'
'; document.body.appendChild(popup); popup.querySelector("#atc-yes").onclick=function(){popup.remove();t.attendance[key][day]=next;saveData();renderTpAttendance();}; popup.querySelector("#atc-no").onclick=function(){popup.remove();}; popup.onclick=function(e){if(e.target===popup)popup.remove();}; } else { // Today or blank cell — direct change t.attendance[key][day]=next; saveData(); renderTpAttendance(); } } // ── TEACHER KHATA / SALARY BOOK ── function toggleSalaryBtn(el) { // Kept for backward compatibility - delegates to payFullMonth or unpayMonth payFullMonth(el); } // ── PRINT BALANCE SHEET ── function printBalanceSheet() { var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var yearSel=g("tp-khata-year"); var year=yearSel?parseInt(yearSel.value):new Date().getFullYear(); var today=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var html2=buildBalanceSheetHTML(t, year, today); var win=window.open("","_blank","width=860,height=700"); win.document.write(html2); win.document.close(); win.focus(); setTimeout(function(){win.print();},400); } function printAllKhata() { if(!teachers.length){showToast("No teachers","error");return;} var yearSel=g("tp-khata-year"); var year=yearSel?parseInt(yearSel.value):new Date().getFullYear(); var today=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var W='' +''; // Cover summary table W+='
'; W+='
'; W+='
Sara Azam Memorial Education
'; W+='
All Teachers Salary Summary · '+year+' · Printed: '+today+'
'; W+='
'; // Summary table var totalAnnual=0, totalPaid=0; W+=''; W+='' +'' +'' +'' +'' +'' +'' +'' +''; teachers.forEach(function(t,ti){ if(!t.salaryLedger) t.salaryLedger={}; var monthlySal=parseInt(t.salary||0); var annual=monthlySal*12; var paidTotal=0; SESSION_MONTH_NAMES.forEach(function(mn,idx){ var realMon=SESSION_TO_REAL[idx]; var yr=realMon>=3?year:year+1; var k=yr+"-"+String(realMon+1).padStart(2,"0"); getKhataEntries(t,k).forEach(function(e){paidTotal+=e.amt||0;}); }); var balance=annual-paidTotal; totalAnnual+=annual; totalPaid+=paidTotal; var statusLabel=paidTotal===0?"Pending":balance<=0?"Fully Paid":"Partial"; var statusCol=balance<=0?"#15803D":paidTotal===0?"#DC2626":"#B45309"; var statusBg=balance<=0?"#F0FDF4":paidTotal===0?"#FEF2F2":"#FFFBEB"; W+='' +'' +'' +'' +'' +'' +'' +'' +''; }); var totalBalance=totalAnnual-totalPaid; W+='' +'' +'' +'' +'' +''; W+='
TeacherSubjectMonthlyAnnualPaidBalanceStatus
'+t.name+''+t.subject+'Rs.'+monthlySal.toLocaleString("en-IN")+'Rs.'+annual.toLocaleString("en-IN")+'Rs.'+paidTotal.toLocaleString("en-IN")+'Rs.'+Math.abs(balance).toLocaleString("en-IN")+(balance<0?" (Cr)":"")+''+statusLabel+'
TOTAL ('+teachers.length+' staff)Rs.'+totalAnnual.toLocaleString("en-IN")+'Rs.'+totalPaid.toLocaleString("en-IN")+'Rs.'+totalBalance.toLocaleString("en-IN")+'
'; // Individual sheets (one per teacher, new page) teachers.forEach(function(t,ti){ W+='
'; W+=buildBalanceSheetHTML(t, year, today); W+='
'; }); W+=''; var win=window.open("","_blank","width=900,height=700"); if(!win){showToast("Popup blocked!","error");return;} win.document.write(W); win.document.close(); win.focus(); setTimeout(function(){win.print();},500); } function buildBalanceSheetHTML(t, year, today) { var monthlySal=parseInt(t.salary||0); var annual=monthlySal*12; var paidTotal=0; var rows=""; SESSION_MONTH_NAMES.forEach(function(mn,idx){ var realMon=SESSION_TO_REAL[idx]; var yr=realMon>=3?year:year+1; var k=yr+"-"+String(realMon+1).padStart(2,"0"); var entries=(t.salaryLedger&&t.salaryLedger[k])||[]; var paidAmt=entries.reduce(function(s,e){return s+(e.amt||0);},0); var balance=monthlySal-paidAmt; var status=paidAmt===0?"Pending":paidAmt>=monthlySal?"Paid":"Partial"; var statusCol=paidAmt===0?"#DC2626":paidAmt>=monthlySal?"#15803D":"#D97706"; var statusBg=paidAmt===0?"#FEF2F2":paidAmt>=monthlySal?"#F0FDF4":"#FFFBEB"; paidTotal+=paidAmt; rows+='' +''+mn+" "+yr+'' +'₹'+monthlySal.toLocaleString("en-IN")+'' +''+(paidAmt>0?"₹"+paidAmt.toLocaleString("en-IN"):"—")+'' +''+(balance>0?"₹"+balance.toLocaleString("en-IN"):"—")+'' +''+status+'' +''; }); var h='' +'' +''; // Header h+='
'; h+='
'; h+='
'; h+='
Sara Azam Memorial Education
'; h+='
Salary Balance Sheet · Session '+SESSION_START_YEAR+'–'+String(SESSION_END_YEAR).slice(-2)+'
'; h+='
Printed: '+today+'
'; h+='
'; // Teacher info h+='
'; [["Teacher Name",t.name],["Subject / Class",t.subject+(t.class?" · "+t.class:"")],["Qualification",t.qual||"—"], ["Phone",t.phone||"—"],["Joining Date",fixDateFormat(t.joining)],["Monthly Salary","₹"+monthlySal.toLocaleString("en-IN")] ].forEach(function(f){ h+='
'; h+='
'+f[0]+'
'; h+='
'+f[1]+'
'; }); h+='
'; // Summary banner var pending=annual-paidTotal; h+='
'; [["Annual Salary","₹"+annual.toLocaleString("en-IN"),"#1D4ED8","#EFF6FF","#BFDBFE"], ["Total Paid","₹"+paidTotal.toLocaleString("en-IN"),"#15803D","#F0FDF4","#86EFAC"], ["Balance Due","₹"+pending.toLocaleString("en-IN"),pending>0?"#DC2626":"#15803D",pending>0?"#FEF2F2":"#F0FDF4",pending>0?"#FECACA":"#86EFAC"] ].forEach(function(s){ h+='
'; h+='
'+s[0]+'
'; h+='
'+s[1]+'
'; }); h+='
'; // Table h+=''; h+=''; ["Month","Salary Due","Paid","Balance","Status"].forEach(function(col){ h+=''; }); h+=''+rows+''; h+=''; h+=''; h+=''; h+=''; h+=''; h+='
'+col+'
TOTAL₹'+annual.toLocaleString("en-IN")+'₹'+paidTotal.toLocaleString("en-IN")+'₹'+pending.toLocaleString("en-IN")+'
'; // Signatures h+='
'; [["Teacher Signature",""],["Verified By",""],["Principal","Date: "+today]].forEach(function(s){ h+='
'; h+='
'+s[0]+'
'; if(s[1]) h+='
'+s[1]+'
'; h+='
'; }); h+='
'; h+=''; return h; } // ── PRINT INDIVIDUAL TEACHER ATTENDANCE ── function printOneTeacherAtt() { var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var monthSel=g("tp-att-month"), yearSel=g("tp-att-year"); var sessionMonthIdx=monthSel?parseInt(monthSel.value):0; var year=yearSel?parseInt(yearSel.value):new Date().getFullYear(); var today=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var html2=buildAttendanceHTML([t], sessionMonthIdx, year, today, true); var win=window.open("","_blank","width=860,height=700"); win.document.write(html2); win.document.close(); win.focus(); setTimeout(function(){win.print();},400); } // ── PRINT ALL TEACHERS ATTENDANCE ── // ── TEACHER TAB SWITCHER ── // SMART ATTENDANCE SYSTEM var _saStream=null; var _saQrStream=null; var _saQrInterval=null; function openSmartAttendance() { var now=new Date(); var monthSel=document.getElementById("bulk-att-month"); var yearSel=document.getElementById("bulk-att-year"); var sessionMonthIdx=monthSel?parseInt(monthSel.value):0; var year=yearSel?parseInt(yearSel.value):now.getFullYear(); var realMonth=SESSION_TO_REAL[sessionMonthIdx]; var monthName=SESSION_MONTH_NAMES[sessionMonthIdx]; var lbl=document.getElementById("sa-att-date-label"); var dk=getTodayDK(); if(lbl) lbl.textContent=monthName+" "+year+" · "+now.toLocaleDateString("en-IN",{day:"numeric",month:"long"})+" · Day "+parseInt(dk); document.getElementById("modal-smart-att").classList.remove("hidden"); document.body.style.overflow="hidden"; _openModalCount++; buildTeacherBtns(); updateSaTodayList(); updateSaFooter(); // Auto-focus ID input setTimeout(function(){var i=document.getElementById("sa-id-input");if(i)i.focus();},200); } function closeSmartAttendance() { stopCamera(); stopQRScan(); document.getElementById("modal-smart-att").classList.add("hidden"); _openModalCount=Math.max(0,_openModalCount-1); if(_openModalCount===0) document.body.style.overflow=""; renderBulkAttendance(); // refresh main attendance table showToast("Attendance saved!"); } function saTab(name) { // No-op: all panels now visible simultaneously if(name==="id"){ updateSaTodayList(); setTimeout(function(){var i=document.getElementById("sa-id-input");if(i)i.focus();},100);} } function getAttKey() { var monthSel=document.getElementById("bulk-att-month"); var yearSel=document.getElementById("bulk-att-year"); var sessionMonthIdx=monthSel?parseInt(monthSel.value):0; var year=yearSel?parseInt(yearSel.value):new Date().getFullYear(); var realMonth=SESSION_TO_REAL[sessionMonthIdx]; var yr=realMonth>=3?year:year+1; return yr+"-"+String(realMonth+1).padStart(2,"0"); } function getTodayDK(){var d=new Date();return String(d.getDate()).padStart(2,"00");} function markTeacherPresent(tid,source) { var t=teachers.find(function(x){return x.id===tid;}); if(!t) return false; var key=getAttKey(); var dk=getTodayDK(); if(!t.attendance) t.attendance={}; if(!t.attendance[key]) t.attendance[key]={}; t.attendance[key][dk]="P"; saveData(); updateSaFooter(); updateSaTodayList(); buildTeacherBtns(); var cell=document.querySelector('td[data-tid="'+tid+'"][data-key="'+key+'"][data-day="'+dk+'"]'); if(cell){ cell.style.background="#DCFCE7"; var sp=cell.querySelector("span"); if(sp){sp.style.color="#15803D";sp.textContent="P";} var pEl=document.getElementById("sc-P-"+tid); if(pEl){ var P2=0,dim=new Date(parseInt(key.split("-")[0]),parseInt(key.split("-")[1]),0).getDate(); for(var d=1;d<=dim;d++){var dkk=String(d).padStart(2,"0");if(t.attendance[key][dkk]==="P")P2++;} pEl.textContent=P2; } } return true; } function updateSaFooter() { var key=getAttKey(); var dk=getTodayDK(); var markedCount=teachers.filter(function(t){return t.attendance&&t.attendance[key]&&t.attendance[key][dk]==="P";}).length; var el=document.getElementById("sa-footer-count"); if(el) el.textContent="Today Present: "+markedCount+" / "+teachers.length+" teachers"; } function buildTeacherBtns() { var el=document.getElementById("sa-teacher-btns"); if(!el) return; var key=getAttKey(); var dk=getTodayDK(); el.innerHTML=teachers.map(function(t){ var marked=t.attendance&&t.attendance[key]&&t.attendance[key][dk]==="P"; return ''; }).join(""); } function startCamera() { if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia){showToast("Camera not supported","error");return;} navigator.mediaDevices.getUserMedia({video:{facingMode:"user"},audio:false}).then(function(stream){ _saStream=stream; var vid=document.getElementById("sa-video"); if(vid) vid.srcObject=stream; document.getElementById("sa-cam-start").style.display="none"; document.getElementById("sa-cam-capture").style.display="inline-flex"; document.getElementById("sa-cam-stop").style.display="inline-flex"; }).catch(function(e){showToast("Camera denied: "+e.message,"error");}); } function captureAndMark() { var res=document.getElementById("sa-cam-result"); var msg=document.getElementById("sa-cam-msg"); if(res&&msg){res.style.display="block";msg.innerHTML="Photo captured! Neeche teacher select karo";} buildTeacherBtns(); } function stopCamera() { if(_saStream){_saStream.getTracks().forEach(function(t){t.stop();});_saStream=null;} var vid=document.getElementById("sa-video"); if(vid) vid.srcObject=null; var s=document.getElementById("sa-cam-start"); var c=document.getElementById("sa-cam-capture"); var st=document.getElementById("sa-cam-stop"); if(s) s.style.display="inline-flex"; if(c) c.style.display="none"; if(st) st.style.display="none"; } function saIdSearch(val) { var sugg=document.getElementById("sa-id-suggestions"); if(!val||val.length<1){if(sugg)sugg.style.display="none";return;} var key=getAttKey(); var dk=getTodayDK(); var matches=teachers.filter(function(t){ return String(t.id).includes(val)||t.name.toLowerCase().includes(val.toLowerCase()); }).slice(0,6); if(!matches.length){if(sugg)sugg.style.display="none";return;} sugg.style.display="block"; sugg.innerHTML=matches.map(function(t){ var marked=t.attendance&&t.attendance[key]&&t.attendance[key][dk]==="P"; return '
' +'#'+t.id+'' +''+t.name+'' +''+t.subject+'' +(marked?'✓P':'')+'
'; }).join(""); } function saSelectTeacher(tid) { markTeacherPresent(tid,'id'); var t=teachers.find(function(x){return x.id===tid;}); if(t) showToast("✓ "+t.name+" - Present!"); var inp=document.getElementById("sa-id-input"); var sugg=document.getElementById("sa-id-suggestions"); if(inp){inp.value="";inp.focus();} if(sugg) sugg.style.display="none"; } function saIdMark() { var inp=document.getElementById("sa-id-input"); if(!inp) return; var val=inp.value.trim(); var t=teachers.find(function(x){return String(x.id)===val||x.name.toLowerCase()===val.toLowerCase();}); if(t) saSelectTeacher(t.id); else showToast("Teacher not found","error"); } function updateSaTodayList() { var el=document.getElementById("sa-id-today"); if(!el) return; var key=getAttKey(); var dk=getTodayDK(); var present=teachers.filter(function(t){return t.attendance&&t.attendance[key]&&t.attendance[key][dk]==="P";}); var notMarked=teachers.filter(function(t){return !t.attendance||!t.attendance[key]||!t.attendance[key][dk];}); el.innerHTML='
Aaj Ki Attendance
' +'
' +'
Present ('+present.length+')
' +present.map(function(t){return '
'+t.name+'
';}).join("") +(present.length===0?'
Koi nahi
':'') +'
' +'
Unmarked ('+notMarked.length+')
' +notMarked.slice(0,8).map(function(t){return '
'+t.name+'
';}).join("") +(notMarked.length>8?'
+'+(notMarked.length-8)+' more
':'') +'
'; } function startQRScan() { if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia){showToast("Camera not supported","error");return;} var vid=document.getElementById("sa-qr-video"); var canvas=document.createElement("canvas"); var ctx=canvas.getContext("2d"); navigator.mediaDevices.getUserMedia({video:{facingMode:"environment",width:{ideal:1280},height:{ideal:720}},audio:false}).then(function(stream){ _saQrStream=stream; if(vid){vid.srcObject=stream;vid.style.display="block";} var st2=document.getElementById("sa-qr-start"); var sp2=document.getElementById("sa-qr-stop"); if(st2)st2.style.display="none"; if(sp2)sp2.style.display="inline-flex"; vid.onloadedmetadata=function(){canvas.width=vid.videoWidth;canvas.height=vid.videoHeight;}; _saQrInterval=setInterval(function(){ if(!vid||!_saQrStream||vid.readyState!==4) return; try{ canvas.width=vid.videoWidth||320; canvas.height=vid.videoHeight||240; ctx.drawImage(vid,0,0,canvas.width,canvas.height); var imgData=ctx.getImageData(0,0,canvas.width,canvas.height); if(typeof jsQR==="undefined") return; var code=jsQR(imgData.data,imgData.width,imgData.height,{inversionAttempts:"dontInvert"}); if(code&&code.data) saProcessQRValue(code.data); }catch(e){} },300); }).catch(function(e){ showToast("Camera denied. Neeche manual input use karo.","error"); var notice=document.getElementById("sa-qr-notice"); if(notice) notice.style.display="block"; }); } function stopQRScan() { if(_saQrInterval){clearInterval(_saQrInterval);_saQrInterval=null;} if(_saQrStream){_saQrStream.getTracks().forEach(function(t){t.stop();});_saQrStream=null;} var vid=document.getElementById("sa-qr-video"); if(vid){vid.srcObject=null;vid.style.display="none";} var st=document.getElementById("sa-qr-start"); var sp=document.getElementById("sa-qr-stop"); if(st)st.style.display="inline-flex"; if(sp)sp.style.display="none"; } function saQrManualMark(val) { var v=val.trim(); if(!v) return; // Accept plain ID like "101" or full "TEACHER-101" if(/^\d+$/.test(v)) v="TEACHER-"+v; saProcessQRValue(v); var inp=document.getElementById("sa-qr-manual"); if(inp){inp.value="";inp.focus();} } function saProcessQRValue(val) { var match=val.match(/^TEACHER-(\d+)$/); if(!match){showToast("Invalid QR: "+val,"error");return;} var tid=parseInt(match[1]); var t=teachers.find(function(x){return x.id===tid;}); if(!t){showToast("Teacher not found","error");return;} markTeacherPresent(tid,"qr"); var res=document.getElementById("sa-qr-result"); var msg=document.getElementById("sa-qr-msg"); if(res&&msg){ res.style.display="block"; msg.innerHTML="✓ "+t.name+" - Present!"; setTimeout(function(){res.style.display="none";},2000); } showToast("✓ "+t.name+" marked P!"); } function printAllQRCodes() { var W=''; teachers.forEach(function(t){ var qrVal="TEACHER-"+t.id; var qrUrl="https://api.qrserver.com/v1/create-qr-code/?size=100x100&data="+encodeURIComponent(qrVal); W+='
' +'
Sara Azam Memorial Education
' +'' +'
'+t.name+'
' +'
'+t.subject+'
' +'
ID: '+t.id+'
' +'
'; }); W+=''; var win=window.open("","_blank","width=900,height=700"); if(!win){showToast("Popup blocked","error");return;} win.document.write(W); win.document.close(); win.focus(); setTimeout(function(){win.print();},1200); } function switchTeacherTab(tab) { var views = {staff:"t-view-staff", att:"t-view-att", salary:"t-view-salary"}; var btns = {staff:"t-tab-staff", att:"t-tab-att", salary:"t-tab-salary"}; // Hide all Object.keys(views).forEach(function(k){ var v=document.getElementById(views[k]); if(v) v.style.display="none"; var b=document.getElementById(btns[k]); if(b){b.style.color="#94A3B8";b.style.fontWeight="600";b.style.borderBottomColor="transparent";} }); // Show selected var selView=document.getElementById(views[tab]); if(selView) selView.style.display=""; var selBtn=document.getElementById(btns[tab]); if(selBtn){selBtn.style.color="#1E3A5F";selBtn.style.fontWeight="700";selBtn.style.borderBottomColor="#1E3A5F";} // Render — set month/year FIRST, then render if(tab==="att") { var now2=new Date(); var curSessionM=now2.getMonth()>=3?(now2.getMonth()-3):(now2.getMonth()+9); var bm=document.getElementById("bulk-att-month"); var by=document.getElementById("bulk-att-year"); // Always set current month (not just first open) if(bm) bm.value=String(curSessionM); if(by) by.value=String(now2.getFullYear()); renderBulkAttendance(); // render after setting correct month } if(tab==="salary") { // Sync month/year from attendance selector var attM=document.getElementById("bulk-att-month"); var attY=document.getElementById("bulk-att-year"); var salM=document.getElementById("sal-sheet-month"); var salY=document.getElementById("sal-sheet-year"); if(attM&&salM) salM.value=attM.value; if(attY&&salY) salY.value=attY.value; renderSalarySheet(); } } // ── SALARY SHEET ── function renderSalarySheet() { var monthSel = document.getElementById("sal-sheet-month"); var yearSel = document.getElementById("sal-sheet-year"); var freeLvEl = document.getElementById("sal-free-leave"); var workDaysEl = document.getElementById("sal-working-days"); var sessionMonthIdx = monthSel ? parseInt(monthSel.value) : 0; var year = yearSel ? parseInt(yearSel.value)||2026 : 2026; var freeLeave = freeLvEl ? parseInt(freeLvEl.value)||1 : 1; var workingDays = workDaysEl ? parseInt(workDaysEl.value)||26 : 26; var realMonth = SESSION_TO_REAL[sessionMonthIdx]; var monthName = SESSION_MONTH_NAMES[sessionMonthIdx]; var daysInMonth = new Date(year, realMonth+1, 0).getDate(); var key = year + "-" + String(realMonth+1).padStart(2,"0"); var now = new Date(); // ── Block: Current month salary sirf month complete hone ke baad ── var yr2 = realMonth>=3 ? year : year+1; var isCurrentMonth = (yr2===now.getFullYear() && realMonth===now.getMonth()); var isFutureMonth = (yr2>now.getFullYear()) || (yr2===now.getFullYear() && realMonth>now.getMonth()); var monthLastDay = daysInMonth; var isMonthComplete = !isCurrentMonth && !isFutureMonth; // past month = complete var el=document.getElementById("salary-sheet-table"); if(!teachers.length) { if(el) el.innerHTML='
No teachers found.
'; return; } if(isFutureMonth) { if(el) el.innerHTML='
' +'
📅
' +'
Future Month
' +'
'+monthName+' '+yr2+' abhi aaya nahi hai
'; return; } if(isCurrentMonth) { if(el) el.innerHTML='
' +'
' +'
Month Incomplete
' +'
'+monthName+' '+yr2+' ka month abhi chal raha hai
' +'Salary '+monthLastDay+' '+monthName+' ke baad generate hogi
' +'
Aaj: '+now.getDate()+' '+monthName+' '+yr2+' · '+ +(monthLastDay-now.getDate())+' din baaki hain
'; return; } var totalMonthly=0, totalFinal=0, totalDeduction=0, totalBonus=0; var rows = teachers.map(function(t, ti) { // Skip teacher if not yet joined in this month if(t.joining){ var jd2=fixDateFormat(t.joining); var jp2=jd2.split("."); if(jp2.length>=3){ var jDay2=parseInt(jp2[0]),jMon2=parseInt(jp2[1])-1,jYr2=parseInt(jp2[2]); var joinDate2=new Date(jYr2,jMon2,jDay2); var sheetMonthEnd=new Date(yr2,realMonth+1,0); if(sheetMonthEnd 0 ? monthlySal / daysInMonth : 0; var att = (t.attendance && t.attendance[key]) || {}; var P=0,A=0,H=0,L=0; for(var d=1;d<=daysInMonth;d++){ var dk=String(d).padStart(2,"0"); var st=att[dk]||""; if(st==="P")P++; else if(st==="A")A++; else if(st==="H")H++; else if(st==="L")L++; } var hasAtt = (P+A+H+L) > 0; // Effective worked days: Present + free leave used (free leave = worked day) var freeLeaveUsed = Math.min(L, freeLeave); var effectiveDays = P + freeLeaveUsed; // Deduction: Absent + extra leave beyond freeLeave var extraLeave = Math.max(0, L - freeLeave); var cutDays = A + extraLeave; var deduction = Math.round(cutDays * perDay); // Overtime: effectiveDays beyond working days input var overtimeDays = Math.max(0, effectiveDays - workingDays); var bonus = Math.round(overtimeDays * perDay); // Final salary = Monthly - Deduction + Bonus var finalSal = Math.max(0, monthlySal - deduction + bonus); totalMonthly += monthlySal; totalFinal += finalSal; totalDeduction+= deduction; totalBonus += bonus; // Status badge var statusBadge = bonus > 0 && deduction === 0 ? '🔺 Overtime' : bonus > 0 && deduction > 0 ? '⚡ OT+Cut' : deduction === 0 ? '✓ Full' : '✗ Deducted'; // Attendance pills var attPills = hasAtt ? '
' +(P>0?'P:'+P+'':'') +(A>0?'A:'+A+'':'') +(L>0?'L:'+L+'':'') +(H>0?'H:'+H+'':'') +'
' : '
No attendance
'; // Cut note var cutNote = cutDays > 0 ? '
' +'Cut: '+cutDays+' day(s) × Rs.'+Math.round(perDay)+' = -Rs.'+deduction.toLocaleString("en-IN") +(extraLeave>0?' ('+A+' absent + '+extraLeave+' extra L)':'') +'
' : ''; // Overtime note var otNote = overtimeDays > 0 ? '
' +'Overtime: P('+P+')+FL('+freeLeaveUsed+')='+effectiveDays+' > WD:'+workingDays+' → +'+overtimeDays+' × Rs.'+Math.round(perDay)+' = +Rs.'+bonus.toLocaleString("en-IN") +'
' : ''; var finalColor = bonus>0 && deduction===0 ? "#065F46" : finalSal > monthlySal ? "#065F46" : finalSal === monthlySal ? "#15803D" : "#DC2626"; var rowBg = ti%2===0?"#fff":"#FAFAFA"; return '' // Sr +''+(ti+1)+'' // Name +'' +'
'+t.name+'
' +'
'+t.subject+(t.class?' · '+t.class:'')+'
' +'' // Total Salary + per day +'' +'
Rs.'+monthlySal.toLocaleString("en-IN")+'
' +'
Per day: Rs.'+Math.round(perDay)+'
' +'
(÷'+daysInMonth+' days in month)
' +'' // Working days / Attendance +'' +'
Working: '+workingDays+' days
' +attPills +'' // Absent / Overtime +'' +(A>0||extraLeave>0?'
'+(cutDays)+' cut day(s)
'+cutNote:'') +(overtimeDays>0?'
+'+overtimeDays+' overtime
'+otNote:'') +(A===0&&extraLeave===0&&overtimeDays===0?'
':'') +'' // Final Salary +'' +'
Rs.'+finalSal.toLocaleString("en-IN")+'
' +(deduction>0?'
-Rs.'+deduction.toLocaleString("en-IN")+'
':'') +(bonus>0?'
+Rs.'+bonus.toLocaleString("en-IN")+'
':'') +'
'+statusBadge+'
' +'' +''; }).join(""); // Total row var netChange = totalBonus - totalDeduction; var totalRow = '' +'TOTAL ('+teachers.length+' teachers)' +'Rs.'+totalMonthly.toLocaleString("en-IN")+'' +''+workingDays+' working days' +'' +(totalDeduction>0?'
-Rs.'+totalDeduction.toLocaleString("en-IN")+'
':'') +(totalBonus>0?'
+Rs.'+totalBonus.toLocaleString("en-IN")+'
':'') +'' +'Rs.'+totalFinal.toLocaleString("en-IN")+'' +''; var infoBar = '
' +'📅 '+monthName+' '+year+'' +'⚙️ Working days: '+workingDays+'' +'📐 Per day = Salary ÷ '+workingDays+' month days' +'🎁 Free leave: '+freeLeave+'/month' +'🔺 Overtime if Present > '+workingDays+'' +'
'; var tableHtml = infoBar +'
' +'' +'' +'' +'' +'' +'' +'' +'' +'' +'' +'' +''+rows+'' +''+totalRow+'' +'
#Teacher NameTotal SalaryWorking DaysDeduction / OvertimeFinal Salary
'; var el=document.getElementById("salary-sheet-table"); if(el) el.innerHTML=tableHtml; // Store calculated results for khata push window._lastSalaryCalc = { key:key, monthName:monthName, year:year, daysInMonth:daysInMonth, workingDays:workingDays, freeLeave:freeLeave, results: teachers.map(function(t){ var monthlySal=parseInt(t.salary||0); var perDay=daysInMonth>0?monthlySal/daysInMonth:0; var att=(t.attendance&&t.attendance[key])||{}; var P=0,A=0,H=0,L=0; for(var d=1;d<=daysInMonth;d++){var dk=String(d).padStart(2,"0");var st=att[dk]||"";if(st==="P")P++;else if(st==="A")A++;else if(st==="H")H++;else if(st==="L")L++;} var freeLeaveUsed=Math.min(L,freeLeave); var effectiveDays=P+freeLeaveUsed; var cutDays=A+Math.max(0,L-freeLeave); var deduction=Math.round(cutDays*perDay); var overtimeDays=Math.max(0,effectiveDays-workingDays); var bonus=Math.round(overtimeDays*perDay); var finalSal=Math.max(0,monthlySal-deduction+bonus); return {tid:t.id,name:t.name,monthlySal:monthlySal,finalSal:finalSal,deduction:deduction,bonus:bonus,P:P,A:A,L:L,H:H}; }) }; } // ── PUSH SALARY TO KHATA BOOK ── function pushSalaryToKhata() { if(!window._lastSalaryCalc){showToast("Pehle salary sheet render karo","error");return;} var calc=window._lastSalaryCalc; var updated=0; calc.results.forEach(function(r){ var t=teachers.find(function(x){return x.id===r.tid;}); if(!t) return; if(!t.salaryLedger) t.salaryLedger={}; if(!t.salaryLedger[calc.key]) t.salaryLedger[calc.key]=[]; // Store earned salary data (not payment) — overwrite if exists var existing=t.salaryLedger[calc.key]; // Remove old auto-calc entries (they start with "Earned:") t.salaryLedger[calc.key]=existing.filter(function(e){return !e.note||e.note.indexOf("Earned:")!==0;}); // Add earned record (NOT a payment — amt:0 means no payment made) t.salaryLedger[calc.key].push({ earned:r.finalSal, amt:0, date:"", note:"Earned: "+calc.monthName+" "+calc.year +(r.deduction>0?" | Cut: Rs."+r.deduction:"") +(r.bonus>0?" | OT: +Rs."+r.bonus:"") }); updated++; }); saveData(); showToast("📒 "+updated+" teachers ka earned salary khata mein update hua! Manual payment karo to mark as Paid.","success"); } // ── PRINT ALL SALARY SLIPS ── function printAllSalarySlips() { if(!window._lastSalaryCalc){showToast("Pehle salary sheet render karo","error");return;} var calc=window._lastSalaryCalc; var today=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var W='' +''; calc.results.forEach(function(r,i){ var t=teachers.find(function(x){return x.id===r.tid;}); if(!t) return; var isLast=i===calc.results.length-1; W+='
'; // Header W+='
'; W+='
'; W+='
'; W+='
Sara Azam Memorial Education
'; W+='
Salary Slip · '+calc.monthName+' '+calc.year+'
'; W+='
Date: '+today+'
'; // Teacher info W+='
'; [["Employee Name",r.name],["Designation",t.subject||"—"],["Class",t.class||"—"],["Emp. ID","EMP-"+String(r.tid).slice(-4)]].forEach(function(f){ W+='
'; W+='
'+f[0]+'
'; W+='
'+f[1]+'
'; }); W+='
'; // Attendance summary W+='
'; W+='
📅 Attendance — '+calc.monthName+' '+calc.year+' ('+calc.daysInMonth+' days)
'; W+='
'; [["Present",r.P,"#15803D"],["Absent",r.A,"#DC2626"],["Leave",r.L,"#1D4ED8"],["Holiday",r.H,"#B45309"]].forEach(function(s){ W+='
'; W+='
'+s[1]+'
'; W+='
'+s[0]+'
'; }); W+='
'; // Salary breakdown W+=''; W+=''; W+=''; var salRows=[ ["Monthly Salary","Rs."+r.monthlySal.toLocaleString("en-IN"),"#0F172A",false], ["Per Day Rate","Rs."+Math.round(r.monthlySal/calc.daysInMonth)+" (÷"+calc.daysInMonth+" days)","#374151",false], ["Working Days",calc.workingDays+" days","#374151",false], ["Free Leave",calc.freeLeave+" day(s) per month","#1D4ED8",false] ]; if(r.deduction>0) salRows.push(["Deduction ("+r.A+" absent"+(r.L>calc.freeLeave?"+extra L":"")+")","-Rs."+r.deduction.toLocaleString("en-IN"),"#DC2626",false]); if(r.bonus>0) salRows.push(["Overtime Bonus","+Rs."+r.bonus.toLocaleString("en-IN"),"#065F46",false]); salRows.forEach(function(row,ri){ W+=''; W+=''; W+=''; }); W+=''; W+=''; W+=''; W+=''; W+='
Salary Details
'+row[0]+''+row[1]+'
NET SALARY PAYABLERs.'+r.finalSal.toLocaleString("en-IN")+'
'; // Declaration W+='
'; W+='
📜 Declaration / घोषणापत्र
'; W+='
मैं '+r.name+' घोषणा करता/करती हूँ कि मुझे माह '+calc.monthName+' '+calc.year+' की वेतन राशि Rs.'+r.finalSal.toLocaleString("en-IN")+' पूर्ण रूप से प्राप्त हो गई है। मैं इससे पूर्णतः सहमत हूँ।
'; W+='
I, '+r.name+', hereby declare that I have received salary of Rs.'+r.finalSal.toLocaleString("en-IN")+' for '+calc.monthName+' '+calc.year+' in full and accept the same.
'; W+='
'; // Signatures W+='
'; W+='
'; W+='
'; W+='
'+r.name+' (Employee)
'; W+='
Signature & Date
'; W+='
'; W+='
'; W+='
Principal / Disbursed By
'; W+='
Date: '+today+'
'; W+='
'; W+='
'; // end slip }); W+=''; var win=window.open("","_blank","width=800,height=700"); if(!win){showToast("Popup blocked!","error");return;} win.document.write(W); win.document.close(); win.focus(); setTimeout(function(){win.print();},400); } function printSalarySheet() { var monthSel=document.getElementById("sal-sheet-month"); var yearSel=document.getElementById("sal-sheet-year"); var freeLvEl=document.getElementById("sal-free-leave"); var sessionMonthIdx=monthSel?parseInt(monthSel.value):0; var year=yearSel?parseInt(yearSel.value):new Date().getFullYear(); var freeLeave=freeLvEl?parseInt(freeLvEl.value)||1:1; var realMonth=SESSION_TO_REAL[sessionMonthIdx]; var monthName=SESSION_MONTH_NAMES[sessionMonthIdx]; var daysInMonth=new Date(year,realMonth+1,0).getDate(); var key=year+"-"+String(realMonth+1).padStart(2,"0"); var today=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var totalM=0,totalF=0,totalDed=0; var printRows=teachers.map(function(t,ti){ var monthlySal=parseInt(t.salary||0); var perDay=monthlySal/daysInMonth; var att=(t.attendance&&t.attendance[key])||{}; var P=0,A=0,H=0,L=0; for(var d=1;d<=daysInMonth;d++){ var dk=String(d).padStart(2,"0"); var st=att[dk]||""; if(st==="P")P++;else if(st==="A")A++;else if(st==="H")H++;else if(st==="L")L++; } var extraLeave=Math.max(0,L-freeLeave); var cutDays=A+extraLeave; var deduction=Math.round(cutDays*perDay); var finalSal=Math.max(0,monthlySal-deduction); totalM+=monthlySal; totalF+=finalSal; totalDed+=deduction; var attStr=(P>0?"P:"+P+" ":"")+(A>0?"A:"+A+" ":"")+(L>0?"L:"+L+" ":"")+(H>0?"H:"+H:""); return '' +''+(ti+1)+'' +'
'+t.name+'
'+t.subject+'
' +'Rs.'+monthlySal.toLocaleString("en-IN")+'' +''+daysInMonth+'' +''+(attStr||'—')+'' +''+(A>0?A+' ('+cutDays+' cut)':'0')+'' +'Rs.'+finalSal.toLocaleString("en-IN")+'' +''; }).join(""); var W='' +''; // Header W+='
'; W+='
'; W+='
'; W+='
Sara Azam Memorial Education
'; W+='
Salary Sheet · '+monthName+' '+year+'
'; W+='
Printed: '+today+'
'; W+='
Free Leave: '+freeLeave+'  ·  Per Day = Salary ÷ '+daysInMonth+' days
'; W+='
'; // Summary W+='
'; [["Total Monthly Salary","Rs."+totalM.toLocaleString("en-IN"),"#1D4ED8","#EFF6FF","#BFDBFE"], ["Total Deductions","-Rs."+totalDed.toLocaleString("en-IN"),"#DC2626","#FEF2F2","#FECACA"], ["Total Final Salary","Rs."+totalF.toLocaleString("en-IN"),"#15803D","#F0FDF4","#86EFAC"] ].forEach(function(s){ W+='
'; W+='
'+s[0]+'
'; W+='
'+s[1]+'
'; }); W+='
'; // Table W+=''; W+=''; ["#","Teacher Name","Total Salary","Days","Attendance","Absent/Cut","Final Salary"].forEach(function(h,i){ W+=''; }); W+=''+printRows+''; W+='' +'' +'' +'' +'' +'' +'' +'
'+h+'
TOTAL ('+teachers.length+' staff)Rs.'+totalM.toLocaleString("en-IN")+''+daysInMonth+'-Rs.'+totalDed.toLocaleString("en-IN")+'Rs.'+totalF.toLocaleString("en-IN")+'
'; // Signatures W+='
'; ["Prepared By","Checked By","Principal / HM"].forEach(function(s){ W+='
'; W+='
'+s+'
'; }); W+='
'; W+=''; var win=window.open("","_blank","width=900,height=700"); if(!win){showToast("Popup blocked!","error");return;} win.document.write(W); win.document.close(); win.focus(); setTimeout(function(){win.print();},400); } // ── BULK ATTENDANCE RENDER ── // ════════════════════════════════════════════════ // STUDENT ATTENDANCE SYSTEM // ════════════════════════════════════════════════ function initStudentAtt() { var now=new Date(); // Set current month based on session var sessionMonthIdx=now.getMonth()>=3?(now.getMonth()-3):(now.getMonth()+9); var mSel=document.getElementById("sa-month"); var ySel=document.getElementById("sa-year"); if(mSel&&!mSel._init){mSel.value=String(sessionMonthIdx);mSel._init=true;} if(ySel&&!ySel._init){ySel.value=String(now.getFullYear());ySel._init=true;} renderStudentAtt(); } function getSaKey() { var mSel=document.getElementById("sa-month"); var ySel=document.getElementById("sa-year"); var sessionMonthIdx=mSel?parseInt(mSel.value):0; var year=ySel?parseInt(ySel.value):new Date().getFullYear(); var realMonth=SESSION_TO_REAL[sessionMonthIdx]; var yr=realMonth>=3?year:year+1; return {key:yr+"-"+String(realMonth+1).padStart(2,"0"), realMonth:realMonth, year:year, yr:yr, sessionMonthIdx:sessionMonthIdx, monthName:SESSION_MONTH_NAMES[sessionMonthIdx]}; } function getSaStudents() { var clsF=document.getElementById("sa-class")?document.getElementById("sa-class").value:""; return students.filter(function(s){ if(s.admissionClosed) return false; if(!clsF) return true; return s.class===clsF; }); } function renderStudentAtt() { var info=getSaKey(); var key=info.key; var realMonth=info.realMonth; var year=info.yr; var monthName=info.monthName; var daysInMonth=new Date(year,realMonth+1,0).getDate(); var firstDay=new Date(year,realMonth,1).getDay(); var dayNames=["S","M","T","W","T","F","S"]; var now=new Date(); now.setHours(0,0,0,0); var list=getSaStudents(); var sub=document.getElementById("sa-sub"); if(sub) sub.textContent=monthName+" "+year+" · "+list.length+" students · Click cell to mark"; // Header date columns var headerCols=""; for(var d=1;d<=daysInMonth;d++){ var dow=(firstDay+d-1)%7; var isWe=(dow===0||dow===6); var dk=String(d).padStart(2,"00"); headerCols+='' +'
'+dayNames[dow]+'
'+d+'
' +''; } var rows=list.map(function(s,ti){ if(!s.attendance) s.attendance={}; if(!s.attendance[key]) s.attendance[key]={}; var P=0,A=0,H=0,L=0; var cells=""; for(var d=1;d<=daysInMonth;d++){ var dk=String(d).padStart(2,"00"); var dow=(firstDay+d-1)%7; var isWe=(dow===0||dow===6); var st=s.attendance[key][dk]||""; var cellDate=new Date(year,realMonth,d); var isFuture=cellDate>now; var isToday=(cellDate.toDateString()===new Date().toDateString()); if(st==="P")P++; else if(st==="A")A++; else if(st==="H")H++; else if(st==="L")L++; var bg=isFuture?"#F5F5F5":st==="P"?"#DCFCE7":st==="A"?"#FEE2E2":st==="H"?"#FEF3C7":st==="L"?"#DBEAFE":isWe?"#F0F0F0":"#fff"; var col=isFuture?"#D1D5DB":st==="P"?"#15803D":st==="A"?"#B91C1C":st==="H"?"#B45309":st==="L"?"#1D4ED8":"#9CA3AF"; var todayBorder=isToday?"box-shadow:inset 0 0 0 2px #2563EB;":""; cells+='' +''+(isFuture?"":st||"")+''; } var total=P+A+H+L; var pct=total>0?Math.round(P/total*100):0; var pctColor=pct>=75?"#15803D":pct>=60?"#B45309":"#DC2626"; var rowBg=ti%2===0?"":"background:#FAFAFA;"; var avInfo=avt(s.name,students.indexOf(s)); return '' +'' +'
' +(s.photo?'' :'
'+avInfo.init+'
') +'
'+s.name+'
' +'
'+s.class+'
' +'
' +'' +cells +''+P+'' +''+A+'' +''+H+'' +''+L+'' +''+pct+'%' +''; }).join(""); var tableHtml='' +'' +'' +'' +headerCols +'' +'' +'' +''+rows+'
StudentP A H L%
'; var wrap=document.getElementById("sa-table-wrap"); if(wrap) wrap.innerHTML=tableHtml; renderSaSummary(key, daysInMonth); } function renderSaSummary(key, daysInMonth) { var list=getSaStudents(); var totP=0,totA=0,totH=0,totL=0; list.forEach(function(s){ if(!s.attendance||!s.attendance[key]) return; for(var d=1;d<=daysInMonth;d++){ var dk=String(d).padStart(2,"00"); var st=s.attendance[key][dk]||""; if(st==="P")totP++; else if(st==="A")totA++; else if(st==="H")totH++; else if(st==="L")totL++; } }); var el=document.getElementById("sa-summary"); if(!el) return; el.innerHTML=[["Present",totP,"#15803D","#F0FDF4","#86EFAC","P"],["Absent",totA,"#DC2626","#FEF2F2","#FECACA","A"],["Holiday",totH,"#B45309","#FFFBEB","#FDE68A","H"],["Leave",totL,"#1D4ED8","#EFF6FF","#BFDBFE","L"]].map(function(s){ return '
' +'
'+s[5]+'
' +'
'+s[1]+'
' +'
Total '+s[0]+'
' +'
'; }).join(""); } function saCycleCell(el) { if(el.getAttribute("data-future")==="1") return; var sid=parseInt(el.getAttribute("data-sid")); var key=el.getAttribute("data-key"); var day=el.getAttribute("data-day"); var s=students.find(function(x){return x.id===sid;}); if(!s) return; if(!s.attendance) s.attendance={}; if(!s.attendance[key]) s.attendance[key]={}; var seq=["","P","A","H","L"]; var cur=s.attendance[key][day]||""; var next=seq[(seq.indexOf(cur)+1)%seq.length]; // Back date popup var parts=key.split("-"); var cellDate=new Date(parseInt(parts[0]),parseInt(parts[1])-1,parseInt(day)); var isToday=(cellDate.toDateString()===new Date().toDateString()); var isBack=cellDate=3){ var admDateObj=new Date(parseInt(ap[2]),parseInt(ap[1])-1,parseInt(ap[0])); if(cellDate < admDateObj){ showToast(s.name+" ki admission date "+s.admDate+" hai — pehle ki date mein attendance nahi ho sakti","error"); return; } } } function doIt(){ s.attendance[key][day]=next; saveData(); var bg=next==="P"?"#DCFCE7":next==="A"?"#FEE2E2":next==="H"?"#FEF3C7":next==="L"?"#DBEAFE":"#fff"; var col=next==="P"?"#15803D":next==="A"?"#B91C1C":next==="H"?"#B45309":next==="L"?"#1D4ED8":"#9CA3AF"; el.style.background=bg; var sp=el.querySelector("span"); if(sp){sp.style.color=col;sp.textContent=next;} // Update row summary var info=getSaKey(); var dim=new Date(info.yr,info.realMonth+1,0).getDate(); var P2=0,A2=0,H2=0,L2=0; for(var d=1;d<=dim;d++){var dk=String(d).padStart(2,"00");var st=s.attendance[key][dk]||"";if(st==="P")P2++;else if(st==="A")A2++;else if(st==="H")H2++;else if(st==="L")L2++;} var pEl=document.getElementById("sasc-P-"+sid); if(pEl) pEl.textContent=P2; var aEl=document.getElementById("sasc-A-"+sid); if(aEl) aEl.textContent=A2; var hEl=document.getElementById("sasc-H-"+sid); if(hEl) hEl.textContent=H2; var lEl=document.getElementById("sasc-L-"+sid); if(lEl) lEl.textContent=L2; renderSaSummary(key,dim); } if(cur && isBack){ var dateStr=parseInt(day)+"."+parts[1]+"."+parts[0]; var popup=document.createElement("div"); popup.style.cssText="position:fixed;inset:0;background:rgba(15,23,42,.5);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(3px);"; popup.innerHTML='
' +'
📅
' +'
Back Date Change?
' +'
'+s.name+'
Date: '+dateStr+'   '+(cur||"blank")+''+(next||"blank")+'
' +'
' +'' +'' +'
'; document.body.appendChild(popup); popup.querySelector("#sac-yes").onclick=function(){popup.remove();doIt();}; popup.querySelector("#sac-no").onclick=function(){popup.remove();}; popup.onclick=function(e){if(e.target===popup)popup.remove();}; } else { doIt(); } } function saFillDate(th) { var key=th.getAttribute("data-key"); var day=th.getAttribute("data-day"); var daysInMonth=parseInt(th.getAttribute("data-days")||30); var now=new Date(); now.setHours(0,0,0,0); var parts=key.split("-"); var cellDate=new Date(parseInt(parts[0]),parseInt(parts[1])-1,parseInt(day)); if(cellDate>now){showToast("Future date — attendance nahi lag sakti","error");return;} var list=getSaStudents(); var seq=["","P","A","H","L"]; var counts={"":0,"P":0,"A":0,"H":0,"L":0}; list.forEach(function(s){var st=(s.attendance&&s.attendance[key]&&s.attendance[key][day])||"";counts[st]=(counts[st]||0)+1;}); var dominant=""; var mx=0; Object.keys(counts).forEach(function(k){if(counts[k]>mx){mx=counts[k];dominant=k;}}); var nextSt=seq[(seq.indexOf(dominant)+1)%seq.length]; list.forEach(function(s){ if(!s.attendance) s.attendance={}; if(!s.attendance[key]) s.attendance[key]={}; s.attendance[key][day]=nextSt; }); saveData(); var bg=nextSt==="P"?"#DCFCE7":nextSt==="A"?"#FEE2E2":nextSt==="H"?"#FEF3C7":nextSt==="L"?"#DBEAFE":"#fff"; var col=nextSt==="P"?"#15803D":nextSt==="A"?"#B91C1C":nextSt==="H"?"#B45309":nextSt==="L"?"#1D4ED8":"#9CA3AF"; document.querySelectorAll('td[data-key="'+key+'"][data-day="'+day+'"]').forEach(function(c){ c.style.background=bg; var sp=c.querySelector("span"); if(sp){sp.style.color=col;sp.textContent=nextSt;} }); list.forEach(function(s){ var dim=daysInMonth; var P2=0,A2=0,H2=0,L2=0; for(var d=1;d<=dim;d++){var dk=String(d).padStart(2,"00");var st=s.attendance&&s.attendance[key]?s.attendance[key][dk]||"":"";if(st==="P")P2++;else if(st==="A")A2++;else if(st==="H")H2++;else if(st==="L")L2++;} var p=document.getElementById("sasc-P-"+s.id); if(p)p.textContent=P2; var a=document.getElementById("sasc-A-"+s.id); if(a)a.textContent=A2; var h=document.getElementById("sasc-H-"+s.id); if(h)h.textContent=H2; var l=document.getElementById("sasc-L-"+s.id); if(l)l.textContent=L2; }); renderSaSummary(key,daysInMonth); showToast("Day "+parseInt(day)+" — all students: "+(nextSt||"blank")); } function saBulkToday(status) { var info=getSaKey(); var now=new Date(); if(info.year!==now.getFullYear()||info.realMonth!==now.getMonth()){showToast("Selected month current month nahi hai","error");return;} var dk=String(now.getDate()).padStart(2,"00"); getSaStudents().forEach(function(s){ if(!s.attendance) s.attendance={}; if(!s.attendance[info.key]) s.attendance[info.key]={}; s.attendance[info.key][dk]=status; }); saveData(); renderStudentAtt(); showToast("Aaj ("+now.getDate()+") sabhi students Present!"); } function printStudentAtt() { var info=getSaKey(); var key=info.key; var realMonth=info.realMonth; var year=info.yr; var daysInMonth=new Date(year,realMonth+1,0).getDate(); var firstDay=new Date(year,realMonth,1).getDay(); var dayNames=["S","M","T","W","T","F","S"]; var list=getSaStudents(); var today=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var clsF=document.getElementById("sa-class")?document.getElementById("sa-class").value:"All Classes"; var headerCols=""; for(var d=1;d<=daysInMonth;d++){var dow=(firstDay+d-1)%7;headerCols+=''+d+'';} var rows=list.map(function(s,idx){ var cells=""; var P=0,A=0; for(var d=1;d<=daysInMonth;d++){ var dk=String(d).padStart(2,"00"); var st=s.attendance&&s.attendance[key]?s.attendance[key][dk]||"":""; if(st==="P")P++; else if(st==="A")A++; var bg=st==="P"?"#DCFCE7":st==="A"?"#FEE2E2":st==="H"?"#FEF3C7":st==="L"?"#DBEAFE":"#fff"; cells+=''+(st||"")+''; } var total=daysInMonth; var pct=total>0?Math.round(P/total*100):0; return '' +''+(idx+1)+'' +''+s.name+'' +''+s.class+'' +cells +''+P+'' +'=60?"#B45309":"#DC2626")+'">'+(pct>=75?"✓ ":"")+pct+'%' +''; }).join(""); var W=''; W+='
' +'
' +'
' +'
Sara Azam Memorial Education
' +'
Attendance Register · '+info.monthName+' '+info.yr+' · '+(clsF||"All Classes")+'
' +'
Printed: '+today+'
Total: '+list.length+' students
' +'
'; W+='' +'' +'' +'' +headerCols +'' +'' +''+rows+'
#StudentClassP%
'; W+=''; var win=window.open("","_blank","width=1100,height=700"); if(!win){showToast("Popup blocked","error");return;} win.document.write(W); win.document.close(); win.focus(); setTimeout(function(){win.print();},400); } // ── STUDENT SMART ATTENDANCE ── var _ssaStream=null, _ssaQrStream=null, _ssaQrInterval=null; function openStudentSmartAtt() { var info=getSaKey(); var now=new Date(); var lbl=document.getElementById("ssa-date-label"); if(lbl) lbl.textContent=info.monthName+" "+info.yr+" · "+now.toLocaleDateString("en-IN",{day:"numeric",month:"long"})+" · Day "+now.getDate(); document.getElementById("modal-stu-smart-att").classList.remove("hidden"); document.body.style.overflow="hidden"; _openModalCount++; ssaBuildBtns(); ssaUpdateList(); ssaUpdateFooter(); setTimeout(function(){var i=document.getElementById("ssa-id-input");if(i)i.focus();},200); } function closeStudentSmartAtt() { ssaStopCamera(); ssaStopQR(); document.getElementById("modal-stu-smart-att").classList.add("hidden"); _openModalCount=Math.max(0,_openModalCount-1); if(_openModalCount===0) document.body.style.overflow=""; renderStudentAtt(); showToast("Attendance saved!"); } function ssaGetKey(){return getSaKey().key;} function ssaGetDK(){return String(new Date().getDate()).padStart(2,"00");} function ssaMarkPresent(sid) { var s=students.find(function(x){return x.id===sid;}); if(!s) return false; var key=ssaGetKey(); var dk=ssaGetDK(); if(!s.attendance) s.attendance={}; if(!s.attendance[key]) s.attendance[key]={}; s.attendance[key][dk]="P"; saveData(); ssaUpdateFooter(); ssaUpdateList(); ssaBuildBtns(); return true; } function ssaUpdateFooter() { var key=ssaGetKey(); var dk=ssaGetDK(); var list=getSaStudents(); var cnt=list.filter(function(s){return s.attendance&&s.attendance[key]&&s.attendance[key][dk]==="P";}).length; var el=document.getElementById("ssa-footer-count"); if(el) el.textContent="Today Present: "+cnt+" / "+list.length+" students"; } function ssaBuildBtns() { var el=document.getElementById("ssa-student-btns"); if(!el) return; var key=ssaGetKey(); var dk=ssaGetDK(); var list=getSaStudents(); // Group by class for easier selection var clsFilter=document.getElementById("sa-class")?document.getElementById("sa-class").value:""; if(!list.length){ el.innerHTML='
Class filter se students select karo
'; return; } el.innerHTML=list.map(function(s){ var marked=s.attendance&&s.attendance[key]&&s.attendance[key][dk]==="P"; return ''; }).join(""); } function ssaUpdateList() { var el=document.getElementById("ssa-today-list"); if(!el) return; var key=ssaGetKey(); var dk=ssaGetDK(); var list=getSaStudents(); var present=list.filter(function(s){return s.attendance&&s.attendance[key]&&s.attendance[key][dk]==="P";}); var unmarked=list.filter(function(s){return !s.attendance||!s.attendance[key]||!s.attendance[key][dk];}); el.innerHTML='
Aaj Ki Attendance
' +'
' +'
✓ Present ('+present.length+')
' +present.map(function(s){return '
'+s.name+'
';}).join("") +(present.length===0?'
Koi nahi
':'') +'
' +'
○ Unmarked ('+unmarked.length+')
' +unmarked.slice(0,8).map(function(s){return '
'+s.name+'
';}).join("") +(unmarked.length>8?'
+'+(unmarked.length-8)+' more
':'') +'
'; } function ssaIdSearch(val) { var sugg=document.getElementById("ssa-id-suggestions"); if(!val||val.length<1){if(sugg)sugg.style.display="none";return;} var key=ssaGetKey(); var dk=ssaGetDK(); var matches=getSaStudents().filter(function(s){ return (s.roll&&s.roll.toLowerCase().includes(val.toLowerCase()))||s.name.toLowerCase().includes(val.toLowerCase()); }).slice(0,6); if(!matches.length){if(sugg)sugg.style.display="none";return;} sugg.style.display="block"; sugg.innerHTML=matches.map(function(s){ var marked=s.attendance&&s.attendance[key]&&s.attendance[key][dk]==="P"; return '
' +''+s.roll+'' +''+s.name+'' +''+s.class+'' +(marked?'✓P':'')+'
'; }).join(""); } function ssaSelectStudent(sid) { ssaMarkPresent(sid); var s=students.find(function(x){return x.id===sid;}); if(s) showToast("✓ "+s.name+" — Present!"); var inp=document.getElementById("ssa-id-input"); var sugg=document.getElementById("ssa-id-suggestions"); if(inp){inp.value="";inp.focus();} if(sugg) sugg.style.display="none"; } function ssaIdMark() { var inp=document.getElementById("ssa-id-input"); if(!inp) return; var val=inp.value.trim(); var s=getSaStudents().find(function(x){return (x.roll&&x.roll.toLowerCase()===val.toLowerCase())||x.name.toLowerCase()===val.toLowerCase();}); if(s) ssaSelectStudent(s.id); else showToast("Student not found","error"); } function ssaStartCamera() { if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia){ showToast("Camera is not supported on this browser/file. Use naam search instead.","error"); return; } navigator.mediaDevices.getUserMedia({video:{facingMode:"user",width:{ideal:640},height:{ideal:480}},audio:false}) .then(function(stream){ _ssaStream=stream; var vid=document.getElementById("ssa-video"); if(vid){vid.srcObject=stream;vid.style.display="block";} var s=document.getElementById("ssa-cam-start"); var st=document.getElementById("ssa-cam-stop"); if(s) s.style.display="none"; if(st) st.style.display="inline-flex"; showToast("Camera started! Neeche naam pe click karke P mark karo."); }) .catch(function(e){ var msg=e.name==="NotAllowedError"?"Camera permission denied. Browser settings mein allow karo.": e.name==="NotFoundError"?"Camera nahi mili device pe.": "Camera error: "+e.message; showToast(msg,"error"); // Show helpful message in video area var vid=document.getElementById("ssa-video"); if(vid) vid.style.background="#1E1E1E"; }); } function ssaMarkAllPresent() { var key=ssaGetKey(); var dk=ssaGetDK(); var list=getSaStudents(); var now2=new Date(); now2.setHours(0,0,0,0); var cellDate=new Date(parseInt(key.split("-")[0]),parseInt(key.split("-")[1])-1,parseInt(dk)); if(cellDate>now2){showToast("Future date — attendance nahi lag sakti","error");return;} list.forEach(function(s){ if(!s.attendance) s.attendance={}; if(!s.attendance[key]) s.attendance[key]={}; s.attendance[key][dk]="P"; }); saveData(); ssaUpdateFooter(); ssaUpdateList(); ssaBuildBtns(); showToast("Sabhi students Present mark ho gaye!"); } function ssaStopCamera() { if(_ssaStream){_ssaStream.getTracks().forEach(function(t){t.stop();});_ssaStream=null;} var vid=document.getElementById("ssa-video"); if(vid) vid.srcObject=null; var s=document.getElementById("ssa-cam-start"); var st=document.getElementById("ssa-cam-stop"); if(s) s.style.display="inline-flex"; if(st) st.style.display="none"; } function ssaStartQR() { if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia){showToast("Camera not supported","error");return;} var vid=document.getElementById("ssa-qr-video"); var canvas=document.createElement("canvas"); var ctx=canvas.getContext("2d"); navigator.mediaDevices.getUserMedia({video:{facingMode:"environment",width:{ideal:1280},height:{ideal:720}},audio:false}).then(function(stream){ _ssaQrStream=stream; if(vid){vid.srcObject=stream;vid.style.display="block";} var st3=document.getElementById("ssa-qr-start"); var sp3=document.getElementById("ssa-qr-stop"); if(st3)st3.style.display="none"; if(sp3)sp3.style.display="inline-flex"; vid.onloadedmetadata=function(){canvas.width=vid.videoWidth;canvas.height=vid.videoHeight;}; _ssaQrInterval=setInterval(function(){ if(!vid||!_ssaQrStream||vid.readyState!==4) return; try{ canvas.width=vid.videoWidth; canvas.height=vid.videoHeight; ctx.drawImage(vid,0,0,canvas.width,canvas.height); var imgData=ctx.getImageData(0,0,canvas.width,canvas.height); var code=jsQR(imgData.data,imgData.width,imgData.height,{inversionAttempts:"dontInvert"}); if(code&&code.data) ssaProcessQR(code.data); }catch(e){} },300); }).catch(function(e){ showToast("Camera denied. Manual input use karo.","error"); }); } function ssaStopQR() { if(_ssaQrInterval){clearInterval(_ssaQrInterval);_ssaQrInterval=null;} if(_ssaQrStream){_ssaQrStream.getTracks().forEach(function(t){t.stop();});_ssaQrStream=null;} var vid=document.getElementById("ssa-qr-video"); if(vid){vid.srcObject=null;vid.style.display="none";} var st=document.getElementById("ssa-qr-start"); var sp=document.getElementById("ssa-qr-stop"); if(st)st.style.display="inline-flex"; if(sp)sp.style.display="none"; } function ssaQrMark(val) { var v=val.trim(); if(!v) return; if(/^\d+$/.test(v)) v="STUDENT-"+v; ssaProcessQR(v); var inp=document.getElementById("ssa-qr-manual"); if(inp){inp.value="";inp.focus();} } function ssaProcessQR(val) { var match=val.match(/^STUDENT-(\d+)$/); if(!match){showToast("Invalid QR: "+val,"error");return;} var sid=parseInt(match[1]); var s=students.find(function(x){return x.id===sid;}); if(!s){showToast("Student not found","error");return;} ssaMarkPresent(sid); var res=document.getElementById("ssa-qr-result"); var msg=document.getElementById("ssa-qr-msg"); if(res&&msg){res.style.display="block";msg.innerHTML="✓ "+s.name+" — Present!";setTimeout(function(){res.style.display="none";},2000);} showToast("✓ "+s.name+" marked P!"); } function printStudentQRCards() { var list=getSaStudents(); var W=''; list.forEach(function(s){ var qrUrl="https://api.qrserver.com/v1/create-qr-code/?size=80x80&data="+encodeURIComponent("STUDENT-"+s.id); W+='
' +'
Sara Azam Memorial Education
' +'' +'
'+s.name+'
' +'
'+s.class+' · Roll: '+s.roll+'
' +'
'; }); W+=''; var win=window.open("","_blank","width=900,height=700"); if(!win){showToast("Popup blocked","error");return;} win.document.write(W); win.document.close(); win.focus(); setTimeout(function(){win.print();},1200); } function renderBulkAttendance() { if(!teachers.length) { var tbl = document.getElementById("bulk-att-table"); if(tbl) tbl.innerHTML = '
No teachers added yet.
'; return; } var monthSel = document.getElementById("bulk-att-month"); var yearSel = document.getElementById("bulk-att-year"); var sessionMonthIdx = monthSel ? parseInt(monthSel.value) : 0; var year = yearSel ? parseInt(yearSel.value) : new Date().getFullYear(); var realMonth = SESSION_TO_REAL[sessionMonthIdx]; var monthName = SESSION_MONTH_NAMES[sessionMonthIdx]; // Session year fix: Jan/Feb/Mar belong to NEXT calendar year var yr = realMonth >= 3 ? year : year + 1; var daysInMonth = new Date(yr, realMonth+1, 0).getDate(); var firstDay = new Date(yr, realMonth, 1).getDay(); var key = yr + "-" + String(realMonth+1).padStart(2,"0"); // Update session label in header var sessionLabelEl = document.getElementById("bulk-att-session-label"); if(sessionLabelEl) { sessionLabelEl.textContent = "Session: " + CURRENT_SESSION + " · " + monthName + " " + yr; } // Build header: Name | S M T W T F S columns var dayNames = ["S","M","T","W","T","F","S"]; var headerCols = ""; for(var d=1; d<=daysInMonth; d++) { var dow = (firstDay + d - 1) % 7; var isWe = (dow===0 || dow===6); var dk2 = String(d).padStart(2,"0"); headerCols += '' + '
'+dayNames[dow]+'
' + '
'+d+'
' + ''; } var rows = teachers.map(function(t, ti) { if(!t.attendance) t.attendance = {}; if(!t.attendance[key]) t.attendance[key] = {}; var cells = ""; var P=0,A=0,H=0,L=0; for(var d=1; d<=daysInMonth; d++) { var dk = String(d).padStart(2,"0"); var dow = (firstDay+d-1)%7; var isWe = (dow===0||dow===6); var st = t.attendance[key][dk] || ""; if(st==="P") P++; else if(st==="A") A++; else if(st==="H") H++; else if(st==="L") L++; var cellDateX=new Date(yr,realMonth,d); var todayX=new Date(); todayX.setHours(0,0,0,0); var isFutureX=cellDateX>todayX; var isTodayX=(cellDateX.toDateString()===new Date().toDateString()); // If future date has data saved (bug from old version), clear it if(isFutureX && st) { t.attendance[key][dk]=""; st=""; } var bg = isFutureX?"#F5F5F5":st==="P"?"#DCFCE7":st==="A"?"#FEE2E2":st==="H"?"#FEF3C7":st==="L"?"#DBEAFE":isWe?"#F0F0F0":"#fff"; var col = isFutureX?"#D1D5DB":st==="P"?"#15803D":st==="A"?"#B91C1C":st==="H"?"#B45309":st==="L"?"#1D4ED8":"#9CA3AF"; var todayRing=isTodayX?"box-shadow:inset 0 0 0 2px #2563EB;":""; var cellOnClick=isFutureX?"":"onclick='bulkCycleDay(this)'"; var cellTitle=isFutureX?"Future locked":isTodayX?"Today: "+t.name:t.name+" Day "+d; cells += '' + ''+(isFutureX?"":st||"")+'' + ''; } // Summary cells — with unique IDs for live update var tid2=t.id; var sumCells = ''+P+'' +''+A+'' +''+H+'' +''+L+''; var rowBg = ti%2===0 ? "" : "background:#FAFAFA;"; return '' + '' + '
'+t.name+'
' + '
'+t.subject+'
' + '' + cells + sumCells + ''; }).join(""); var html2 = '
' + '' + '' + '' + '' + headerCols + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + rows + '
TeacherSummary
'+monthName+' '+year+'PAHL
'; var tbl = document.getElementById("bulk-att-table"); if(tbl) tbl.innerHTML = html2; // Always update summary after table render saveData(); // save cleared future dates setTimeout(function(){ renderBulkSummary(key, monthName, yr, daysInMonth); }, 50); } function renderBulkSummary(key, monthName, year, daysInMonth) { // Count directly from attendance data — always accurate var totP=0,totA=0,totH=0,totL=0; teachers.forEach(function(t){ if(!t.attendance||!t.attendance[key]) return; // Count only days 1 to daysInMonth of this month for(var d=1;d<=daysInMonth;d++){ var dk=String(d).padStart(2,"0"); var st=t.attendance[key][dk]||""; if(st==="P") totP++; else if(st==="A") totA++; else if(st==="H") totH++; else if(st==="L") totL++; } }); var sumEl=document.getElementById("bulk-att-summary"); if(!sumEl) return; sumEl.innerHTML=[ ["Present",totP,"#15803D","#F0FDF4","#86EFAC","P"], ["Absent",totA,"#DC2626","#FEF2F2","#FECACA","A"], ["Holiday",totH,"#B45309","#FFFBEB","#FDE68A","H"], ["Leave",totL,"#1D4ED8","#EFF6FF","#BFDBFE","L"] ].map(function(s){ return '
' +'
'+s[5]+'
' +'
'+s[1]+'
' +'
Total '+s[0]+'
' +'
'; }).join(""); } function bulkCycleDay(el) { var tid = parseInt(el.getAttribute("data-tid")); var key = el.getAttribute("data-key"); var day = el.getAttribute("data-day"); if(el.getAttribute("data-future")==="1") return; var t = teachers.find(function(x){return x.id===tid;}); if(!t) return; if(!t.attendance) t.attendance={}; if(!t.attendance[key]) t.attendance[key]={}; var seq=["","P","A","H","L"]; var cur=t.attendance[key][day]||""; var next=seq[(seq.indexOf(cur)+1)%seq.length]; function doChange(){ t.attendance[key][day]=next; saveData(); var bg=next==="P"?"#DCFCE7":next==="A"?"#FEE2E2":next==="H"?"#FEF3C7":next==="L"?"#DBEAFE":"#fff"; var col=next==="P"?"#15803D":next==="A"?"#B91C1C":next==="H"?"#B45309":next==="L"?"#1D4ED8":"#9CA3AF"; el.style.background=bg; var sp=el.querySelector("span"); if(sp){sp.style.color=col;sp.textContent=next;} var monthSel=document.getElementById("bulk-att-month"); var yearSel=document.getElementById("bulk-att-year"); var realMon=SESSION_TO_REAL[monthSel?parseInt(monthSel.value):0]; var yr=yearSel?parseInt(yearSel.value):new Date().getFullYear(); var dim=new Date(yr,realMon+1,0).getDate(); var P2=0,A2=0,H2=0,L2=0; for(var d=1;d<=dim;d++){var dk=String(d).padStart(2,"0");var s2=t.attendance[key][dk]||"";if(s2==="P")P2++;else if(s2==="A")A2++;else if(s2==="H")H2++;else if(s2==="L")L2++;} var pEl=document.getElementById("sc-P-"+tid);var aEl=document.getElementById("sc-A-"+tid); var hEl=document.getElementById("sc-H-"+tid);var lEl=document.getElementById("sc-L-"+tid); if(pEl)pEl.textContent=P2;if(aEl)aEl.textContent=A2;if(hEl)hEl.textContent=H2;if(lEl)lEl.textContent=L2; renderBulkSummary(key,SESSION_MONTH_NAMES[monthSel?parseInt(monthSel.value):0],yr,dim); } // Check if this is today or a back date var nowCheck=new Date(); var partsCheck=key.split("-"); var cellDateCheck=new Date(parseInt(partsCheck[0]),parseInt(partsCheck[1])-1,parseInt(day)); var isTodayCheck=(cellDateCheck.toDateString()===nowCheck.toDateString()); var isBackDateCheck=cellDateCheck📅' +'
Back Date Change?
' +'
'+t.name+'
' +'
Date: '+dateStr+'   ' +''+(cur||"—")+'' +' → ' +''+(next||"blank")+'' +'
' +'
' +'' +'' +'
'; document.body.appendChild(popup); popup.querySelector("#bc-yes").onclick=function(){popup.remove();doChange();}; popup.querySelector("#bc-no").onclick=function(){popup.remove();}; popup.onclick=function(e){if(e.target===popup)popup.remove();}; } else { // Today or blank cell — direct change, no popup doChange(); } } function bulkFillAll(status) { var monthSel=document.getElementById("bulk-att-month"); var yearSel=document.getElementById("bulk-att-year"); var sessionMonthIdx=monthSel?parseInt(monthSel.value):0; var year=yearSel?parseInt(yearSel.value):new Date().getFullYear(); var realMonth=SESSION_TO_REAL[sessionMonthIdx]; var yr=realMonth>=3?year:year+1; var key=yr+"-"+String(realMonth+1).padStart(2,"00"); var today=new Date(); var todayYear=today.getFullYear(), todayMonth=today.getMonth(), todayDate=today.getDate(); if(todayYear!==yr||todayMonth!==realMonth){ showToast("Aaj ki date selected month mein nahi hai","error"); return; } var dk=String(todayDate).padStart(2,"00"); teachers.forEach(function(t){ if(!t.attendance) t.attendance={}; if(!t.attendance[key]) t.attendance[key]={}; t.attendance[key][dk]=status; }); saveData(); renderBulkAttendance(); showToast("Aaj ("+todayDate+") sabhi teachers Present mark ho gaye!"); } function bulkFillDate(th) { var key = th.getAttribute("data-key"); var day = th.getAttribute("data-day"); var daysInMonth = parseInt(th.getAttribute("data-days")||"30"); if(!key||!day) return; // Block future dates var partsK=key.split("-"); var cellDateK=new Date(parseInt(partsK[0]),parseInt(partsK[1])-1,parseInt(day)); var todayK=new Date(); todayK.setHours(0,0,0,0); if(cellDateK>todayK){showToast("Future date — attendance nahi lag sakti","error");return;} // Determine current status of this column (majority vote) var seq=["","P","A","H","L"]; var counts={"":0,"P":0,"A":0,"H":0,"L":0}; teachers.forEach(function(t){ var st=(t.attendance&&t.attendance[key]&&t.attendance[key][day])||""; counts[st]=(counts[st]||0)+1; }); // Find most common status to decide next cycle step var dominant=""; var maxCount=0; Object.keys(counts).forEach(function(s){ if(counts[s]>maxCount){maxCount=counts[s];dominant=s;} }); var nextStatus=seq[(seq.indexOf(dominant)+1)%seq.length]; // Apply to all teachers teachers.forEach(function(t){ if(!t.attendance) t.attendance={}; if(!t.attendance[key]) t.attendance[key]={}; t.attendance[key][day]=nextStatus; }); saveData(); // Update all cells in this column var bg2=nextStatus==="P"?"#DCFCE7":nextStatus==="A"?"#FEE2E2":nextStatus==="H"?"#FEF3C7":nextStatus==="L"?"#DBEAFE":"#fff"; var col2=nextStatus==="P"?"#15803D":nextStatus==="A"?"#B91C1C":nextStatus==="H"?"#B45309":nextStatus==="L"?"#1D4ED8":"#9CA3AF"; var cells2=document.querySelectorAll("td[data-key='"+key+"'][data-day='"+day+"']"); cells2.forEach(function(cell){ cell.style.background=bg2; var span=cell.querySelector("span"); if(span){span.style.color=col2;span.textContent=nextStatus;} }); // Update each teacher row summary + bottom summary var monthSel=document.getElementById("bulk-att-month"); var yearSel=document.getElementById("bulk-att-year"); var realMonth=SESSION_TO_REAL[monthSel?parseInt(monthSel.value):0]; var year=yearSel?parseInt(yearSel.value):new Date().getFullYear(); teachers.forEach(function(t){ var P2=0,A2=0,H2=0,L2=0; for(var d=1;d<=daysInMonth;d++){ var dk=String(d).padStart(2,"0"); var sv=t.attendance&&t.attendance[key]?t.attendance[key][dk]||"":""; if(sv==="P")P2++; else if(sv==="A")A2++; else if(sv==="H")H2++; else if(sv==="L")L2++; } var pEl=document.getElementById("sc-P-"+t.id); var aEl=document.getElementById("sc-A-"+t.id); var hEl=document.getElementById("sc-H-"+t.id); var lEl=document.getElementById("sc-L-"+t.id); if(pEl)pEl.textContent=P2; if(aEl)aEl.textContent=A2; if(hEl)hEl.textContent=H2; if(lEl)lEl.textContent=L2; }); renderBulkSummary(key,SESSION_MONTH_NAMES[monthSel?parseInt(monthSel.value):0],year,daysInMonth); var label=nextStatus||"(blank)"; showToast("Day "+parseInt(day)+" — sab teachers: "+label); } function printBulkAttendance() { var monthSel=document.getElementById("bulk-att-month"); var yearSel=document.getElementById("bulk-att-year"); var sessionMonthIdx=monthSel?parseInt(monthSel.value):0; var year=yearSel?parseInt(yearSel.value):new Date().getFullYear(); var realMonthPrint=SESSION_TO_REAL[sessionMonthIdx]; var yrPrint=realMonthPrint>=3?year:year+1; var today=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var html3=buildAttendanceHTML(teachers, sessionMonthIdx, yrPrint, today, false); var win=window.open("","_blank","width=900,height=700"); if(!win){showToast("Popup blocked!","error");return;} win.document.write(html3); win.document.close(); win.focus(); setTimeout(function(){win.print();},400); } function printAllTeachersAttFromDash() { printBulkAttendance(); } function printAllTeachersAttFromDash() { if(!teachers.length){showToast("No teachers to print","error");return;} var monthSel=document.getElementById("all-att-month"); var yearSel=document.getElementById("all-att-year"); var sessionMonthIdx=monthSel?parseInt(monthSel.value):0; var year=yearSel?parseInt(yearSel.value):new Date().getFullYear(); var today=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var html2=buildAttendanceHTML(teachers, sessionMonthIdx, year, today, false); var win=window.open("","_blank","width=900,height=700"); win.document.write(html2); win.document.close(); win.focus(); setTimeout(function(){win.print();},400); } function printAllTeachersAtt() { if(!teachers.length){showToast("No teachers to print","error");return;} var monthSel=g("tp-att-month"), yearSel=g("tp-att-year"); var sessionMonthIdx=monthSel?parseInt(monthSel.value):0; var year=yearSel?parseInt(yearSel.value):new Date().getFullYear(); var today=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var html2=buildAttendanceHTML(teachers, sessionMonthIdx, year, today, false); var win=window.open("","_blank","width=900,height=700"); win.document.write(html2); win.document.close(); win.focus(); setTimeout(function(){win.print();},400); } function buildAttendanceHTML(teacherList, sessionMonthIdx, year, today, individual) { var realMonth=SESSION_TO_REAL[sessionMonthIdx]; var monthName=SESSION_MONTH_NAMES[sessionMonthIdx]; var daysInMonth=new Date(year,realMonth+1,0).getDate(); var firstDay=new Date(year,realMonth,1).getDay(); var key=year+"-"+String(realMonth+1).padStart(2,"0"); var dayNames=["S","M","T","W","T","F","S"]; var h='' +''; // Cover header h+='
'; h+='
'; h+='
Sara Azam Memorial Education
'; h+='
'+(individual?"Teacher":"All Staff")+" Attendance · "+monthName+" "+year+' · Printed: '+today+'
'; if(!individual) { // ALL TEACHERS: summary register table var cols=""; for(var d=1;d<=daysInMonth;d++) cols+=''+(d<=9?"0"+d:d)+''; h+=''; h+=''; h+=''; h+=cols; h+=''; h+=''; h+=''; h+=''; h+=''; teacherList.forEach(function(t,ti){ if(!t.attendance) t.attendance={}; if(!t.attendance[key]) t.attendance[key]={}; var P=0,A=0,H=0,L=0; var cells=""; for(var d=1;d<=daysInMonth;d++){ var dk=String(d).padStart(2,"0"); var st=t.attendance[key][dk]||""; if(st==="P")P++; else if(st==="A")A++; else if(st==="H")H++; else if(st==="L")L++; var isWe=((firstDay+d-1)%7===0||((firstDay+d-1)%7===6)); var bg=st==="P"?"#DCFCE7":st==="A"?"#FEE2E2":st==="H"?"#FEF3C7":st==="L"?"#DBEAFE":isWe?"#F1F5F9":""; var col=st==="P"?"#15803D":st==="A"?"#B91C1C":st==="H"?"#B45309":st==="L"?"#1D4ED8":"#9CA3AF"; cells+=''; } h+=''; h+=cells; h+=''; h+=''; h+=''; h+=''; }); h+='
TEACHERPAHL
'+(st||"")+'
'+t.name+'
'+t.subject+'
'+P+''+A+''+H+''+L+'
'; h+='
P=Present · A=Absent · H=Holiday · L=Leave
'; } else { // INDIVIDUAL: calendar layout var t=teacherList[0]; if(!t.attendance) t.attendance={}; if(!t.attendance[key]) t.attendance[key]={}; h+='
'; [["Name",t.name],["Subject",t.subject],["Class",t.class||"—"]].forEach(function(f){ h+='
'; h+='
'+f[0]+'
'; h+='
'+f[1]+'
'; }); h+='
'; // Calendar grid h+='
'; h+=''; h+=''; dayNames.forEach(function(d,i){ var isWe=(i===0||i===6); h+=''; }); h+=''; var cells=[]; for(var e=0;e'); var P=0,A=0,H=0,L=0; for(var d=1;d<=daysInMonth;d++){ var dk=String(d).padStart(2,"0"); var st=t.attendance[key][dk]||""; if(st==="P")P++; else if(st==="A")A++; else if(st==="H")H++; else if(st==="L")L++; var isWe=((firstDay+d-1)%7===0||((firstDay+d-1)%7===6)); var bg=st==="P"?"#DCFCE7":st==="A"?"#FEE2E2":st==="H"?"#FEF3C7":st==="L"?"#DBEAFE":isWe?"#F5F5F5":"#fff"; var col=st==="P"?"#15803D":st==="A"?"#B91C1C":st==="H"?"#B45309":st==="L"?"#1D4ED8":"#374151"; cells.push(''); if((firstDay+d)%7===0||d===daysInMonth){ var remaining=7-((firstDay+d)%7); if(d===daysInMonth&&remaining<7) for(var x=0;x'); h+=''+cells.join("")+''; cells=[]; } } h+='
'+d+'
' +'
'+d+'
' +'
'+(st||"—")+'
' +'
'; h+='
'; [["Present",P,"#15803D","#F0FDF4","#86EFAC"],["Absent",A,"#DC2626","#FEF2F2","#FECACA"], ["Holiday",H,"#B45309","#FFFBEB","#FDE68A"],["Leave",L,"#1D4ED8","#EFF6FF","#BFDBFE"] ].forEach(function(s){ h+='
'; h+='
'+s[1]+'
'; h+='
'+s[0]+'
'; }); h+='
'; h+='
Working Days: '+(P+A+L)+' · Total: '+daysInMonth+'
'; } // Signature h+='
'; [["Teacher",""],["Class Incharge",""],["Principal","Date: "+today]].forEach(function(s){ h+='
'; h+='
'+s[0]+'
'; }); h+='
'; h+=''; return h; } function toggleSalary(el) { var key = el.getAttribute("data-key"); var key = el.getAttribute("data-key"); var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; if(!t.salaryPaid) t.salaryPaid={}; t.salaryPaid[key]=!t.salaryPaid[key]; saveData(); renderTpKhata(); showToast(t.salaryPaid[key]?"Salary paid!":"Salary marked unpaid"); } // ── dashboard ── function updateDash(){ var hr = new Date().getHours(); var greet = hr < 12 ? "Good morning" : hr < 17 ? "Good afternoon" : "Good evening"; var grEl = document.getElementById("dash-greeting"); if(grEl) grEl.textContent = greet + ", Principal 🙏"; // Update session labels dynamically var sc = document.getElementById("session-chip"); if(sc) sc.textContent = SESSION_SHORT; var sl = document.getElementById("session-label"); if(sl) sl.textContent = SESSION_LABEL; // Fee status — use actual feePayments with carry-forward logic var now7=new Date(); var sessStart7=now7.getMonth()>=3?now7.getFullYear():now7.getFullYear()-1; if(ACTIVE_SESSION_START) sessStart7=ACTIVE_SESSION_START; var paid=0, due=0; students.forEach(function(s){ try{ var feeData7=calculateMonthlyFees(s); var elapsed7=0, covered7=0; var realMM7={April:3,May:4,June:5,July:6,August:7,September:8,October:9,November:10,December:11,January:0,February:1,March:2}; feeData7.forEach(function(r){ if(r.type!=="regular") return; var rm7=realMM7[r.month]; if(rm7===undefined) return; var yr7=rm7>=3?sessStart7:sessStart7+1; if(yr7>now7.getFullYear()||(yr7===now7.getFullYear()&&rm7>now7.getMonth())) return; elapsed7++; if(r.paidAmt>0||r.cfCovered||r.hasCF||r.netPayable<=0) covered7++; }); if(elapsed7>0 && covered7>=elapsed7) paid++; else if(elapsed7>0) due++; }catch(e){} }); var avg=0; try{avg=students.length?Math.round(students.reduce(function(a,s){return a+(parseInt(s.marks)||0);},0)/students.length):0;}catch(e){} g("kpi-s").textContent=students.length; g("kpi-t").textContent=teachers.length; g("kpi-m").textContent=students.length?avg+"%":"—"; g("kpi-f").textContent=students.length?Math.round(paid/students.length*100)+"%":"—"; g("d-paid").textContent=paid; g("d-due").textContent=due; // Today's collection var _td = new Date(); var todayStr7 = String(_td.getDate()).padStart(2,"0")+"."+String(_td.getMonth()+1).padStart(2,"0")+"."+_td.getFullYear(); var todayCol = 0; students.forEach(function(s){ if(!s.feePayments) return; Object.keys(s.feePayments).forEach(function(mk){ var pmts = s.feePayments[mk]; if(!Array.isArray(pmts)) return; pmts.forEach(function(p){ if(p.date && p.date === todayStr7 && p.amt) todayCol += p.amt; }); }); // Also check admPayments if(s.admPayments && Array.isArray(s.admPayments)){ s.admPayments.forEach(function(p){ if(p.date && p.date === todayStr7 && p.amt) todayCol += p.amt; }); } }); var tcEl = g("d-today-collection"); if(tcEl) tcEl.textContent = "₹" + todayCol.toLocaleString("en-IN"); // cats var cats={General:0,OBC:0,SC:0,ST:0},catC={General:"#1D4ED8",OBC:"#92400E",SC:"#7E22CE",ST:"#0F766E"},catB={General:"b-blue",OBC:"b-amber",SC:"b-purple",ST:"b-teal"}; students.forEach(function(s){if(cats[s.category]!==undefined)cats[s.category]++;}); g("d-cats").innerHTML=students.length?Object.keys(cats).map(function(c){return'
'+c+'
'+cats[c]+'
';}).join(""):'
No data yet.
'; // ── Exam schedule on dashboard ── var schedEl=g("d-exam-schedule"); if(schedEl){ loadExams(); if(!EXAMS.length){ schedEl.innerHTML='
Koi exam nahi hai.
'; } else { var today6=new Date(); var colors6=[ {bg:"linear-gradient(135deg,#F0FDF4,#DCFCE7)",border:"#86EFAC",col:"#15803D",badge:"✓ Done"}, {bg:"linear-gradient(135deg,#EFF6FF,#DBEAFE)",border:"#93C5FD",col:"#1D4ED8",badge:"Upcoming"}, {bg:"linear-gradient(135deg,#FFFBEB,#FEF3C7)",border:"#FDE68A",col:"#B45309",badge:"Upcoming"}, {bg:"linear-gradient(135deg,#FAF5FF,#EDE9FE)",border:"#C4B5FD",col:"#7C3AED",badge:"Upcoming"}, {bg:"linear-gradient(135deg,#FFF1F2,#FFE4E6)",border:"#FCA5A5",col:"#DC2626",badge:"Upcoming"}, ]; schedEl.innerHTML=EXAMS.map(function(ex,i){ var c6=colors6[i%colors6.length]; var isPast=false; if(ex.date){var dp=ex.date.split(".");if(dp.length>=3){var ed=new Date(parseInt(dp[2]),parseInt(dp[1])-1,parseInt(dp[0]));isPast=ed' +''+badge+'' +'
'+ex.name+'
' +'
'+(ex.date||"No date")+' · '+ex.maxMarks+' marks
' +''; }).join(""); } } } // ── students ── var _studentView = 'thumb'; function setStudentView(v) { _studentView = v; ["thumb","list","alpha"].forEach(function(n){ var btn=document.getElementById("sv-"+n); if(!btn) return; if(n===v){ btn.style.background="#1E3A5F"; btn.style.color="#fff"; } else { btn.style.background="transparent"; btn.style.color="#64748B"; } }); renderStudents(); } function renderStudents(){ var srch=(val("ss")||"").toLowerCase(); var clsF=val("sc"); var secF=val("s-sec")||""; var catF=val("scat")||""; // Session filter — show only current session students in main list var curSessYear3 = ACTIVE_SESSION_START || SESSION_START_YEAR; var list=students.filter(function(s){ var clsBase=s.class?s.class.split("-")[0]:""; var clsSec=s.class?s.class.split("-")[1]||"A":""; if(s.admissionClosed && !srch) return false; // Session filter: hide students from other sessions (unless searching) if(!srch && s.admDate){ var ap=s.admDate.split("."); if(ap.length>=3){var ay=parseInt(ap[2]),am=parseInt(ap[1])-1;var sSY=am>=3?ay:ay-1;if(sSY!==curSessYear3)return false;} } return (!srch || s.name.toLowerCase().includes(srch) || (s.roll&&s.roll.toLowerCase().includes(srch)) || (s.phone&&s.phone.toLowerCase().includes(srch)) || (s.city&&s.city.toLowerCase().includes(srch))) && (!clsF || clsBase===clsF) && (!secF || clsSec===secF) && (!catF || s.category===catF); }); g("s-count").textContent=students.length+" students enrolled"; var footer=g("s-footer"); if(footer) footer.textContent="Showing "+list.length+" of "+students.length+" students"; var grid=g("s-grid"); if(!grid) return; if(!list.length){ grid.style.display="block"; grid.style.gridTemplateColumns=""; grid.style.gap=""; grid.innerHTML='
' +'
🖉
' +'
No Students Found
' +'
Try changing your search or filters
'; return; } // ── THUMBNAIL VIEW ── if(_studentView==='thumb') { grid.style.display="grid"; grid.style.gridTemplateColumns="repeat(auto-fill,minmax(260px,1fr))"; grid.style.gap="12px"; grid.innerHTML=list.map(function(s){ var i=students.indexOf(s); var a=avt(s.name,i); var clsParts=s.class?s.class.split("-"):["",""]; var clsBase=clsParts[0]; var clsSec=clsParts[1]||"A"; var closedBadge=s.admissionClosed?'🔒':''; return '
' +'
' +'
' +'
' +(s.photo?'' :'
'+a.init+'
') +'
'+s.name+closedBadge+'
' +'
'+s.roll+'
' +'
' +'
Class
' +'
'+clsBase+' Sec '+clsSec+'
' +'
Category
' +''+s.category+'
' +'
Father
' +'
'+(s.father||"—")+'
' +'
Phone
' +'
'+(s.phone||"—")+'
' +'
' +'
' +'
'+(s.dob||"")+'
' +'
View Profile
' +'
'; }).join(""); } // ── LIST VIEW ── else if(_studentView==='list') { grid.style.display="block"; grid.style.gridTemplateColumns=""; grid.style.gap=""; var rows=list.map(function(s,idx){ var a=avt(s.name,students.indexOf(s)); var clsParts=s.class?s.class.split("-"):["",""]; var clsBase=clsParts[0]; var clsSec=clsParts[1]||"A"; var rowBg=idx%2===0?"#fff":"#F8FAFC"; var photoEl=s.photo?'' :'
'+a.init+'
'; return '' +''+(idx+1)+'' +'
' +photoEl+'
'+s.name+(s.admissionClosed?' 🔒':'')+'
' +'
'+s.roll+'
' +''+clsBase+' Sec '+clsSec+'' +''+s.category+'' +''+(s.father||"—")+'' +''+(s.phone||"—")+'' +''+(s.dob||"")+'' +''; }).join(""); grid.innerHTML='
' +'' +'' +'' +'' +'' +'' +'' +'' +'' +''+rows+'
#NameClassCategoryFatherPhoneDOB
'; } // ── ALPHABET VIEW ── else if(_studentView==='alpha') { grid.style.display="block"; grid.style.gridTemplateColumns=""; grid.style.gap=""; var grouped={}; list.sort(function(a,b){return a.name.localeCompare(b.name);}); list.forEach(function(s){ var letter=(s.name||"?")[0].toUpperCase(); if(!grouped[letter]) grouped[letter]=[]; grouped[letter].push(s); }); var letters=Object.keys(grouped).sort(); grid.innerHTML=letters.map(function(letter){ var items=grouped[letter].map(function(s){ var a=avt(s.name,students.indexOf(s)); var clsParts=s.class?s.class.split("-"):["",""]; var clsBase=clsParts[0]; var clsSec=clsParts[1]||"A"; return '
' +(s.photo?'' :'
'+a.init+'
') +'
'+s.name+(s.admissionClosed?' 🔒':'')+'
' +'
'+s.roll+'  ·  '+clsBase+' Sec '+clsSec+'
' +''+s.category+'' +'
'; }).join(""); return '
' +'
' +'
'+letter+'
' +'
' +'
'+grouped[letter].length+' student'+(grouped[letter].length>1?'s':'')+'
' +'
' +'
'+items+'
' +'
'; }).join(""); } } // ── teachers ── // ── TEACHER REMOVE MODE ── var _teacherRemoveMode = false; var _selectedTeachers = {}; function toggleTeacherRemoveMode() { _teacherRemoveMode = !_teacherRemoveMode; _selectedTeachers = {}; var bar = document.getElementById("t-remove-bar"); var btn = document.getElementById("t-remove-mode-btn"); if(_teacherRemoveMode) { if(bar){bar.style.display="flex";} if(btn){btn.style.background="#DC2626";btn.style.color="#fff";btn.textContent="✕ Cancel";} } else { if(bar){bar.style.display="none";} if(btn){btn.style.background="#FEF2F2";btn.style.color="#B91C1C";btn.textContent="🗑 Remove Staff";} } renderTeachers(); } function toggleTeacherSelect(tid) { if(_selectedTeachers[tid]) delete _selectedTeachers[tid]; else _selectedTeachers[tid]=true; // Update this card visually without full re-render var card=document.getElementById("tc-"+tid); if(card){ var sel=!!_selectedTeachers[tid]; card.style.border=sel?"2.5px solid #DC2626":"1px solid #E2E8F0"; card.style.background=sel?"#FEF2F2":"#fff"; var cb=card.querySelector(".t-checkbox"); if(cb) cb.style.background=sel?"#DC2626":"#fff"; if(cb) cb.innerHTML=sel?"✓":""; } // Update count var cnt=Object.keys(_selectedTeachers).length; var bar=document.getElementById("t-remove-bar"); if(bar){ var btn=bar.querySelector("button"); if(btn) btn.textContent="🗑 Remove Selected ("+(cnt||0)+")"; } } function removeSelectedTeachers() { var ids=Object.keys(_selectedTeachers).map(Number); if(!ids.length){showToast("Koi teacher select nahi kiya","error");return;} var names=ids.map(function(id){var t=teachers.find(function(x){return x.id===id;});return t?t.name:"";}).filter(Boolean); var popup=document.createElement("div"); popup.style.cssText="position:fixed;inset:0;background:rgba(15,23,42,.55);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);"; popup.innerHTML='
' +'
🗑️
' +'
'+ids.length+' Staff Remove Karen?
' +'
' +names.map(function(n){return '• '+n;}).join('
') +'
' +'
' +'' +'' +'
'; document.body.appendChild(popup); popup.querySelector("#rms-yes").onclick=function(){ popup.remove(); teachers=teachers.filter(function(t){return !_selectedTeachers[t.id];}); saveData(); updateDash(); showToast(ids.length+" staff removed","error"); _teacherRemoveMode=false; _selectedTeachers={}; var bar=document.getElementById("t-remove-bar"); if(bar) bar.style.display="none"; var btn=document.getElementById("t-remove-mode-btn"); if(btn){btn.style.background="#FEF2F2";btn.style.color="#B91C1C";btn.textContent="🗑 Remove Staff";} renderTeachers(); }; popup.querySelector("#rms-no").onclick=function(){popup.remove();}; popup.onclick=function(e){if(e.target===popup)popup.remove();}; } var _teacherView='thumb'; function setTeacherView(v){ _teacherView=v; ["thumb","list","alpha"].forEach(function(n){ var btn=document.getElementById("tv-"+n); if(!btn) return; if(n===v){btn.style.background="#1E3A5F";btn.style.color="#fff";} else{btn.style.background="transparent";btn.style.color="#64748B";} }); renderTeachers(); } function renderTeachers(){ g("t-count").textContent=teachers.length+" faculty members"; var gd=g("t-grid"); if(!teachers.length){ gd.style.display="block"; gd.innerHTML='
👪

No Teachers Added
Use "Add Staff" button above.
'; return; } var noPrefix=["Play","Nursery","L.K.G","U.K.G"]; // ── THUMBNAIL VIEW ── if(_teacherView==='thumb'){ gd.style.display="grid"; gd.style.gridTemplateColumns="repeat(auto-fill,minmax(310px,1fr))"; gd.style.gap="13px"; gd.innerHTML=teachers.map(function(t,i){ var a=avt(t.name,i+2); var clsBase=t.class?t.class.split("-")[0]:""; var clsLabel=t.class?((noPrefix.indexOf(clsBase)>=0?"":"Class ")+clsBase):"—"; var studCount=students.filter(function(s){return s.class&&s.class.split("-")[0]===clsBase;}).length; var sal=t.salary?("Rs."+parseInt(t.salary||0).toLocaleString("en-IN")+"/mo"):"—"; var photoHtml=t.photo ?'' :'
'+a.init+'
'; var isSel=!!_selectedTeachers[t.id]; return '
' +'
' +'
' +'
' +photoHtml +'
' +'
'+t.name+'
' +'
'+t.subject+(t.category_type?' '+t.category_type+'':'')+'
' +'
' +(_teacherRemoveMode ? '
'+(isSel?"✓":"")+'
' : '') +'
' +'
' +'
Class
'+clsLabel+'
' +'
Students
'+studCount+' enrolled
' +'
Experience
'+(t.exp||"—")+'
' +'
Salary
'+sal+'
' +'
' +'
' +'
'+(t.phone||"")+'
' +'
View Profile →
' +'
' +'
'; }).join(""); } // ── LIST VIEW ── else if(_teacherView==='list'){ gd.style.display="block"; gd.style.gridTemplateColumns=""; gd.style.gap=""; var rows=teachers.map(function(t,idx){ var a=avt(t.name,idx+2); var clsBase=t.class?t.class.split("-")[0]:""; var clsLabel=t.class?((noPrefix.indexOf(clsBase)>=0?"":"Class ")+clsBase):"—"; var sal=t.salary?("Rs."+parseInt(t.salary||0).toLocaleString("en-IN")):"—"; var rowBg=idx%2===0?"#fff":"#FAFAFA"; var photoEl=t.photo ?'' :'
'+a.init+'
'; return '' +''+(idx+1)+'' +'' +'
' +photoEl +'
'+t.name+'
' +'
'+t.subject+'
' +'
' +'' +''+clsLabel+'' +''+(t.qual||"—")+'' +''+(t.exp||"—")+'' +''+sal+'' +''+(t.phone||"—")+'' +'' +(_teacherRemoveMode ? '
'+(_selectedTeachers[t.id]?"✓":"")+'
' : '') +'' +''; }).join(""); gd.innerHTML='
' +'' +'' +'' +'' +'' +'' +'' +'' +'' +'' +''+rows+'
#NameClassQualificationExperienceSalaryPhoneAction
'; } // ── ALPHABET VIEW ── else if(_teacherView==='alpha'){ gd.style.display="block"; gd.style.gridTemplateColumns=""; gd.style.gap=""; var sorted=teachers.slice().sort(function(a,b){return a.name.localeCompare(b.name);}); var grouped={}; sorted.forEach(function(t){ var letter=(t.name||"?")[0].toUpperCase(); if(!grouped[letter]) grouped[letter]=[]; grouped[letter].push(t); }); var letters=Object.keys(grouped).sort(); gd.innerHTML=letters.map(function(letter){ var items=grouped[letter].map(function(t){ var a=avt(t.name,teachers.indexOf(t)+2); var clsBase=t.class?t.class.split("-")[0]:""; var clsLabel=t.class?((noPrefix.indexOf(clsBase)>=0?"":"Class ")+clsBase):"—"; return '
' +(t.photo?'' :'
'+a.init+'
') +'
' +'
'+t.name+'
' +'
'+t.subject+'  ·  '+clsLabel+'
' +'
' +(_teacherRemoveMode ? '
'+(_selectedTeachers[t.id]?"✓":"")+'
' : '') +'
'; }).join(""); return '
' +'
' +'
'+letter+'
' +'
' +'
'+grouped[letter].length+' teacher'+(grouped[letter].length>1?'s':'')+'
' +'
' +'
'+items+'
' +'
'; }).join(""); } } function confirmRemoveTeacher(tid) { var t=teachers.find(function(x){return x.id===tid;}); if(!t) return; var popup=document.createElement("div"); popup.style.cssText="position:fixed;inset:0;background:rgba(15,23,42,.55);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);"; popup.innerHTML='
' +'
🗑️
' +'
Remove Staff?
' +'
'+t.name+' ko staff list se hata diya jaayega.
' +'
' +'' +'' +'
'; document.body.appendChild(popup); popup.querySelector("#rt-yes").onclick=function(){ popup.remove(); teachers=teachers.filter(function(x){return x.id!==tid;}); saveData(); renderTeachers(); updateDash(); showToast(t.name+" removed","error"); }; popup.querySelector("#rt-no").onclick=function(){popup.remove();}; popup.onclick=function(e){if(e.target===popup)popup.remove();}; } // ── fees ── // ════════════════════════════════════════ // STUDENT FEE LEDGER SYSTEM // ════════════════════════════════════════ // ═══ SHARED FEE CALCULATION — used by Student Profile + Quick Panel + Register ═══ function calculateMonthlyFees(s, overrideSessionYear){ if(!s) return []; try{var lf=localStorage.getItem("saraAzam_classFees_v1");if(lf)Object.assign(CLASS_FEES,JSON.parse(lf));}catch(e){} var cls=s.class?s.class.split("-")[0]:""; var cf=CLASS_FEES[cls]||{}; var tuitionFee=parseInt(cf.tuition)||0; var busFee=s.bus==="Yes"?(parseInt(s.busFare)||parseInt(cf.bus)||0):0; var examFeeAmt=parseInt(cf.exam)||0; var examMonths=getExamMonths(); // Session year var now=new Date(); var sessionYear=overrideSessionYear||0; if(!sessionYear) sessionYear=now.getMonth()>=3?now.getFullYear():now.getFullYear()-1; if(!overrideSessionYear && ACTIVE_SESSION_START) sessionYear=ACTIVE_SESSION_START; if(s.admDate){var ap=s.admDate.split(".");if(ap.length>=3){var ay=parseInt(ap[2]),am=parseInt(ap[1])-1;var aSY=am>=3?ay:ay-1;if(aSY>=sessionYear) sessionYear=aSY;}} // Admission carry var admTotal=(parseInt(cf.admission)||0)+(parseInt(cf.annual)||0)+(parseInt(cf.exam)||0)+(parseInt(cf.tuition)||0)+(s.bus==="Yes"?(parseInt(s.busFare)||parseInt(cf.bus)||0):0); var admNet=Math.max(0,admTotal-(parseInt(s.discount)||0)); var admPaid=parseInt(s.admFeePaid)||0; var carry=admNet-admPaid; // Admission month index var admMIdx=-1; var m12=["January","February","March","April","May","June","July","August","September","October","November","December"]; if(s.admDate){var ap2=s.admDate.split(".");if(ap2.length>=3){var amr=parseInt(ap2[1])-1,ay2=parseInt(ap2[2]),asy=amr>=3?ay2:ay2-1;if(asy===sessionYear) admMIdx=FEE_MONTHS.indexOf(m12[amr]||"");}} var realMonthMap={April:3,May:4,June:5,July:6,August:7,September:8,October:9,November:10,December:11,January:0,February:1,March:2}; function getKey(mn){var rm=realMonthMap[mn];var yr=rm>=3?sessionYear:sessionYear+1;return yr+"-"+String(rm+1).padStart(2,"0");} if(!s.feePayments) s.feePayments={}; var results=[]; FEE_MONTHS.forEach(function(mn,idx){ var mk=getKey(mn); if(admMIdx>=0 && idx=0 && idx===admMIdx){results.push({month:mn,mk:mk,idx:idx,type:"admission",sessionYear:sessionYear});return;} var examFee=examMonths.indexOf(mn)>=0?examFeeAmt:0; var mTotal=tuitionFee+busFee+examFee; var pmts=s.feePayments[mk]||s.feePayments[mn]||[]; if(!Array.isArray(pmts)) pmts=[]; var paidAmt=pmts.reduce(function(t,p){return t+(p.amt||0);},0); var hasCF=pmts.some(function(p){return p.note==="Carry forward";}); var netPayable=mTotal+carry; var balance=netPayable-paidAmt; var oldCarry=carry; carry=balance; results.push({ month:mn, mk:mk, idx:idx, type:"regular", tuitionFee:tuitionFee, busFee:busFee, examFee:examFee, mTotal:mTotal, carry:oldCarry, netPayable:netPayable, paidAmt:paidAmt, balance:balance, hasCF:hasCF, pmts:pmts, sessionYear:sessionYear, cfCovered:false }); }); // BACKWARD PASS: mark months covered by future lump-sum payments // Only if a month's payment fully absorbed the carry (balance <= 0) for(var bi=results.length-1;bi>=0;bi--){ var r=results[bi]; if(r.type!=="regular") continue; // Payment must have cleared the carry (balance <= 0 means all carry absorbed) if(r.paidAmt>0 && r.carry>0 && r.balance<=0){ // This month's payment absorbed carry from earlier months for(var bj=bi-1;bj>=0;bj--){ if(results[bj].type!=="regular") continue; if(results[bj].paidAmt===0 && results[bj].balance>0){ results[bj].cfCovered=true; } } } } return results; } var FEE_MONTHS = ["April","May","June","July","August","September","October","November","December","January","February","March"]; // ── Fee Tab Switch ── function feeTab(name) { ["adm","monthly"].forEach(function(t){ var p=document.getElementById("fee-panel-"+t); var b=document.getElementById("fee-tab-"+t); if(p) p.style.display="none"; if(b){b.style.color="#94A3B8";b.style.fontWeight="600";b.style.borderBottomColor="transparent";} }); var ap=document.getElementById("fee-panel-"+name); var ab=document.getElementById("fee-tab-"+name); if(ap) ap.style.display="block"; // panels inside scrollable div if(ab){ab.style.color="#1D4ED8";ab.style.fontWeight="700";ab.style.borderBottomColor="#1D4ED8";} if(name==="monthly") { // Pre-set session year dropdown to student's admission session var sid2 = currentDetailId||currentStudentId; var s2 = students.find(function(x){return x.id===sid2;}); if(s2) { var sess2 = getStudentSession(s2); var selEl = document.getElementById("mf-session-year"); if(selEl && (!selEl.getAttribute("data-prev-student") || parseInt(selEl.getAttribute("data-prev-student"))!==sid2)) { selEl.value = sess2.year; } } renderMonthlyFeeTable(); } } // ── Monthly Fee Table ── function renderMonthlyFeeTable() { // Only render when monthly fee panel is open if(!document.getElementById("sd-monthly-fee-rows")) return; var sid = currentDetailId || currentStudentId; var s = students.find(function(x){ return x.id===sid; }); if(!s) return; currentDetailId = s.id; if(!s.feePayments) s.feePayments={}; // Load latest CLASS_FEES try{ var lf=localStorage.getItem("saraAzam_classFees_v1"); if(lf) Object.assign(CLASS_FEES,JSON.parse(lf)); }catch(e){} // Build session dropdown from admission date → current year only var sessionYearEl = document.getElementById("mf-session-year"); var now2 = new Date(); var currentSessionYear = now2.getMonth()>=3 ? now2.getFullYear() : now2.getFullYear()-1; // Find student's admission session year var admSessionStartYear = currentSessionYear; // default if(s.admDate) { var ap2=s.admDate.split("."); if(ap2.length>=3){ var ay=parseInt(ap2[2]), am=parseInt(ap2[1])-1; admSessionStartYear = am>=3 ? ay : ay-1; // April+ = same year, Jan-Mar = prev year } } // Rebuild dropdown: only from admission session to current session if(sessionYearEl) { var prevVal = parseInt(sessionYearEl.getAttribute("data-prev-student"))===sid?parseInt(sessionYearEl.value):admSessionStartYear; sessionYearEl.innerHTML = ""; for(var sy=admSessionStartYear; sy<=currentSessionYear; sy++){ var opt=document.createElement("option"); opt.value=sy; opt.textContent=sy+"-"+(sy+1).toString().slice(-2); sessionYearEl.appendChild(opt); } // Default: admission session (or previously selected if same student) sessionYearEl.value = prevVal>=admSessionStartYear && prevVal<=currentSessionYear ? prevVal : admSessionStartYear; sessionYearEl.setAttribute("data-prev-student", sid); } var sessionYear = sessionYearEl ? parseInt(sessionYearEl.value)||admSessionStartYear : admSessionStartYear; var sessionLabel = document.getElementById("mf-session-label"); if(sessionLabel) sessionLabel.textContent = "Session: "+sessionYear+"-"+(sessionYear+1).toString().slice(-2)+" (April "+sessionYear+" – March "+(sessionYear+1)+")"; // Use shared calculation function (same as Quick Panel) var feeData = []; try { feeData = calculateMonthlyFees(s, sessionYear); } catch(e) { console.error("calculateMonthlyFees error:", e); } // Store for receipt if(!window._monthFeeCalc) window._monthFeeCalc={}; feeData.forEach(function(r){if(r.type==="regular") window._monthFeeCalc[r.mk]=r;}); var tbody2 = document.getElementById("sd-monthly-fee-rows"); if(!tbody2) return; tbody2.innerHTML = feeData.map(function(r, idx){ if(r.type==="hidden") return ''; if(r.type==="admission"){ return '' +''+r.month+'' +'✓ Tuition & Transport included in Admission Fee' +''; } var mn=r.month, mk=r.mk, monthTotal=r.mTotal, carry=r.carry, netPayable=r.netPayable, paidAmt=r.paidAmt, balance=r.balance; var pmts=r.pmts||[]; var hasCarryMarker=pmts.some(function(p){return p.note==="Carry forward";}); var tuitionFee=r.tuitionFee, busFee=r.busFee, examFee=r.examFee; var oldCarry=carry; var rowBg = idx%2===0?"#fff":"#FAFAFA"; var balCol = hasCarryMarker ? "#B45309" : (balance>0?"#DC2626":balance<0?"#0891B2":"#15803D"); var receivedCell, lastCol; if(paidAmt > 0) { receivedCell = 'Rs.'+paidAmt.toLocaleString("en-IN") +'
'+pmts.map(function(p){return p.date;}).join(", ")+'
'; lastCol = '' +''; } else if(hasCarryMarker) { receivedCell = '↪ CF'; lastCol = ''; } else if(r.cfCovered) { // Covered by future lump-sum payment receivedCell = 'CF ✓ Covered'; lastCol = ''; balCol = "#1D4ED8"; } else { var inputId = 'mpay-'+mk; receivedCell = '
' +'' +'' +'
'; lastCol = ''; } return '' +''+mn+'' +'Rs.'+tuitionFee.toLocaleString("en-IN")+'' +''+(busFee>0?"Rs."+busFee.toLocaleString("en-IN"):"—")+'' +''+(examFee>0?"Rs."+examFee.toLocaleString("en-IN"):"—")+'' +'Rs.'+monthTotal.toLocaleString("en-IN")+'' +'' +(oldCarry>0?"Rs."+oldCarry.toLocaleString("en-IN"):oldCarry<0?"Cr "+Math.abs(oldCarry).toLocaleString("en-IN"):"—") +'' +'Rs.'+netPayable.toLocaleString("en-IN")+'' +''+receivedCell+'' +'' +(balance>0?"Rs."+balance.toLocaleString("en-IN"):balance<0?"Cr "+Math.abs(balance).toLocaleString("en-IN"):"✓") +'' +''+lastCol+'' +''; }).join(""); } // Helper: called from input Enter key function mthPayFromInput(inp) { var mk = inp.getAttribute("data-mk"); var mn = inp.getAttribute("data-mn"); if(mk && mn) saveMonthPayInline(mk, mn); } // Helper: called from ✓ button function mthPayFromBtn(btn) { var mk = btn.getAttribute("data-mk"); var mn = btn.getAttribute("data-mn"); // Find the input in same div var inp = btn.parentNode ? btn.parentNode.querySelector("input") : null; if(!inp) inp = document.getElementById("mpay-"+mk); if(mk && mn) saveMonthPayInline(mk, mn); } function saveMonthPayInline(mk, mn) { var s = students.find(function(x){ return x.id===(currentDetailId||currentStudentId); }); if(!s) return; var inp = document.getElementById("mpay-"+mk); var totalPaid = inp ? parseInt(inp.value)||0 : 0; if(totalPaid<=0){ if(inp){inp.style.borderColor="#DC2626";inp.focus();} showToast("Amount enter karo","error"); return; } var today2 = new Date(); var dispDate = String(today2.getDate()).padStart(2,"0")+"."+String(today2.getMonth()+1).padStart(2,"0")+"."+today2.getFullYear(); if(!s.feePayments) s.feePayments={}; // Load fees try{var lf=localStorage.getItem("saraAzam_classFees_v1");if(lf)Object.assign(CLASS_FEES,JSON.parse(lf));}catch(e){} var cls5=s.class?s.class.split("-")[0]:""; var cf5=CLASS_FEES[cls5]||{}; var tuitionF=parseInt(cf5.tuition)||0; var busF=s.bus==="Yes"?(parseInt(s.busFare)||0):0; var examFeeAmt5=parseInt(cf5.exam)||0; // Admission balance (carry) var admTotal5=(parseInt(cf5.admission)||0)+(parseInt(cf5.annual)||0)+(parseInt(cf5.exam)||0)+(parseInt(cf5.tuition)||0)+(s.bus==="Yes"?(parseInt(s.busFare)||0):0); var admNet5=Math.max(0,admTotal5-(parseInt(s.discount)||0)); var admPaid5=parseInt(s.admFeePaid)||0; var admBal5=admNet5-admPaid5; // positive=due, negative=credit // Session year var syEl=document.getElementById("mf-session-year"); var sy5=syEl?parseInt(syEl.value)||0:0; if(!sy5){var n5=new Date();sy5=n5.getMonth()>=3?n5.getFullYear():n5.getFullYear()-1;} // Exam months var examMonthsX=["March"]; if(document.getElementById("exam-month-sep")&&document.getElementById("exam-month-sep").checked) examMonthsX.push("September"); if(document.getElementById("exam-month-dec")&&document.getElementById("exam-month-dec").checked) examMonthsX.push("December"); // Admission month index var rmMap5={April:3,May:4,June:5,July:6,August:7,September:8,October:9,November:10,December:11,January:0,February:1,March:2}; var admIdx5=-1; if(s.admDate){var ap5=s.admDate.split(".");if(ap5.length>=3){var amr5=parseInt(ap5[1])-1,ay5=parseInt(ap5[2]),asy5=amr5>=3?ay5:ay5-1;if(asy5===sy5){var m12x=["January","February","March","April","May","June","July","August","September","October","November","December"];admIdx5=FEE_MONTHS.indexOf(m12x[amr5]||"");}}} // Find clicked month index var clickedIdx5=-1; FEE_MONTHS.forEach(function(m,i){var rm=rmMap5[m];var yr=rm>=3?sy5:sy5+1;var k=yr+"-"+String(rm+1).padStart(2,"00");if(k===mk)clickedIdx5=i;}); // ── Compute Net Payable of clicked month (running carry) ── var runCarry=admBal5; for(var ci=0;ci=0&&ci<=admIdx5) continue; var cm5=FEE_MONTHS[ci]; var crm5=rmMap5[cm5];var cyr5=crm5>=3?sy5:sy5+1; var ck5=cyr5+"-"+String(crm5+1).padStart(2,"00"); var examF5=examMonthsX.indexOf(cm5)>=0?examFeeAmt5:0; var mTotal5=tuitionF+busF+examF5; var pmts5=(s.feePayments[ck5]||[]); var paid5=pmts5.reduce(function(t,p){return t+(p.amt||0);},0); if(ci=0?examFeeAmt5:0; var clickedMonthFee=tuitionF+busF+clickedExamF; var clickedNetPayable=clickedMonthFee+runCarry; // ── If payment >= net payable of clicked month → clear all previous unpaid months ── if(totalPaid>=clickedNetPayable){ // Save Rs.0 to all unpaid months before clicked month (carry forward marker) for(var ci2=0;ci2=0&&ci2<=admIdx5) continue; var cm2=FEE_MONTHS[ci2]; var crm2=rmMap5[cm2];var cyr2=crm2>=3?sy5:sy5+1; var ck2=cyr2+"-"+String(crm2+1).padStart(2,"00"); var existPaid2=(s.feePayments[ck2]||[]).reduce(function(t,p){return t+(p.amt||0);},0); if(existPaid2>0) continue; // already paid if(!s.feePayments[ck2]) s.feePayments[ck2]=[]; s.feePayments[ck2].push({amt:0,date:dispDate,note:"Carry forward"}); } // Save EXACT net payable to clicked month (clears balance to zero) if(!s.feePayments[mk]) s.feePayments[mk]=[]; s.feePayments[mk].push({amt:totalPaid,date:dispDate}); } else { // Payment less than net payable — just save to clicked month // Rs.0 markers to previous unpaid months for(var ci3=0;ci3=0&&ci3<=admIdx5) continue; var cm3=FEE_MONTHS[ci3]; var crm3=rmMap5[cm3];var cyr3=crm3>=3?sy5:sy5+1; var ck3=cyr3+"-"+String(crm3+1).padStart(2,"00"); var existPaid3=(s.feePayments[ck3]||[]).reduce(function(t,p){return t+(p.amt||0);},0); if(existPaid3>0) continue; if(!s.feePayments[ck3]) s.feePayments[ck3]=[]; if(ck3===mk){ s.feePayments[ck3].push({amt:totalPaid,date:dispDate}); } else { s.feePayments[ck3].push({amt:0,date:dispDate,note:"Carry forward"}); } } } // Admission auto-marker (Rs.0) if unpaid if(admBal5>0&&admPaid5===0){ if(!s.admPayments) s.admPayments=[]; var hasAdmMarker=s.admPayments.some(function(p){return p.amt===0;}); if(!hasAdmMarker) s.admPayments.push({amt:0,date:dispDate,note:"Carry forward via monthly"}); } saveData(); showToast("Rs."+totalPaid.toLocaleString("en-IN")+" — "+mn+" saved!"); renderMonthlyFeeTable(); var admPanel=document.getElementById("fee-panel-adm"); if(admPanel&&admPanel.style.display!=="none") renderFeeLedger(); // Refresh fee management register + ledger if(typeof renderFees==="function") renderFees(); setTimeout(function(){printMonthFeeReceiptKey(mk,mn);},300); } // ════════════════════════════════════════ // ADMIN PANEL // ════════════════════════════════════════ var ADMIN_PASSWORD = "sara2025"; // Default password function openAdminPanel() { var popup = document.getElementById("admin-panel-popup"); if(popup) { popup.style.display="flex"; return; } // Check if logged in if(!sessionStorage.getItem("adminLoggedIn")) { showAdminLogin(); } else { showAdminDashboard(); } } function showAdminLogin() { var existing = document.getElementById("admin-panel-popup"); if(existing) existing.remove(); var d = document.createElement("div"); d.id="admin-panel-popup"; d.style.cssText="position:fixed;inset:0;background:rgba(10,20,40,.7);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);"; d.innerHTML='
' +'
🔐
' +'
Admin Panel
' +'
Sara Azam Memorial Education
' +'' +'' +'
' +'' +'' +'
'; document.body.appendChild(d); setTimeout(function(){ var inp=document.getElementById("admin-pwd-input"); if(inp) inp.focus(); },100); } function adminLogin() { var inp = document.getElementById("admin-pwd-input"); var pwd = inp ? inp.value : ""; var savedPwd = localStorage.getItem("saraAzam_adminPwd") || ADMIN_PASSWORD; if(pwd === savedPwd) { sessionStorage.setItem("adminLoggedIn","1"); var p=document.getElementById("admin-panel-popup"); if(p) p.remove(); showAdminDashboard(); } else { var err=document.getElementById("admin-pwd-error"); if(err) err.style.display="block"; if(inp){ inp.style.borderColor="#DC2626"; inp.value=""; inp.focus(); } } } function showAdminDashboard() { var existing = document.getElementById("admin-panel-popup"); if(existing) existing.remove(); var totalStudents = students.length; var totalTeachers = (typeof teachers!=="undefined"?teachers:TCS||[]).length; var d = document.createElement("div"); d.id="admin-panel-popup"; d.style.cssText="position:fixed;inset:0;background:rgba(10,20,40,.7);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);"; d.innerHTML='
' // Header +'
' +'
' +'
⚙️ Admin Panel
' +'
Sara Azam Memorial Education
' +'
' +'' +'
' // Stats +'
' +'
' +'
' +'
'+totalStudents+'
' +'
Total Students
' +'
' +'
' +'
'+totalTeachers+'
' +'
Total Staff
' +'
' +'
' +'
' // Actions +'
' +'
Data Management
' // Clear Students +'
' +'
' +'
🗑 Clear All Students
' +'
Sabhi students ka data delete karo
' +'
' +'' +'
' // Clear Teachers +'
' +'
' +'
🗑 Clear All Staff
' +'
Sabhi teachers ka data delete karo
' +'
' +'' +'
' // Clear Report Cards & Marks +'
' +'
' +'
🗑 Clear Marks & Report Cards
' +'
Sabhi students ke marks + report card data
' +'
' +'' +'
' // Clear Exams +'
' +'
' +'
🗑 Clear All Exams
' +'
Sabhi exam data + marks delete karo
' +'
' +'' +'
' // Clear Exam Routine +'
' +'
' +'
🗑 Clear Exam Routine
' +'
Timetable / Routine data delete karo
' +'
' +'' +'
' // Clear All +'
' +'
' +'
⚠️ Clear All Data
' +'
Students + Staff + Exams + Fees — sab kuch
' +'
' +'' +'
' // Change Password +'
' +'
🔑 Change Password
' +'
' +'' +'' +'
' +'
' // School Name +'
' +'
🏫 School Info
' +'' +'' +'
' +'
' +'
'; document.body.appendChild(d); d.onclick=function(e){ if(e.target===d) d.remove(); }; } function adminLogout() { sessionStorage.removeItem("adminLoggedIn"); var p=document.getElementById("admin-panel-popup"); if(p) p.remove(); } function adminConfirm(msg, cb) { var popup = document.createElement("div"); popup.style.cssText="position:fixed;inset:0;background:rgba(15,23,42,.6);z-index:999999;display:flex;align-items:center;justify-content:center;"; popup.innerHTML='
' +'
⚠️
' +'
Are you sure?
' +'
'+msg+'
' +'
' +'' +'' +'
'; document.body.appendChild(popup); popup.querySelector("#ac-yes").onclick=function(){ popup.remove(); cb(); }; popup.querySelector("#ac-no").onclick=function(){ popup.remove(); }; } function adminClearStudents() { adminConfirm("Sabhi "+students.length+" students delete ho jaayenge. Yeh wapas nahi aayenge!", function(){ students=[]; localStorage.removeItem("saraAzam_students_v1"); saveData(); renderStudents(); updateDash(); var p=document.getElementById("admin-panel-popup"); if(p) p.remove(); showToast("Sabhi students delete ho gaye!","error"); }); } function adminClearTeachers() { var tArr = typeof teachers!=="undefined"?teachers:(TCS||[]); adminConfirm("Sabhi "+tArr.length+" staff delete ho jaayenge!", function(){ if(typeof teachers!=="undefined") teachers=[]; if(typeof TCS!=="undefined") TCS=[]; localStorage.removeItem("saraAzam_teachers_v3"); saveData(); renderTeachers(); updateDash(); var p=document.getElementById("admin-panel-popup"); if(p) p.remove(); showToast("Sabhi staff delete ho gaye!","error"); }); } function adminClearReportCards(){ adminConfirm("Sabhi students ke marks aur report cards delete ho jaayenge!", function(){ // Reset marks in all students students.forEach(function(s){ s.marks=0; delete s.examResults; delete s.reportCard; }); if(typeof EXAMS!=="undefined"){ EXAMS.forEach(function(ex){ex.results={};}); saveExams(); } localStorage.removeItem("saraAzam_reportCards"); saveData(); var p=document.getElementById("admin-panel-popup"); if(p) p.remove(); showToast("Sabhi marks aur report cards clear ho gaye!","error"); try{renderExams();}catch(e){} }); } function adminClearExams() { var cnt = typeof EXAMS!=="undefined" ? EXAMS.length : 0; adminConfirm("Sabhi "+cnt+" exams aur marks delete ho jaayenge!", function(){ if(typeof EXAMS!=="undefined") EXAMS=[]; localStorage.removeItem("saraAzam_exams_v1"); try{saveExams();}catch(e){} var p=document.getElementById("admin-panel-popup"); if(p) p.remove(); showToast("Sabhi exams delete ho gaye!","error"); try{renderExams();}catch(e){} }); } function adminClearRoutine() { var cnt = typeof EXAM_ROUTINE!=="undefined" ? EXAM_ROUTINE.length : 0; adminConfirm("Exam routine ("+cnt+" entries) delete ho jaayegi!", function(){ if(typeof EXAM_ROUTINE!=="undefined") EXAM_ROUTINE=[]; localStorage.removeItem("saraAzam_examRoutine"); var p=document.getElementById("admin-panel-popup"); if(p) p.remove(); showToast("Exam routine clear ho gayi!","error"); }); } function adminClearAll() { adminConfirm("SABHI DATA DELETE HO JAAYEGA — Students, Staff, Fees, Attendance — Sab kuch! Yeh wapas nahi aayega!", function(){ students=[]; if(typeof teachers!=="undefined") teachers=[]; if(typeof TCS!=="undefined") TCS=[]; if(typeof EXAMS!=="undefined") EXAMS=[]; if(typeof EXAM_ROUTINE!=="undefined") EXAM_ROUTINE=[]; issuedTCs=[]; // Clear all localStorage keys including report card data ["saraAzam_students_v1","saraAzam_teachers_v3","saraAzam_tcs_v1", "saraAzam_classFees_v1","saraAzam_exams_v1","saraAzam_examRoutine", "saraAzam_reportCards","saraAzam_dataVersion"].forEach(function(k){ localStorage.removeItem(k); }); saveData(); try{renderStudents();}catch(e){} try{renderTeachers();}catch(e){} try{renderExams();}catch(e){} updateDash(); var p=document.getElementById("admin-panel-popup"); if(p) p.remove(); showToast("Sab kuch clear ho gaya!","error"); }); } function adminChangePwd() { var inp=document.getElementById("admin-new-pwd"); var pwd=inp?inp.value.trim():""; if(pwd.length<4){ showToast("Password kam se kam 4 characters ka hona chahiye","error"); return; } localStorage.setItem("saraAzam_adminPwd",pwd); if(inp) inp.value=""; showToast("Password change ho gaya!"); } function adminSaveSchoolInfo() { var nameEl=document.getElementById("admin-school-name"); if(nameEl&&nameEl.value.trim()){ localStorage.setItem("saraAzam_schoolName",nameEl.value.trim()); showToast("School info saved!"); } } // ════════════════════════════════════════ // QUICK PAY FEE PANEL // ════════════════════════════════════════ function openQuickPayFee() { var existing = document.getElementById("quick-pay-popup"); if(existing) { existing.remove(); return; } var d = document.createElement("div"); d.id = "quick-pay-popup"; d.style.cssText = "position:fixed;inset:0;background:rgba(10,20,40,.65);z-index:99990;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);padding:16px;"; d.innerHTML = '
' // Header +'
' +'
' +'
💳 Quick Pay Fee
' +'
Student search karo aur payment karo
' +'
' +'' +'
' // Search bar +'
' +'
' +'🔍' +'' +'
' // Search results dropdown +'' +'
' // Student info + fee table +'
' +'
' +'
🔍
' +'
Student search karo upar
' +'
' +'
' +'
'; document.body.appendChild(d); d.querySelector("#qpf-close").onclick = function(){ d.remove(); }; d.onclick = function(e){ if(e.target===d) d.remove(); }; setTimeout(function(){ var inp=document.getElementById("qpf-search"); if(inp) inp.focus(); },100); } function qpfSearch(val) { var res = document.getElementById("qpf-results"); if(!res) return; val = val.trim().toLowerCase(); if(!val){ res.style.display="none"; return; } var matches = students.filter(function(s){ return s.name.toLowerCase().includes(val) || (s.roll||"").toLowerCase().includes(val) || (s.class||"").toLowerCase().includes(val); }).slice(0,10); if(!matches.length){ res.style.display="none"; return; } res.style.display="block"; res.innerHTML = matches.map(function(s){ var a = avt(s.name, students.indexOf(s)); return '
' +'
'+a.init+'
' +'
'+s.name+'
' +'
'+s.roll+' · '+(s.class||'—')+'
' +'
'; }).join(""); } function qpfSelectStudent(id) { var s = students.find(function(x){ return x.id===id; }); if(!s) return; var res = document.getElementById("qpf-results"); if(res) res.style.display="none"; var inp = document.getElementById("qpf-search"); if(inp) inp.value = s.name+" ("+s.roll+")"; currentDetailId = s.id; // Use shared calculation var feeData = []; try { feeData = calculateMonthlyFees(s); } catch(e) { console.error("calculateMonthlyFees error:", e); } // Extract sessionYear from feeData var sessionYear = 0; for(var fi=0;fi=3?_n.getFullYear():_n.getFullYear()-1;} // Store calc for receipt if(!window._monthFeeCalc) window._monthFeeCalc={}; feeData.forEach(function(r){if(r.type==="regular") window._monthFeeCalc[r.mk]=r;}); var rows = ""; feeData.forEach(function(r){ if(r.type==="hidden") return; if(r.type==="admission"){ rows+=''+r.month+'' +'✓ Included in Admission Fee'; return; } var rowBg=r.idx%2===0?"#fff":"#FAFAFA"; var balCol=r.balance>0?"#DC2626":r.balance<0?"#0891B2":"#15803D"; var inputId="qpf-inp-"+r.mk; // Determine pay cell var payCell=''; if(r.balance<=0&&r.paidAmt>0){ // Paid - Clear or Credit payCell='
' +''+(r.balance<0?'Cr '+Math.abs(r.balance).toLocaleString('en-IN'):'✓ Clear')+'' +'' +'
'; } else if(r.cfCovered){ // Covered by future lump-sum payment payCell='CF ✓ Covered'; } else if(r.netPayable<=0&&r.balance<=0){ // Covered by credit carry payCell='CF ✓ Covered'; } else { // Unpaid - show input payCell='
' +(r.paidAmt>0?'Part':'') +'' +'' +'
'; } // Print cell var printCell=r.paidAmt>0?'':''; rows+='' +''+r.month+'' +'Rs.'+r.mTotal.toLocaleString("en-IN")+'' +''+(r.carry!==0?(r.carry>0?"Rs."+r.carry.toLocaleString("en-IN"):"Cr "+Math.abs(r.carry).toLocaleString("en-IN")):"—")+'' +'Rs.'+Math.max(0,r.netPayable).toLocaleString("en-IN")+'' +''+(r.paidAmt>0?"Rs."+r.paidAmt.toLocaleString("en-IN"):"—")+'' +''+(r.cfCovered?"CF ✓":r.balance>0?"Rs."+r.balance.toLocaleString("en-IN"):r.balance<0?"Cr "+Math.abs(r.balance).toLocaleString("en-IN"):"✓")+'' +''+payCell+'' +''+printCell+'' +''; }); var content = document.getElementById("qpf-content"); if(!content) return; var a2=avt(s.name,students.indexOf(s)); content.innerHTML = // Student card — name clickable to open profile '
' +'
'+a2.init+'
' +'
' +'
'+s.name+' ↗
' +'
'+s.roll+' · '+(s.class||"—")+(s.father?" · "+s.father:"")+'
' +'
' +'
' +'
Session
' +'
'+sessionYear+'-'+(sessionYear+1).toString().slice(-2)+'
' +'
' +'
' // Table +'
' +'' +'' +'' +'' +'' +'' +'' +'' +'' +'' +'' +''+rows+'' +'
MonthTotalCarryNet PayableReceivedBalancePay🖨️
'; } function qpfPrintReceipt(btn) { var mn = btn.getAttribute("data-mn"); var mk = btn.getAttribute("data-mk"); if(mn && mk) printMonthFeeReceipt(mn, mk); } function qpfUnpay(mk, mn, studentId) { var s = students.find(function(x){ return x.id===studentId; }); if(!s||!s.feePayments) return; if(confirm(mn+" ka payment undo karna chahte ho?")) { delete s.feePayments[mk]; saveData(); showToast(mn+" payment undo ho gaya","error"); currentDetailId = studentId; qpfSelectStudent(studentId); } } function qpfPay(mk, mn, studentId) { var s = students.find(function(x){ return x.id===studentId; }); if(!s) return; var inp = document.getElementById("qpf-inp-"+mk); var totalPaid = inp ? parseInt(inp.value)||0 : 0; if(totalPaid<=0){ if(inp){inp.style.borderColor="#DC2626";inp.focus();} showToast("Amount enter karo","error"); return; } // Set currentDetailId so saveMonthPayInline works correctly currentDetailId = studentId; var today2 = new Date(); var dispDate = String(today2.getDate()).padStart(2,"0")+"."+String(today2.getMonth()+1).padStart(2,"0")+"."+today2.getFullYear(); if(!s.feePayments) s.feePayments={}; // Load fees try{var lf=localStorage.getItem("saraAzam_classFees_v1");if(lf)Object.assign(CLASS_FEES,JSON.parse(lf));}catch(e){} var cls5=s.class?s.class.split("-")[0]:""; var cf5=CLASS_FEES[cls5]||{}; var tuitionF=parseInt(cf5.tuition)||0; var busF=s.bus==="Yes"?(parseInt(s.busFare)||0):0; var examFeeAmt5=parseInt(cf5.exam)||0; // Admission balance var admTotal5=(parseInt(cf5.admission)||0)+(parseInt(cf5.annual)||0)+(parseInt(cf5.exam)||0)+(parseInt(cf5.tuition)||0)+(s.bus==="Yes"?(parseInt(s.busFare)||0):0); var admNet5=Math.max(0,admTotal5-(parseInt(s.discount)||0)); var admPaid5=parseInt(s.admFeePaid)||0; var admBal5=admNet5-admPaid5; // positive=due, negative=credit // Session year from Quick Pay panel or current var n5=new Date(); var sy5=n5.getMonth()>=3?n5.getFullYear():n5.getFullYear()-1; // Try to get from student's session if(s.admDate){var ap5q=s.admDate.split(".");if(ap5q.length>=3){var ay5q=parseInt(ap5q[2]),am5q=parseInt(ap5q[1])-1;var asy5q=am5q>=3?ay5q:ay5q-1;if(asy5q>=sy5) sy5=asy5q;}} // Exam months from config var examMonthsX = getExamMonths(); // Admission month index var rmMap5={April:3,May:4,June:5,July:6,August:7,September:8,October:9,November:10,December:11,January:0,February:1,March:2}; var admIdx5=-1; if(s.admDate){var ap5=s.admDate.split(".");if(ap5.length>=3){var amr5=parseInt(ap5[1])-1,ay5=parseInt(ap5[2]),asy5=amr5>=3?ay5:ay5-1;if(asy5===sy5){var m12x=["January","February","March","April","May","June","July","August","September","October","November","December"];admIdx5=FEE_MONTHS.indexOf(m12x[amr5]||"");}}} // Find clicked month index var clickedIdx5=-1; FEE_MONTHS.forEach(function(m,i){var rm=rmMap5[m];var yr=rm>=3?sy5:sy5+1;var k=yr+"-"+String(rm+1).padStart(2,"00");if(k===mk)clickedIdx5=i;}); // Compute net payable of clicked month (running carry) var runCarry=admBal5; for(var ci=0;ci=0&&ci<=admIdx5) continue; var cm5=FEE_MONTHS[ci]; var crm5=rmMap5[cm5];var cyr5=crm5>=3?sy5:sy5+1; var ck5=cyr5+"-"+String(crm5+1).padStart(2,"00"); var examF5=examMonthsX.indexOf(cm5)>=0?examFeeAmt5:0; var mTotal5=tuitionF+busF+examF5; var pmts5=(s.feePayments[ck5]||[]); var paid5=pmts5.reduce(function(t,p){return t+(p.amt||0);},0); if(ci=0?examFeeAmt5:0; var clickedMonthFee=tuitionF+busF+clickedExamF; var clickedNetPayable=clickedMonthFee+runCarry; // If payment >= net payable → clear all previous unpaid as CF + save to clicked month if(totalPaid>=clickedNetPayable){ for(var ci2=0;ci2=0&&ci2<=admIdx5) continue; var cm2=FEE_MONTHS[ci2]; var crm2=rmMap5[cm2];var cyr2=crm2>=3?sy5:sy5+1; var ck2=cyr2+"-"+String(crm2+1).padStart(2,"00"); var existPaid2=(s.feePayments[ck2]||[]).reduce(function(t,p){return t+(p.amt||0);},0); if(existPaid2>0) continue; if(!s.feePayments[ck2]) s.feePayments[ck2]=[]; s.feePayments[ck2].push({amt:0,date:dispDate,note:"Carry forward"}); } if(!s.feePayments[mk]) s.feePayments[mk]=[]; s.feePayments[mk].push({amt:totalPaid,date:dispDate}); } else { for(var ci3=0;ci3=0&&ci3<=admIdx5) continue; var cm3=FEE_MONTHS[ci3]; var crm3=rmMap5[cm3];var cyr3=crm3>=3?sy5:sy5+1; var ck3=cyr3+"-"+String(crm3+1).padStart(2,"00"); var existPaid3=(s.feePayments[ck3]||[]).reduce(function(t,p){return t+(p.amt||0);},0); if(existPaid3>0) continue; if(!s.feePayments[ck3]) s.feePayments[ck3]=[]; if(ck3===mk){ s.feePayments[ck3].push({amt:totalPaid,date:dispDate}); } else { s.feePayments[ck3].push({amt:0,date:dispDate,note:"Carry forward"}); } } } // Admission CF marker if(admBal5>0&&admPaid5===0){ if(!s.admPayments) s.admPayments=[]; var hasAdmM=s.admPayments.some(function(p){return p.amt===0;}); if(!hasAdmM) s.admPayments.push({amt:0,date:dispDate,note:"Carry forward via monthly"}); } saveData(); showToast("Rs."+totalPaid.toLocaleString("en-IN")+" — "+s.name+" saved!"); qpfSelectStudent(studentId); if(typeof renderFees==="function") renderFees(); setTimeout(function(){printMonthFeeReceipt(mn,mk);},400); } function renderFeeLedger() { // Only render when fee panel is open if(!document.getElementById("sd-adm-fee-rows")) return; var sid = currentDetailId || currentStudentId; var s = students.find(function(x){ return x.id === sid; }); if(!s) return; currentDetailId = s.id; // ensure it's set if(!s.feePayments) s.feePayments = {}; // Always load latest CLASS_FEES from localStorage (fresh read) try { var lf = localStorage.getItem("saraAzam_classFees_v1"); if(lf) { var parsed = JSON.parse(lf); Object.keys(parsed).forEach(function(k){ CLASS_FEES[k] = parsed[k]; }); } } catch(e){} var cls = s.class ? s.class.split("-")[0] : ""; var cf = CLASS_FEES[cls] || {}; var admFee = parseInt(cf.admission) || 0; var annualFee = parseInt(cf.annual) || 0; var examFee = parseInt(cf.exam) || 0; var tuitionFee = parseInt(cf.tuition) || 0; // busFee: student ke profile se — agar set nahi to 0 show karo + row dikhao var busFee = 0; if(s.bus === "Yes") { busFee = parseInt(s.busFare) || 0; } var showBusRow = (s.bus === "Yes"); // hamesha row show karo agar bus=Yes var discount = parseInt(s.discount) || 0; // ── ADMISSION FEE TABLE — always show all rows ── var sno = 1; var admRows = [ {sno:sno++, desc:"Admission Fee", amt:admFee}, {sno:sno++, desc:"Annual Charge", amt:annualFee}, {sno:sno++, desc:"Examination Fee", amt:examFee}, {sno:sno++, desc:"Tuition Fee", amt:tuitionFee} ]; if(showBusRow) admRows.push({sno:sno++, desc:"Transport Fee", amt:busFee}); var admTotal = admRows.reduce(function(t,r){ return t+r.amt; }, 0); var admNet = admTotal - discount; var admPaid = parseInt(s.admFeePaid) || 0; var admBal = admNet - admPaid; var tbody = document.getElementById("sd-adm-fee-rows"); if(tbody) tbody.innerHTML = admRows.map(function(r,i){ var amtCell = (r.desc==="Transport Fee" && r.amt===0) ? 'Not set — Edit Profile me Bus Fare set karo' : 'Rs.'+r.amt.toLocaleString("en-IN"); return '' +''+r.sno+'' +''+r.desc+'' +''+amtCell+'' +''; }).join(""); var tfoot = document.getElementById("sd-adm-fee-foot"); if(tfoot) tfoot.innerHTML = '' +'Total' +'Rs.'+admTotal.toLocaleString("en-IN")+'' +'' +'' +'Discount' +'' +'' +''+(discount>0?"- Rs."+discount.toLocaleString("en-IN"):"—")+'' +'' +'Net Payable' +'Rs.'+admNet.toLocaleString("en-IN")+'' // Check if admission has carry-forward marker +(function(){ // Check CF in admPayments OR any monthly month with Rs.0 (carry forward marker) var hasAdmCF = (s.admPayments && s.admPayments.some(function(p){return p.amt===0;})); if(!hasAdmCF && s.feePayments) { var fpKeys = Object.keys(s.feePayments); for(var fi=0;fi=0);})){ hasAdmCF = true; break; } } } if(admPaid > 0) { // Fully paid return '' +'✓ Payment Received' +'Rs.'+admPaid.toLocaleString("en-IN")+''; } else if(hasAdmCF) { // Carry forward — show CF, no input return '' +'↪ Carry Forward' +'Rs.'+admBal.toLocaleString("en-IN")+' → Monthly'; } else { // Not paid, no CF — show input return '' +'Payment Received' +'' +'' +''; } })() +'0?"#FECACA":admBal<0?"#93C5FD":"#86EFAC")+'">' +'Balance' +'' +(admBal>0?"Rs."+admBal.toLocaleString("en-IN"):admBal<0?"Rs."+Math.abs(admBal).toLocaleString("en-IN")+" CR":"✓ Clear") +'' +(admBal<0?'💡 Rs.'+Math.abs(admBal).toLocaleString("en-IN")+' extra amount monthly fee mein carry forward hoga':'') // Action row — Pay + Print below Balance +'' +'' +'
' +(function(){ var hasCF2 = (s.admPayments && s.admPayments.some(function(p){return p.amt===0;})); if(!hasCF2 && s.feePayments){var fpK=Object.keys(s.feePayments);for(var fi2=0;fi2=0);})){hasCF2=true;break;}}} if(admPaid>0){ return ''; } else if(hasCF2){ return ''; } else { return ''; } })() +'' +'
'; // ── MONTHLY FEE TABLE ── var monthlyTotal = tuitionFee + busFee; // busFee=0 if not set // Carry starts from admission balance: positive=due, negative=credit (overpayment) var carry = admBal > 0 ? admBal : 0; // admission due carries forward var admCredit = admBal < 0 ? Math.abs(admBal) : 0; // overpayment credit var tbody2 = document.getElementById("sd-monthly-fee-rows"); if(tbody2) tbody2.innerHTML = FEE_MONTHS.map(function(mn, idx){ var pmts = s.feePayments[mn] || []; var paidAmt = pmts.reduce(function(t,p){ return t+(p.amt||0); }, 0); var netPayable = monthlyTotal + carry; // Apply admission credit to first month(s) if(admCredit > 0 && idx === 0){ netPayable = netPayable - admCredit; if(netPayable < 0){ admCredit = Math.abs(netPayable); netPayable = 0; } else admCredit = 0; } var balance = netPayable - paidAmt; var oldCarry = carry; carry = balance > 0 ? balance : 0; var rowBg = idx%2===0 ? "#fff" : "#FAFAFA"; var balCol = balance>0 ? "#DC2626" : balance<0 ? "#1D4ED8" : "#15803D"; var paymentCell = paidAmt>0 ? ('Rs.'+paidAmt.toLocaleString("en-IN") +'
' +pmts.map(function(p){return p.date+(p.note?" ("+p.note+")":"");}).join("
") +'
') : '—'; return '' +''+mn+'' +'Rs.'+tuitionFee.toLocaleString("en-IN")+'' +''+(busFee>0?"Rs."+busFee.toLocaleString("en-IN"):"—")+'' +'—' +'Rs.'+monthlyTotal.toLocaleString("en-IN")+'' +''+(oldCarry>0?"Rs."+oldCarry.toLocaleString("en-IN"):"—")+'' +'Rs.'+netPayable.toLocaleString("en-IN")+'' +''+paymentCell+'' +''+(balance>0?"Rs."+balance.toLocaleString("en-IN"):balance<0?"Rs."+Math.abs(balance).toLocaleString("en-IN")+" CR":"✓")+'' +'' +'' +(paidAmt>0?'':'') +'' +''; }).join(""); // Set today date var today = new Date(); var tv = today.getFullYear()+"-"+String(today.getMonth()+1).padStart(2,"0")+"-"+String(today.getDate()).padStart(2,"0"); ["fp-date","ap-date"].forEach(function(id){ var el=document.getElementById(id); if(el&&!el.value) el.value=tv; }); } // ── Admission Fee Payment ── function doAdmPay() { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; var paidEl = document.getElementById("adm-paid-input"); var amt = paidEl ? (parseInt(paidEl.value)||0) : 0; // Allow 0 — means admission carry is settled via monthly payment var today = new Date(); var dispDate = String(today.getDate()).padStart(2,"00")+"."+String(today.getMonth()+1).padStart(2,"00")+"."+today.getFullYear(); if(amt === 0) { // Rs.0 = carry forward marker only — admFeePaid stays 0, balance carries to monthly if(!s.admPayments) s.admPayments = []; // Only add marker if not already added var hasMarker = s.admPayments.some(function(p){return p.amt===0;}); if(!hasMarker) { s.admPayments.push({amt:0, date:dispDate, note:"Carry forward to monthly"}); } // admFeePaid stays 0 — balance remains as carry in monthly fee saveData(); renderFeeLedger(); showToast("Admission balance carry forward ho gaya!"); return; } s.admFeePaid = amt; if(!s.admPayments) s.admPayments = []; s.admPayments.push({amt:amt, date:dispDate}); saveData(); renderFeeLedger(); showToast("Rs."+amt.toLocaleString("en-IN")+" payment saved!"); setTimeout(function(){ printAdmissionFeeReceipt(); }, 300); } function undoAdmCF() { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; if(confirm("Admission carry forward undo karna chahte ho?")) { s.admPayments = (s.admPayments||[]).filter(function(p){return p.amt!==0;}); saveData(); renderFeeLedger(); showToast("Admission CF undo ho gaya"); } } function undoAdmPay() { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; var popup = document.createElement("div"); popup.style.cssText = "position:fixed;inset:0;background:rgba(15,23,42,.5);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(3px);"; popup.innerHTML = '
' +'
' +'
Payment Undo Karen?
' +'
Rs.'+(s.admFeePaid||0).toLocaleString("en-IN")+' payment hata diya jaayega
' +'
' +'' +'' +'
'; document.body.appendChild(popup); popup.querySelector("#undo-yes").onclick = function(){ popup.remove(); s.admFeePaid = 0; s.admPayments = []; saveData(); renderFeeLedger(); showToast("Payment undo ho gaya","error"); }; popup.querySelector("#undo-no").onclick = function(){ popup.remove(); }; popup.onclick = function(e){ if(e.target===popup) popup.remove(); }; } function updateAdmPaid(val) { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; var paid = Math.max(0, parseInt(val)||0); s.admFeePaid = paid; saveData(); // Recalculate balance var disc = parseInt(s.discount)||0; var cls = s.class?s.class.split("-")[0]:""; var cf = CLASS_FEES[cls]||{}; var total = (parseInt(cf.admission)||0)+(parseInt(cf.annual)||0)+(parseInt(cf.exam)||0)+(parseInt(cf.tuition)||0); if(s.bus==="Yes") total += parseInt(s.busFare)||0; var net = Math.max(0, total-disc); var bal = net - paid; // positive=due, negative=credit var balEl = document.getElementById("adm-bal-display"); if(balEl){ if(bal>0){ balEl.textContent = "Rs."+bal.toLocaleString("en-IN"); balEl.style.color = "#DC2626"; balEl.parentElement.style.background = "#FEF2F2"; } else if(bal<0){ balEl.textContent = "Rs."+Math.abs(bal).toLocaleString("en-IN")+" CR"; balEl.style.color = "#1D4ED8"; balEl.parentElement.style.background = "#EFF6FF"; } else { balEl.textContent = "✓ Clear"; balEl.style.color = "#15803D"; balEl.parentElement.style.background = "#F0FDF4"; } } } function printAdmissionFeeReceipt() { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; var paidEl = document.getElementById("adm-paid-input"); if(paidEl) { s.admFeePaid = parseInt(paidEl.value)||0; saveData(); } printAdmissionReceipt(s); } function saveAndPrintAdmReceipt() { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; // Save current paid amount from input var paidEl = document.getElementById("adm-paid-input"); if(paidEl) { s.admFeePaid = parseInt(paidEl.value)||0; saveData(); } printAdmissionReceipt(s); } function updateAdmDiscount(val) { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; var disc = parseInt(val)||0; s.discount = disc; saveData(); // Recalculate net payable and balance live var cls = s.class?s.class.split("-")[0]:""; var cf = CLASS_FEES[cls]||{}; var admFee = parseInt(cf.admission)||0; var annualFee = parseInt(cf.annual)||0; var examFee = parseInt(cf.exam)||0; var tuitionFee = parseInt(cf.tuition)||0; var busFee = (s.bus==="Yes"&&s.busFare)?parseInt(s.busFare)||0:0; var admTotal = admFee+annualFee+examFee+tuitionFee+busFee; var admNet = Math.max(0, admTotal-disc); var admPaid = parseInt(s.admFeePaid)||0; var admBal = admNet-admPaid; var discEl=document.getElementById("adm-discount-display"); var netEl=document.getElementById("adm-net-display"); var balEl=document.getElementById("adm-bal-display"); if(discEl) discEl.textContent=disc>0?"- Rs."+disc.toLocaleString("en-IN"):"—"; if(netEl) netEl.textContent="Rs."+admNet.toLocaleString("en-IN"); if(balEl){ balEl.textContent=admBal>0?"Rs."+admBal.toLocaleString("en-IN"):"✓ Clear"; balEl.style.color=admBal>0?"#DC2626":"#15803D"; } } function openAdmPayment() { var box = document.getElementById("sd-adm-payment-box"); if(box) box.style.display = box.style.display==="none"?"block":"none"; } function payAdmFee() { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; var paidEl = document.getElementById("adm-paid-input"); var amt = paidEl ? (parseInt(paidEl.value)||0) : 0; if(amt <= 0) { showToast("Amount enter karo","error"); if(paidEl) paidEl.focus(); return; } s.admFeePaid = amt; saveData(); renderFeeLedger(); showToast("Rs."+amt.toLocaleString("en-IN")+" — Payment saved!"); // Auto print receipt setTimeout(function(){ saveAndPrintAdmReceipt(); }, 300); } function saveAdmPayment() { // Kept for backward compatibility payAdmFee(); } function printAdmissionReceipt(s) { var cls = s.class?s.class.split("-")[0]:""; var cf = CLASS_FEES[cls]||{}; var admFee = parseInt(cf.admission)||0; var annualFee = parseInt(cf.annual)||0; var examFee = parseInt(cf.exam)||0; var tuitionFee= parseInt(cf.tuition)||0; var busFee = (s.bus==="Yes"&&s.busFare)?parseInt(s.busFare)||0:0; var discount = parseInt(s.discount)||0; var admRows = [{desc:"Admission Fee",amt:admFee},{desc:"Annual Charge",amt:annualFee},{desc:"Examination Fee",amt:examFee},{desc:"Tuition Fee",amt:tuitionFee}]; if(busFee>0) admRows.push({desc:"Transport Fee",amt:busFee}); var admTotal = admRows.reduce(function(t,r){return t+r.amt;},0); var admNet = admTotal - discount; var admPaid = parseInt(s.admFeePaid)||0; var admBal = admNet - admPaid; var today = new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var noPrefix = ["Play","Nursery","L.K.G","U.K.G"]; var clsLabel = (noPrefix.indexOf(cls)>=0?cls:"Class "+cls) + (s.class.split("-")[1]?" - Section "+s.class.split("-")[1]:""); var W = '' +''; // Header W += '
'; W += '
'; W += '
'; W += '
Sara Azam Memorial Education
'; W += '
Admission Fee Receipt · Session '+SESSION_SHORT+'
'; W += '
'+today+'
'+s.roll+'
'; W += '
'; // Student info W += '
'; [["Student",s.name],["Class",clsLabel],["Father",s.father||"—"],["Adm. Date",s.admDate||today]].forEach(function(f){ W += '
'; W += '
'+f[0]+'
'; W += '
'+f[1]+'
'; }); W += '
'; // Fee table W += ''; W += '' +'' +'' +'' +''; admRows.forEach(function(r,i){ W += '' +'' +'' +''; }); W += ''; W += ''; if(discount>0) W += ''; W += ''; W += ''; var balBg=admBal>0?"#FEF2F2":admBal<0?"#EFF6FF":"#F0FDF4"; var balBdr=admBal>0?"#FECACA":admBal<0?"#93C5FD":"#86EFAC"; var balCol2=admBal>0?"#DC2626":admBal<0?"#1D4ED8":"#15803D"; var balText=admBal>0?"Rs."+admBal.toLocaleString("en-IN")+" Due":admBal<0?"Rs."+Math.abs(admBal).toLocaleString("en-IN")+" CR":"✓ Clear"; W += '' +'' +''; if(admBal<0) W += ''; W += '
S.NoDescriptionAmount
'+(i+1)+''+r.desc+'Rs.'+r.amt.toLocaleString("en-IN")+'
TotalRs.'+admTotal.toLocaleString("en-IN")+'
Discount- Rs.'+discount.toLocaleString("en-IN")+'
Net PayableRs.'+admNet.toLocaleString("en-IN")+'
Payment ReceivedRs.'+admPaid.toLocaleString("en-IN")+'
Balance'+balText+'
* Rs.'+Math.abs(admBal).toLocaleString("en-IN")+' credit will be adjusted in monthly fee
'; // Signatures W += '
'; W += '
Parent / Guardian
'; W += '
Fee Collector
'+today+'
'; W += '
'; var win = window.open("","_blank","width=700,height=620"); if(!win){showToast("Popup blocked","error");return;} win.document.write(W); win.document.close(); win.focus(); setTimeout(function(){win.print();},400); } // ── Monthly Payment ── function openMonthPayment(month) { var box = document.getElementById("sd-add-payment-box"); var mSel = document.getElementById("fp-month"); if(mSel) mSel.value = month; if(box) box.style.display = "block"; var amtEl = document.getElementById("fp-amount"); if(amtEl) { amtEl.value=""; amtEl.focus(); } // Store month key for saving var sessionYearEl = document.getElementById("mf-session-year"); var sessionYear = sessionYearEl?parseInt(sessionYearEl.value):new Date().getFullYear(); var realMonthMap={April:3,May:4,June:5,July:6,August:7,September:8,October:9,November:10,December:11,January:0,February:1,March:2}; var rm = realMonthMap[month]; var yr = rm>=3?sessionYear:sessionYear+1; var mk = yr+"-"+String(rm+1).padStart(2,"00"); var box2 = document.getElementById("sd-add-payment-box"); if(box2) box2.setAttribute("data-month-key", mk); } function unpayMonth(key, mn) { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; var pmts = s.feePayments&&s.feePayments[key] ? s.feePayments[key] : []; var total = pmts.reduce(function(t,p){return t+(p.amt||0);},0); var popup = document.createElement("div"); popup.style.cssText="position:fixed;inset:0;background:rgba(15,23,42,.5);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(3px);"; popup.innerHTML='
' +'
' +'
'+mn+' Payment Undo?
' +'
Rs.'+total.toLocaleString("en-IN")+' payment hata diya jaayega
' +'
' +'' +'' +'
'; document.body.appendChild(popup); popup.querySelector("#um-yes").onclick=function(){ popup.remove(); if(s.feePayments&&s.feePayments[key]) delete s.feePayments[key]; saveData(); renderMonthlyFeeTable(); showToast(mn+" payment undo ho gaya","error"); }; popup.querySelector("#um-no").onclick=function(){popup.remove();}; popup.onclick=function(e){if(e.target===popup)popup.remove();}; } function openMonthPaymentKey(key) { // Find month name from key var parts = key.split("-"); var rm = parseInt(parts[1])-1; var months12=["January","February","March","April","May","June","July","August","September","October","November","December"]; var mn = months12[rm]||""; var box = document.getElementById("sd-add-payment-box"); var mSel = document.getElementById("fp-month"); if(mSel) mSel.value = mn; if(box) { box.style.display="block"; box.setAttribute("data-month-key",key); } var amtEl = document.getElementById("fp-amount"); if(amtEl) { amtEl.value=""; amtEl.focus(); } } function printMonthFeeReceiptKey(key, mn) { printMonthFeeReceipt(mn, key); } function saveFeePayment() { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; var amtEl = document.getElementById("fp-amount"); var dateEl = document.getElementById("fp-date"); var noteEl = document.getElementById("fp-note"); var mSel = document.getElementById("fp-month"); var amt = amtEl ? parseInt(amtEl.value)||0 : 0; var dateV = dateEl ? dateEl.value : ""; var note = noteEl ? noteEl.value.trim() : ""; var month = mSel ? mSel.value : ""; if(!month) { showToast("Month select karo","error"); return; } if(amt<=0) { showToast("Amount enter karo","error"); return; } if(!dateV) { showToast("Date select karo","error"); return; } var parts = dateV.split("-"); var dispDate = parts.length===3?(parts[2]+"."+parts[1]+"."+parts[0]):dateV; // Use data-month-key if available (session-aware key) var box = document.getElementById("sd-add-payment-box"); var mk = box ? box.getAttribute("data-month-key") : null; if(!mk) { // Build key from month + current session year var sessionYearEl = document.getElementById("mf-session-year"); var sy = sessionYearEl?parseInt(sessionYearEl.value):new Date().getFullYear(); var rm2Map={April:3,May:4,June:5,July:6,August:7,September:8,October:9,November:10,December:11,January:0,February:1,March:2}; var rm2=rm2Map[month]; var yr2=rm2>=3?sy:sy+1; mk=yr2+"-"+String(rm2+1).padStart(2,"00"); } if(!s.feePayments) s.feePayments={}; if(!s.feePayments[mk]) s.feePayments[mk]=[]; s.feePayments[mk].push({amt:amt,date:dispDate,note:note}); saveData(); box.style.display="none"; if(amtEl) amtEl.value=""; if(noteEl) noteEl.value=""; renderMonthlyFeeTable(); showToast("Rs."+amt.toLocaleString("en-IN")+" — "+month+" saved!"); printMonthFeeReceipt(month, mk); } function openAddFeePayment() { var box = document.getElementById("sd-add-payment-box"); if(box) box.style.display = box.style.display==="none"?"block":"none"; } function saveFeePayment() { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) return; var month = document.getElementById("fp-month")?document.getElementById("fp-month").value:""; var amtEl = document.getElementById("fp-amount"); var dateEl = document.getElementById("fp-date"); var noteEl = document.getElementById("fp-note"); var amt = amtEl?parseInt(amtEl.value)||0:0; var dateVal = dateEl?dateEl.value:""; var note = noteEl?noteEl.value.trim():""; if(!month){showToast("Month select karo","error");return;} if(amt<=0){showToast("Amount enter karo","error");return;} if(!dateVal){showToast("Date select karo","error");return;} var parts = dateVal.split("-"); var dispDate = parts.length===3?(parts[2]+"."+parts[1]+"."+parts[0]):dateVal; if(!s.feePayments) s.feePayments={}; if(!s.feePayments[month]) s.feePayments[month]=[]; s.feePayments[month].push({amt:amt,date:dispDate,note:note}); saveData(); // Clear form if(amtEl) amtEl.value=""; if(noteEl) noteEl.value=""; document.getElementById("sd-add-payment-box").style.display="none"; renderFeeLedger(); showToast("Rs."+amt.toLocaleString("en-IN")+" — "+month+" payment saved!"); // Auto print receipt printMonthFeeReceipt(month); } function printMonthFeeReceipt(month, mk) { try { var s = students.find(function(x){ return x.id===currentDetailId; }); if(!s) { showToast("Student not found for printing","error"); return; } // Load latest fees try{ var lf=localStorage.getItem("saraAzam_classFees_v1"); if(lf) Object.assign(CLASS_FEES,JSON.parse(lf)); }catch(e){} var cls = s.class?s.class.split("-")[0]:""; var classFee = CLASS_FEES[cls]||{}; // Get session year var sessionYear = 0; if(mk){var mkP=mk.split("-");var mkY=parseInt(mkP[0]),mkM=parseInt(mkP[1]);sessionYear=mkM>=4?mkY:mkY-1;} if(!sessionYear){var syEl=document.getElementById("mf-session-year");if(syEl)sessionYear=parseInt(syEl.value)||0;} if(!sessionYear&&s.admDate){var apS=s.admDate.split(".");if(apS.length>=3){var ayS=parseInt(apS[2]),amS=parseInt(apS[1])-1;sessionYear=amS>=3?ayS:ayS-1;}} if(!sessionYear) sessionYear=ACTIVE_SESSION_START||SESSION_START_YEAR; // Build mk if not provided var realMonthMap2={April:3,May:4,June:5,July:6,August:7,September:8,October:9,November:10,December:11,January:0,February:1,March:2}; if(!mk){var rm3=realMonthMap2[month];var yr3=rm3>=3?sessionYear:sessionYear+1;mk=yr3+"-"+String(rm3+1).padStart(2,"00");} // Use stored calculation from monthly table (guaranteed correct) var calc = window._monthFeeCalc && window._monthFeeCalc[mk]; var tuitionFee, busFee, examFee, monthlyTotal, carry3, netPayable, paidAmt, balance; if(calc){ // Use exact values from table tuitionFee = calc.tuitionFee; busFee = calc.busFee; examFee = calc.examFee; monthlyTotal = calc.mTotal; carry3 = calc.carry; netPayable = calc.netPayable; paidAmt = calc.paidAmt; balance = calc.balance; } else { // Fallback: recalculate (if table wasn't rendered) tuitionFee = parseInt(classFee.tuition)||0; busFee = (s.bus==="Yes"&&s.busFare)?parseInt(s.busFare)||0:0; var examFeeAmt = parseInt(classFee.exam)||0; var examMonths2 = getExamMonths(); examFee = examMonths2.indexOf(month)>=0?examFeeAmt:0; monthlyTotal = tuitionFee + busFee + examFee; var payments2 = (s.feePayments&&s.feePayments[mk])||(s.feePayments&&s.feePayments[month])||[]; paidAmt = payments2.reduce(function(t,p){return t+(p.amt||0);},0); // Recalculate carry var admTotal3=(parseInt(classFee.admission)||0)+(parseInt(classFee.annual)||0)+(parseInt(classFee.exam)||0)+(parseInt(classFee.tuition)||0)+(s.bus==="Yes"?(parseInt(s.busFare)||0):0); var admNet3=Math.max(0,admTotal3-(parseInt(s.discount)||0)); carry3=admNet3-(parseInt(s.admFeePaid)||0); var admMonthIdx3=-1; if(s.admDate){var ap3=s.admDate.split(".");if(ap3.length>=3){var amr=parseInt(ap3[1])-1;var ay3=parseInt(ap3[2]);var asy3=amr>=3?ay3:ay3-1;if(asy3===sessionYear){var m12b=["January","February","March","April","May","June","July","August","September","October","November","December"];admMonthIdx3=FEE_MONTHS.indexOf(m12b[amr]||"");}}} var clickedMnIdx=FEE_MONTHS.indexOf(month); for(var ci=0;ci=0&&ci<=admMonthIdx3) continue; var cm=FEE_MONTHS[ci];var crm=realMonthMap2[cm];var cyr=crm>=3?sessionYear:sessionYear+1; var ck=cyr+"-"+String(crm+1).padStart(2,"00"); var cExam=getExamMonths().indexOf(cm)>=0?(parseInt(classFee.exam)||0):0; var cTotal=tuitionFee+busFee+cExam; var cPmts=(s.feePayments&&(s.feePayments[ck]||s.feePayments[cm]))||[]; var cPaid=cPmts.reduce(function(t,p){return t+(p.amt||0);},0); carry3=(cTotal+carry3)-cPaid; } netPayable = monthlyTotal + carry3; balance = netPayable - paidAmt; } // Re-read payments for history display var payments = (s.feePayments&&s.feePayments[mk])||(s.feePayments&&s.feePayments[month])||[]; var today = new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var realMonthMap9={April:4,May:5,June:6,July:7,August:8,September:9,October:10,November:11,December:12,January:1,February:2,March:3}; var rcptMonthNum=realMonthMap9[month]||(new Date().getMonth()+1); var receiptNo=sessionYear+"-"+String(s.id).padStart(4,"0")+"-"+String(rcptMonthNum).padStart(2,"0"); var W='' +''; var schoolName = localStorage.getItem("saraAzam_schoolName")||"Sara Azam Memorial Education"; W+='
'; W+='
'; W+='
'; W+='
'+schoolName+'
'; W+='
Fee Receipt · '+month+' '+sessionYear+'
'; W+='
'; W+='
RECEIPT NO.
'; W+='
#'+receiptNo+'
'; W+='
'+today+'
'; W+='
'; // Build address string var addrParts = []; if(s.addr1) addrParts.push(s.addr1); if(s.village) addrParts.push(s.village); if(s.district) addrParts.push(s.district); if(s.state) addrParts.push(s.state); var fullAddr = addrParts.join(", ")||"—"; W+='
'; W+='
'; [["Student Name",s.name],["Roll No.",s.roll||"—"],["Class",s.class||"—"],["Month",month], ["Father",s.father||"—"],["Mobile",s.phone||"—"]].forEach(function(f){ W+='
'; W+='
'+f[0]+'
'; W+='
'+f[1]+'
'; }); W+='
'; // Address on full width below if(fullAddr!=="—"){ W+='
'; W+='
Address
'; W+='
'+fullAddr+'
'; W+='
'; } W+='
'; W+=''; W+=''; var feeRows = [["Tuition Fee","Rs."+tuitionFee.toLocaleString("en-IN")]]; if(busFee>0) feeRows.push(["Transport Fee","Rs."+busFee.toLocaleString("en-IN")]); if(examFee>0) feeRows.push(["Exam Fee","Rs."+examFee.toLocaleString("en-IN")]); feeRows.forEach(function(r,i){ W+=''; W+=''; W+=''; }); W+=''; W+=''; if(carry3!==0){ var carryLabel=carry3>0?"Carry Forward (Pending)":"Credit (Advance)"; var carryVal=carry3>0?("Rs."+carry3.toLocaleString("en-IN")):("Cr "+Math.abs(carry3).toLocaleString("en-IN")); var carryCol=carry3>0?"#DC2626":"#0891B2"; W+=''; } W+=''; W+=''; W+='0?"#FECACA":balance<0?"#93C5FD":"#86EFAC")+'">'; W+=''; W+=''; W+='
Fee Breakdown
'+r[0]+''+r[1]+'
Monthly TotalRs.'+monthlyTotal.toLocaleString("en-IN")+'
'+carryLabel+''+carryVal+'
Net Payable'+(netPayable>=0?"Rs."+netPayable.toLocaleString("en-IN"):"Cr "+Math.abs(netPayable).toLocaleString("en-IN"))+'
Payment ReceivedRs.'+paidAmt.toLocaleString("en-IN")+'
Balance'+(balance>0?"Rs."+balance.toLocaleString("en-IN"):balance<0?"Cr "+Math.abs(balance).toLocaleString("en-IN"):"✓ Clear")+'
'; // Payment history if(payments.length>0){ W+='
'; W+='
Payment History
'; payments.forEach(function(p){ W+='
'; W+=''+p.date+(p.note?" · "+p.note:"")+''; W+='Rs.'+p.amt.toLocaleString("en-IN")+'
'; }); W+='
'; } W+='
'; W+='
Parent Signature
'; W+='
Fee Collector
'+today+'
'; W+='
'; W+=''; var win=window.open("","_blank"); if(!win){ // Fallback to printHTMLPage if popup blocked printHTMLPage(W,"Fee Receipt - "+month,"@page{margin:8mm;size:A4;}",false); return; } win.document.write(W); win.document.close(); win.focus(); setTimeout(function(){try{win.print();}catch(e){}},400); } catch(err) { showToast("Print error: "+err.message,"error"); console.error("Print error:",err); } } function printFeeLedger() { var s=students.find(function(x){return x.id===currentDetailId;}); if(!s) return; // Simple print of full ledger -- same as renderFeeLedger but A4 showToast("Ledger print ho raha hai..."); setTimeout(function(){ var win=window.open("","_blank","width=900,height=700"); if(!win){showToast("Popup blocked","error");return;} // Build minimal print HTML var W='' +''; W+='
'; W+='
Sara Azam Memorial Education
'; W+='
Fee Ledger · '+s.name+' · '+s.class+' · '+s.roll+'
'; W+='
'; // Clone the table content from DOM var t1=document.getElementById("sd-adm-fee-rows"); var t2=document.getElementById("sd-monthly-fee-rows"); if(t1) W+='

Admission Fee

' +'' +''+t1.innerHTML+'
S.NoDescriptionAmount
'; if(t2) W+='

Monthly Fee

' +'' +''+t2.innerHTML+'
MonthTuitionTransportExamTotalBalanceNet PayablePaymentBalancePrint
'; W+=''; win.document.write(W); win.document.close(); win.focus(); setTimeout(function(){win.print();},400); },200); } function renderFees(){ var clsFilter = (document.getElementById("fee-cls-filter")||{}).value || ""; var statusFilter = (document.getElementById("fee-status-filter")||{}).value || ""; var srch = ((document.getElementById("fee-search")||{}).value||"").toLowerCase(); // ── Current session ── var now = new Date(); var nowMonth = now.getMonth(); var nowYear = now.getFullYear(); var sessionStart = nowMonth>=3 ? nowYear : nowYear-1; // correct session from current date if(ACTIVE_SESSION_START) sessionStart = ACTIVE_SESSION_START; var SESSION_TO_REAL = [3,4,5,6,7,8,9,10,11,0,1,2]; var SESSION_MTH_LABELS = ["Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","Jan","Feb","Mar"]; var SESSION_MTH_FULL = ["April","May","June","July","August","September","October","November","December","January","February","March"]; var realMM2 = {April:3,May:4,June:5,July:6,August:7,September:8,October:9,November:10,December:11,January:0,February:1,March:2}; // Helper: get month key for a FEE_MONTH name function getMK(mn){ var rm=realMM2[mn]; var yr=rm>=3?sessionStart:sessionStart+1; return yr+"-"+String(rm+1).padStart(2,"0"); } // How many months have elapsed in current session var elapsedMonths = 0; for(var mi=0;mi<12;mi++){ var rm2=SESSION_TO_REAL[mi]; var ry2=rm2>=3?sessionStart:sessionStart+1; if(ry2=3?sessionStart:sessionStart+1; if(yr4>now.getFullYear()||(yr4===now.getFullYear()&&rm4>now.getMonth())) continue; // Count as covered if: paid, cfCovered, or net<=0 (credit covers) if(r2.paidAmt>0 || r2.cfCovered || r2.hasCF || r2.netPayable<=0) cnt++; } return cnt; } // Count total elapsed months AFTER admission for a student function countElapsedForStudent(s){ var admMIdx2=-1; var m12n2=["January","February","March","April","May","June","July","August","September","October","November","December"]; if(s.admDate){var adP2=s.admDate.split(".");if(adP2.length>=3){var adM2=parseInt(adP2[1])-1;admMIdx2=SESSION_MTH_FULL.indexOf(m12n2[adM2]||"");}} var count=0; for(var mi3=0;mi3=0 && mi3<=admMIdx2) continue; count++; } return count; } // Current month label for display var currMthIdx = SESSION_MTH_LABELS.indexOf( now.toLocaleString("en-IN",{month:"short"}) ); // ── Filter students ── var list = students.filter(function(s){ var base = s.class?s.class.split("-")[0]:""; if(clsFilter && base!==clsFilter) return false; if(srch && !s.name.toLowerCase().includes(srch) && !(s.roll&&s.roll.toLowerCase().includes(srch))) return false; // Status filter if(statusFilter){ var sf2 = CLASS_FEES[base]||{tuition:0,bus:0}; var mFee = (parseInt(sf2.tuition)||0)+(s.bus==="Yes"?(parseInt(s.busFare)||parseInt(sf2.bus)||0):0); var paid2 = countPaidMonths(s); var sElapsed = countElapsedForStudent(s); var dues2 = Math.max(0,sElapsed-paid2)*mFee; if(statusFilter==="due" && dues2<=0) return false; if(statusFilter==="clear"&& dues2>0) return false; } return true; }); // ── Aggregate totals ── var totalCollected=0, totalDue=0, collectedCount=0, dueCount=0, monthlyTotal=0; students.forEach(function(s){ var base=s.class?s.class.split("-")[0]:""; var sf2=CLASS_FEES[base]||{tuition:0,bus:0,admission:0,annual:0,exam:0}; var mFee=(parseInt(sf2.tuition)||0)+(s.bus==="Yes"?(parseInt(s.busFare)||parseInt(sf2.bus)||0):0); var paid2=countPaidMonths(s); var sElapsed=countElapsedForStudent(s); var collected=Math.min(paid2,sElapsed)*mFee; var dues=Math.max(0,sElapsed-paid2)*mFee; totalCollected+=collected; totalDue+=dues; if(dues>0) dueCount++; else collectedCount++; monthlyTotal+=mFee; }); // ── Update KPI cards ── var colEl=g("f-col"); if(colEl) colEl.textContent=inr(totalCollected); var penEl=g("f-pen"); if(penEl) penEl.textContent=inr(totalDue); var colCt=document.getElementById("f-col-count"); if(colCt) colCt.textContent=collectedCount+" students clear"; var penCt=document.getElementById("f-pen-count"); if(penCt) penCt.textContent=dueCount+" students due"; var mthEl=document.getElementById("f-month"); if(mthEl) mthEl.textContent=inr(monthlyTotal); var mthLb=document.getElementById("f-month-label"); if(mthLb) mthLb.textContent=SESSION_MTH_LABELS[Math.min(elapsedMonths-1,11)]||"—"; var tot2=totalCollected+totalDue; var pct2=tot2>0?Math.round(totalCollected/tot2*100):0; var pctEl=g("f-pct"); if(pctEl) pctEl.textContent=pct2+"%"; var pctBar=document.getElementById("f-pct-bar"); if(pctBar) pctBar.style.width=pct2+"%"; var qtrEl=g("f-qtr"); if(qtrEl) qtrEl.textContent="Showing "+list.length+" / "+students.length+" students"; var footEl=g("f-footer"); if(footEl) footEl.textContent="Total shown: "+list.length+" students · Collected: "+inr(totalCollected)+" · Pending: "+inr(totalDue); // ── Month-wise overview bars ── var overviewEl=document.getElementById("f-month-overview"); if(overviewEl){ var mthHtml=""; SESSION_MTH_LABELS.forEach(function(m,idx){ var realM=SESSION_TO_REAL[idx]; var realY=realM>=3?sessionStart:sessionStart+1; var hasStarted=realY0 || fd7[fi7].cfCovered || fd7[fi7].netPayable<=0; } } }catch(e){} return false; }).length; var pct3=students.length?Math.round(paidInMonth/students.length*100):0; var col3=hasStarted?(pct3>=80?"#15803D":pct3>=50?"#B45309":"#B91C1C"):"#CBD5E1"; mthHtml+='
'; mthHtml+='
'+m+'
'; mthHtml+='
'; if(hasStarted){ mthHtml+='
'; } mthHtml+='
'; mthHtml+='
'+(hasStarted?pct3+"%":"—")+'
'; mthHtml+='
'; }); overviewEl.innerHTML=mthHtml; } // ── Student rows ── var tb=g("f-tbody"); if(!tb) return; if(!list.length){ tb.innerHTML='
💳

No students match
Try changing filters.
'; return; } tb.innerHTML=list.map(function(s,i){ var a=avt(s.name,students.indexOf(s)); var base=s.class?s.class.split("-")[0]:""; var sf2=CLASS_FEES[base]||{tuition:0,bus:0}; var mFee=(parseInt(sf2.tuition)||0)+(s.bus==="Yes"?(parseInt(s.busFare)||parseInt(sf2.bus)||0):0); var paid2=countPaidMonths(s); var studentElapsed=countElapsedForStudent(s); var dueMonths=Math.max(0,studentElapsed-paid2); var dues=dueMonths*mFee; var isDue=dues>0; return '' +'' +''+s.name+'' +'
'+s.roll+'
' +'' +''+displayClass(s.class)+'' +''+(s.category||"—")+'' +'Rs.'+mFee.toLocaleString("en-IN")+'' +''+paid2+'/'+studentElapsed+'' +''+(isDue?"Rs."+dues.toLocaleString("en-IN"):"✓ Clear")+'' +'' +'' +''; }).join(""); // Auto-refresh ledger if it's visible var ledView=document.getElementById("fee-ledger-view"); if(ledView&&ledView.style.display!=="none") renderFeeLedgerRegister(); } // ── attendance ── function renderAtt(){ var tb=g("a-tbody"); if(!students.length){tb.innerHTML='


No Students
Add students first.
';return;} tb.innerHTML=students.map(function(s,i){var a=avt(s.name,i),days=[true,true,true,true,true],tot=5,pct=100;return'
'+a.init+'
'+s.name+'
'+s.class+''+days.map(function(p){return'
'+(p?"P":"A")+'
';}).join("")+''+tot+'/5=60?"#B45309":"#B91C1C")+';">'+pct+'%';}).join(""); } // ════════════════════════════════════════ // EXAM MANAGEMENT // ════════════════════════════════════════ var EXAMS = []; // {id, name, date, maxMarks, subjects, classes, results:{studentId:{subject:marks}}} var EXAM_STORAGE_KEY = "saraAzam_exams_v1"; var _examViewSession = null; // current session being viewed in exams tab function getExamStorageKey(sessionStart){ return "saraAzam_exams_s" + sessionStart; } function loadExams(){ var ss = _examViewSession || ACTIVE_SESSION_START || SESSION_START_YEAR; var key = getExamStorageKey(ss); try{ var s=localStorage.getItem(key); if(s){ EXAMS=JSON.parse(s); return; } }catch(e){} // Migration: if new key doesn't exist, try old key for current session if(ss === (ACTIVE_SESSION_START||SESSION_START_YEAR)){ try{ var old=localStorage.getItem(EXAM_STORAGE_KEY); if(old){ EXAMS=JSON.parse(old); saveExams(); localStorage.removeItem(EXAM_STORAGE_KEY); return; } }catch(e2){} } EXAMS=[]; } function saveExams(){ var ss = _examViewSession || ACTIVE_SESSION_START || SESSION_START_YEAR; localStorage.setItem(getExamStorageKey(ss), JSON.stringify(EXAMS)); } // ════════════════════════════════════════ // EXAM ROUTINE (TIMETABLE) // ════════════════════════════════════════ var EXAM_ROUTINE = []; // [{id, date, day, cls, subject, time, venue}] // Structure: one entry per class per date function saveExamRoutine(){ var ss = _examViewSession || ACTIVE_SESSION_START || SESSION_START_YEAR; try{localStorage.setItem("saraAzam_examRoutine_s"+ss,JSON.stringify(EXAM_ROUTINE));}catch(e){} } function loadExamRoutine(){ var ss = _examViewSession || ACTIVE_SESSION_START || SESSION_START_YEAR; try{ var s=localStorage.getItem("saraAzam_examRoutine_s"+ss); if(s){ EXAM_ROUTINE=JSON.parse(s); return; } // Migration from old key if(ss === (ACTIVE_SESSION_START||SESSION_START_YEAR)){ var old=localStorage.getItem("saraAzam_examRoutine"); if(old){ EXAM_ROUTINE=JSON.parse(old); saveExamRoutine(); localStorage.removeItem("saraAzam_examRoutine"); return; } } EXAM_ROUTINE=[]; }catch(e){ EXAM_ROUTINE=[]; } } function openExamRoutine(){ loadExams(); loadExamRoutine(); var existing=document.getElementById("exam-routine-popup"); if(existing) existing.remove(); var d=document.createElement("div"); d.id="exam-routine-popup"; d.style.cssText="position:fixed;inset:0;background:rgba(10,20,40,.7);z-index:99990;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);padding:12px;"; d.innerHTML='
' // Header +'
' +'
📅 Exam Routine
' +'
Class-wise timetable generator
' +'' +'
' // Body +'
' // ── Generate section ── +'
' +'
⚡ Auto-Generate Timetable
' // Exam selector +(EXAMS.length?'
Select Exam (optional)
' +'
' :'') +'
' +'
Starting Date *
' +'
' +'
Exam Time
' +'
' +'
' // Skip days +'
' +'
Skip Days:
' +'
' +[["Sun",0],["Mon",1],["Tue",2],["Wed",3],["Thu",4],["Fri",5],["Sat",6]].map(function(p){ return ''; }).join('') +'
' +'
' +'
' +'' +'' +'
' +'
' // ── Class filter tabs ── +'
' +'
Class Filter:
' +'
' +'' +['Play','Nursery','L.K.G','U.K.G','I','II','III','IV','V','VI','VII','VIII','IX','X'].map(function(c){ return ''; }).join('') +'
' +'
' // ── Routine table ── +'
' +'
All Classes
' +'
' +'' +'' +'
' +'
' +'
' +'
' +'
'; document.body.appendChild(d); d.querySelector("#er-close").onclick=function(){d.remove();}; d.onclick=function(e){if(e.target===d)d.remove();}; renderRoutineTable("ALL"); } var _currentRoutineClass = "ALL"; function filterRoutineClass(cls, btn){ _currentRoutineClass = cls; // Update button styles var tabs = document.getElementById("er-class-tabs"); if(tabs) tabs.querySelectorAll("button").forEach(function(b){ b.style.background="#fff"; b.style.color="#374151"; b.style.borderColor="#E2E8F0"; }); if(btn){btn.style.background="#0B1F3A";btn.style.color="#fff";btn.style.borderColor="#0B1F3A";} var label = document.getElementById("er-filter-label"); if(label) label.textContent = cls==="ALL" ? "All Classes" : "Class "+cls; renderRoutineTable(cls); } function fillRoutineDateFromExam(){ var sel=document.getElementById("er-exam-select"); if(!sel||!sel.value) return; var dateEl=document.getElementById("er-start-date"); if(dateEl) dateEl.value=sel.value; } function autoGenerateExamRoutine(){ loadExams(); // Ensure latest exam data var startDateStr=(document.getElementById("er-start-date")||{}).value||""; if(!startDateStr){showToast("Starting date zaroori hai","error");document.getElementById("er-start-date").focus();return;} var parts=startDateStr.split("."); if(parts.length!==3){showToast("Date format: DD.MM.YYYY","error");return;} var startDate=new Date(parseInt(parts[2]),parseInt(parts[1])-1,parseInt(parts[0])); if(isNaN(startDate.getTime())){showToast("Invalid date","error");return;} var timeDefault=(document.getElementById("er-time-default")||{}).value||"10:00 AM"; var skipDays=[]; for(var sd=0;sd<=6;sd++){var el=document.getElementById("er-skip-"+sd);if(el&&el.checked)skipDays.push(sd);} var days=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]; var newRoutine=[]; // Build subjects per class var classSubjectsMap={}; BASE_CLASSES.forEach(function(cls){ classSubjectsMap[cls]=getClassSubjects(cls); }); // Total exam days = max subjects any class has var maxDays=0; BASE_CLASSES.forEach(function(cls){ if(classSubjectsMap[cls].length>maxDays) maxDays=classSubjectsMap[cls].length; }); var juniorG=["Play","Nursery","L.K.G","U.K.G"]; // Each class tracks its OWN exam day counter (independent of other classes) // classDay[cls] = how many exams this class has had so far var classDay={}; BASE_CLASSES.forEach(function(cls){ classDay[cls]=0; }); // Starting subject offset per class (for variety on same date) // Use class index mod subjects — this only affects WHICH subject on day 1 var classOffset={}; BASE_CLASSES.forEach(function(cls,ci){ var subs=classSubjectsMap[cls]; classOffset[cls] = subs.length>0 ? ci % subs.length : 0; }); // Check if any class still has exams left function anyLeft(){ return BASE_CLASSES.some(function(cls){ return classSubjectsMap[cls].length>0 && classDay[cls]=0){ currentDate.setDate(currentDate.getDate()+1); } var dd=String(currentDate.getDate()).padStart(2,"0"); var mm=String(currentDate.getMonth()+1).padStart(2,"0"); var yyyy=currentDate.getFullYear(); var dateStr=dd+"."+mm+"."+yyyy; var dayName=days[currentDate.getDay()]; BASE_CLASSES.forEach(function(cls){ var subs=classSubjectsMap[cls]; if(!subs.length) return; if(classDay[cls]>=subs.length) return; // this class done // Subject: (classDay + startingOffset) mod subs.length var subIdx=(classDay[cls]+classOffset[cls]) % subs.length; newRoutine.push({ id:"r"+Date.now()+cls+classDay[cls]+(Math.random()*1000|0), date:dateStr, day:dayName, cls:cls, subject:subs[subIdx], time:timeDefault, venue:juniorG.indexOf(cls)>=0?"Class Room":"Exam Hall" }); classDay[cls]++; }); currentDate.setDate(currentDate.getDate()+1); } // Sort by date then class order newRoutine.sort(function(a,b){ var da=a.date.split(".").reverse().join(""); var db=b.date.split(".").reverse().join(""); if(da!==db) return da📅' +'
'+(EXAM_ROUTINE.length?"Is class mein koi entry nahi":"Starting date daalke Generate karo")+'
' +''; return; } // Group by date var byDate={}; var dateOrder=[]; filtered.forEach(function(r){ if(!byDate[r.date]){byDate[r.date]=[];dateOrder.push(r.date);} byDate[r.date].push(r); }); var clsColors={"Play":"#C53030","Nursery":"#C05621","L.K.G":"#B7791F","U.K.G":"#276749", "I":"#2B6CB0","II":"#6B46C1","III":"#C53030","IV":"#C05621","V":"#B7791F", "VI":"#276749","VII":"#2B6CB0","VIII":"#6B46C1","IX":"#744210","X":"#1A365D"}; var html='
' +'' +'' +'' +'' +(cls==="ALL"?'':'') +'' +'' +'' +''; var ri=0; dateOrder.forEach(function(date){ var entries=byDate[date]; var rowBg=ri%2===0?"#fff":"#F8FAFC"; ri++; var isFirst=true; entries.forEach(function(r){ var clsCol=clsColors[r.cls]||"#374151"; html+=''; if(isFirst){ html+=''; isFirst=false; } html+=''; if(cls==="ALL"){ html+=''; } html+='' +'' +'' +''; }); }); html+='
DateDayClassSubjectTimeDel
' +r.date+'
'+r.day+'
'+r.day+''+r.cls+''+r.subject+''+r.time+'
'; wrap.innerHTML=html; } function printCurrentClassRoutine(){ var cls=_currentRoutineClass; if(cls==="ALL"){ printExamRoutineClasswise(); return; } // Print single class loadExamRoutine(); var rows=EXAM_ROUTINE.filter(function(r){return r.cls===cls;}); if(!rows.length){showToast("Is class mein koi routine nahi","error");return;} var schoolName=localStorage.getItem("saraAzam_schoolName")||"Sara Azam Memorial Education"; var today5=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var clsColors={"Play":"#C53030","Nursery":"#C05621","L.K.G":"#B7791F","U.K.G":"#276749", "I":"#2B6CB0","II":"#6B46C1","III":"#C53030","IV":"#C05621","V":"#B7791F", "VI":"#276749","VII":"#2B6CB0","VIII":"#6B46C1","IX":"#744210","X":"#1A365D"}; var col=clsColors[cls]||"#0B1F3A"; var W='' +''; W+='
'; W+='
'; W+='
'; W+='
'+schoolName+'
'; W+='
' +'
EXAM TIME TABLE
' +'
Class: '+cls+'
' +'
'+today5+'
' +'
'; W+='' +'' +'' +'' +'' +'' +'' +''; rows.forEach(function(r,i){ var bg=i%2===0?"#fff":col+"11"; W+='' +'' +'' +'' +'' +'' +'' +''; }); W+='
S.NoDateDaySubjectTimeVenue
'+(i+1)+''+r.date+''+r.day+''+r.subject+''+r.time+''+r.venue+'
'; W+='
'; ["Class Teacher","Exam Controller","Principal"].forEach(function(s){ W+='
' +'
' +'
'+s+'
'; }); W+='
'; var win=window.open("","_blank","width=900,height=700"); if(!win){showToast("Popup blocked","error");return;} win.document.open(); win.document.write(W); win.document.close(); setTimeout(function(){try{win.focus();win.print();}catch(e){}},600); } function printExamRoutine(){ loadExamRoutine(); if(!EXAM_ROUTINE.length){showToast("Koi routine nahi hai","error");return;} var schoolName=localStorage.getItem("saraAzam_schoolName")||"Sara Azam Memorial Education"; var today4=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var junior=EXAM_ROUTINE.filter(function(r){return ["Play","Nursery","L.K.G","U.K.G"].indexOf(r.cls)>=0;}); var senior=EXAM_ROUTINE.filter(function(r){return ["Play","Nursery","L.K.G","U.K.G"].indexOf(r.cls)<0;}); // Deduplicate by date+subject for grouped view function dedup(rows){var seen={};return rows.filter(function(r){var k=r.date+r.subject;if(seen[k])return false;seen[k]=1;return true;});} var W='' +''; function makeHdr(title){ return '
' +'
' +'
' +'
'+schoolName+'
' +'
'+title+'
' +'
'+today4+'
'; } function makeTable(col,rows){ var d=dedup(rows); return '' +'' +'' +'' +'' +d.map(function(r,i){return '' +'' +'' +'';}).join("") +'
S.NoDateDaySubjectTime
'+(i+1)+''+r.date+''+r.day+''+r.subject+''+r.time+'
'; } if(junior.length){W+=makeHdr("PRIMARY SECTION (Play–U.K.G)");W+=makeTable("#065F46",junior);} if(senior.length){W+='
';W+=makeHdr("SECONDARY SECTION (Class I–X)");W+=makeTable("#1D4ED8",senior);} W+=''; var win=window.open("","_blank","width=900,height=700"); if(!win){showToast("Popup blocked","error");return;} win.document.open();win.document.write(W);win.document.close(); setTimeout(function(){try{win.focus();win.print();}catch(e){}},600); } function switchExamSession(val){ _examViewSession = parseInt(val); loadExams(); renderExams(); } function populateExamSessionDropdown(){ var sel = document.getElementById("exam-session-select"); if(!sel) return; var curSS = ACTIVE_SESSION_START || SESSION_START_YEAR; var viewSS = _examViewSession || curSS; // Build list of sessions that have exam data + current session var sessions = {}; sessions[curSS] = true; // Scan localStorage for exam session keys for(var i=0;i'+label+(isCurrent?' (Current)':'')+''; }).join(""); } function renderExams(){ if(!_examViewSession) _examViewSession = ACTIVE_SESSION_START || SESSION_START_YEAR; populateExamSessionDropdown(); loadExams(); var grid=document.getElementById("exam-cards-grid"); var empty=document.getElementById("exam-empty-state"); var results=document.getElementById("exam-results-panel"); if(!grid) return; if(results) results.style.display="none"; if(!EXAMS.length){ grid.innerHTML=""; if(empty) empty.style.display="block"; var sub1=document.getElementById("exam-tab-sub"); if(sub1) sub1.textContent="Session "+_examViewSession+"–"+String(_examViewSession+1).slice(-2)+" · No exams created"; return; } if(empty) empty.style.display="none"; var sub2=document.getElementById("exam-tab-sub"); if(sub2) sub2.textContent="Session "+_examViewSession+"–"+String(_examViewSession+1).slice(-2)+" · "+EXAMS.length+" exam(s)"; var colors=["#0B1F3A","#065F46","#6B21A8","#9A3412","#1E40AF","#0F766E","#7C2D12","#1D4ED8"]; grid.innerHTML=""; EXAMS.forEach(function(ex,i){ var col=colors[i%colors.length]; var totalStudents=students.filter(function(s){return !ex.classes||ex.classes.length===0||ex.classes.indexOf(s.class.split("-")[0])>=0;}).length; var marked=Object.keys(ex.results||{}).length; var today3=new Date(); var exDate=ex.date?new Date(ex.date.split(".").reverse().join("-")):null; var status=!exDate?"Scheduled":exDate>today3?"Upcoming":exDate.toDateString()===today3.toDateString()?"Today":"Completed"; var stCol=status==="Completed"?"#15803D":status==="Today"?"#D97706":"#1D4ED8"; var stBg=status==="Completed"?"#DCFCE7":status==="Today"?"#FEF3C7":"#DBEAFE"; var card=document.createElement("div"); card.className="card"; card.style.cssText="padding:0;overflow:hidden;cursor:pointer;"; card.innerHTML='
' +'
' +'
'+ex.name+'
' +''+status+'' +'
' +'
'+(ex.date||"Date TBD")+'  ·  Max: '+ex.maxMarks+' marks
' +'
' +'
' +'
'+marked+'/'+totalStudents+' students marked
' +'
' +'
'; card.onclick=function(){openExamResults(ex.id);}; grid.appendChild(card); // Add buttons after append (avoids quotes issue) (function(eid){ var btnDiv=document.getElementById("exam-btns-"+i); if(!btnDiv) return; var b1=document.createElement("button"); b1.innerHTML="📋 Marks"; b1.setAttribute("data-eid",eid); b1.style.cssText="background:#EFF6FF;color:#1D4ED8;border:1px solid #BFDBFE;border-radius:6px;padding:5px 10px;font-size:11px;font-weight:700;cursor:pointer;font-family:inherit;"; b1.onclick=function(ev){ev.stopPropagation();openExamResults(this.getAttribute("data-eid"));}; var b2=document.createElement("button"); b2.innerHTML="✏️"; b2.setAttribute("data-eid",eid); b2.style.cssText="background:#FFFBEB;color:#B45309;border:1px solid #FDE68A;border-radius:6px;padding:5px 8px;font-size:11px;cursor:pointer;font-family:inherit;"; b2.onclick=function(ev){ev.stopPropagation();editExam(this.getAttribute("data-eid"));}; var b3=document.createElement("button"); b3.innerHTML="🗑"; b3.setAttribute("data-eid",eid); b3.style.cssText="background:#FEF2F2;color:#DC2626;border:1px solid #FECACA;border-radius:6px;padding:5px 8px;font-size:11px;cursor:pointer;font-family:inherit;"; b3.onclick=function(ev){ev.stopPropagation();deleteExam(this.getAttribute("data-eid"));}; btnDiv.appendChild(b1); btnDiv.appendChild(b2); btnDiv.appendChild(b3); })(ex.id); }); } // ── CLASS-WISE SUBJECTS CONFIG ── // Stored in CLASS_FEES[cls].subjects = ["Hindi","English",...] function getClassSubjects(cls) { try{var lf=localStorage.getItem("saraAzam_classFees_v1");if(lf)Object.assign(CLASS_FEES,JSON.parse(lf));}catch(e){} var cf = CLASS_FEES[cls]||{}; if(cf.subjects && cf.subjects.length) return cf.subjects; // Defaults var junior = ["Play","Nursery","L.K.G","U.K.G"]; if(junior.indexOf(cls)>=0) return ["Hindi","English","Mathematics","Drawing","G.K."]; if(cls==="I"||cls==="II") return ["Hindi","English","Mathematics","E.V.S.","Drawing"]; if(cls==="III"||cls==="IV"||cls==="V") return ["Hindi","English","Mathematics","Science","Social Science"]; return ["Hindi","English","Mathematics","Science","Social Science","Sanskrit","Computer"]; } function setClassSubjects(cls, subjects){ try{var lf=localStorage.getItem("saraAzam_classFees_v1");if(lf)Object.assign(CLASS_FEES,JSON.parse(lf));}catch(e){} if(!CLASS_FEES[cls]) CLASS_FEES[cls]={}; CLASS_FEES[cls].subjects = subjects; try{localStorage.setItem("saraAzam_classFees_v1",JSON.stringify(CLASS_FEES));}catch(e){} } // ── SUBJECTS MANAGER in Fee Settings (called from renderFeeSettings) ── function smCopyFromAbove(btn){ var idx=parseInt(btn.getAttribute("data-idx")); var popup=document.getElementById("subj-mgr-popup"); if(!popup) return; var allInputs=popup.querySelectorAll("input[data-cls]"); if(idx>0&&allInputs[idx-1]&&allInputs[idx]){ allInputs[idx].value=allInputs[idx-1].value; allInputs[idx].style.borderColor="#6EE7B7"; setTimeout(function(){allInputs[idx].style.borderColor="#E2E8F0";},800); } } function openSubjectManager(){ var existing = document.getElementById("subj-mgr-popup"); if(existing){existing.remove();return;} var d = document.createElement("div"); d.id="subj-mgr-popup"; d.style.cssText="position:fixed;inset:0;background:rgba(10,20,40,.65);z-index:99991;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);padding:12px;"; var rows = BASE_CLASSES.map(function(cls,ci){ var subs = getClassSubjects(cls).join(", "); return '' +''+cls+'' +'' +'
' +'' +(ci>0?'':'') +'
' +'' +''; }).join(""); d.innerHTML='
' +'
' +'
📚 Class-wise Subjects
' +'
Har class ke subjects set karo
' +'' +'
' +'
' +''+rows+'
' +'
' +'
' +'' +'' +'
' +'
'; document.body.appendChild(d); d.querySelector("#sm-close").onclick=d.querySelector("#sm-cancel").onclick=function(){d.remove();}; d.onclick=function(e){if(e.target===d)d.remove();}; d.copySubjectsFromAbove=function(btn){ var idx=parseInt(btn.getAttribute("data-idx")); var allInputs=d.querySelectorAll("input[data-cls]"); if(idx>0&&allInputs[idx-1]&&allInputs[idx]){ allInputs[idx].value=allInputs[idx-1].value; allInputs[idx].style.borderColor="#6EE7B7"; setTimeout(function(){allInputs[idx].style.borderColor="#E2E8F0";},1000); } }; d.querySelector("#sm-save").onclick=function(){ d.querySelectorAll("input[data-cls]").forEach(function(inp){ var cls=inp.getAttribute("data-cls"); var subs=inp.value.split(",").map(function(s){return s.trim();}).filter(Boolean); if(subs.length) setClassSubjects(cls,subs); }); showToast("Subjects saved!"); d.remove(); }; } // ── IMPROVED openCreateExam ── function editExam(examId){ var ex=EXAMS.find(function(e){return e.id===examId;}); if(!ex) return; var existing=document.getElementById("create-exam-popup"); if(existing) existing.remove(); var popup=document.createElement("div"); popup.id="create-exam-popup"; popup.style.cssText="position:fixed;inset:0;background:rgba(10,20,40,.7);z-index:99990;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);padding:16px;"; var clsChk=BASE_CLASSES.map(function(c){ var chk=!ex.classes||ex.classes.indexOf(c)>=0; return ''; }).join(""); popup.innerHTML='
' +'
' +'
✏️ Edit Exam
' +'' +'
' +'
' +'
Exam Name *
' +'
' +'
' +'
Exam Date
' +'
' +'
Max Marks
' +'
' +'
' +'
' +'
📚 Class-wise subjects use honge
' +'' +'
' +'
' +'
Classes
' +'
'+clsChk+'
' +'
' // Delete button +'
' +'' +'' +'' +'
' +'
' +'
'; document.body.appendChild(popup); document.getElementById("ce-close").onclick=document.getElementById("ce-cancel").onclick=function(){popup.remove();}; popup.onclick=function(e){if(e.target===popup)popup.remove();}; document.getElementById("ce-delete").onclick=function(){ if(confirm(ex.name+" exam delete karna chahte ho?")){ EXAMS=EXAMS.filter(function(e){return e.id!==examId;}); saveExams(); popup.remove(); renderExams(); updateDash(); showToast(ex.name+" deleted!","error"); } }; document.getElementById("ce-save").onclick=function(){ var name=document.getElementById("ce-name").value.trim(); if(!name){showToast("Exam name required","error");return;} var classes=Array.from(document.querySelectorAll("#ce-classes input:checked")).map(function(c){return c.value;}); var classSubjects={}; classes.forEach(function(cls){classSubjects[cls]=getClassSubjects(cls);}); ex.name=name; ex.date=document.getElementById("ce-date").value.trim(); ex.maxMarks=parseInt(document.getElementById("ce-marks").value)||100; ex.classes=classes; ex.classSubjects=classSubjects; saveExams(); popup.remove(); renderExams(); updateDash(); showToast(name+" updated!"); }; } function openCreateExam(){ // Always create exam in current session _examViewSession = ACTIVE_SESSION_START || SESSION_START_YEAR; var existing=document.getElementById("create-exam-popup"); if(existing) existing.remove(); var popup=document.createElement("div"); popup.id="create-exam-popup"; popup.style.cssText="position:fixed;inset:0;background:rgba(10,20,40,.7);z-index:99990;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);padding:16px;"; var clsChk=BASE_CLASSES.map(function(c){ return ''; }).join(""); popup.innerHTML='
' +'
' +'
+ Create New Exam
' +'' +'
' +'
' +'
' +'
Exam Name *
' +'' +'
' +'
' +'
Exam Date
' +'
' +'
Max Marks
' +'
' +'
' // Subjects note +'
' +'
📚 Class-wise subjects use honge
' +'
Har class ke subjects automatically Fee Settings → Subjects se aayenge
' +'' +'
' // Classes +'
' +'
Classes
' +'
'+clsChk+'
' +'
' +'
' +'' +'' +'
' +'
' +'
'; document.body.appendChild(popup); document.getElementById("ce-close").onclick=document.getElementById("ce-cancel").onclick=function(){popup.remove();}; popup.onclick=function(e){if(e.target===popup)popup.remove();}; document.getElementById("ce-save").onclick=function(){ var name=document.getElementById("ce-name").value.trim(); if(!name){showToast("Exam name required","error");document.getElementById("ce-name").focus();return;} var classes=Array.from(document.querySelectorAll("#ce-classes input:checked")).map(function(c){return c.value;}); // Build class-wise subjects var classSubjects={}; classes.forEach(function(cls){ classSubjects[cls]=getClassSubjects(cls); }); var ex={ id:"exam_"+Date.now(), name:name, date:document.getElementById("ce-date").value.trim(), maxMarks:parseInt(document.getElementById("ce-marks").value)||100, classSubjects:classSubjects, classes:classes, results:{} }; EXAMS.push(ex); saveExams(); popup.remove(); renderExams(); showToast(name+" exam created!"); setTimeout(function(){openExamResults(ex.id);},300); }; document.getElementById("ce-name").focus(); } // ── CLASS-WISE MARKS ENTRY ── function openExamResults(examId){ var ex=EXAMS.find(function(e){return e.id===examId;}); if(!ex) return; var existing=document.getElementById("exam-marks-popup"); if(existing) existing.remove(); var d=document.createElement("div"); d.id="exam-marks-popup"; d.style.cssText="position:fixed;inset:0;background:rgba(10,20,40,.7);z-index:99990;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);padding:12px;"; // Class tabs var classes=ex.classes&&ex.classes.length?ex.classes:BASE_CLASSES; var tabs=classes.map(function(cls,i){ return ''; }).join(""); d.innerHTML='
' +'
' +'
📝 '+ex.name+' — Marks Entry
' +'
Max: '+ex.maxMarks+' per subject
' +'' +'
' // Class tabs +'
'+tabs+'
' // Content +'
' // Save + Print buttons +'
' +'
' +'' +'' +'
' +'' +'
' +'
'; document.body.appendChild(d); d.querySelector("#em-close").onclick=function(){d.remove();}; d.onclick=function(e){if(e.target===d)d.remove();}; // Add import buttons above save button var footerDiv=d.querySelector('div[style*="border-top:1px solid #E2E8F0"]'); if(footerDiv){ var importRow=document.createElement("div"); importRow.style.cssText="display:flex;gap:8px;margin-bottom:8px;"; var bExcel=document.createElement("button"); bExcel.innerHTML="📊 Import Excel/CSV"; bExcel.setAttribute("data-eid",examId); bExcel.style.cssText="flex:1;background:#EFF6FF;color:#1D4ED8;border:1.5px solid #BFDBFE;border-radius:9px;padding:9px;font-size:13px;font-weight:700;cursor:pointer;font-family:inherit;"; bExcel.onclick=function(){importMarksFromExcel(this.getAttribute("data-eid"));}; var bImg=document.createElement("button"); bImg.innerHTML="📸 Import Image"; bImg.setAttribute("data-eid",examId); bImg.style.cssText="flex:1;background:#F5F3FF;color:#6D28D9;border:1.5px solid #DDD6FE;border-radius:9px;padding:9px;font-size:13px;font-weight:700;cursor:pointer;font-family:inherit;"; bImg.onclick=function(){importMarksFromImage(this.getAttribute("data-eid"));}; importRow.appendChild(bExcel); importRow.appendChild(bImg); footerDiv.insertBefore(importRow, footerDiv.firstChild); } // Load first class if(classes.length) renderExamClassMarks(examId, classes[0]); } function switchExamClass(btn, examId){ // Update tab styles btn.closest("div").querySelectorAll("button").forEach(function(b){ b.style.background="transparent"; b.style.color="#64748B"; b.style.borderBottomColor="transparent"; }); btn.style.background="#0B1F3A"; btn.style.color="#fff"; btn.style.borderBottomColor="#3B82F6"; renderExamClassMarks(examId, btn.getAttribute("data-cls")); } function renderExamClassMarks(examId, cls){ var ex=EXAMS.find(function(e){return e.id===examId;}); if(!ex) return; var content=document.getElementById("exam-class-content"); if(!content) return; // Get subjects for this class var subjects=(ex.classSubjects&&ex.classSubjects[cls])?ex.classSubjects[cls]:getClassSubjects(cls); // Get students of this class (current session) var curSY=ACTIVE_SESSION_START||SESSION_START_YEAR; var clsStudents=students.filter(function(s){ if(s.class.split("-")[0]!==cls) return false; if(s.admDate){var ap=s.admDate.split(".");if(ap.length>=3){var ay=parseInt(ap[2]),am=parseInt(ap[1])-1;var sSY=am>=3?ay:ay-1;if(sSY!==curSY)return false;}} return true; }).sort(function(a,b){ // Sort by roll number numerically var ra=a.roll||""; var rb=b.roll||""; // Extract sequence number from end of roll (e.g. 2026-PL-A-001 → 1) var na=parseInt((ra.split("-").pop())||"0")||0; var nb=parseInt((rb.split("-").pop())||"0")||0; if(na!==nb) return na-nb; return ra.localeCompare(rb); }); if(!clsStudents.length){ content.innerHTML='
👥
'+cls+' mein koi student nahi hai
'; return; } var html='
' +'' +''; subjects.forEach(function(sub){ html+=''; }); html+='' +'' +''; clsStudents.forEach(function(s,i){ var res=ex.results[s.id]||{}; var total=subjects.reduce(function(t,sub){return t+(parseInt(res[sub])||0);},0); var maxTotal=ex.maxMarks*subjects.length; var pct=maxTotal>0?Math.round(total/maxTotal*100):0; var pctCol=pct>=60?"#15803D":pct>=33?"#B45309":"#B91C1C"; var a=avt(s.name,i); html+='' +''; subjects.forEach(function(sub){ var val=res[sub]||""; html+=''; }); html+='' +'' +''; }); html+='
Student'+sub+'Total%
' +'
' +'
'+a.init+'
' +'
'+s.name+'
' +'
' +'' +''+(total>0?total:"—")+''+(pct>0?pct+"%":"—")+'
'; content.innerHTML=html; content.setAttribute("data-cls",cls); } function enforceMaxMarks(inp, maxMarks){ var val = inp.value; // Negative values not allowed if(val !== "" && parseInt(val) < 0){ inp.value = 0; } var numVal = parseInt(inp.value); if(!isNaN(numVal) && numVal > maxMarks){ inp.value = maxMarks; inp.style.borderColor = "#DC2626"; inp.style.background = "#FEF2F2"; showToast("Maximum " + maxMarks + " marks allowed! " + numVal + " enter nahi ho sakta.", "error"); setTimeout(function(){ inp.style.borderColor = "#E2E8F0"; inp.style.background = "#fff"; }, 1500); } else if(!isNaN(numVal)){ inp.style.borderColor = "#059669"; inp.style.background = "#F0FDF4"; setTimeout(function(){ inp.style.borderColor = "#E2E8F0"; inp.style.background = "#fff"; }, 500); } } function validateExamMark(inp){ var max=parseInt(inp.getAttribute("data-max"))||100; var val=parseInt(inp.value); if(!isNaN(val)&&val>max){ inp.value=max; inp.style.borderColor="#DC2626"; inp.style.background="#FEF2F2"; showToast("Maximum "+max+" marks allowed!","error"); setTimeout(function(){inp.style.borderColor="#E2E8F0";inp.style.background="#fff";},1500); } else { inp.style.borderColor="#E2E8F0"; inp.style.background="#fff"; } } function updateExamRowTotal(inp, examId, subCount){ var sid=inp.getAttribute("data-sid"); var row=inp.closest("tr"); if(!row) return; var inputs=row.querySelectorAll("input[data-sid]"); var total=0; inputs.forEach(function(i){total+=parseInt(i.value)||0;}); var totEl=document.getElementById("exam-tot-"+sid); if(totEl) totEl.textContent=total>0?total:"—"; } // ════════════════════════════════════════ // MARKS IMPORT — EXCEL // ════════════════════════════════════════ function importMarksFromExcel(examId) { var ex = EXAMS.find(function(e){ return e.id===examId; }); if(!ex) return; var content = document.getElementById("exam-class-content"); var cls = content ? content.getAttribute("data-cls") : ""; var subjects = (ex.classSubjects&&ex.classSubjects[cls]) ? ex.classSubjects[cls] : getClassSubjects(cls); // Create file input var input = document.createElement("input"); input.type = "file"; input.accept = ".xlsx,.xls,.csv"; input.style.display = "none"; document.body.appendChild(input); input.onchange = function(){ var file = input.files[0]; if(!file){ input.remove(); return; } var reader = new FileReader(); if(file.name.toLowerCase().endsWith(".csv")){ reader.onload = function(e){ var text = e.target.result; parseCSVMarks(text, examId, cls, subjects); input.remove(); }; reader.readAsText(file); } else { // Excel — use SheetJS if available, else show instructions reader.onload = function(e){ try { // Try using XLSX library if loaded if(typeof XLSX !== "undefined"){ var wb = XLSX.read(e.target.result, {type:"array"}); var ws = wb.Sheets[wb.SheetNames[0]]; var csv = XLSX.utils.sheet_to_csv(ws); parseCSVMarks(csv, examId, cls, subjects); } else { // Fallback: show paste interface showPasteMarksDialog(examId, cls, subjects); } } catch(err){ showPasteMarksDialog(examId, cls, subjects); } input.remove(); }; reader.readAsArrayBuffer(file); } }; input.click(); } function parseCSVMarks(csvText, examId, cls, subjects){ var ex = EXAMS.find(function(e){ return e.id===examId; }); if(!ex) return; var lines = csvText.split("\n").map(function(l){ return l.trim(); }).filter(Boolean); if(!lines.length){ showToast("File mein koi data nahi", "error"); return; } // Parse header row to find subject columns var headers = lines[0].split(",").map(function(h){ return h.trim().replace(/"/g,"").toLowerCase(); }); var rollCol = -1, nameCol = -1; var subjectCols = {}; headers.forEach(function(h, i){ if(h==="roll" || h==="roll no" || h==="roll number" || h==="roll no.") rollCol=i; if(h==="name" || h==="student name" || h==="student") nameCol=i; subjects.forEach(function(sub){ if(h===sub.toLowerCase() || h===sub.toLowerCase().replace(/\./g,"")) subjectCols[sub]=i; }); }); var matched=0, notFound=0; for(var r=1; r=0 ? cols[rollCol]||"" : ""; var nameVal = nameCol>=0 ? cols[nameCol]||"" : ""; // Find student: match by roll number first, then by name var stu = null; if(rollVal){ stu = students.find(function(s){ return s.class.split("-")[0]===cls && s.roll.toLowerCase()===rollVal.toLowerCase(); }); } if(!stu && nameVal){ stu = students.find(function(s){ return s.class.split("-")[0]===cls && s.name.toLowerCase()===nameVal.toLowerCase(); }); } if(!stu){ notFound++; continue; } // Import marks for each subject if(!ex.results[stu.id]) ex.results[stu.id]={}; var hasAny=false; subjects.forEach(function(sub){ var ci=subjectCols[sub]; if(ci===undefined) return; var mark = parseInt(cols[ci]); if(!isNaN(mark) && mark>=0 && mark<=ex.maxMarks){ ex.results[stu.id][sub]=mark; hasAny=true; } }); if(hasAny) matched++; } saveExams(); renderExamClassMarks(examId, cls); showToast(matched+" students ke marks import ho gaye!"+(notFound?" ("+notFound+" not found)":"")); } // ── Paste marks dialog ── function showPasteMarksDialog(examId, cls, subjects){ var ex = EXAMS.find(function(e){ return e.id===examId; }); if(!ex) return; var d = document.createElement("div"); d.style.cssText="position:fixed;inset:0;background:rgba(10,20,40,.7);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);padding:12px;"; var headerRow = "Roll No,Name,"+subjects.join(","); var sampleRow = "2026-PL-A-001,Student Name,"+subjects.map(function(){return "85";}).join(","); d.innerHTML='
' +'
' +'
📋 Paste Marks Data (CSV)
' +'' +'
' +'
' +'
Format: pehle row headers, phir student data
' +'
'+headerRow+'
'+sampleRow+'
' +'' +'
' +'
' +'' +'' +'
' +'
'; document.body.appendChild(d); d.querySelector("#pd-close").onclick=d.querySelector("#pd-cancel").onclick=function(){d.remove();}; d.querySelector("#pd-import").onclick=function(){ var text=d.querySelector("#pd-text").value.trim(); if(!text){showToast("Koi data nahi","error");return;} d.remove(); parseCSVMarks(text, examId, cls, subjects); }; } // ════════════════════════════════════════ // MARKS IMPORT — IMAGE (via Claude AI) // ════════════════════════════════════════ function importMarksFromImage(examId) { var ex = EXAMS.find(function(e){ return e.id===examId; }); if(!ex) return; var content = document.getElementById("exam-class-content"); var cls = content ? content.getAttribute("data-cls") : ""; var subjects = (ex.classSubjects&&ex.classSubjects[cls]) ? ex.classSubjects[cls] : getClassSubjects(cls); // Get current class students list (for context) var curSY = ACTIVE_SESSION_START||SESSION_START_YEAR; var clsStudents = students.filter(function(s){ if(s.class.split("-")[0]!==cls) return false; if(s.admDate){var ap=s.admDate.split(".");if(ap.length>=3){var ay=parseInt(ap[2]),am=parseInt(ap[1])-1;var sSY=am>=3?ay:ay-1;if(sSY!==curSY)return false;}} return true; }).sort(function(a,b){ var na=parseInt((a.roll||"").split("-").pop())||0; var nb=parseInt((b.roll||"").split("-").pop())||0; return na-nb; }); var d=document.createElement("div"); d.style.cssText="position:fixed;inset:0;background:rgba(10,20,40,.7);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);padding:12px;"; d.innerHTML='
' +'
' +'
📸 Image se Marks Import
' +'
AI image se marks extract karega
' +'' +'
' +'
' // Drop zone +'
' +'
📷
' +'
Image select karo
' +'
Marks sheet ka photo ya screenshot
' +'' +'
' +'' // Context +'
' +'
Class: '+cls+'  ·  Subjects: '+subjects.join(", ")+'
' +'
Students: '+clsStudents.map(function(s){return s.roll+" ("+s.name+")";}).join(", ")+'
' +'
' +'' +'' +'
' +'
' +'' +'' +'' +'
' +'
'; document.body.appendChild(d); var selectedImageBase64 = null; var extractedMarks = null; d.querySelector("#im-close").onclick=d.querySelector("#im-cancel").onclick=function(){d.remove();}; // Image selection d.querySelector("#img-file-input").onchange=function(e){ var file=e.target.files[0]; if(!file) return; var reader=new FileReader(); reader.onload=function(ev){ selectedImageBase64=ev.target.result.split(",")[1]; var preview=d.querySelector("#img-preview"); var wrap=d.querySelector("#img-preview-wrap"); if(preview){preview.src=ev.target.result; wrap.style.display="block";} var btn=d.querySelector("#im-extract"); btn.disabled=false; btn.style.background="#6D28D9"; btn.style.color="#fff"; btn.style.cursor="pointer"; }; reader.readAsDataURL(file); }; // Drag and drop var dropzone=d.querySelector("#img-dropzone"); dropzone.ondragover=function(e){e.preventDefault();dropzone.style.background="#EDE9FE";}; dropzone.ondragleave=function(){dropzone.style.background="#FAFAFF";}; dropzone.ondrop=function(e){ e.preventDefault(); dropzone.style.background="#FAFAFF"; var file=e.dataTransfer.files[0]; if(!file||!file.type.startsWith("image/")) return; var dt=new DataTransfer(); dt.items.add(file); d.querySelector("#img-file-input").files=dt.files; d.querySelector("#img-file-input").dispatchEvent(new Event("change")); }; // Extract button d.querySelector("#im-extract").onclick=function(){ if(!selectedImageBase64){showToast("Pehle image select karo","error");return;} d.querySelector("#img-status").style.display="block"; d.querySelector("#img-result").style.display="none"; d.querySelector("#im-extract").disabled=true; var prompt="You are extracting exam marks from an image of a mark sheet.\n" +"Class: "+cls+"\n" +"Subjects to extract: "+subjects.join(", ")+"\n" +"Students (Roll No → Name):\n" +clsStudents.map(function(s){return s.roll+" → "+s.name;}).join("\n")+"\n\n" +"From the image, extract marks for each student for each subject.\n" +"Match students by Roll Number first, then by Name.\n" +"Return ONLY a JSON array in this exact format (no other text):\n" +'[{"roll":"2026-PL-A-001","name":"Student Name","marks":{"Hindi":85,"English":78}},...]' +"\nOnly include students where you can clearly read marks.\n" +"Max marks per subject: "+ex.maxMarks; fetch("https://api.anthropic.com/v1/messages",{ method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify({ model:"claude-sonnet-4-20250514", max_tokens:2000, messages:[{ role:"user", content:[ {type:"image",source:{type:"base64",media_type:"image/jpeg",data:selectedImageBase64}}, {type:"text",text:prompt} ] }] }) }) .then(function(r){return r.json();}) .then(function(data){ d.querySelector("#img-status").style.display="none"; var text=data.content&&data.content[0]?data.content[0].text:""; // Parse JSON from response var jsonMatch=text.match(/\[[\s\S]*\]/); if(!jsonMatch){ d.querySelector("#img-result").style.display="block"; d.querySelector("#img-result").innerHTML='
❌ Marks extract nahi ho sake. Clearer image try karo.
'; d.querySelector("#im-extract").disabled=false; return; } extractedMarks=JSON.parse(jsonMatch[0]); // Show preview table var preview='
✓ '+extractedMarks.length+' students ke marks extract ho gaye:
'; preview+='
'; preview+=''; subjects.forEach(function(s){preview+='';}); preview+=''; extractedMarks.forEach(function(m,i){ preview+=''; preview+=''; preview+=''; subjects.forEach(function(sub){ var v=m.marks&&m.marks[sub]!==undefined?m.marks[sub]:"—"; preview+=''; }); preview+=''; }); preview+='
RollName'+s+'
'+m.roll+''+m.name+''+v+'
'; d.querySelector("#img-result").style.display="block"; d.querySelector("#img-result").innerHTML=preview; var applyBtn=d.querySelector("#im-apply"); applyBtn.style.display="block"; d.querySelector("#im-extract").disabled=false; }) .catch(function(err){ d.querySelector("#img-status").style.display="none"; d.querySelector("#img-result").style.display="block"; d.querySelector("#img-result").innerHTML='
❌ Error: '+err.message+'
'; d.querySelector("#im-extract").disabled=false; }); }; // Apply extracted marks d.querySelector("#im-apply").onclick=function(){ if(!extractedMarks) return; var ex2=EXAMS.find(function(e){return e.id===examId;}); var matched=0; extractedMarks.forEach(function(m){ var stu=null; if(m.roll) stu=students.find(function(s){return s.class.split("-")[0]===cls&&s.roll.toLowerCase()===m.roll.toLowerCase();}); if(!stu&&m.name) stu=students.find(function(s){return s.class.split("-")[0]===cls&&s.name.toLowerCase()===m.name.toLowerCase();}); if(!stu) return; if(!ex2.results[stu.id]) ex2.results[stu.id]={}; var hasAny=false; subjects.forEach(function(sub){ if(m.marks&&m.marks[sub]!==undefined){ var mark=parseInt(m.marks[sub]); if(!isNaN(mark)&&mark>=0&&mark<=ex2.maxMarks){ex2.results[stu.id][sub]=mark;hasAny=true;} } }); if(hasAny) matched++; }); saveExams(); d.remove(); renderExamClassMarks(examId,cls); showToast(matched+" students ke marks apply ho gaye!"); }; } // ══════════════════════════════════════ // MARKS IMPORT — EXCEL/CSV // ══════════════════════════════════════ function importMarksFromExcel(examId){ var ex=EXAMS.find(function(e){return e.id===examId;}); if(!ex) return; var content=document.getElementById("exam-class-content"); var cls=content?content.getAttribute("data-cls"):""; // File input var inp=document.createElement("input"); inp.type="file"; inp.accept=".csv,.xlsx,.xls,.tsv"; inp.style.display="none"; document.body.appendChild(inp); inp.click(); inp.onchange=function(){ var file=inp.files[0]; if(!file){inp.remove();return;} var reader=new FileReader(); reader.onload=function(e){ var text=e.target.result; parseExcelMarks(text, examId, cls, file.name); inp.remove(); }; // Read as text for CSV/TSV, or try for xlsx if(file.name.match(/\.xlsx?$/i)){ reader.readAsBinaryString(file); } else { reader.readAsText(file); } }; } function parseExcelMarks(raw, examId, cls, filename){ var ex=EXAMS.find(function(e){return e.id===examId;}); if(!ex) return; var subjects=(ex.classSubjects&&ex.classSubjects[cls])?ex.classSubjects[cls]:getClassSubjects(cls); // Parse CSV (comma or tab or semicolon separated) var lines=raw.split(/\r?\n/).filter(function(l){return l.trim();}); if(!lines.length){showToast("File empty hai","error");return;} // Auto-detect delimiter var delim=","; if(lines[0].indexOf("\t")>-1) delim="\t"; else if(lines[0].indexOf(";")>-1) delim=";"; var headers=lines[0].split(delim).map(function(h){return h.trim().replace(/"/g,"").toLowerCase();}); // Find column indices var rollCol=-1, nameCol=-1; headers.forEach(function(h,i){ if(h.indexOf("roll")>=0||h.indexOf("admission")>=0||h==="id") rollCol=i; if(h.indexOf("name")>=0) nameCol=i; }); // Find subject columns (match subject names) var subjectCols={}; subjects.forEach(function(sub){ var subLower=sub.toLowerCase(); headers.forEach(function(h,i){ if(h.indexOf(subLower)>=0||subLower.indexOf(h)>=0) subjectCols[sub]=i; }); }); // Get students of this class var curSY=ACTIVE_SESSION_START||SESSION_START_YEAR; var clsStudents=students.filter(function(s){ return s.class.split("-")[0]===cls; }); var matched=0, failed=[]; lines.slice(1).forEach(function(line){ if(!line.trim()) return; var cells=line.split(delim).map(function(c){return c.trim().replace(/"/g,"");}); // Find matching student by roll or name var student=null; if(rollCol>=0 && cells[rollCol]){ var rollVal=cells[rollCol].toLowerCase(); student=clsStudents.find(function(s){ return (s.roll||"").toLowerCase()===rollVal || (s.roll||"").toLowerCase().indexOf(rollVal)>=0 || rollVal.indexOf((s.roll||"").split("-").pop())>=0; }); } if(!student && nameCol>=0 && cells[nameCol]){ var nameVal=cells[nameCol].toLowerCase().trim(); student=clsStudents.find(function(s){ return s.name.toLowerCase()===nameVal || s.name.toLowerCase().indexOf(nameVal)>=0 || nameVal.indexOf(s.name.toLowerCase().split(" ")[0])>=0; }); } if(!student){ if(cells[nameCol||0]) failed.push(cells[nameCol||0]||cells[rollCol||0]); return; } if(!ex.results[student.id]) ex.results[student.id]={}; var anyMark=false; subjects.forEach(function(sub){ var ci=subjectCols[sub]; if(ci===undefined) return; var val=parseInt(cells[ci]); if(!isNaN(val)&&val>=0&&val<=ex.maxMarks){ ex.results[student.id][sub]=val; anyMark=true; } }); if(anyMark) matched++; }); saveExams(); renderExamClassMarks(examId, cls); var msg=matched+" students ke marks import ho gaye!"; if(failed.length) msg+=" | "+failed.length+" match nahi hua: "+failed.slice(0,3).join(", "); showToast(msg, matched>0?"success":"error"); } // ══════════════════════════════════════ // MARKS IMPORT — IMAGE (Claude AI) // ══════════════════════════════════════ function importMarksFromImage(examId){ var ex=EXAMS.find(function(e){return e.id===examId;}); if(!ex) return; var content=document.getElementById("exam-class-content"); var cls=content?content.getAttribute("data-cls"):""; var subjects=(ex.classSubjects&&ex.classSubjects[cls])?ex.classSubjects[cls]:getClassSubjects(cls); // Get students list for this class var clsStudents=students.filter(function(s){return s.class.split("-")[0]===cls;}); var studentList=clsStudents.map(function(s){ return "Roll: "+(s.roll||s.id)+" | Name: "+s.name; }).join("\n"); // Show image upload popup var d=document.createElement("div"); d.id="img-import-popup"; d.style.cssText="position:fixed;inset:0;background:rgba(10,20,40,.75);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);padding:16px;"; d.innerHTML='
' +'
' +'
📸 Image se Marks Import
' +'
Class: '+cls+' | '+subjects.length+' subjects
' +'' +'
' +'
' +'
' +'
📄
' +'
Image ya PDF click karo
' +'
Marks sheet, attendance sheet, handwritten list
' +'' +'
' +'' +'
' +'' +'
' +'
'; document.body.appendChild(d); var popup=d; popup.querySelector("#img-close").onclick=function(){popup.remove();}; popup.onclick=function(e){if(e.target===popup)popup.remove();}; var fileInp=popup.querySelector("#img-file-inp"); var dropArea=popup.querySelector("#img-drop-area"); var extractBtn=popup.querySelector("#img-extract-btn"); var statusEl=popup.querySelector("#img-status"); var previewEl=popup.querySelector("#img-preview"); var previewImg=popup.querySelector("#img-preview-el"); var selectedFile=null; dropArea.onclick=function(){fileInp.click();}; fileInp.onchange=function(){ selectedFile=fileInp.files[0]; if(!selectedFile) return; statusEl.textContent=selectedFile.name+" selected"; extractBtn.disabled=false; extractBtn.style.cssText="width:100%;background:linear-gradient(135deg,#4C1D95,#6D28D9);color:#fff;border:none;border-radius:9px;padding:11px;font-size:14px;font-weight:700;cursor:pointer;font-family:inherit;"; // Preview var reader=new FileReader(); reader.onload=function(ev){ previewImg.src=ev.target.result; previewEl.style.display="block"; }; reader.readAsDataURL(selectedFile); }; extractBtn.onclick=function(){ if(!selectedFile) return; statusEl.innerHTML='
🤖 AI marks extract kar raha hai... ruko
'; extractBtn.disabled=true; var reader=new FileReader(); reader.onload=function(ev){ var base64=ev.target.result.split(",")[1]; var mediaType=selectedFile.type||"image/jpeg"; var prompt="This image contains a student marks sheet or list. Extract ALL student marks from it.\n\n" +"Class: "+cls+"\n" +"Subjects to find: "+subjects.join(", ")+"\n" +"Students in this class (match by roll number OR name):\n"+studentList+"\n\n" +"Return ONLY a JSON array, no other text:\n" +'[{"roll":"2026-PL-A-001","name":"Student Name","marks":{"Hindi":85,"English":72,"Maths":90}},...]' +"\n\nRules:\n" +"- Match students by roll number (preferred) or name (partial match OK)\n" +"- Only include subjects from the list above\n" +"- Skip if marks not visible\n" +"- Numbers only, no text\n" +"- Return valid JSON array only"; fetch("https://api.anthropic.com/v1/messages",{ method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify({ model:"claude-sonnet-4-20250514", max_tokens:2000, messages:[{role:"user",content:[ {type:"image",source:{type:"base64",media_type:mediaType,data:base64}}, {type:"text",text:prompt} ]}] }) }) .then(function(r){return r.json();}) .then(function(data){ var text=data.content&&data.content[0]?data.content[0].text:""; // Extract JSON from response var jsonMatch=text.match(/\[[\s\S]*\]/); if(!jsonMatch){ statusEl.innerHTML='
❌ Marks extract nahi ho sake. Clearer image try karo.
'; extractBtn.disabled=false; return; } try{ var extracted=JSON.parse(jsonMatch[0]); applyImageMarks(extracted, examId, cls, subjects, popup); }catch(e){ statusEl.innerHTML='
❌ Parse error. Try again.
'; extractBtn.disabled=false; } }) .catch(function(err){ statusEl.innerHTML='
❌ Error: '+err.message+'
'; extractBtn.disabled=false; }); }; reader.readAsDataURL(selectedFile); }; } function applyImageMarks(extracted, examId, cls, subjects, popup){ var ex=EXAMS.find(function(e){return e.id===examId;}); if(!ex) return; var clsStudents=students.filter(function(s){return s.class.split("-")[0]===cls;}); var matched=0, failed=[]; extracted.forEach(function(entry){ var student=null; // Match by roll if(entry.roll){ var rv=String(entry.roll).toLowerCase(); student=clsStudents.find(function(s){ return (s.roll||"").toLowerCase()===rv|| (s.roll||"").toLowerCase().endsWith("-"+rv)|| rv===(s.roll||"").split("-").pop(); }); } // Match by name if(!student&&entry.name){ var nv=entry.name.toLowerCase().trim(); student=clsStudents.find(function(s){ var sn=s.name.toLowerCase(); return sn===nv||sn.indexOf(nv)>=0||nv.indexOf(sn.split(" ")[0])>=0; }); } if(!student){failed.push(entry.name||entry.roll||"?");return;} if(!ex.results[student.id]) ex.results[student.id]={}; var marks=entry.marks||{}; var anyMark=false; subjects.forEach(function(sub){ // Find mark — try exact, case-insensitive, partial match var val=marks[sub]; if(val===undefined){ Object.keys(marks).forEach(function(k){ if(k.toLowerCase()===sub.toLowerCase()||sub.toLowerCase().indexOf(k.toLowerCase())>=0) val=marks[k]; }); } if(val!==undefined&&!isNaN(parseInt(val))){ ex.results[student.id][sub]=Math.min(parseInt(val),ex.maxMarks); anyMark=true; } }); if(anyMark) matched++; }); saveExams(); renderExamClassMarks(examId, cls); if(popup) popup.remove(); var msg="✅ "+matched+" students ke marks import ho gaye!"; if(failed.length) msg+=" | "+failed.length+" nahi mila: "+failed.slice(0,3).join(", "); showToast(msg, matched>0?"success":"error"); } function saveExamMarksAll(examId){ var ex=EXAMS.find(function(e){return e.id===examId;}); if(!ex) return; var inputs=document.querySelectorAll("#exam-class-content input[data-sid]"); var capped = 0; inputs.forEach(function(inp){ var sid=parseInt(inp.getAttribute("data-sid")); var sub=inp.getAttribute("data-sub"); var val=parseInt(inp.value); if(!ex.results[sid]) ex.results[sid]={}; if(!isNaN(val)){ if(val < 0) val = 0; if(val > ex.maxMarks){ val = ex.maxMarks; capped++; } inp.value = val; ex.results[sid][sub]=val; } else delete ex.results[sid][sub]; }); saveExams(); if(capped > 0) showToast(capped + " marks max " + ex.maxMarks + " pe cap kiye gaye!", "error"); else showToast("Marks saved!"); renderExams(); } // ── PRINT MARKS ENTRY SHEET ── function printMarksEntrySheet(examId, withMarks) { var ex = EXAMS.find(function(e){ return e.id === examId; }); if(!ex) return; var cls = document.getElementById("exam-class-content").getAttribute("data-cls"); if(!cls){ showToast("Pehle class select karo!","error"); return; } var subjects = (ex.classSubjects && ex.classSubjects[cls]) ? ex.classSubjects[cls] : getClassSubjects(cls); var curSY = ACTIVE_SESSION_START || SESSION_START_YEAR; var clsStudents = students.filter(function(s){ if(s.class.split("-")[0] !== cls) return false; if(s.admDate){ var ap=s.admDate.split("."); if(ap.length>=3){ var ay=parseInt(ap[2]),am=parseInt(ap[1])-1; var sSY=am>=3?ay:ay-1; if(sSY!==curSY) return false; } } return true; }).sort(function(a,b){ var ra=a.roll||"", rb=b.roll||""; var na=parseInt((ra.split("-").pop())||"0")||0; var nb=parseInt((rb.split("-").pop())||"0")||0; if(na!==nb) return na-nb; return ra.localeCompare(rb); }); if(!clsStudents.length){ showToast("Is class mein koi student nahi!","error"); return; } var html = '
'; // Header html += '
'; html += '
SARA AZAM MEMORIAL EDUCATION
'; html += '
Affiliated to CBSE · Session ' + CURRENT_SESSION + '
'; html += '
'; html += 'Exam: ' + ex.name + ''; html += 'Class: ' + cls + ''; html += 'Max Marks: ' + ex.maxMarks + ''; html += 'Subjects: ' + subjects.length + ''; html += '
'; html += '
' + (withMarks ? '📊 Marks Entry Sheet (Filled)' : '📝 Blank Marks Entry Sheet') + ' · Total Students: ' + clsStudents.length + '
'; html += '
'; // Table html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; subjects.forEach(function(sub){ html += ''; }); html += ''; html += ''; html += ''; html += ''; clsStudents.forEach(function(s, i){ var res = ex.results[s.id] || {}; var total = 0; var hasMark = false; subjects.forEach(function(sub){ var v = parseInt(res[sub]); if(!isNaN(v)){ total += v; hasMark = true; } }); var maxTotal = ex.maxMarks * subjects.length; var pct = (hasMark && maxTotal > 0) ? Math.round(total / maxTotal * 100) : 0; var grade = pct >= 91 ? "A1" : pct >= 81 ? "A2" : pct >= 71 ? "B1" : pct >= 61 ? "B2" : pct >= 51 ? "C1" : pct >= 41 ? "C2" : pct >= 33 ? "D" : "E"; var bg = i % 2 ? "#FAFAFA" : "#fff"; html += ''; html += ''; html += ''; html += ''; html += ''; subjects.forEach(function(sub){ var val = res[sub]; if(withMarks && val !== undefined && val !== ""){ var numVal = parseInt(val); var col = numVal >= (ex.maxMarks * 0.6) ? "#15803D" : numVal >= (ex.maxMarks * 0.33) ? "#B45309" : "#B91C1C"; html += ''; } else { html += ''; } }); if(withMarks && hasMark){ var pctCol = pct >= 60 ? "#15803D" : pct >= 33 ? "#B45309" : "#B91C1C"; html += ''; html += ''; html += ''; } else { html += ''; html += ''; html += ''; } html += ''; }); html += '
S.NoRoll NoStudent NameFather Name' + sub + '
(' + ex.maxMarks + ')
Total
(' + (ex.maxMarks * subjects.length) + ')
%Grade
' + (i+1) + '' + (s.roll || '—') + '' + s.name + '' + (s.father || '—') + '' + val + ' ' + total + '' + pct + '%' + grade + '
'; // Footer html += '
'; html += '
Generated: ' + new Date().toLocaleDateString("en-IN",{day:"2-digit",month:"short",year:"numeric"}) + ' · Sara Azam School ERP
'; html += '
'; html += '
Class Teacher
'; html += '
Principal
'; html += '
'; html += '
'; var title = (withMarks ? "Marks Sheet" : "Blank Marks Sheet") + " - " + ex.name + " - " + cls; printHTMLPage(html, title, "@page{margin:8mm;size:A4 landscape;}", true); } function saveExamMark(inp){ var sid=parseInt(inp.getAttribute("data-sid")); var examId=inp.getAttribute("data-exam"); var sub=inp.getAttribute("data-sub"); var val2=parseInt(inp.value)||0; var ex=EXAMS.find(function(e){return e.id===examId;}); if(!ex) return; if(!ex.results[sid]) ex.results[sid]={}; ex.results[sid][sub]=val2; saveExams(); // Update total cell var res=ex.results[sid]; var total=ex.subjects.reduce(function(t,s){return t+(parseInt(res[s])||0);},0); var totEl=document.getElementById("exam-tot-"+sid); if(totEl) totEl.textContent=total>0?total:"—"; } function closeExamResults(){ var panel=document.getElementById("exam-results-panel"); if(panel) panel.style.display="none"; } function deleteExam(examId){ if(!confirm("Yeh exam delete karna chahte ho?")) return; EXAMS=EXAMS.filter(function(e){return e.id!==examId;}); saveExams(); renderExams(); showToast("Exam delete ho gaya","error"); } // ── exams ── function renderExams_OLD(){ var list=students.filter(function(s){return s.class&&s.class.split('-')[0]==='X';}); var tb=g("e-tbody"); if(!list.length){tb.innerHTML='
📝

No Class X Students
Add students to Class X.
';return;} tb.innerHTML=list.map(function(s,i){var a=avt(s.name,i),b=Math.min(s.marks,25),sc=[b,Math.max(b-2,8),Math.max(b-4,8),Math.min(b+1,25),Math.max(b-1,8)],tot=sc.reduce(function(a,b){return a+b;},0);return'
'+a.init+'
'+s.name+'
'+s.roll+''+sc.map(function(sc){return''+sc+'/25';}).join("")+''+tot+'/125'+gr(tot/125*100)+'';}).join(""); } // ── report card ── function renderRC(){ var srch=(val("rc-s")||"").toLowerCase(); var list=students.filter(function(s){return!srch||s.name.toLowerCase().includes(srch);}); var tb=g("rc-tbody"); if(!list.length){tb.innerHTML='
📋

No Students
Add students first.
';return;} tb.innerHTML=list.map(function(s){var a=avt(s.name,students.indexOf(s));return'
'+a.init+'
'+s.name+'
'+s.dob+'
'+displayClass(s.class)+''+s.roll+''+s.marks+'%'+gr(s.marks)+''+(s.marks>=33?"PASS ✓":"FAIL ✗")+'';}).join(""); } function viewRC(id){ var s=students.find(function(x){return x.id===id;}); if(!s) return; loadExams(); var noPrefix=["Play","Nursery","L.K.G","U.K.G"]; var base=s.class?s.class.split("-")[0]:""; var sec=s.class?s.class.split("-")[1]||"A":"A"; var clsLabel=(noPrefix.indexOf(base)>=0?"":"Class ")+base+" — Section "+sec; // ── Build marks from EXAMS data ── // Aggregate all exam results for this student, grouped by subject var subjectMarks={}; // {subject: {obtained, max, exams:[]}} var examList=[]; // list of exams this student participated in EXAMS.forEach(function(ex){ if(!ex.results||!ex.results[s.id]) return; if(ex.classes&&ex.classes.length&&ex.classes.indexOf(base)<0) return; var res=ex.results[s.id]; var subjects=(ex.classSubjects&&ex.classSubjects[base])?ex.classSubjects[base]:getClassSubjects(base); var hasMarks=false; subjects.forEach(function(sub){ var mark=res[sub]; if(mark===undefined||mark===null) return; hasMarks=true; if(!subjectMarks[sub]) subjectMarks[sub]={obtained:0,max:0,exams:[]}; subjectMarks[sub].obtained+=parseInt(mark)||0; subjectMarks[sub].max+=ex.maxMarks||100; subjectMarks[sub].exams.push({name:ex.name,mark:mark,max:ex.maxMarks||100}); }); if(hasMarks) examList.push(ex.name); }); var subsList=Object.keys(subjectMarks); var totObtained=0,totMax=0; subsList.forEach(function(sub){totObtained+=subjectMarks[sub].obtained;totMax+=subjectMarks[sub].max;}); var pct=totMax>0?Math.round(totObtained/totMax*100):0; // Grade function getGrade(p){return p>=91?"A+":p>=81?"A":p>=71?"B+":p>=61?"B":p>=51?"C+":p>=41?"C":p>=33?"D":"F";} function getGradeColor(p){return p>=75?"#15803D":p>=60?"#1D4ED8":p>=33?"#B45309":"#B91C1C";} var gradeColor=getGradeColor(pct); var gradeBg=pct>=75?"#DCFCE7":pct>=60?"#DBEAFE":pct>=33?"#FEF3C7":"#FEE2E2"; var addr=(s.addr1||"")+(s.village?", "+s.village:"")+(s.district?", "+s.district:"")+(s.state?", "+s.state:"")+(s.pin?" - "+s.pin:"")||"—"; var stSess=getStudentSession(s); var schoolName=localStorage.getItem("saraAzam_schoolName")||"Sara Azam Memorial Education"; var photoHtml=s.photo ?'' :'
📷
'; var html=''; // ── HEADER ── html+='
'; html+='
'; html+='
'; html+='
'; html+='
'; html+='
🏫
'; html+='
'+schoolName+'
'; html+='
CBSE Affiliated · '+stSess.short+' · Estd. 2005
'; html+='
REPORT CARD
'; html+='
'; html+='
'+photoHtml+'
'; // ── STUDENT INFO ── html+='
'; html+='
'; [["Student Name",s.name],["Roll No.",s.roll],["Class",clsLabel], ["Father",s.father||"—"],["Session",stSess.short],["Category",s.category||"—"]].forEach(function(f){ html+='
'+f[0]+'
' +'
'+f[1]+'
'; }); html+='
'; // ── MARKS TABLE ── html+='
'; if(!subsList.length){ // No exam data html+='
'; html+='
📝
'; html+='
Koi marks enter nahi kiye gaye
'; html+='
Examinations section mein marks enter karo
'; html+='
'; } else { html+=''; html+=''; html+=''; // Exam columns examList.forEach(function(en){ html+=''; }); html+=''; html+=''; html+=''; html+=''; subsList.forEach(function(sub,i){ var sm=subjectMarks[sub]; var sp=sm.max>0?Math.round(sm.obtained/sm.max*100):0; var sc=getGradeColor(sp); html+=''; html+=''; // Per-exam marks examList.forEach(function(en){ var ex=EXAMS.find(function(e){return e.name===en;}); var m=ex&&ex.results&&ex.results[s.id]?ex.results[s.id][sub]:null; html+=''; }); html+=''; html+=''; html+=''; html+=''; }); // Total row html+=''; html+=''; examList.forEach(function(){html+='';}); html+=''; html+=''; html+=''; html+=''; html+='
Subject'+en+'Total%Grade
'+sub+'' +(m!==null&&m!==undefined?m+"/"+(ex.maxMarks||100):"—")+''+sm.obtained+'/'+sm.max+''+sp+'%'+getGrade(sp)+'
TOTAL'+totObtained+'/'+totMax+''+pct+'%'+getGrade(pct)+'
'; // Overall grade card html+='
'; html+='
Overall Performance
'; html+='
'+pct+'% · '+getGrade(pct)+'
'; html+='
'+totObtained+' out of '+totMax+' marks
'; html+='
'+getGrade(pct)+'
'; html+='
'; } // Signatures html+='
'; ["Class Teacher","Parent / Guardian","Principal"].forEach(function(sg){ html+='
'; html+='
'; html+='
'+sg+'
'; }); html+='
'; // Render in report card panel var rc=g("rc-student-card"); if(rc){ rc.innerHTML=html; rc.style.display="block"; } // Scroll to it if(rc) rc.scrollIntoView({behavior:"smooth"}); } // ── TC ── function renderTC(){ var srch=(val("tc-s")||"").toLowerCase(); var list=students.filter(function(s){return!srch||s.name.toLowerCase().includes(srch);}); var tb=g("tc-tbody"); if(!list.length){tb.innerHTML='
📄

No Students
Add students first.
';return;} tb.innerHTML=list.map(function(s){var a=avt(s.name,students.indexOf(s)),iss=issuedTCs.find(function(x){return x.id===s.id;});return'
'+a.init+'
'+s.name+'
'+s.phone+'
'+s.class+''+s.roll+''+s.category+''+(s.fee==="Paid"?"✓ Paid":"✗ Due")+''+(iss?"Issued":"Not Issued")+''+(iss?iss.tcNo:"—")+'
';}).join(""); } function issueTC(id){ var s=students.find(function(x){return x.id===id;});if(!s)return; if(issuedTCs.find(function(x){return x.id===id;})){showToast("TC already issued","error");return;} issuedTCs.push(Object.assign({},s,{tcDate:new Date().toLocaleDateString("en-IN"),tcNo:"SAME/TC/"+new Date().getFullYear()+"/"+String(id).slice(-4).padStart(4,"0")})); saveData(); showToast("TC issued for "+s.name);renderTC();renderTCY(); } function viewTC(id){ var s=students.find(function(x){return x.id===id;});if(!s)return; var today=new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}),tcNo="SAME/TC/"+new Date().getFullYear()+"/"+String(id).slice(-4).padStart(4,"0"); var addr=(s.addr1||"")+(s.city?", "+s.city:"")+(s.dist?", "+s.dist:"")+(s.state?", "+s.state:"")+(s.pin?" - "+s.pin:"")||"—"; var fields=[["1.","Name of Student (Block Letters)",s.name.toUpperCase()],["2.","Father's / Guardian's Name",s.father||"—"],["3.","Mother's Name",s.mother||"—"],["4.","Residential Address",addr],["5.","Nationality","Indian"],["6.","Category",s.category],["7.","Date of Birth",s.dob||"—"],["8.","Date of Admission","01 Apr 2022"],["9.","Class at time of leaving",displayClass(s.class)],["10.","All dues paid",s.fee==="Paid"?"Yes — Fee Cleared":"No — Pending"],["11.","Character and Conduct","Good"],["12.","Reason for leaving","Parent's Request / Transfer"]]; g("tc-content").innerHTML='
Sara Azam Memorial Education
CBSE Affiliated School · New Delhi · Est. 2005
Affiliation No.: 2730123 · School Code: 55421
TRANSFER CERTIFICATE
TC No.: '+tcNo+'
Date: '+today+'
'+fields.map(function(f,i){return'
'+f[0]+'
'+f[1]+'
'+f[2]+'
';}).join("")+'
Certified that the above information is correct as per school records.
This TC is issued on request of the parent/guardian.
'+["Class Teacher","Exam Controller","Principal"].map(function(sig){return'
'+sig+'
Sara Azam Memorial Education
';}).join("")+'
⚠️ Physical copy with authorized signature and school seal is mandatory.
'; g("doc-tc").classList.remove("hidden"); } // ── TCY ── function renderTCY(){ g("tcy-tot").textContent=issuedTCs.length; g("tcy-ses").textContent=issuedTCs.length; g("tcy-pen").textContent=students.length-issuedTCs.length; g("tcy-count").textContent="Total: "+issuedTCs.length+" records"; var srch=(val("tcy-s")||"").toLowerCase(); var list=issuedTCs.filter(function(s){return!srch||s.name.toLowerCase().includes(srch)||s.tcNo.toLowerCase().includes(srch);}); if(!issuedTCs.length){g("tcy-empty").classList.remove("hidden");g("tcy-table").classList.add("hidden");} else{ g("tcy-empty").classList.add("hidden");g("tcy-table").classList.remove("hidden"); g("tcy-tbody").innerHTML=list.map(function(s,i){var a=avt(s.name,i);return''+(i+1)+''+s.tcNo+'
'+a.init+'
'+s.name+'
'+s.dob+'
'+displayClass(s.class)+''+s.roll+''+s.father+''+s.category+''+s.tcDate+''+(s.fee==="Paid"?"✓":"✗")+'';}).join(""); } } // ════════════════════════════════════════ // CLASS ROUTINE — SINGLE TABLE (Mon-Sat) // ════════════════════════════════════════ var _crGroup = "junior"; var CR_GROUP_CLASSES = { junior:["Play","Nursery","L.K.G","U.K.G"], primary:["I","II","III","IV","V"], senior:["VI","VII","VIII","IX","X"] }; var CR_GROUP_CAT = { junior:"Pre-Primary", primary:"Primary", senior:"Upper Primary" }; var CR_GROUP_LABELS = { junior:"Pre-Primary (Play – U.K.G)", primary:"Primary (I – V)", senior:"Upper Primary (VI – X)" }; var CR_DAYS = ["Mon","Tue","Wed","Thu","Fri","Sat"]; var CR_STORAGE_KEY = "saraAzam_classRoutine_v4"; function loadClassRoutines(){ try{ var s=localStorage.getItem(CR_STORAGE_KEY); if(s) return JSON.parse(s); }catch(e){} return {}; } function saveClassRoutineData(data){ localStorage.setItem(CR_STORAGE_KEY, JSON.stringify(data)); } function switchRoutineGroup(grp){ _crGroup=grp; ["junior","primary","senior"].forEach(function(k){ var btn=document.getElementById("cr-tab-"+k); if(!btn) return; var active=k===grp; var cols={junior:["#6B21A8","#7C3AED"],primary:["#0B1F3A","#1E3A5F"],senior:["#065F46","#059669"]}; btn.style.background=active?"linear-gradient(135deg,"+cols[k][0]+","+cols[k][1]+")":"#F1F5F9"; btn.style.color=active?"#fff":"#374151"; btn.style.border=active?"none":"1px solid #DDE3EC"; }); renderClassRoutine(); } function getCRTimeSlots(startTime,periods,duration){ var parts=startTime.split(":"),h=parseInt(parts[0])||8,m=parseInt(parts[1])||0,slots=[]; for(var i=0;i=60){h++;m-=60;} var ap=sh>=12?"PM":"AM",h12=sh%12||12; slots.push({num:i+1,time:h12+":"+String(sm).padStart(2,"0")+ap}); if(i===3){m+=20;while(m>=60){h++;m-=60;} slots.push({num:"L",time:"Lunch",isBreak:true});} } return slots; } function getTeachersForCategory(catName){ return teachers.filter(function(t){ var tc=t.category_type||getTeacherCategoryFromClass(t.class); return tc===catName; }); } function renderClassRoutine(){ var classes=CR_GROUP_CLASSES[_crGroup], catName=CR_GROUP_CAT[_crGroup]; var periods=parseInt(document.getElementById("cr-periods").value)||7; var duration=parseInt(document.getElementById("cr-duration").value)||45; var startTime=document.getElementById("cr-start-time").value||"08:00"; var slots=getCRTimeSlots(startTime,periods,duration); var allData=loadClassRoutines(), saved=allData[_crGroup]||{}; var catTeachers=getTeachersForCategory(catName); var subEl=document.getElementById("cr-sub"); if(subEl) subEl.textContent=CR_GROUP_LABELS[_crGroup]+" · "+catTeachers.length+" Teachers"; var wrap=document.getElementById("cr-table-wrap"); if(!catTeachers.length){ wrap.innerHTML='
' +'
👨‍🏫
' +'
No "'+catName+'" Teachers Found
' +'
Teachers panel mein staff add karo aur Teacher Category = "'+catName+'" select karo.
'; return; } // Build teacher options for dropdown: "TeacherName / ClassName" var cellOptions = []; catTeachers.forEach(function(t){ classes.forEach(function(cls){ cellOptions.push({label: t.name+" / "+cls, value: t.name+"__"+cls, teacher:t.name, cls:cls}); }); }); // Also specials ["Assembly","PT/Sports","Library","Free Period"].forEach(function(sp){ cellOptions.push({label:sp, value:sp, teacher:"", cls:""}); }); var html='
' +'
' +''+CR_GROUP_LABELS[_crGroup]+' — Weekly Routine' +'Sara Azam Memorial Education' +'
' +'
' +'' +''; slots.forEach(function(sl){ if(sl.isBreak){ html+=''; } else { html+=''; } }); html+=''; var dayColors=["#1E3A5F","#065F46","#D97706","#B91C1C","#6B21A8","#0F766E"]; CR_DAYS.forEach(function(day,di){ html+='' +''; slots.forEach(function(sl){ if(sl.isBreak){ html+=''; return; } var cellKey=day+"_P"+sl.num; var cellVal=saved[cellKey]||""; // Parse "Name__Class" format var dispTeacher="",dispClass=""; if(cellVal && cellVal.indexOf("__")>=0){ var pp=cellVal.split("__"); dispTeacher=pp[0]; dispClass=pp[1]; } else if(cellVal){ dispTeacher=cellVal; dispClass=""; } // Build select var opts=''; cellOptions.forEach(function(co){ opts+=''; }); html+=''; }); html+=''; }); html+='
DayLunch'+sl.num+'
'+sl.time+'
'+day+'' +'
🍽️
' +''; // Show preview below select if(dispTeacher && dispClass){ html+='
' +'
'+dispTeacher+'
' +'
'+dispClass+'
'; } else if(cellVal && cellVal.indexOf("__")<0 && cellVal){ html+='
'+cellVal+'
'; } html+='
'; wrap.innerHTML=html; } function onRoutineCellChange(sel){ // Update the preview div below select var val2=sel.value; var previewDiv=sel.parentNode.querySelector("div"); if(val2 && val2.indexOf("__")>=0){ var pp=val2.split("__"); if(previewDiv) previewDiv.outerHTML='
'+pp[0]+'
'+pp[1]+'
'; else sel.insertAdjacentHTML("afterend",'
'+pp[0]+'
'+pp[1]+'
'); } else if(val2){ if(previewDiv) previewDiv.outerHTML='
'+val2+'
'; else sel.insertAdjacentHTML("afterend",'
'+val2+'
'); } else { if(previewDiv) previewDiv.remove(); } } function autoGenerateRoutine(){ var classes=CR_GROUP_CLASSES[_crGroup], catName=CR_GROUP_CAT[_crGroup]; var periods=parseInt(document.getElementById("cr-periods").value)||7; var catTeachers=getTeachersForCategory(catName); if(!catTeachers.length){ showToast("Pehle '"+catName+"' teachers add karo!","error"); return; } var allData=loadClassRoutines(), saved={}; var nT=catTeachers.length, nC=classes.length; // Algorithm: For each day, for each period, assign teacher→class // Teacher rotates across classes, ensuring no 2 teachers in same class at same period // Each cell has ONE teacher+class combo // Period 1: Teacher0→Class0, Period 2: Teacher0→Class1, etc // But we show ONE entry per cell (Day × Period), so we need to decide which teacher goes in each period // Since there may be more or fewer teachers than classes, // We create a rotation: each period cycles through teachers, each teacher cycles through classes CR_DAYS.forEach(function(day,dayIdx){ for(var p=1;p<=periods;p++){ // Pick teacher for this cell: rotate through all teachers across periods and days var tIdx = (p-1 + dayIdx) % nT; var t = catTeachers[tIdx]; // Pick class: rotate based on period + day offset var cIdx = (p-1 + dayIdx) % nC; var cellKey = day+"_P"+p; saved[cellKey] = t.name + "__" + classes[cIdx]; } }); allData[_crGroup]=saved; saveClassRoutineData(allData); renderClassRoutine(); showToast("⚡ Routine generated! "+nT+" teachers rotating across "+nC+" classes","success"); } function saveClassRoutine(){ var allData=loadClassRoutines(), saved=allData[_crGroup]||{}; var selects=document.querySelectorAll("#cr-table-wrap select[data-day]"); selects.forEach(function(s){ var key=s.getAttribute("data-day")+"_P"+s.getAttribute("data-period"); if(s.value) saved[key]=s.value; else delete saved[key]; }); allData[_crGroup]=saved; saveClassRoutineData(allData); showToast("💾 "+CR_GROUP_LABELS[_crGroup]+" routine saved!","success"); } function printClassRoutine(){ var classes=CR_GROUP_CLASSES[_crGroup], catName=CR_GROUP_CAT[_crGroup]; var periods=parseInt(document.getElementById("cr-periods").value)||7; var duration=parseInt(document.getElementById("cr-duration").value)||45; var startTime=document.getElementById("cr-start-time").value||"08:00"; var slots=getCRTimeSlots(startTime,periods,duration); var allData=loadClassRoutines(), saved=allData[_crGroup]||{}; var groupLabel=CR_GROUP_LABELS[_crGroup]; var html='
'; html+='
'; html+='
SARA AZAM MEMORIAL EDUCATION
'; html+='
Affiliated to CBSE · Session '+CURRENT_SESSION+'
'; html+='
CLASS ROUTINE — '+groupLabel.toUpperCase()+'
'; html+='
'; html+=''; html+=''; html+=''; slots.forEach(function(sl){ if(sl.isBreak){ html+=''; } else { html+=''; } }); html+=''; CR_DAYS.forEach(function(day,di){ html+=''; slots.forEach(function(sl){ if(sl.isBreak){ html+=''; return; } var v=saved[day+"_P"+sl.num]||""; var dT="",dC=""; if(v && v.indexOf("__")>=0){ var pp2=v.split("__"); dT=pp2[0]; dC=pp2[1]; } else { dT=v; } if(dT && dC){ html+=''; } else if(dT){ html+=''; } else { html+=''; } }); html+=''; }); html+='
DayLunch'+sl.num+'
'+sl.time+'
'+day+'🍽️' +'
'+dT+'
' +'
'+dC+'
'+dT+'
'; html+='
'; html+='
Generated: '+new Date().toLocaleDateString("en-IN",{day:"2-digit",month:"short",year:"numeric"})+'
'; html+='
'; html+='
Coordinator
'; html+='
Principal
'; html+='
'; printHTMLPage(html, "Class Routine - "+groupLabel, "@page{margin:8mm;size:A4 landscape;}", true); } // ════════════════════════════════════════ // STUDENT ENQUIRY MANAGEMENT // ════════════════════════════════════════ var ENQUIRIES = []; var ENQ_STORAGE_KEY = "saraAzam_enquiry_v1"; function loadEnquiries(){ try{var s=localStorage.getItem(ENQ_STORAGE_KEY);if(s)ENQUIRIES=JSON.parse(s);}catch(e){ENQUIRIES=[];} } function saveEnquiries(){ localStorage.setItem(ENQ_STORAGE_KEY,JSON.stringify(ENQUIRIES)); } function openAddEnquiry(editId){ var existing=document.getElementById("enq-modal"); if(existing) existing.remove(); loadEnquiries(); var enq = editId ? ENQUIRIES.find(function(e){return e.id===editId;}) : null; var isEdit = !!enq; var d=document.createElement("div"); d.id="enq-modal"; d.style.cssText="position:fixed;inset:0;background:rgba(10,20,40,.7);z-index:99990;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);padding:12px;"; var classOpts=''; BASE_CLASSES.forEach(function(bc){ classOpts+=''; }); d.innerHTML=''; document.body.appendChild(d); d.onclick=function(e){if(e.target===d)d.remove();}; } function saveEnquiryForm(editId){ var name=(document.getElementById("enq-name")||{}).value||""; var phone=(document.getElementById("enq-phone")||{}).value||""; var classFor=(document.getElementById("enq-class")||{}).value||""; if(!name.trim()){showToast("Student name zaroori hai!","error");return;} if(!classFor){showToast("Class select karo!","error");return;} loadEnquiries(); var data={ name:name.trim(), father:((document.getElementById("enq-father")||{}).value||"").trim(), mother:((document.getElementById("enq-mother")||{}).value||"").trim(), phone:phone.trim(), classFor:classFor, dob:((document.getElementById("enq-dob")||{}).value||"").trim(), gender:((document.getElementById("enq-gender")||{}).value||""), prevSchool:((document.getElementById("enq-prev-school")||{}).value||"").trim(), village:((document.getElementById("enq-village")||{}).value||"").trim(), status:((document.getElementById("enq-status")||{}).value||"New"), remarks:((document.getElementById("enq-remarks")||{}).value||"").trim() }; if(editId){ var idx=ENQUIRIES.findIndex(function(e){return e.id===editId;}); if(idx>=0){ data.id=editId; data.date=ENQUIRIES[idx].date; ENQUIRIES[idx]=data; } } else { data.id=Date.now(); var td=new Date(); data.date=String(td.getDate()).padStart(2,"0")+"."+String(td.getMonth()+1).padStart(2,"0")+"."+td.getFullYear(); ENQUIRIES.push(data); } saveEnquiries(); var modal=document.getElementById("enq-modal"); if(modal) modal.remove(); renderEnquiry(); showToast(editId?"Enquiry updated!":"Enquiry saved!","success"); } function deleteEnquiry(id){ if(!confirm("Ye enquiry delete karna chahte ho?")) return; loadEnquiries(); ENQUIRIES=ENQUIRIES.filter(function(e){return e.id!==id;}); saveEnquiries(); renderEnquiry(); showToast("Enquiry deleted","error"); } function convertEnquiryToAdmission(id){ loadEnquiries(); var enq=ENQUIRIES.find(function(e){return e.id===id;}); if(!enq){showToast("Enquiry not found","error");return;} if(enq.status==="Converted"){showToast("Ye enquiry pehle hi admission mein convert ho chuki hai!","error");return;} if(!confirm("🎓 "+enq.name+" ka admission lena hai?\n\nClass: "+(enq.classFor||"—")+"\nFather: "+(enq.father||"—")+"\nPhone: "+(enq.phone||"—")+"\n\nStudent form mein saara data auto-fill ho jaayega.")) return; // Mark as converted enq.status="Converted"; saveEnquiries(); renderEnquiry(); // Close any existing modal document.querySelectorAll(".modal").forEach(function(m){m.remove();}); document.body.style.overflow=""; // Switch to students tab showTab("students"); setTimeout(function(){ openModal("student"); // Wait for modal + dropdowns to fully populate setTimeout(function(){ // Personal Info var sn=document.getElementById("sn"); if(sn){sn.value=enq.name||""; try{sn.dispatchEvent(new Event("input"));}catch(e){}} var sdb=document.getElementById("sdb"); if(sdb) sdb.value=enq.dob||""; var sgender=document.getElementById("sgender"); if(sgender) sgender.value=enq.gender||""; // Class — must re-populate dropdown first then set value var scl=document.getElementById("scl"); if(scl && enq.classFor){ // Ensure dropdown is populated populateClassDropdown("scl"); // Try setting value with section A, then B, then C var sections=["A","B","C"]; var classSet=false; for(var si=0;si'+bc+''; }); cfSel.value=curVal||"ALL"; } var filtered=ENQUIRIES.filter(function(e){ if(statusFilter!=="ALL"&&e.status!==statusFilter) return false; if(classFilter!=="ALL"&&e.classFor!==classFilter) return false; if(search){ var hay=(e.name+"|"+e.father+"|"+e.phone+"|"+e.classFor+"|"+(e.village||e.address||"")).toLowerCase(); if(hay.indexOf(search)<0) return false; } return true; }).sort(function(a,b){return(b.id||0)-(a.id||0);}); // Stats var statNew=0,statFollowUp=0,statConverted=0,statCancelled=0; ENQUIRIES.forEach(function(e){ if(e.status==="New") statNew++; else if(e.status==="Follow-up") statFollowUp++; else if(e.status==="Converted") statConverted++; else if(e.status==="Cancelled") statCancelled++; }); var statsEl=document.getElementById("enq-stats"); if(statsEl) statsEl.innerHTML=[ {n:ENQUIRIES.length,l:"Total",bg:"#EFF6FF",bc:"#93C5FD",c:"#1D4ED8",icon:"📋"}, {n:statNew,l:"New",bg:"#FFFBEB",bc:"#FDE68A",c:"#B45309",icon:"🟡"}, {n:statFollowUp,l:"Follow-up",bg:"#EFF6FF",bc:"#93C5FD",c:"#1D4ED8",icon:"🔵"}, {n:statConverted,l:"Converted",bg:"#F0FDF4",bc:"#86EFAC",c:"#15803D",icon:"🟢"} ].map(function(s){ return '
' +'
'+s.n+'
' +'
'+s.icon+' '+s.l+'
'; }).join(""); var subEl=document.getElementById("enq-sub"); if(subEl) subEl.textContent="Total "+ENQUIRIES.length+" enquiries · "+statNew+" new · "+statConverted+" converted"; var cntEl=document.getElementById("enq-count"); if(cntEl) cntEl.textContent=filtered.length+" records"; var wrap=document.getElementById("enq-table-wrap"); if(!filtered.length){ wrap.innerHTML='
' +'
📋
' +'
No Enquiries Found
' +'
Click "+ New Enquiry" to add one.
'; return; } var statusColors={New:"#F59E0B", "Follow-up":"#3B82F6", Converted:"#10B981", Cancelled:"#EF4444"}; var statusBg={New:"#FFFBEB", "Follow-up":"#EFF6FF", Converted:"#F0FDF4", Cancelled:"#FEF2F2"}; var html=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; filtered.forEach(function(e,i){ var bg=i%2?"#FAFAFA":"#fff"; var sc=statusColors[e.status]||"#64748B"; var sb=statusBg[e.status]||"#F1F5F9"; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; }); html+='
#Student NameFatherClassPhoneVillageDateStatusActions
'+(i+1)+''+e.name+''+(e.father||"—")+''+(e.classFor||"—")+''+(e.phone||"—")+''+(e.village||e.address||"—")+''+(e.date||"—")+''+e.status+'' +'
' +'' +(e.status!=="Converted"?'':'✅ Admitted') +'' +'
'; wrap.innerHTML=html; } function printEnquiryList(){ loadEnquiries(); var statusFilter=((document.getElementById("enq-filter-status")||{}).value||"ALL"); var classFilter=((document.getElementById("enq-filter-class")||{}).value||"ALL"); var search=((document.getElementById("enq-search")||{}).value||"").toLowerCase(); var filtered=ENQUIRIES.filter(function(e){ if(statusFilter!=="ALL"&&e.status!==statusFilter) return false; if(classFilter!=="ALL"&&e.classFor!==classFilter) return false; if(search){ var hay=(e.name+"|"+e.father+"|"+e.phone+"|"+e.classFor).toLowerCase(); if(hay.indexOf(search)<0) return false; } return true; }).sort(function(a,b){return(b.id||0)-(a.id||0);}); var filterLabel = statusFilter!=="ALL"?statusFilter:"All"; if(classFilter!=="ALL") filterLabel+=" · Class "+classFilter; var html='
'; html+='
'; html+='
SARA AZAM MEMORIAL EDUCATION
'; html+='
Affiliated to CBSE · Session '+CURRENT_SESSION+'
'; html+='
ENQUIRY LIST — '+filterLabel.toUpperCase()+'
'; html+='
Total: '+filtered.length+' records · Generated: '+new Date().toLocaleDateString("en-IN",{day:"2-digit",month:"short",year:"numeric"})+'
'; html+='
'; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; filtered.forEach(function(e,i){ html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; }); html+='
S.NoStudent NameFather NameMother NameClassPhoneDOBGenderDateStatusRemarks
'+(i+1)+''+e.name+''+(e.father||"—")+''+(e.mother||"—")+''+(e.classFor||"—")+''+(e.phone||"—")+''+(e.dob||"—")+''+(e.gender||"—")+''+(e.date||"—")+''+e.status+''+(e.remarks||"—")+'
'; html+='
'; html+='
Sara Azam School ERP
'; html+='
'; html+='
Office Incharge
'; html+='
Principal
'; html+='
'; printHTMLPage(html, "Enquiry List", "@page{margin:8mm;size:A4 landscape;}", true); } // ════════════════════════════════════════ // STUDENT DIARY / HOMEWORK // ════════════════════════════════════════ var DIARY_STORAGE_KEY = "saraAzam_diary_v1"; function loadDiary(){ try{var s=localStorage.getItem(DIARY_STORAGE_KEY);return s?JSON.parse(s):[];}catch(e){return [];} } function saveDiary(arr){ localStorage.setItem(DIARY_STORAGE_KEY,JSON.stringify(arr)); } function openAddDiaryEntry(editId){ var entries=loadDiary(); var entry=editId?entries.find(function(e){return e.id===editId;}):null; var isEdit=!!entry; var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var existing=document.getElementById("diary-modal");if(existing)existing.remove(); var d=document.createElement("div");d.id="diary-modal"; d.style.cssText="position:fixed;inset:0;background:rgba(10,20,40,.7);z-index:99990;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);padding:12px;"; // Class options from teacher's category var catType=t.category_type||getTeacherCategoryFromClass(t.class); var clsList=[]; if(catType==="Pre-Primary") clsList=["Play","Nursery","L.K.G","U.K.G"]; else if(catType==="Primary") clsList=["I","II","III","IV","V"]; else if(catType==="Upper Primary") clsList=["VI","VII","VIII","IX","X"]; else BASE_CLASSES.forEach(function(bc){clsList.push(bc);}); var clsOpts=''; clsList.forEach(function(c){clsOpts+='';}); // Subject options var subOpts=''; var tSubs=(t.subject||"").split(/[,&\/]+/).map(function(s){return s.trim();}); tSubs.forEach(function(sub){if(sub) subOpts+='';}); // Also add common ["Hindi","English","Math","Science","EVS","SST","GK","Computer","Drawing"].forEach(function(sub){ if(tSubs.indexOf(sub)<0) subOpts+=''; }); var today=new Date(); var todayStr=String(today.getDate()).padStart(2,"0")+"."+String(today.getMonth()+1).padStart(2,"0")+"."+today.getFullYear(); d.innerHTML=''; document.body.appendChild(d); d.onclick=function(e){if(e.target===d)d.remove();}; } function saveDiaryEntry(editId){ var date=(document.getElementById("diary-date")||{}).value||""; var classFor=(document.getElementById("diary-class")||{}).value||"ALL"; var subject=(document.getElementById("diary-subject")||{}).value||""; var type2=(document.getElementById("diary-type")||{}).value||"Homework"; var desc=(document.getElementById("diary-desc")||{}).value||""; if(!desc.trim()){showToast("Description zaroori hai!","error");return;} var t=teachers.find(function(x){return x.id===currentTeacherId;}); var entries=loadDiary(); var data={date:date,classFor:classFor,subject:subject,type:type2,desc:desc.trim(),teacherId:currentTeacherId,teacherName:t?t.name:"",category:t?t.category_type||"":""}; if(editId){ var idx=entries.findIndex(function(e){return e.id===editId;}); if(idx>=0){data.id=editId;entries[idx]=data;} } else { data.id=Date.now(); entries.unshift(data); } saveDiary(entries); var modal=document.getElementById("diary-modal");if(modal)modal.remove(); renderTeacherDiary(); showToast(editId?"Entry updated!":"Diary entry saved!","success"); } function deleteDiaryEntry(id){ if(!confirm("Ye diary entry delete karna hai?")) return; var entries=loadDiary(); entries=entries.filter(function(e){return e.id!==id;}); saveDiary(entries); renderTeacherDiary(); showToast("Entry deleted","error"); } function renderTeacherDiary(){ var t=teachers.find(function(x){return x.id===currentTeacherId;}); if(!t) return; var entries=loadDiary().filter(function(e){return e.teacherId===currentTeacherId;}); var el=document.getElementById("tp-diary-list"); if(!el) return; if(!entries.length){ el.innerHTML='
📝
No diary entries yet
Click "+ Add Entry" to create homework
'; return; } var typeIcons={Homework:"📚",Classwork:"📝",Notice:"📢",Test:"📋"}; var typeColors={Homework:"#1D4ED8",Classwork:"#059669",Notice:"#D97706",Test:"#B91C1C"}; var typeBg={Homework:"#EFF6FF",Classwork:"#F0FDF4",Notice:"#FFFBEB",Test:"#FEF2F2"}; el.innerHTML=entries.map(function(e){ var ic=typeIcons[e.type]||"📝"; var col=typeColors[e.type]||"#374151"; var bg=typeBg[e.type]||"#F8FAFC"; return '
' +'
' +'
' +''+ic+'' +''+e.type+'' +''+e.date+'' +''+(e.classFor==="ALL"?"All Classes":e.classFor)+'' +(e.subject?''+e.subject+'':'') +'
' +'
' +'' +'' +'
' +'
' +'
'+e.desc+'
' +'
'; }).join(""); } function renderStudentDiary(){ var s=students.find(function(x){return x.id===currentDetailId;}); if(!s) return; var clsBase=s.class?s.class.split("-")[0]:""; var entries=loadDiary().filter(function(e){ return e.classFor==="ALL"||e.classFor===clsBase; }); var el=document.getElementById("sd-diary-list"); if(!el) return; if(!entries.length){ el.innerHTML='
' +'
📖
' +'
Diary is Empty
' +'
Jab teacher homework ya notice likhenge tab yahan dikhega
'; return; } // Group by date var dateGroups={}; entries.forEach(function(e){ var dt=e.date||"Unknown"; if(!dateGroups[dt]) dateGroups[dt]=[]; dateGroups[dt].push(e); }); // Sort dates newest first var dateKeys=Object.keys(dateGroups).sort(function(a,b){ var pa=a.split("."),pb=b.split("."); if(pa.length>=3&&pb.length>=3){ var da=new Date(parseInt(pa[2]),parseInt(pa[1])-1,parseInt(pa[0])); var db=new Date(parseInt(pb[2]),parseInt(pb[1])-1,parseInt(pb[0])); return db-da; } return b.localeCompare(a); }); var dayNames=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]; var monthNames=["","January","February","March","April","May","June","July","August","September","October","November","December"]; var typeIcons={Homework:"📚",Classwork:"📝",Notice:"📢",Test:"📋"}; var typeBg={Homework:"#DBEAFE",Classwork:"#D1FAE5",Notice:"#FEF3C7",Test:"#FEE2E2"}; var typeCol={Homework:"#1D4ED8",Classwork:"#059669",Notice:"#B45309",Test:"#B91C1C"}; var html=''; dateKeys.forEach(function(dt,di){ var group=dateGroups[dt]; var dp=dt.split("."); var dayNum=dp[0]||"",monNum=parseInt(dp[1])||0,yearNum=dp[2]||""; var dayName=""; if(dp.length>=3){try{var dd=new Date(parseInt(dp[2]),parseInt(dp[1])-1,parseInt(dp[0]));dayName=dayNames[dd.getDay()]||"";}catch(e){}} var monName=monthNames[monNum]||""; // Check if today var today=new Date(); var todayStr=String(today.getDate()).padStart(2,"0")+"."+String(today.getMonth()+1).padStart(2,"0")+"."+today.getFullYear(); var isToday=dt===todayStr; // Diary page design html+='
'; // Date header - diary style html+='
'; html+='
' +'
'+dayNum+'
' +'
'+monName.substring(0,3)+'
' +'
'; html+='
' +'
'+dayName+(isToday?' TODAY':'')+'
' +'
'+dt+' · '+group.length+' '+(group.length>1?'entries':'entry')+'
' +'
'; // Lined paper background for entries html+='
'; // Table header html+=''; html+='' +'' +'' +'' +'' +'' +''; group.forEach(function(e,ei){ var ic=typeIcons[e.type]||"📝"; var tbg=typeBg[e.type]||"#F1F5F9"; var tcol=typeCol[e.type]||"#374151"; html+='' +'' +'' +'' +'' +'' +''; }); html+='
#SubjectHomework / TaskTypeTeacher
'+(ei+1)+'' +''+(e.subject||"—")+'' +''+e.desc+'' +''+ic+' '+e.type+'' +'' +'
'+e.teacherName+'
' +'
'; }); el.innerHTML=html; } // ── FEE REGISTER PRINT ── function printFeeRegister(){ var clsF=(document.getElementById("fee-cls-filter")||{}).value||""; var now8=new Date();var sessStart8=now8.getMonth()>=3?now8.getFullYear():now8.getFullYear()-1; if(ACTIVE_SESSION_START)sessStart8=ACTIVE_SESSION_START; var realMM8={April:3,May:4,June:5,July:6,August:7,September:8,October:9,November:10,December:11,January:0,February:1,March:2}; var SESSION8=["April","May","June","July","August","September","October","November","December","January","February","March"]; var list8=students.filter(function(s){ var clsBase=s.class?s.class.split("-")[0]:""; if(s.admissionClosed) return false; if(clsF&&clsBase!==clsF) return false; return true; }).sort(function(a,b){if(a.class!==b.class)return(a.class||"").localeCompare(b.class||"");return((a.roll||"").localeCompare(b.roll||""));}); var html='
'; html+='
SARA AZAM MEMORIAL EDUCATION
'; html+='
CBSE · Session '+CURRENT_SESSION+'
'; html+='
FEE REGISTER'+(clsF?' — CLASS '+clsF:'')+'
'; html+=''; html+=''; list8.forEach(function(s,i){ var base8=s.class?s.class.split("-")[0]:""; var sf8=CLASS_FEES[base8]||{tuition:0,bus:0}; var mFee8=(parseInt(sf8.tuition)||0)+(s.bus==="Yes"?(parseInt(s.busFare)||parseInt(sf8.bus)||0):0); var feeData8=[];try{feeData8=calculateMonthlyFees(s);}catch(e){} var elapsed8=0,covered8=0; feeData8.forEach(function(r){ if(r.type!=="regular")return;var rm8=realMM8[r.month];if(rm8===undefined)return; var yr8=rm8>=3?sessStart8:sessStart8+1; if(yr8>now8.getFullYear()||(yr8===now8.getFullYear()&&rm8>now8.getMonth()))return; elapsed8++;if(r.paidAmt>0||r.cfCovered||r.hasCF||r.netPayable<=0)covered8++; }); var dues8=Math.max(0,elapsed8-covered8)*mFee8; html+=''; }); html+='
#NameClassRollCategoryMonthlyPaidDues
'+(i+1)+''+s.name+''+(s.class||"—")+''+(s.roll||"—")+''+(s.category||"—")+'₹'+mFee8.toLocaleString("en-IN")+''+covered8+'/'+elapsed8+''+(dues8>0?"₹"+dues8.toLocaleString("en-IN"):"✓")+'
'; html+='
'+new Date().toLocaleDateString("en-IN",{day:"2-digit",month:"short",year:"numeric"})+'
Accountant
Principal
'; printHTMLPage(html,"Fee Register","@page{margin:8mm;size:A4 landscape;}",true); } // ── FEE LEDGER REGISTER ── function switchFeeView(view){ var regEl=document.getElementById("fee-register-view"); var ledEl=document.getElementById("fee-ledger-view"); var regBtn=document.getElementById("fee-view-register"); var ledBtn=document.getElementById("fee-view-ledger"); var printRegBtn=document.getElementById("fee-register-print-btn"); var printBtn=document.getElementById("fee-ledger-print-btn"); if(view==="register"){ if(regEl)regEl.style.display="block"; if(ledEl)ledEl.style.display="none"; if(regBtn){regBtn.style.background="linear-gradient(135deg,#0B1F3A,#1E3A5F)";regBtn.style.color="#fff";regBtn.style.border="none";} if(ledBtn){ledBtn.style.background="#F1F5F9";ledBtn.style.color="#374151";ledBtn.style.border="1px solid #DDE3EC";} if(printBtn)printBtn.style.display="none"; if(printRegBtn)printRegBtn.style.display="inline-block"; } else { if(regEl)regEl.style.display="none"; if(ledEl)ledEl.style.display="block"; if(ledBtn){ledBtn.style.background="linear-gradient(135deg,#0B1F3A,#1E3A5F)";ledBtn.style.color="#fff";ledBtn.style.border="none";} if(regBtn){regBtn.style.background="#F1F5F9";regBtn.style.color="#374151";regBtn.style.border="1px solid #DDE3EC";} if(printBtn)printBtn.style.display="inline-block"; if(printRegBtn)printRegBtn.style.display="none"; renderFeeLedgerRegister(); } } function renderFeeLedgerRegister(){ var clsF=(document.getElementById("fee-cls-filter")||{}).value||""; var searchF=((document.getElementById("fee-search")||{}).value||"").toLowerCase(); var curSY=ACTIVE_SESSION_START||SESSION_START_YEAR; var months=["Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","Jan","Feb","Mar"]; // Filter students var list=students.filter(function(s){ var clsBase=s.class?s.class.split("-")[0]:""; if(s.admissionClosed) return false; if(s.admDate){ var ap=s.admDate.split("."); if(ap.length>=3){var ay=parseInt(ap[2]),am=parseInt(ap[1])-1;var sSY=am>=3?ay:ay-1;if(sSY!==curSY)return false;} } if(clsF&&clsBase!==clsF) return false; if(searchF){ var hay=(s.name+"|"+(s.roll||"")+"|"+(s.phone||"")).toLowerCase(); if(hay.indexOf(searchF)<0) return false; } return true; }).sort(function(a,b){ var ra=a.roll||"",rb=b.roll||""; var na=parseInt((ra.split("-").pop())||"0")||0,nb=parseInt((rb.split("-").pop())||"0")||0; if(a.class!==b.class) return (a.class||"").localeCompare(b.class||""); return na-nb; }); // Get fee config var feeConfig={}; try{var fc=localStorage.getItem("saraAzam_feeConfig_v1");if(fc)feeConfig=JSON.parse(fc);}catch(e){} // Group by class var classGroups={}; list.forEach(function(s){ var cls=s.class||"Unknown"; if(!classGroups[cls]) classGroups[cls]=[]; classGroups[cls].push(s); }); var el=document.getElementById("fee-ledger-content"); if(!el) return; if(!list.length){ el.innerHTML='
📒
No students found
'; return; } var html=''; var classKeys=Object.keys(classGroups).sort(function(a,b){ var ia=BASE_CLASSES.indexOf(a.split("-")[0]),ib=BASE_CLASSES.indexOf(b.split("-")[0]); if(ia!==ib)return ia-ib; return a.localeCompare(b); }); classKeys.forEach(function(cls){ var clsStudents=classGroups[cls]; var clsColors={"Play":"#6B21A8","Nursery":"#1D4ED8","L.K.G":"#059669","U.K.G":"#D97706","I":"#B91C1C","II":"#0F766E","III":"#9A3412","IV":"#7C3AED","V":"#0369A1","VI":"#DC2626","VII":"#065F46","VIII":"#B45309","IX":"#1E3A5F","X":"#6B21A8"}; var clsBase=cls.split("-")[0]; var headerCol=clsColors[clsBase]||"#1E3A5F"; html+='
'; html+='
'; html+='📚 '+cls+''; html+=''+clsStudents.length+' Students'; html+='
'; html+='
'; html+=''; html+=''; html+=''; html+=''; html+=''; months.forEach(function(m){ html+=''; }); html+=''; html+=''; html+=''; clsStudents.forEach(function(s,si){ var bg=si%2?"#FAFAFA":"#fff"; var totalPaid=0,monthsCovered=0,elapsedCount=0; var clsBase2=cls.split("-")[0]; var sf3=CLASS_FEES[clsBase2]||{tuition:0,bus:0}; var monthlyFee=(parseInt(sf3.tuition)||0)+(s.bus==="Yes"?(parseInt(s.busFare)||parseInt(sf3.bus)||0):0); // Use shared calculateMonthlyFees for consistent data var feeData7=[]; try{feeData7=calculateMonthlyFees(s);}catch(e){} var mFullNames=["April","May","June","July","August","September","October","November","December","January","February","March"]; var realMMLedger={April:3,May:4,June:5,July:6,August:7,September:8,October:9,November:10,December:11,January:0,February:1,March:2}; var nowL=new Date(); html+=''; html+=''; html+=''; html+=''; months.forEach(function(m,mi){ var fullMn=mFullNames[mi]; // Find this month in feeData var r=null; for(var fi=0;fi=3?curSY:curSY+1; var isElapsed=(yrL0){ totalPaid+=r.paidAmt; monthsCovered++; html+=''; } else if(r.cfCovered || r.hasCF || r.netPayable<=0){ monthsCovered++; html+=''; } else if(!isElapsed){ // Future month - not yet due html+=''; } else { html+=''; } }); // Dues = only elapsed unpaid months var elapsedCovered=0; feeData7.forEach(function(r){ if(r.type!=="regular") return; var rmL2=realMMLedger[r.month];if(rmL2===undefined) return; var yrL2=rmL2>=3?curSY:curSY+1; if(yrL2>nowL.getFullYear()||(yrL2===nowL.getFullYear()&&rmL2>nowL.getMonth())) return; if(r.paidAmt>0||r.cfCovered||r.hasCF||r.netPayable<=0) elapsedCovered++; }); var totalMonthlyDue=Math.max(0,(elapsedCount-elapsedCovered)*monthlyFee); html+=''; html+=''; html+=''; }); html+='
#Student NameRoll No'+m+'Total PaidDue
'+(si+1)+''+s.name+''+(s.roll||"—")+'' +'
₹'+r.paidAmt.toLocaleString("en-IN")+'
' +'
✅ Paid
' +'
' +'
CF
' +'
' +'
' +'
Due
₹'+totalPaid.toLocaleString("en-IN")+''+(totalMonthlyDue>0?"₹"+totalMonthlyDue.toLocaleString("en-IN"):"✅ Clear")+'
'; }); el.innerHTML=html; } function printFeeLedgerRegister(){ var clsF=(document.getElementById("fee-cls-filter")||{}).value||""; var curSY=ACTIVE_SESSION_START||SESSION_START_YEAR; var months=["Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","Jan","Feb","Mar"]; var feeConfig={}; try{var fc=localStorage.getItem("saraAzam_feeConfig_v1");if(fc)feeConfig=JSON.parse(fc);}catch(e){} var list=students.filter(function(s){ var clsBase=s.class?s.class.split("-")[0]:""; if(s.admissionClosed) return false; if(s.admDate){var ap=s.admDate.split(".");if(ap.length>=3){var ay=parseInt(ap[2]),am=parseInt(ap[1])-1;var sSY=am>=3?ay:ay-1;if(sSY!==curSY)return false;}} if(clsF&&clsBase!==clsF) return false; return true; }).sort(function(a,b){if(a.class!==b.class)return(a.class||"").localeCompare(b.class||"");var na=parseInt(((a.roll||"").split("-").pop())||"0")||0,nb=parseInt(((b.roll||"").split("-").pop())||"0")||0;return na-nb;}); var classGroups={}; list.forEach(function(s){var cls=s.class||"Unknown";if(!classGroups[cls])classGroups[cls]=[];classGroups[cls].push(s);}); var classKeys=Object.keys(classGroups).sort(function(a,b){var ia=BASE_CLASSES.indexOf(a.split("-")[0]),ib=BASE_CLASSES.indexOf(b.split("-")[0]);if(ia!==ib)return ia-ib;return a.localeCompare(b);}); var html='
'; html+='
'; html+='
SARA AZAM MEMORIAL EDUCATION
'; html+='
CBSE · Session '+CURRENT_SESSION+'
'; html+='
FEE LEDGER REGISTER'+(clsF?' — CLASS '+clsF.toUpperCase():'')+'
'; classKeys.forEach(function(cls){ var clsStudents=classGroups[cls]; html+='
'+cls+' ('+clsStudents.length+' students)
'; html+=''; html+=''; months.forEach(function(m){html+='';}); html+=''; clsStudents.forEach(function(s,si){ var payments=s.feePayments||{};var totalP=0,mPaid=0; var clsB3=cls.split("-")[0];var sf4=CLASS_FEES[clsB3]||{tuition:0,bus:0}; var mFee=(parseInt(sf4.tuition)||0)+(s.bus==="Yes"?(parseInt(s.busFare)||parseInt(sf4.bus)||0):0); var mFullN=["April","May","June","July","August","September","October","November","December","January","February","March"]; var mRMap={April:3,May:4,June:5,July:6,August:7,September:8,October:9,November:10,December:11,January:0,February:1,March:2}; html+=''; html+=''; html+=''; months.forEach(function(m,mi2){ var fn2=mFullN[mi2];var rm7=mRMap[fn2];var yr7=rm7>=3?curSY:curSY+1; var mk7=yr7+"-"+String(rm7+1).padStart(2,"0"); var pmts=payments[mk7]||payments[fn2]||payments[m]; var paid=pmts&&Array.isArray(pmts)&&pmts.length>0;var mAmt=0; if(paid){pmts.forEach(function(p){mAmt+=(p.amt||0);});if(mAmt>0){totalP+=mAmt;mPaid++;}} html+=''; }); var due2=Math.max(0,(12-mPaid)*mFee); html+=''; html+=''; }); html+='
#NameRoll'+m+'TotalDue
'+(si+1)+''+s.name+''+(s.roll||"—")+''+(paid&&mAmt>0?"₹"+mAmt:"—")+'₹'+totalP.toLocaleString("en-IN")+''+(due2>0?"₹"+due2.toLocaleString("en-IN"):"✅")+'
'; }); html+='
'; html+='
'+new Date().toLocaleDateString("en-IN",{day:"2-digit",month:"short",year:"numeric"})+'
'; html+='
Accountant
Principal
'; printHTMLPage(html,"Fee Ledger Register","@page{margin:6mm;size:A4 landscape;}",true); } // ════════════════════════════════════════ // NOTICE BOARD // ════════════════════════════════════════ var NOTICE_STORAGE_KEY="saraAzam_notices_v1"; function loadNotices(){try{var s=localStorage.getItem(NOTICE_STORAGE_KEY);return s?JSON.parse(s):[];}catch(e){return [];}} function saveNotices(arr){localStorage.setItem(NOTICE_STORAGE_KEY,JSON.stringify(arr));} function openAddNotice(editId){ var notices=loadNotices(); var n=editId?notices.find(function(e){return e.id===editId;}):null; var isEdit=!!n; var existing=document.getElementById("notice-modal");if(existing)existing.remove(); var d=document.createElement("div");d.id="notice-modal"; d.style.cssText="position:fixed;inset:0;background:rgba(10,20,40,.7);z-index:99990;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);padding:12px;"; var today=new Date();var todayStr=String(today.getDate()).padStart(2,"0")+"."+String(today.getMonth()+1).padStart(2,"0")+"."+today.getFullYear(); d.innerHTML=''; document.body.appendChild(d); d.onclick=function(e){if(e.target===d)d.remove();}; } function selectNoticeTheme(theme){ document.getElementById("notice-theme").value=theme; var grid=document.getElementById("notice-theme-grid"); if(grid){ var cols={marquee:"#0B1F3A",gradient:"#7C3AED",neon:"#06B6D4",ribbon:"#DC2626",glass:"#059669",alert:"#D97706"}; grid.querySelectorAll("[data-theme]").forEach(function(d){ var t=d.getAttribute("data-theme"); d.style.borderColor=t===theme?cols[t]:"#E2E8F0"; d.style.background=t===theme?"#EFF6FF":"#fff"; }); } } function saveNotice(editId){ var title=(document.getElementById("notice-title")||{}).value||""; var msg=(document.getElementById("notice-msg")||{}).value||""; if(!title.trim()||!msg.trim()){showToast("Title aur message zaroori hai!","error");return;} var data={title:title.trim(),msg:msg.trim(),date:(document.getElementById("notice-date")||{}).value||"",priority:(document.getElementById("notice-priority")||{}).value||"Normal",forWho:(document.getElementById("notice-for")||{}).value||"All",theme:(document.getElementById("notice-theme")||{}).value||"marquee"}; var notices=loadNotices(); if(editId){var idx=notices.findIndex(function(e){return e.id===editId;});if(idx>=0){data.id=editId;notices[idx]=data;}} else{data.id=Date.now();notices.unshift(data);} saveNotices(notices); var modal=document.getElementById("notice-modal");if(modal)modal.remove(); renderNoticeBoard(); showToast("📢 Notice published!","success"); } function deleteNotice(id){ if(!confirm("Notice delete karna hai?"))return; var notices=loadNotices().filter(function(e){return e.id!==id;}); saveNotices(notices);renderNoticeBoard();showToast("Notice deleted","error"); } function renderNoticeBoard(){ var notices=loadNotices(); var el=document.getElementById("notice-list");if(!el)return; var sub=document.getElementById("notice-sub"); if(sub) sub.textContent=notices.length+" notices"; if(!notices.length){ el.innerHTML='
📢
No Notices Yet
Click "+ New Notice" to publish
'; return; } var priCol={Normal:"#059669",Important:"#D97706",Urgent:"#DC2626"}; var priBg={Normal:"#F0FDF4",Important:"#FFFBEB",Urgent:"#FEF2F2"}; var priBdr={Normal:"#86EFAC",Important:"#FDE68A",Urgent:"#FECACA"}; var priIcon={Normal:"🟢",Important:"🟡",Urgent:"🔴"}; el.innerHTML=notices.map(function(n){ var col=priCol[n.priority]||"#374151";var bg=priBg[n.priority]||"#F8FAFC";var bdr=priBdr[n.priority]||"#E2E8F0";var ic=priIcon[n.priority]||"📢"; return '
' +'
' +'
' +''+ic+'' +'
'+n.title+'
' +'
'+n.date+' · For: '+n.forWho+' · '+n.priority+'
' +'
' +'
' +'' +'' +'
' +'
' +'
'+n.msg+'
' +'
'; }).join(""); } // Flash notices — ADVANCED THEMED function renderNoticeFlash(containerId, forFilter){ var el=document.getElementById(containerId);if(!el)return; var notices=loadNotices().filter(function(n){return n.forWho==="All"||n.forWho===forFilter;}); var now=new Date(); notices=notices.filter(function(n){ if(n.priority==="Urgent") return true; if(!n.date) return true; var dp=n.date.split(".");if(dp.length<3) return true; var nd=new Date(parseInt(dp[2]),parseInt(dp[1])-1,parseInt(dp[0])); return (now-nd)<7*24*60*60*1000; }); if(!notices.length){el.innerHTML="";return;} var html='
'; notices.forEach(function(n,ni){ var theme=n.theme||"marquee"; var isUrgent=n.priority==="Urgent"; var isImportant=n.priority==="Important"; var priIcon=isUrgent?"\u{1F534}":isImportant?"\u{1F7E1}":"\u{1F7E2}"; if(theme==="marquee"){ html+='
' +'
'+priIcon+'
' +'
' +'\u{1F4E2} '+n.title+' \u2014' +''+n.msg+'' +'\u{1F4E2} '+n.title+' \u2014' +''+n.msg+'' +'
'; } else if(theme==="gradient"){ var grads=["linear-gradient(135deg,#667EEA,#764BA2)","linear-gradient(135deg,#F093FB,#F5576C)","linear-gradient(135deg,#4FACFE,#00F2FE)","linear-gradient(135deg,#43E97B,#38F9D7)","linear-gradient(135deg,#FA709A,#FEE140)"]; html+='
' +'
' +'
' +'
\u{1F4E2}
' +'
'+n.title+'
' +'
'+n.msg+'
' +'
'+n.date+'
'; } else if(theme==="neon"){ var neonCol=isUrgent?"#EF4444":isImportant?"#FBBF24":"#22D3EE"; html+='
' +'
' +'
'+priIcon+'
' +'
'+n.title.toUpperCase()+'
' +'
'+n.msg+'
' +'
'+n.date+'
'; } else if(theme==="ribbon"){ var ribCol=isUrgent?"#DC2626":isImportant?"#D97706":"#059669"; var ribBg=isUrgent?"linear-gradient(135deg,#FEF2F2,#FEE2E2)":isImportant?"linear-gradient(135deg,#FFFBEB,#FEF3C7)":"linear-gradient(135deg,#F0FDF4,#DCFCE7)"; html+='
' +'
' +'
'+n.priority.toUpperCase()+'
' +'
' +'
\u{1F4E2}
'+n.title+'
' +'
'+n.msg+'
' +'
'+n.date+'
'; } else if(theme==="glass"){ html+='
' +'
' +'
\u{1F4E2}
' +'
'+n.title+' '+n.date+'
' +'
'+n.msg+'
' +''+priIcon+'
'; } else if(theme==="alert"){ var alCol=isUrgent?"#DC2626":isImportant?"#D97706":"#1D4ED8"; var alBg=isUrgent?"#FEF2F2":isImportant?"#FFFBEB":"#EFF6FF"; var alBdr=isUrgent?"#FECACA":isImportant?"#FDE68A":"#BFDBFE"; var alIcon=isUrgent?"\u26A0\uFE0F":isImportant?"\u26A1":"ℹ\uFE0F"; html+='
' +'
' +'
'+alIcon+'
' +'
'+n.title+'
' +'
'+n.msg+'
' +'
\u{1F4C5} '+n.date+' \u00B7 '+n.priority+'
'; } }); html+='
'; el.innerHTML=html; } // ── PINCODE LOOKUP ── function lookupPincode(pin) { var statusEl = g("pin-status"); var distEl = g("s-dist"); var stateEl = g("s-state"); if(!pin || pin.length < 6) { if(statusEl) statusEl.textContent = ""; return; } if(pin.length !== 6) return; if(statusEl){ statusEl.textContent = "⏳ Searching..."; statusEl.style.color="#B45309"; } fetch("https://api.postalpincode.in/pincode/" + pin) .then(function(r){ return r.json(); }) .then(function(data) { if(data && data[0] && data[0].Status === "Success" && data[0].PostOffice && data[0].PostOffice.length > 0) { var po = data[0].PostOffice[0]; var district = po.District || ""; var state = po.State || ""; if(distEl) distEl.value = district; if(stateEl) stateEl.value = state; if(statusEl){ statusEl.textContent = "✓ Found"; statusEl.style.color="#15803D"; } } else { if(distEl) distEl.value = ""; if(stateEl) stateEl.value = ""; if(statusEl){ statusEl.textContent = "✗ Invalid PIN"; statusEl.style.color="#B91C1C"; } } }) .catch(function() { if(statusEl){ statusEl.textContent = "✗ No internet"; statusEl.style.color="#B91C1C"; } }); } // ── PRINT REPORT CARD ── function printRC() { var rcEl = g("rc-content"); if(!rcEl) return; var printWin = window.open("", "_blank", "width=820,height=950"); var html = '' + 'Report Card - Sara Azam Memorial Education' + '' + rcEl.innerHTML + ''; printWin.document.write(html); printWin.document.close(); printWin.focus(); setTimeout(function(){ printWin.print(); }, 800); } // ── ADMISSION RECEIPT ── function showAdmissionReceipt(s) { if(!s) return; var today = new Date().toLocaleDateString("en-IN", {day:"numeric", month:"long", year:"numeric"}); var time = new Date().toLocaleTimeString("en-IN", {hour:"2-digit", minute:"2-digit"}); var base = s.class ? s.class.split("-")[0] : ""; var sec = s.class ? (s.class.split("-")[1]||"A") : "A"; var noPrefix = ["Play","Nursery","L.K.G","U.K.G"]; var clsLabel = (noPrefix.indexOf(base)>=0?"":("Class ")) + base + " - Section " + sec; var f = CLASS_FEES[base] || {admission:0, tuition:0, annual:0, exam:0, bus:0}; var busFee2 = (s.bus==="Yes")?(parseInt(s.busFare)||f.bus||0):0; var gross = (parseInt(f.admission)||0) + (parseInt(f.tuition)||0) + (parseInt(f.annual)||0) + (parseInt(f.exam)||0) + busFee2; var discount2 = parseInt(s.discount)||0; var net = Math.max(0, gross - discount2); var admPaidR = parseInt(s.admFeePaid)||0; var admBalR = net - admPaidR; // positive=due, negative=credit var receiptNo = "SAME/ADM/" + new Date().getFullYear() + "/" + String(s.id).slice(-4).padStart(4,"0"); var addr = [s.addr1, s.city, s.dist, s.state, s.pin ? "PIN: "+s.pin : ""].filter(Boolean).join(", ") || "—"; var html = ""; // Header html += '
'; html += '
'; html += '
Sara Azam Memorial Education
'; html += '
CBSE Affiliated · '+SESSION_SHORT+'
'; html += '
ADMISSION RECEIPT
'; html += '
'; // Receipt meta html += '
'; html += '
Receipt No: ' + receiptNo + '
'; html += '
Date: ' + today + ' · ' + time + '
'; html += '
'; // Student info box html += '
'; html += '
👨‍🎓 Student Details
'; html += '
'; var fields = [ ["Student Name", s.name], ["Roll Number", s.roll], ["Class", clsLabel], ["Category", s.category], ["Date of Birth",s.dob||"—"], ["Gender", s.gender||"—"], ["Father's Name",s.father||"—"], ["Mother's Name",s.mother||"—"], ["Phone", s.phone||"—"], ["Bus Service", s.bus==="Yes"?"✓ Yes (School Bus)":"✗ No"], ["Address", addr], ]; fields.forEach(function(f) { html += '
'; html += '
' + f[0] + '
'; html += '
' + f[1] + '
'; html += '
'; }); html += '
'; html += '
'; // Fee breakdown html += '
'; html += '
💳 Fee Details
'; var feeRows = [ ["Admission Fee", inr(f.admission)], ["Monthly Tuition Fee", inr(f.tuition)], ["Annual Charges", inr(f.annual)], ["Examination Fee", inr(f.exam)], ["Gross Total", inr(gross)], ]; feeRows.forEach(function(row, i) { var isGross = row[0]==="Gross Total"; html += '
'; html += '' + row[0] + ''; html += '' + row[1] + ''; html += '
'; }); if(discount2 > 0) { html += '
'; html += 'Discount'; html += '- ' + inr(discount2) + ''; html += '
'; } // Net payable - big highlight html += '
'; html += 'Net Payable'; html += '' + inr(net) + ''; html += '
'; html += '
'; // Payment details if(admPaidR > 0) { html += '
'; html += '
'; html += '
✓ Payment Received
'; html += '
'+inr(admPaidR)+'
'; } // Balance / Credit var balBgR=admBalR>0?"#FEF2F2":admBalR<0?"#EFF6FF":"#F0FDF4"; var balBdrR=admBalR>0?"#FECACA":admBalR<0?"#93C5FD":"#BBF7D0"; var balColR=admBalR>0?"#B91C1C":admBalR<0?"#1D4ED8":"#15803D"; html += '
'; html += '
'+(admBalR>0?"Balance Due":admBalR<0?"Credit (CR)":"✓ Fully Paid")+'
'; html += '
'+(admBalR>0?inr(admBalR):admBalR<0?inr(Math.abs(admBalR))+" CR":"✓ Clear")+'
'; html += '
'; if(admBalR<0){ html += '
💡 '+inr(Math.abs(admBalR))+' credit will be adjusted in monthly fee
'; } // Signatures html += '
'; ["Parent / Guardian","Cashier","Principal"].forEach(function(sig) { html += '
'; html += '
'; html += '
' + sig + '
'; html += '
Sara Azam Memorial
'; html += '
'; }); html += '
'; html += '
'; html += '⚠️ This is a computer-generated receipt. Please retain for future reference.'; html += '
'; g("receipt-content").innerHTML = html; currentReceiptStudent = s; var overlay = g("receipt-overlay"); overlay.style.display = "flex"; } // ── Helper: Calculate session from student's admDate ── function getStudentSession(s) { var admDate = s.admDate || ""; if(!admDate) { // Fallback: use current session return { short: SESSION_SHORT, full: CURRENT_SESSION, year: SESSION_START_YEAR }; } var parts = admDate.split("."); if(parts.length < 3) return { short: SESSION_SHORT, full: CURRENT_SESSION, year: SESSION_START_YEAR }; var day = parseInt(parts[0]); var mon = parseInt(parts[1]); // 1-indexed var yr = parseInt(parts[2]); // Session: April (4) to March (3) // If month >= 4 → session starts that year, else prev year var sessionStart = mon >= 4 ? yr : yr - 1; var sessionEnd = sessionStart + 1; var short = sessionStart + "-" + String(sessionEnd).slice(-2); var full = sessionStart + "-" + sessionEnd + " (Apr–Mar)"; return { short: short, full: full, year: sessionStart }; } function printAdmissionForm() { var s=students.find(function(x){return x.id===(currentDetailId||currentStudentId);}); if(!s && currentReceiptStudent) s=currentReceiptStudent; if(!s) return; var today=new Date().toLocaleDateString("en-IN",{day:"2-digit",month:"2-digit",year:"numeric"}); var admDate=s.admDate||today; var clsParts=s.class?s.class.split("-"):["","A"]; var clsBase=clsParts[0]; var clsSec=clsParts[1]||"A"; var noPrefix=["Play","Nursery","L.K.G","U.K.G"]; var clsLabel=(noPrefix.indexOf(clsBase)>=0?clsBase:"Class "+clsBase)+" — Section "+clsSec; var busTxt=s.bus==="Yes"?"Yes — Rs."+(s.busFare||"450")+"/mo":"No"; var photoBlock=s.photo ?'' :'
' +'
📷
' +'
Student
Photo
' +'
'; var stSess = getStudentSession(s); var schoolName = localStorage.getItem("saraAzam_schoolName")||"Sara Azam Memorial Education"; var W='' +''; // ══ HEADER ══ W+='
'; W+='
'; W+='
'; // Logo + school name W+='
'; W+='
🏫
'; W+='
'; W+='
'+schoolName+'
'; W+='
CBSE Affiliated  ·  Session '+stSess.short+'  ·  Est. 2005
'; W+='
'; // Right: form badge W+='
'; W+='
ADMISSION FORM
'; W+='
Adm. No: '+s.roll+'
'; W+='
Date: '+admDate+'
'; W+='
'; // ══ COLOURED SECTION BAND ══ W+='
'; // ══ MAIN CONTENT ══ W+='
'; // ── SECTION 1: STUDENT DETAILS ── W+='
'; W+='
'; // Details grid W+='
'; W+='
👤 Student Details
'; W+='
'; [ ["Student Name", s.name, "#1D4ED8"], ["Roll / Adm. Number", s.roll, "#1D4ED8"], ["Date of Birth", s.dob||"—", "#374151"], ["Gender", s.gender||"—", "#374151"], ["Category", s.category||"—", "#374151"], ["Class & Section", clsLabel, "#1D4ED8"], ["Bus Service", busTxt, "#374151"], ["Father's Name", s.father||"—", "#374151"], ].forEach(function(f){ W+='
'+f[0]+'
'+f[1]+'
'; }); W+='
'; // Photo W+='
'; W+=photoBlock; W+='
Student Photo
'; W+='
'; // ── SECTION 2: FAMILY & ADDRESS ── W+='
'; W+='
🏠 Family & Address
'; W+='
'; [ ["Mother's Name", s.mother||"—", "#15803D"], ["Parent Phone", s.phone||"—", "#374151"], ["Alternate Phone",s.phone2||s.altPhone||"—","#374151"], ["House / Street", s.addr1||s.address||"—","#374151"], ["Village / City", s.village||s.city||"—", "#374151"], ["District", s.district||"—", "#374151"], ["State", s.state||"—", "#374151"], ["PIN Code", s.pin||"—", "#374151"], ["Religion", s.religion||"—", "#374151"], ].forEach(function(f){ W+='
'+f[0]+'
'+f[1]+'
'; }); W+='
'; // ── SECTION 3: DECLARATION ── W+='
'; W+='
📜 Declaration / घोषणापत्र
'; W+='
मैं घोषणा करता/करती हूँ कि ऊपर दी गई समस्त जानकारी सत्य एवं सही है। मैं विद्यालय के नियमों का पालन करने एवं समय पर शुल्क जमा करने का वचन देता/देती हूँ।
I hereby declare that the above information is true and correct. I agree to abide by all school rules and pay fees as prescribed from time to time.
'; W+='
'; // ── SIGNATURES ── W+='
'; [["Parent / Guardian Signature",""],["Student Signature",""],["Principal / Admission Officer","Date: "+admDate]].forEach(function(sg){ W+='
'; W+='
'; W+='
'+sg[0]+'
'; if(sg[1]) W+='
'+sg[1]+'
'; W+='
'; }); W+='
'; // ── OFFICE USE ── W+='
'; W+='
⚙️ For Office Use Only
'; W+='
'; ["Fee Received","Receipt No.","Verified By","Checked By"].forEach(function(f){ W+='
'+f+'
'; }); W+='
'; W+='
'; var win=window.open("","_blank","width=900,height=750"); if(!win){showToast("Popup blocked","error");return;} win.document.open(); win.document.write(W); win.document.close(); setTimeout(function(){try{win.focus();win.print();}catch(e){}},600); } function closeReceipt() { var overlay = g("receipt-overlay"); if(overlay) overlay.style.display = "none"; } function printReceipt() { var rc = g("receipt-content"); if(!rc) return; var h = '' + '
' + rc.innerHTML + '
' + ''; var win = window.open('','_blank','width=600,height=800'); win.document.write(h); win.document.close(); win.focus(); setTimeout(function(){ win.print(); }, 400); } // ── DASHBOARD SEARCH ── function dashSearch(query) { var q = query.toLowerCase().trim(); // Sync topbar search var topSearch = document.getElementById("topbar-search"); if(topSearch && topSearch.value !== query) topSearch.value = query; var resultsEl = document.getElementById("dash-results"); if(!resultsEl) return; if(!q) { resultsEl.style.display="none"; return; } // Score-based search — exact match first, then starts-with, then contains var scored = []; students.forEach(function(s) { var name = s.name.toLowerCase(); var roll = (s.roll||"").toLowerCase(); var cls = (s.class||"").toLowerCase(); var father= (s.father||"").toLowerCase(); var score = 0; if(name === q) score = 100; // exact name else if(name.startsWith(q)) score = 80; // name starts with else if(name.includes(q)) score = 60; // name contains else if(roll.startsWith(q)) score = 70; // roll starts with else if(roll.includes(q)) score = 50; // roll contains else if(cls.includes(q)) score = 30; // class else if(father.includes(q)) score = 20; // father name if(score > 0) scored.push({s:s, score:score}); }); // Sort by score descending scored.sort(function(a,b){ return b.score-a.score; }); var matches = scored.slice(0,8).map(function(x){return x.s;}); if(!matches.length) { resultsEl.innerHTML = '
No students found
'; resultsEl.style.display = "block"; return; } var noPrefix = ["Play","Nursery","L.K.G","U.K.G"]; resultsEl.innerHTML = matches.map(function(s, i) { var a = avt(s.name, students.indexOf(s)); var base = s.class ? s.class.split("-")[0] : ""; var sec = s.class ? (s.class.split("-")[1]||"A") : "A"; var clsLabel = (noPrefix.indexOf(base)>=0?"":("Class "))+base+" Sec "+sec; return '
' + '
'+a.init+'
' + '
' + '
'+s.name+'
' + '
'+clsLabel+'  ·  '+s.roll+'
' + '
' + '
View ›
' + '
'; }).join("") + (matches.length===8 ? '
Showing top 8 results — refine your search
' : ''); resultsEl.style.display = "block"; } function dashSelectStudent(id) { var resultsEl = document.getElementById("dash-results"); if(resultsEl) resultsEl.style.display = "none"; var ts=document.getElementById("topbar-search"); if(ts) ts.value=""; openStudentDetail(id); } // Close search on outside click document.addEventListener("click", function(e) { if(!e.target.closest || !e.target.closest("#dash-search") && !e.target.closest("#dash-results")) { var r = document.getElementById("dash-results"); if(r) r.style.display = "none"; } }); // ── MONTHLY FEE RECEIPT ── var MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"]; var SESSION_MONTHS = ["April","May","June","July","August","September","October","November","December","January","February","March"]; function closeMonthPicker() { var el = document.getElementById("month-picker-overlay"); if(el) el.remove(); // Also try querySelectorAll as fallback document.querySelectorAll(".month-picker-overlay").forEach(function(p){ p.remove(); }); } function generateMonthlyReceipt(id) { var s = students.find(function(x){ return x.id===id; }); if(!s) return; var monthEl = document.getElementById("mr-month"); var yearEl = document.getElementById("mr-year"); var month = monthEl ? MONTHS[parseInt(monthEl.value)] : MONTHS[new Date().getMonth()]; var year = yearEl ? parseInt(yearEl.value) : new Date().getFullYear(); // Close picker closeMonthPicker(); var base = s.class ? s.class.split("-")[0] : ""; var f = CLASS_FEES[base] || {admission:0,tuition:0,annual:0,exam:0,bus:0}; var noPrefix = ["Play","Nursery","L.K.G","U.K.G"]; var clsLabel = (noPrefix.indexOf(base)>=0?"":("Class "))+base+ (s.class&&s.class.split("-")[1]?" - Sec "+s.class.split("-")[1]:""); var receiptNo = "SAME/MR/"+year+"/"+month.substring(0,3).toUpperCase()+"/"+String(s.id).slice(-4).padStart(4,"0"); var today = new Date().toLocaleDateString("en-IN",{day:"numeric",month:"long",year:"numeric"}); var tuition = f.tuition; var busFee = s.bus==="Yes" ? (f.bus||0) : 0; var total = tuition + busFee; var addr = [s.city, s.dist, s.state].filter(Boolean).join(", ") || "—"; // Build receipt HTML var html = ""; html += '
'; // Header html += '
'; html += '
'; html += '
Sara Azam Memorial Education
'; html += '
CBSE Affiliated · '+SESSION_SHORT+'
'; html += '
MONTHLY FEE RECEIPT
'; html += '
'; // Meta html += '
'; html += '
Receipt No: '+receiptNo+'
'; html += '
Date: '+today+'
'; html += '
'; // Student info html += '
'; html += '
👨‍🎓 Student Details
'; html += '
'; var sFields = [ ["Name", s.name], ["Class", clsLabel], ["Roll No.", s.roll], ["Father's Name", s.father||"—"], ["Phone", s.phone||"—"], ["Address", addr], ]; sFields.forEach(function(sf) { html += '
'; html += '
'+sf[0]+'
'; html += '
'+sf[1]+'
'; html += '
'; }); html += '
'; // Fee period html += '
'; html += '
Fee Period
'; html += '
'+month+' '+year+'
'; html += '
'; // Fee table html += '
'; html += '
💳 Fee Details
'; var feeItems = [["Monthly Tuition Fee", tuition]]; if(busFee > 0) feeItems.push(["🚌 Bus Fee", busFee]); feeItems.forEach(function(item, i) { html += '
'; html += ''+item[0]+''; html += '₹'+item[1].toLocaleString("en-IN")+''; html += '
'; }); html += '
'; html += 'Total Amount'; html += '₹'+total.toLocaleString("en-IN")+''; html += '
'; // Signatures html += '
'; ["Parent/Guardian","Cashier","Principal"].forEach(function(sig){ html += '
'; html += '
'; html += '
'+sig+'
'; html += '
'; }); html += '
'; html += '
⚠️ Computer generated receipt. Keep for your records.
'; html += '
'; // Print var origBody = document.body.innerHTML; var origTitle = document.title; document.title = "Monthly Fee Receipt - "+s.name+" - "+month+" "+year; document.body.innerHTML = '
'+html+'
'; var st = document.createElement("style"); st.innerHTML = "@media print{body{-webkit-print-color-adjust:exact;print-color-adjust:exact;}@page{margin:6mm;size:A5;}}"; document.head.appendChild(st); window.print(); setTimeout(function(){ document.body.innerHTML = origBody; document.title = origTitle; var _dc2=g("date-chip"); if(_dc2) _dc2.textContent = "📅 " + new Date().toLocaleDateString("en-IN",{day:"numeric",month:"short",year:"numeric"}); updateDash(); renderStudents(); }, 500); } // ── FEE SETTINGS ── // ── SESSION CALCULATION (April - March) ── var _now = new Date(); var _month = _now.getMonth(); // 0=Jan, 3=Apr var _year = _now.getFullYear(); // If current month is April(3) or later -> session starts this year // If Jan(0), Feb(1), Mar(2) -> session started last year var SESSION_START_YEAR = _month >= 3 ? _year : _year - 1; var SESSION_END_YEAR = SESSION_START_YEAR + 1; var CURRENT_SESSION = SESSION_START_YEAR + "–" + String(SESSION_END_YEAR).slice(-2); var SESSION_SHORT_BASE = "Session "; var SESSION_LABEL = "Session " + CURRENT_SESSION + " · Apr " + SESSION_START_YEAR + " to Mar " + SESSION_END_YEAR; var SESSION_SHORT = "Session " + CURRENT_SESSION + " (Apr–Mar)"; var BASE_CLASSES_FEE = ["Play","Nursery","L.K.G","U.K.G","I","II","III","IV","V","VI","VII","VIII","IX","X"]; // Fee Settings: custom fees stored as array // CLASS_FEES[cls].customFees = [{name, amount, showInAdm}] // ── Helper: Get current exam months from config or checkboxes ── function getExamMonths() { // Use EXAM_CONFIG if loaded if(typeof EXAM_CONFIG !== "undefined" && EXAM_CONFIG.months && EXAM_CONFIG.months.length > 0) { return EXAM_CONFIG.months.slice(); } // Fallback: read from checkboxes var em = []; if(document.getElementById("exam-month-sep") && document.getElementById("exam-month-sep").checked) em.push("September"); if(document.getElementById("exam-month-dec") && document.getElementById("exam-month-dec").checked) em.push("December"); if(document.getElementById("exam-month-mar") && document.getElementById("exam-month-mar").checked) em.push("March"); return em.length > 0 ? em : ["March"]; // default fallback } // ── Exam Configuration ── var EXAM_CONFIG = { count: 3, months: ["September","December","March"] }; function loadExamConfig() { try { var saved = localStorage.getItem("saraAzam_examConfig"); if(saved) EXAM_CONFIG = JSON.parse(saved); } catch(e){} renderExamConfig(); } function saveExamConfig() { localStorage.setItem("saraAzam_examConfig", JSON.stringify(EXAM_CONFIG)); } function changeExamCount(delta) { var newCount = Math.max(1, Math.min(6, EXAM_CONFIG.count + delta)); EXAM_CONFIG.count = newCount; // Trim or keep months list while(EXAM_CONFIG.months.length > newCount) EXAM_CONFIG.months.pop(); renderExamConfig(); saveExamConfig(); } function toggleExamMonth(mn) { var idx = EXAM_CONFIG.months.indexOf(mn); if(idx >= 0) { EXAM_CONFIG.months.splice(idx, 1); } else { if(EXAM_CONFIG.months.length >= EXAM_CONFIG.count) { showToast("Pehle count badhao! Max "+EXAM_CONFIG.count+" months", "error"); return; } EXAM_CONFIG.months.push(mn); } renderExamConfig(); saveExamConfig(); // Also sync checkboxes in fee detail panel syncExamMonthCheckboxes(); } function syncExamMonthCheckboxes() { ["September","December","March"].forEach(function(m){ var el = document.getElementById("exam-month-"+m.toLowerCase().substring(0,3)); if(el) el.checked = EXAM_CONFIG.months.indexOf(m) >= 0; }); } function renderExamConfig() { var countEl = document.getElementById("exam-count-display"); if(countEl) countEl.textContent = EXAM_CONFIG.count; var container = document.getElementById("exam-months-config"); if(!container) return; var allMonths = ["April","May","June","July","August","September","October","November","December","January","February","March"]; container.innerHTML = allMonths.map(function(m){ var selected = EXAM_CONFIG.months.indexOf(m) >= 0; return ''; }).join(""); } function renderFeeSettings() { var list = document.getElementById("fee-settings-list"); if(!list) return; var noPrefix = ["Play","Nursery","L.K.G","U.K.G"]; var clsColors = { "Play":"#C53030","Nursery":"#C05621","L.K.G":"#B7791F","U.K.G":"#276749", "I":"#2B6CB0","II":"#6B46C1","III":"#C53030","IV":"#C05621", "V":"#B7791F","VI":"#276749","VII":"#2B6CB0","VIII":"#6B46C1", "IX":"#744210","X":"#1A365D" }; var html = ""; BASE_CLASSES_FEE.forEach(function(cls) { var f = CLASS_FEES[cls] || {admission:0,tuition:0,annual:0,exam:0}; if(!f.customFees) f.customFees = []; var label = (noPrefix.indexOf(cls)>=0 ? "" : "Class ") + cls; var sc = cls.replace(/\./g,"_").replace(/-/g,"_"); var col = clsColors[cls] || "#1A365D"; var stuCount = students.filter(function(s){ return s.class && s.class.split("-")[0]===cls; }).length; html += '
'; // Header html += '
'; html += '
'+label+'
'; html += '
'+stuCount+' students
'; html += '
'; // Default fee rows with checkbox html += ''; var fRows = [ {key:"adm", label:"Admission Fee", val:f.admission, defShow:true}, {key:"tui", label:"Monthly Tuition", val:f.tuition, defShow:true}, {key:"ann", label:"Annual Charges", val:f.annual, defShow:true}, {key:"exam", label:"Exam Fee", val:f.exam, defShow:true}, ]; fRows.forEach(function(r,i) { var bg = i%2===0?"#fff":"#F8FAFC"; var chkId = "fs-chk-"+r.key+"-"+sc; var isChecked = f["show_"+r.key]!==false; // default true html += ''; html += ''; html += ''; html += ''; html += ''; }); // Custom fees (f.customFees||[]).forEach(function(cf,ci) { var cfId = "fs-cf-"+sc+"-"+ci; html += ''; html += ''; html += ''; html += ''; html += ''; }); // Add custom fee button html += ''; html += ''; html += ''; html += '
'+r.label+''; html += '
'; html += ''; html += ''; html += '
'; html += '
'; html += '
'; html += ''; html += ''; html += ''; html += '
'; html += '
'; html += ''; html += '✓ = Admission form me dikhao'; html += '
'; html += '
'; }); list.innerHTML = html; // Attach input listeners for live recalc BASE_CLASSES_FEE.forEach(function(cls) { var sc = cls.replace(/\./g,"_").replace(/-/g,"_"); ["adm","tui","ann","exam"].forEach(function(t) { var el = document.getElementById("fs-"+t+"-"+sc); if(!el) return; el.addEventListener("focus", function(){ this.style.borderColor="#1E3A5F"; }); el.addEventListener("blur", function(){ this.style.borderColor="#DDE3EC"; }); }); }); } function addCustomFee(sc) { // Find class name from sc var cls = sc.replace(/_/g,".").replace(/\.K\.G/g,"K.G"); // Better: find by matching sc var foundCls = BASE_CLASSES_FEE.find(function(c){ return c.replace(/\./g,"_").replace(/-/g,"_")===sc; }); if(!foundCls) return; if(!CLASS_FEES[foundCls]) CLASS_FEES[foundCls]={admission:0,tuition:0,annual:0,exam:0}; if(!CLASS_FEES[foundCls].customFees) CLASS_FEES[foundCls].customFees=[]; CLASS_FEES[foundCls].customFees.push({name:"New Fee",amount:0,showInAdm:true}); renderFeeSettings(); // Scroll to new fee setTimeout(function(){ var el=document.getElementById("fs-cf-"+sc+"-"+(CLASS_FEES[foundCls].customFees.length-1)+"-name"); if(el){el.focus();el.select();} },100); } function removeCF(sc, ci) { var foundCls = BASE_CLASSES_FEE.find(function(c){ return c.replace(/\./g,"_").replace(/-/g,"_")===sc; }); if(!foundCls||!CLASS_FEES[foundCls]||!CLASS_FEES[foundCls].customFees) return; CLASS_FEES[foundCls].customFees.splice(ci,1); renderFeeSettings(); } function feeCalc(cls, sc) { function gv(id){ var e=document.getElementById(id); return e?parseInt(e.value)||0:0; } var adm = gv("fs-adm-"+sc); var tui = gv("fs-tui-"+sc); var ann = gv("fs-ann-"+sc); var exam = gv("fs-exam-"+sc); var total = adm + (tui*12) + ann + exam; var el1 = document.getElementById("fs-total-"+sc); var el2 = document.getElementById("fs-calc-"+sc); if(el1) el1.textContent = "₹"+total.toLocaleString("en-IN"); if(el2) el2.textContent = total.toLocaleString("en-IN"); } function saveFeeSettings() { BASE_CLASSES_FEE.forEach(function(cls) { var sc = cls.replace(/\./g,"_").replace(/-/g,"_"); function gv(id){ var e=document.getElementById(id); return e?parseInt(e.value)||0:0; } function gc(id){ var e=document.getElementById(id); return e?e.checked:true; } // Save default fees + checkbox visibility var prev = CLASS_FEES[cls]||{}; CLASS_FEES[cls] = { admission: gv("fs-adm-"+sc), tuition: gv("fs-tui-"+sc), annual: gv("fs-ann-"+sc), exam: gv("fs-exam-"+sc), show_adm: gc("fs-chk-adm-"+sc), show_tui: gc("fs-chk-tui-"+sc), show_ann: gc("fs-chk-ann-"+sc), show_exam: gc("fs-chk-exam-"+sc), }; // Save custom fees var cfs = []; var ci=0; while(true){ var nameEl=document.getElementById("fs-cf-"+sc+"-"+ci+"-name"); if(!nameEl) break; var amtEl=document.getElementById("fs-cf-"+sc+"-"+ci+"-amt"); var chkEl=document.getElementById("fs-cf-"+sc+"-"+ci+"-chk"); cfs.push({ name: nameEl.value.trim()||"Fee", amount: amtEl?parseInt(amtEl.value)||0:0, showInAdm: chkEl?chkEl.checked:true }); ci++; } CLASS_FEES[cls].customFees = cfs; }); saveData(); showToast("Fee settings saved!"); renderFees(); } // Old compat stubs function calcTotal(cls,sc){ feeCalc(cls,sc||cls.replace(/\./g,"_").replace(/-/g,"_")); } function toggleFeeCard(){} function feeInput(){ return ""; } function updateTotalFee(cls,sc){ feeCalc(cls,sc||cls); } // ── PRINT CLASS LIST ── function printClassList() { var clsF = val("sc"); // currently selected class filter var secF = val("s-sec")||""; var feeF = val("sf"); var catF = val("scat")||""; var srch = (val("ss")||"").toLowerCase(); // Session filter var curSessYear4 = ACTIVE_SESSION_START || SESSION_START_YEAR; // Get filtered list same as displayed var list = students.filter(function(s){ var clsBase = s.class ? s.class.split("-")[0] : ""; var clsSec = s.class ? s.class.split("-")[1]||"A" : ""; // Session filter if(s.admDate){ var ap=s.admDate.split("."); if(ap.length>=3){var ay=parseInt(ap[2]),am=parseInt(ap[1])-1;var sSY=am>=3?ay:ay-1;if(sSY!==curSessYear4)return false;} } return (!srch || s.name.toLowerCase().includes(srch) || (s.roll&&s.roll.toLowerCase().includes(srch)) || (s.phone&&s.phone.toLowerCase().includes(srch)) || (s.city&&s.city.toLowerCase().includes(srch))) && (!clsF || clsBase===clsF) && (!secF || clsSec===secF) && (!catF || s.category===catF); }); if(!list.length){ showToast("No students to print","error"); return; } // Group by class var groups = {}; var BASE_ORDER = ["Play","Nursery","L.K.G","U.K.G","I","II","III","IV","V","VI","VII","VIII","IX","X"]; list.forEach(function(s){ var key = s.class || "Unknown"; if(!groups[key]) groups[key] = []; groups[key].push(s); }); // Sort groups by BASE_ORDER var sortedKeys = BASE_ORDER.reduce(function(acc, bc){ Object.keys(groups).forEach(function(k){ if(k.split("-")[0]===bc && acc.indexOf(k)===-1) acc.push(k); }); return acc; }, []); // Add any remaining Object.keys(groups).forEach(function(k){ if(sortedKeys.indexOf(k)===-1) sortedKeys.push(k); }); var title = clsF ? ("Class " + clsF + (secF?" - Section "+secF:"") + " - Student List") : "All Classes - Student List"; // Build HTML var html = ""; html += "" + title + " - Sara Azam Memorial Education"; html += ""; // School header html += "
"; html += "
"; html += "
Sara Azam Memorial Education
"; html += "
CBSE Affiliated · "+ SESSION_SHORT +" · New Delhi
"; html += "
" + title + "  |  Total Students: " + list.length + "  |  Date: " + new Date().toLocaleDateString("en-IN") + "
"; html += "
"; // Class-wise tables sortedKeys.forEach(function(cls, ci){ var sList = groups[cls]; var parts = cls.split("-"); var base = parts[0]; var sec = parts[1] || "A"; var noPrefix = ["Play","Nursery","L.K.G","U.K.G"]; var clsLabel = (noPrefix.indexOf(base)>=0 ? "" : "Class ") + base + " - Section " + sec; var paidC = sList.filter(function(s){ return s.fee==="Paid"; }).length; if(ci > 0) html += "
"; else html += "
"; html += "
"; html += "" + clsLabel + ""; html += "" + sList.length + " Students"; html += "
"; html += ""; html += ""; html += ""; sList.forEach(function(s, idx){ html += ""; html += ""; html += ""; html += ""; html += ""; html += ""; html += ""; html += ""; html += ""; html += ""; html += ""; }); html += "
#Student NameRoll No.Father's NamePhoneVillageCategoryDOB
" + (idx+1) + "" + s.name + "" + s.roll + "" + (s.father||"—") + "" + (s.phone||"—") + "" + (s.city||"—") + "" + s.category + "" + (s.dob||"—") + "" + (s.fee==="Paid"?"✓ Paid":"✗ Due") + "
"; html += "
"; html += "Total: " + sList.length + ""; html += "Fee Paid: " + paidC + ""; html += "Fee Due: " + (sList.length-paidC) + ""; html += "General: " + sList.filter(function(s){return s.category==="General";}).length + ""; html += "OBC: " + sList.filter(function(s){return s.category==="OBC";}).length + ""; html += "SC: " + sList.filter(function(s){return s.category==="SC";}).length + ""; html += "ST: " + sList.filter(function(s){return s.category==="ST";}).length + ""; html += "
"; html += "
"; }); html += ""; // Use window.print() to avoid blank first page var origBody = document.body.innerHTML; var origTitle = document.title; document.title = title + " - Sara Azam Memorial Education"; document.body.innerHTML = html.replace(""+document.title+"","").replace("",""); var st = document.createElement("style"); st.innerHTML = "body{font-family:'Segoe UI',Tahoma,sans-serif;color:#111;font-size:12px;}*{box-sizing:border-box;margin:0;padding:0;}.school-header{text-align:center;border-bottom:2px solid #0D1B2E;padding-bottom:12px;margin-bottom:20px;}.tricolor{height:4px;background:linear-gradient(90deg,#FF9933 33%,#fff 33%,#fff 66%,#138808 66%);margin-bottom:10px;}.school-name{font-size:18px;font-weight:800;color:#0D1B2E;}.class-section{margin-bottom:24px;}.class-header{background:#0D1B2E;color:#fff;padding:7px 12px;font-size:13px;font-weight:700;display:flex;justify-content:space-between;border-radius:4px 4px 0 0;}table{width:100%;border-collapse:collapse;border:1px solid #ddd;}th{background:#F8FAFC;padding:6px 8px;text-align:left;font-size:10px;font-weight:700;text-transform:uppercase;border-bottom:1.5px solid #ddd;}td{padding:6px 8px;border-bottom:1px solid #eee;font-size:11.5px;}tr:nth-child(even) td{background:#F9F9F9;}.summary{margin-top:4px;font-size:10.5px;color:#555;display:flex;gap:14px;padding:5px 10px;background:#F8FAFC;border:1px solid #ddd;border-top:none;border-radius:0 0 4px 4px;}@media print{body{-webkit-print-color-adjust:exact;print-color-adjust:exact;}@page{margin:8mm;size:A4;}.class-section{page-break-inside:avoid;}}"; document.head.appendChild(st); // Extract just the body content var bodyMatch = html.match(/([\s\S]*)<\/body>/); document.body.innerHTML = bodyMatch ? bodyMatch[1] : html; document.head.appendChild(st); window.print(); setTimeout(function(){ document.body.innerHTML = origBody; document.title = origTitle; updateDash(); renderStudents(); }, 800); } // ── PHOTO UPLOAD ── function previewPhoto(input) { if(!input||!input.files||!input.files[0]) return; var file = input.files[0]; if(!file.type.startsWith("image/")){ showToast("Please select an image file","error"); return; } var reader = new FileReader(); reader.onload = function(e) { var img = g("photo-img"); var icon = g("photo-icon"); var txt = g("photo-text"); var rem = g("photo-remove"); var prev = g("photo-preview"); if(!img||!icon||!txt) return; img.src = e.target.result; img.style.display = "block"; icon.style.display = "none"; txt.style.display = "none"; if(prev){ prev.style.border = "2px solid #BBF7D0"; prev.style.background = "#F0FDF4"; } if(rem) rem.style.display = "block"; }; reader.readAsDataURL(file); } function removePhoto() { ["s-photo-gallery","s-photo-camera"].forEach(function(id){ var el=g(id); if(el) el.value=""; }); var img = g("photo-img"); if(img){ img.src=""; img.style.display="none"; } var icon = g("photo-icon"); if(icon) icon.style.display="block"; var txt = g("photo-text"); if(txt) txt.style.display="block"; var prev = g("photo-preview"); if(prev){ prev.style.border="2px dashed #DDE3EC"; prev.style.background="#F8FAFC"; } var rem = g("photo-remove"); if(rem) rem.style.display="none"; } function getPhotoData() { var img = g("photo-img"); if(img && img.src && img.src.startsWith("data:")) return img.src; return ""; } function resetPhotoField() { removePhoto(); } // ── SESSION MANAGEMENT ── var ACTIVE_SESSION_START = SESSION_START_YEAR; // default auto-detected function updateSession() { var sel = document.getElementById("session-select"); if(!sel) return; ACTIVE_SESSION_START = parseInt(sel.value); var endYear = ACTIVE_SESSION_START + 1; CURRENT_SESSION = ACTIVE_SESSION_START + "–" + String(endYear).slice(-2); SESSION_SHORT = "Session " + CURRENT_SESSION + " (Apr–Mar)"; SESSION_LABEL = "Session " + CURRENT_SESSION + " · Apr " + ACTIVE_SESSION_START + " to Mar " + endYear; // Update all session displays document.querySelectorAll(".dyn-session").forEach(function(el){ el.textContent = CURRENT_SESSION; }); var sc2 = document.getElementById("session-chip"); if(sc2) sc2.textContent = SESSION_SHORT; // Update year options in monthly receipt ACTIVE_YEAR_RANGE = [ACTIVE_SESSION_START, endYear]; showToast("Session updated to " + CURRENT_SESSION); } var ACTIVE_YEAR_RANGE = [SESSION_START_YEAR, SESSION_END_YEAR]; // ── LOCAL STORAGE ── var STORAGE_KEY = "saraAzam_students_v1"; var TC_STORAGE_KEY = "saraAzam_tcs_v1"; var TEACHER_STORAGE_KEY = "saraAzam_teachers_v3"; var _DEFAULT_TEACHERS = [{"id":201,"name":"Ritu Sharma","subject":"Pre-Primary","qualification":"B.Ed","phone":"9100002613","salary":8000,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class Play","attendance":{},"khata":{}},{"id":202,"name":"Priya Singh","subject":"Pre-Primary","qualification":"M.Ed","phone":"9200002626","salary":8500,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class Nursery","attendance":{},"khata":{}},{"id":203,"name":"Anita Verma","subject":"Pre-Primary","qualification":"B.A B.Ed","phone":"9300002639","salary":9000,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class L.K.G","attendance":{},"khata":{}},{"id":204,"name":"Sunita Gupta","subject":"Pre-Primary","qualification":"M.A B.Ed","phone":"9400002652","salary":9500,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class U.K.G","attendance":{},"khata":{}},{"id":205,"name":"Kavita Yadav","subject":"Primary","qualification":"D.El.Ed","phone":"9500002665","salary":10000,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class I","attendance":{},"khata":{}},{"id":206,"name":"Meera Pandey","subject":"Primary","qualification":"B.Ed","phone":"9600002678","salary":10500,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class II","attendance":{},"khata":{}},{"id":207,"name":"Seema Tiwari","subject":"Primary","qualification":"M.Ed","phone":"9700002691","salary":11000,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class III","attendance":{},"khata":{}},{"id":208,"name":"Lata Mishra","subject":"Primary","qualification":"B.A B.Ed","phone":"9800002704","salary":11500,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class IV","attendance":{},"khata":{}},{"id":209,"name":"Rekha Joshi","subject":"Primary","qualification":"M.A B.Ed","phone":"9900002717","salary":12000,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class V","attendance":{},"khata":{}},{"id":210,"name":"Geeta Chauhan","subject":"Hindi","qualification":"D.El.Ed","phone":"9000002730","salary":12500,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class VI","attendance":{},"khata":{}},{"id":211,"name":"Anjali Rajput","subject":"English","qualification":"B.Ed","phone":"9100002743","salary":13000,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class VII","attendance":{},"khata":{}},{"id":212,"name":"Pooja Saxena","subject":"Mathematics","qualification":"M.Ed","phone":"9200002756","salary":13500,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class VIII","attendance":{},"khata":{}},{"id":213,"name":"Neha Srivastava","subject":"Science","qualification":"B.A B.Ed","phone":"9300002769","salary":14000,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class IX","attendance":{},"khata":{}},{"id":214,"name":"Divya Agarwal","subject":"Social Science","qualification":"M.A B.Ed","phone":"9400002782","salary":14500,"joinDate":"01.04.2024","designation":"Teacher","classes":"Class X","attendance":{},"khata":{}}]; // v3: default staff always loaded var FEES_STORAGE_KEY = "saraAzam_classFees_v1"; function saveData() { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(students)); localStorage.setItem(TC_STORAGE_KEY, JSON.stringify(issuedTCs)); localStorage.setItem(TEACHER_STORAGE_KEY, JSON.stringify(teachers)); localStorage.setItem(FEES_STORAGE_KEY, JSON.stringify(CLASS_FEES)); // Cloud sync saveToCloud(); var ind = document.getElementById("save-indicator"); if(ind) { ind.textContent = CLOUD_ENABLED ? "☁️ Synced" : "✓ Saved"; ind.style.color = "#15803D"; setTimeout(function(){ if(ind) ind.textContent = ""; }, 2000); } } catch(e) { console.warn("Storage save failed:", e.message); var ind = document.getElementById("save-indicator"); if(ind) { ind.textContent = "⚠ Save failed"; ind.style.color = "#B91C1C"; } } } function loadData() { try { // ── Students ── var savedStudents = localStorage.getItem(STORAGE_KEY); if(savedStudents) { var parsedStu = JSON.parse(savedStudents); // Agar localStorage mein students saved hain to use karo // Agar empty array hai to default data use karo if(parsedStu && parsedStu.length > 0) { students = parsedStu; } else { // Empty — default students already in var students=[], clear localStorage entry localStorage.removeItem(STORAGE_KEY); } } // Agar students abhi bhi empty hain aur default data available hai to use karo if(students.length === 0) { // students variable mein pehle se default data hai (var students=[...]) // kuch nahi karna } // ── Transfer Certificates ── var savedTCs = localStorage.getItem(TC_STORAGE_KEY); if(savedTCs) { issuedTCs = JSON.parse(savedTCs); } // ── Teachers ── var savedTeachers = localStorage.getItem(TEACHER_STORAGE_KEY); if(savedTeachers) { var parsedTeachers = JSON.parse(savedTeachers); if(parsedTeachers && parsedTeachers.length > 0) { teachers = parsedTeachers; } else { // Empty saved — remove and use defaults localStorage.removeItem(TEACHER_STORAGE_KEY); if(typeof _DEFAULT_TEACHERS!=="undefined") teachers = _DEFAULT_TEACHERS.slice(); } } else { // No saved data — use defaults if(typeof _DEFAULT_TEACHERS!=="undefined") teachers = _DEFAULT_TEACHERS.slice(); } // ── Fee Settings ── var savedFees = localStorage.getItem(FEES_STORAGE_KEY); if(savedFees) { var parsedFees = JSON.parse(savedFees); Object.assign(CLASS_FEES, parsedFees); } } catch(e) { console.warn("Storage load failed:", e.message); } } function clearAllData() { if(confirm("Are you sure? This will permanently delete ALL student, teacher and TC data!")) { localStorage.removeItem(STORAGE_KEY); localStorage.removeItem(TC_STORAGE_KEY); localStorage.removeItem(TEACHER_STORAGE_KEY); students = []; teachers = []; issuedTCs = []; renderStudents(); renderTeachers(); updateDash(); showToast("All data cleared!"); } } // ── init ── // Version check — agar purana data hai to clear karke defaults load karo var DATA_VERSION = "v2026_3"; var storedVersion = localStorage.getItem("saraAzam_dataVersion"); if(storedVersion !== DATA_VERSION) { // Clear old empty/stale data localStorage.removeItem(STORAGE_KEY); localStorage.removeItem(TEACHER_STORAGE_KEY); localStorage.setItem("saraAzam_dataVersion", DATA_VERSION); } // Load saved data from localStorage loadData(); // Load exam config setTimeout(loadExamConfig, 200); // Pre-populate admission session dropdown setTimeout(populateAdmSessionDropdown, 500); // Auto-detect session from system date var defSel = document.getElementById("session-select"); if(defSel) defSel.value = String(SESSION_START_YEAR); ACTIVE_SESSION_START = SESSION_START_YEAR; CURRENT_SESSION = SESSION_START_YEAR + "–" + String(SESSION_END_YEAR).slice(-2); SESSION_SHORT = "Session " + CURRENT_SESSION + " (Apr–Mar)"; SESSION_LABEL = "Session " + CURRENT_SESSION + " · Apr " + SESSION_START_YEAR + " to Mar " + SESSION_END_YEAR; ACTIVE_YEAR_RANGE = [SESSION_START_YEAR, SESSION_END_YEAR]; // Update all dynamic session spans document.querySelectorAll(".dyn-session").forEach(function(el){ el.textContent = CURRENT_SESSION; }); var sc2 = document.getElementById("session-chip"); if(sc2) sc2.textContent = SESSION_SHORT; populateClassDropdown("scl"); populateClassDropdown("tcl"); try{updateDash();}catch(e){console.warn('updateDash:',e.message);} try{renderStudents();}catch(e){console.warn('renderStudents:',e.message);} try{renderTeachers();}catch(e){console.warn('renderTeachers:',e.message);} try{renderFees();}catch(e){console.warn('renderFees:',e.message);} try{renderAtt();}catch(e){console.warn('renderAtt:',e.message);} try{renderExams();}catch(e){console.warn('renderExams:',e.message);} try{renderRC();}catch(e){console.warn('renderRC:',e.message);} try{renderTC();}catch(e){console.warn('renderTC:',e.message);} try{renderTCY();}catch(e){console.warn('renderTCY:',e.message);}