Result Card Tool

Welcome

Choose an option to get started

© 2025 Result Card Portal. All rights reserved.

🔐

Sign in to your School Panel

Use your EMIS code as username

Test / Exam Information

Manage Students

#Roll No.Student Name Father's NameAction

No students added yet.

Enter Student Marks

SubjectTotal MarksMarks ObtainedGrade

Please save Test Info and add students first.

Preview & Publish Result Cards

Roll No.Student NameMarks StatusAction

No students found. Add students and enter marks first.

Publishing makes result cards available for students and parents.

🎓

Find Your Result

Enter your school and student details below

Available on your school notice board.

School Name

School Address

EMIS: -------

Examination Exam Name Date
Student Name ---
Father's Name ---
Class ---
Section ---
Roll No. ---

Subject-wise Marks

Sr.SubjectTotal Marks Marks ObtainedPass MarksGradeStatus
Grand Total --- --- --- ---
Percentage 0%
Overall Grade ---
Result ---
Position in Class ---

Class Teacher

Principal

Parent / Guardian

Please wait...

/* ============================================================ RESULT CARD PORTAL — style.css Theme: Light Blue & White ============================================================ */ /* ── VARIABLES (easy to customize) ── */ :root { --primary: #2196F3; --primary-dark: #1565C0; --primary-light: #E3F2FD; --secondary: #0288D1; --accent: #29B6F6; --white: #FFFFFF; --bg: #F0F8FF; --card-bg: #FFFFFF; --text: #1A2B3C; --text-muted: #607D8B; --border: #BBDEFB; --danger: #E53935; --success: #2E7D32; --warning: #F57C00; --radius: 12px; --radius-sm: 8px; --shadow: 0 4px 20px rgba(33,150,243,0.10); --shadow-strong: 0 8px 32px rgba(33,150,243,0.18); --transition: 0.25s ease; --font: 'Segoe UI', Arial, sans-serif; } /* ── RESET ── */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html { font-size: 16px; scroll-behavior: smooth; } body { font-family: var(--font); background: var(--bg); color: var(--text); min-height: 100vh; } img { max-width: 100%; display: block; } a { color: var(--primary); text-decoration: none; } ul { list-style: none; } /* ── PAGE SYSTEM ── */ .page { display: none; min-height: 100vh; flex-direction: column; } .page.active { display: flex; } /* ============================================================ BUTTONS ============================================================ */ .btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 10px 22px; border: 2px solid transparent; border-radius: var(--radius-sm); font-family: var(--font); font-size: 0.95rem; font-weight: 600; cursor: pointer; transition: all var(--transition); white-space: nowrap; text-align: center; } .btn:active { transform: scale(0.97); } .btn-primary { background: var(--primary); color: var(--white); border-color: var(--primary); } .btn-primary:hover { background: var(--primary-dark); border-color: var(--primary-dark); box-shadow: 0 4px 14px rgba(33,150,243,0.35); } .btn-secondary { background: var(--white); color: var(--primary); border-color: var(--primary); } .btn-secondary:hover { background: var(--primary-light); box-shadow: 0 4px 14px rgba(33,150,243,0.18); } .btn-outline { background: transparent; color: var(--primary); border-color: var(--primary); } .btn-outline:hover { background: var(--primary-light); } .btn-danger { background: var(--danger); color: var(--white); border-color: var(--danger); } .btn-danger:hover { background: #b71c1c; } .btn-back { background: transparent; color: var(--primary); border: none; font-size: 0.95rem; padding: 8px 14px; } .btn-back:hover { background: var(--primary-light); border-radius: var(--radius-sm); } .btn-large { padding: 18px 32px; font-size: 1.05rem; border-radius: var(--radius); flex-direction: column; gap: 4px; min-width: 210px; } .btn-small { padding: 6px 14px; font-size: 0.85rem; } .btn-full { width: 100%; } /* ============================================================ LANDING PAGE ============================================================ */ #page-landing { background: var(--bg); } .site-header { background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%); padding: 40px 24px 32px; text-align: center; box-shadow: var(--shadow); } .logo-area { display: flex; flex-direction: column; align-items: center; gap: 10px; } #site-logo { width: 64px; height: 64px; border-radius: 50%; object-fit: cover; background: rgba(255,255,255,0.2); } .site-title { font-size: 2rem; font-weight: 800; color: var(--white); letter-spacing: 0.5px; } .site-subtitle { font-size: 1rem; color: rgba(255,255,255,0.85); } .landing-main { flex: 1; display: flex; align-items: center; justify-content: center; padding: 48px 24px; } .landing-card { background: var(--card-bg); border-radius: var(--radius); box-shadow: var(--shadow-strong); padding: 48px 40px; text-align: center; max-width: 560px; width: 100%; } .landing-heading { font-size: 1.7rem; color: var(--text); margin-bottom: 8px; } .landing-desc { color: var(--text-muted); margin-bottom: 36px; font-size: 1rem; } .landing-buttons { display: flex; gap: 20px; justify-content: center; flex-wrap: wrap; } .btn-large .btn-icon { font-size: 1.8rem; } .btn-large .btn-label { font-size: 1rem; font-weight: 700; } .btn-large .btn-hint { font-size: 0.78rem; font-weight: 400; opacity: 0.8; } .site-footer { text-align: center; padding: 18px; color: var(--text-muted); font-size: 0.85rem; border-top: 1px solid var(--border); background: var(--white); } /* ============================================================ PAGE HEADER (Login / Generate / Result) ============================================================ */ .page-header { background: var(--white); padding: 16px 24px; display: flex; align-items: center; gap: 16px; border-bottom: 2px solid var(--border); box-shadow: var(--shadow); position: sticky; top: 0; z-index: 100; } .page-title { font-size: 1.2rem; font-weight: 700; color: var(--primary-dark); } /* ============================================================ FORMS ============================================================ */ .form-main { flex: 1; display: flex; align-items: center; justify-content: center; padding: 40px 24px; background: var(--bg); } .form-card { background: var(--card-bg); border-radius: var(--radius); box-shadow: var(--shadow-strong); padding: 40px 36px; width: 100%; max-width: 480px; } .form-header { text-align: center; margin-bottom: 28px; } .form-icon { font-size: 2.4rem; } .form-header h3 { font-size: 1.3rem; color: var(--text); margin: 8px 0 4px; } .form-header p { color: var(--text-muted); font-size: 0.92rem; } .form { display: flex; flex-direction: column; gap: 18px; } .form-row { display: flex; gap: 16px; flex-wrap: wrap; } .form-row .form-group { flex: 1; min-width: 160px; } .form-group { display: flex; flex-direction: column; gap: 6px; } .form-label { font-size: 0.88rem; font-weight: 600; color: var(--text); } .form-input { width: 100%; padding: 10px 14px; border: 2px solid var(--border); border-radius: var(--radius-sm); font-family: var(--font); font-size: 0.95rem; color: var(--text); background: var(--white); transition: border-color var(--transition), box-shadow var(--transition); outline: none; } .form-input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(33,150,243,0.15); } .form-input::placeholder { color: #B0BEC5; } select.form-input { cursor: pointer; } .form-error { font-size: 0.8rem; color: var(--danger); min-height: 16px; } .form-hint { font-size: 0.8rem; color: var(--text-muted); } .form-footer-note { text-align: center; margin-top: 18px; font-size: 0.83rem; color: var(--text-muted); } /* Inline form (add student row) */ .form-inline { flex-direction: row; flex-wrap: wrap; align-items: flex-end; gap: 14px; } .form-inline .form-group { flex: 1; min-width: 150px; } /* ============================================================ ADMIN PANEL ============================================================ */ #page-admin { background: var(--bg); } .admin-topbar { background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%); padding: 14px 28px; display: flex; align-items: center; gap: 20px; flex-wrap: wrap; box-shadow: var(--shadow); position: sticky; top: 0; z-index: 100; } .admin-logo { display: flex; flex-direction: column; gap: 2px; } .admin-school-name { color: var(--white); font-size: 1.1rem; font-weight: 700; } .admin-emis-badge { font-size: 0.78rem; color: rgba(255,255,255,0.8); background: rgba(255,255,255,0.15); padding: 2px 10px; border-radius: 20px; width: fit-content; } .admin-nav { display: flex; gap: 4px; flex: 1; flex-wrap: wrap; } .admin-nav-btn { background: rgba(255,255,255,0.15); border: 2px solid transparent; border-radius: var(--radius-sm); color: rgba(255,255,255,0.85); padding: 8px 16px; font-size: 0.88rem; font-weight: 600; cursor: pointer; font-family: var(--font); transition: all var(--transition); } .admin-nav-btn:hover { background: rgba(255,255,255,0.25); color: var(--white); } .admin-nav-btn.active { background: var(--white); color: var(--primary-dark); border-color: var(--white); } #btn-logout { margin-left: auto; } .admin-main { flex: 1; padding: 28px; } .admin-tab { display: none; } .admin-tab.active { display: block; } .tab-card { background: var(--card-bg); border-radius: var(--radius); box-shadow: var(--shadow); padding: 32px; } .tab-title { font-size: 1.2rem; font-weight: 700; color: var(--primary-dark); margin-bottom: 24px; padding-bottom: 12px; border-bottom: 2px solid var(--primary-light); } /* Subjects list */ .subjects-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 10px; } .subject-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; } .subject-row .subject-name { flex: 2; min-width: 140px; } .subject-row .subject-total, .subject-row .subject-pass { flex: 1; min-width: 100px; } /* ============================================================ TABLES ============================================================ */ .table-wrapper { overflow-x: auto; margin-top: 20px; border-radius: var(--radius-sm); border: 1px solid var(--border); } .data-table { width: 100%; border-collapse: collapse; font-size: 0.92rem; } .data-table th { background: var(--primary-light); color: var(--primary-dark); font-weight: 700; padding: 12px 14px; text-align: left; border-bottom: 2px solid var(--border); white-space: nowrap; } .data-table td { padding: 11px 14px; border-bottom: 1px solid #EEF5FD; color: var(--text); vertical-align: middle; } .data-table tbody tr:last-child td { border-bottom: none; } .data-table tbody tr:hover { background: #F5FAFF; } .empty-msg { text-align: center; color: var(--text-muted); padding: 24px; font-size: 0.92rem; } /* ============================================================ MARKS FILTER ============================================================ */ .marks-filter { max-width: 320px; margin-bottom: 8px; } /* ============================================================ PUBLISH AREA ============================================================ */ .publish-area { margin-top: 28px; padding-top: 24px; border-top: 2px solid var(--primary-light); display: flex; align-items: center; gap: 20px; flex-wrap: wrap; } .publish-note { color: var(--text-muted); font-size: 0.9rem; flex: 1; min-width: 200px; } /* ============================================================ RESULT CARD PAGE ============================================================ */ .result-main { flex: 1; display: flex; justify-content: center; padding: 36px 24px 80px; background: var(--bg); } /* ── THE RESULT CARD ── */ .result-card { background: var(--white); border-radius: var(--radius); box-shadow: var(--shadow-strong); width: 100%; max-width: 820px; padding: 40px 44px; font-size: 0.93rem; color: var(--text); border-top: 6px solid var(--primary); } /* Card Header */ .rc-header { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; } .rc-school-logo { width: 80px; height: 80px; border-radius: 50%; object-fit: cover; border: 3px solid var(--primary-light); background: var(--primary-light); } .rc-school-info { flex: 1; } .rc-school-name { font-size: 1.4rem; font-weight: 800; color: var(--primary-dark); } .rc-school-address { color: var(--text-muted); font-size: 0.88rem; margin-top: 3px; } .rc-school-emis { font-size: 0.82rem; color: var(--text-muted); margin-top: 4px; } .rc-exam-badge { background: var(--primary-light); border: 2px solid var(--border); border-radius: var(--radius-sm); padding: 12px 18px; text-align: center; display: flex; flex-direction: column; gap: 4px; min-width: 150px; } .rc-exam-label { font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 1px; } .rc-exam-name { font-size: 1rem; font-weight: 700; color: var(--primary-dark); } .rc-exam-date { font-size: 0.82rem; color: var(--text-muted); } /* Divider */ .rc-divider { height: 2px; background: linear-gradient(to right, var(--primary-light), var(--border), var(--primary-light)); margin: 22px 0; border-radius: 2px; } /* Student Info Row */ .rc-student-info { display: flex; gap: 12px; flex-wrap: wrap; background: var(--primary-light); border-radius: var(--radius-sm); padding: 16px 20px; } .rc-info-item { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 120px; } .rc-info-label { font-size: 0.74rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.6px; font-weight: 600; } .rc-info-value { font-size: 0.97rem; font-weight: 700; color: var(--text); } /* Section Title */ .rc-section-title { font-size: 1rem; font-weight: 700; color: var(--primary-dark); margin-bottom: 14px; } /* Marks Table */ .rc-marks-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; } .rc-marks-table th { background: var(--primary); color: var(--white); padding: 10px 12px; text-align: center; font-weight: 700; font-size: 0.82rem; letter-spacing: 0.3px; } .rc-marks-table th:first-child { text-align: left; border-radius: 6px 0 0 6px; } .rc-marks-table th:last-child { border-radius: 0 6px 6px 0; } .rc-marks-table td { padding: 10px 12px; text-align: center; border-bottom: 1px solid #EEF5FD; color: var(--text); } .rc-marks-table td:first-child, .rc-marks-table td:nth-child(2) { text-align: left; } .rc-marks-table tbody tr:nth-child(even) { background: #F7FBFF; } .rc-marks-table tbody tr:hover { background: var(--primary-light); } /* Grand Total Row */ .rc-total-row { background: var(--primary-light) !important; } .rc-total-row td { font-weight: 700; color: var(--primary-dark); padding: 12px; } .rc-total-label { text-align: right !important; font-size: 0.92rem; letter-spacing: 0.3px; } /* Status Badges in table */ .badge-pass { color: var(--success); font-weight: 700; } .badge-fail { color: var(--danger); font-weight: 700; } .badge-absent { color: var(--warning); font-weight: 700; } /* Result Summary */ .rc-summary { display: flex; gap: 12px; flex-wrap: wrap; justify-content: space-between; } .rc-summary-item { flex: 1; min-width: 130px; background: var(--primary-light); border: 2px solid var(--border); border-radius: var(--radius-sm); padding: 14px 16px; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 6px; } .rc-summary-label { font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.7px; font-weight: 600; } .rc-summary-value { font-size: 1.3rem; font-weight: 800; color: var(--primary-dark); } /* Pass / Fail badge */ .result-badge { display: inline-block; padding: 4px 18px; border-radius: 20px; font-size: 1rem !important; font-weight: 800 !important; } .result-badge.pass { background: #E8F5E9; color: var(--success); } .result-badge.fail { background: #FFEBEE; color: var(--danger); } /* Remarks */ .rc-footer-info { margin-top: 4px; } .rc-remarks { background: #FFFDE7; border: 1px solid #FDD835; border-radius: var(--radius-sm); padding: 10px 16px; font-size: 0.92rem; color: var(--text); } /* Signatures */ .rc-signatures { display: flex; justify-content: space-between; margin-top: 32px; gap: 20px; flex-wrap: wrap; } .rc-sign-box { flex: 1; min-width: 140px; text-align: center; font-size: 0.85rem; color: var(--text-muted); } .rc-sign-line { height: 2px; background: var(--border); margin-bottom: 8px; border-radius: 2px; } /* Card Footer */ .rc-card-footer { margin-top: 24px; text-align: center; font-size: 0.78rem; color: var(--text-muted); padding-top: 14px; border-top: 1px dashed var(--border); } /* ============================================================ FLOATING ACTION BUTTONS (Print & Download) ============================================================ */ .fab-container { position: fixed; bottom: 28px; right: 28px; display: flex; flex-direction: column; gap: 12px; z-index: 200; } .fab { display: flex; align-items: center; gap: 10px; padding: 14px 22px; border: none; border-radius: 50px; font-size: 0.95rem; font-weight: 700; font-family: var(--font); cursor: pointer; box-shadow: 0 6px 20px rgba(0,0,0,0.18); transition: all var(--transition); } .fab:hover { transform: translateY(-3px); box-shadow: 0 10px 28px rgba(0,0,0,0.22); } .fab:active { transform: scale(0.97); } .fab-print { background: var(--white); color: var(--primary-dark); border: 2px solid var(--border); } .fab-download { background: var(--primary); color: var(--white); } /* ============================================================ TOAST NOTIFICATION ============================================================ */ .toast { position: fixed; bottom: 32px; left: 50%; transform: translateX(-50%) translateY(80px); background: #263238; color: var(--white); padding: 13px 28px; border-radius: 50px; font-size: 0.92rem; font-weight: 500; z-index: 999; opacity: 0; transition: all 0.35s ease; pointer-events: none; white-space: nowrap; box-shadow: 0 6px 24px rgba(0,0,0,0.22); } .toast.show { opacity: 1; transform: translateX(-50%) translateY(0); } .toast.success { background: var(--success); } .toast.error { background: var(--danger); } /* ============================================================ LOADING OVERLAY ============================================================ */ .loading-overlay { display: none; position: fixed; inset: 0; background: rgba(255,255,255,0.82); z-index: 900; align-items: center; justify-content: center; flex-direction: column; gap: 16px; backdrop-filter: blur(3px); } .loading-overlay.show { display: flex; } .loading-spinner { width: 46px; height: 46px; border: 5px solid var(--primary-light); border-top-color: var(--primary); border-radius: 50%; animation: spin 0.8s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } .loading-text { color: var(--text-muted); font-size: 0.95rem; } /* ============================================================ CONFIRM MODAL ============================================================ */ .modal-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.35); z-index: 800; align-items: center; justify-content: center; backdrop-filter: blur(2px); } .modal-overlay.show { display: flex; } .modal-box { background: var(--white); border-radius: var(--radius); box-shadow: var(--shadow-strong); padding: 36px 32px; max-width: 380px; width: 90%; animation: popIn 0.2s ease; } @keyframes popIn { from { transform: scale(0.92); opacity: 0; } to { transform: scale(1); opacity: 1; } } .modal-title { font-size: 1.1rem; font-weight: 700; color: var(--text); margin-bottom: 10px; } .modal-message { color: var(--text-muted); font-size: 0.95rem; margin-bottom: 24px; } .modal-actions { display: flex; gap: 12px; justify-content: flex-end; } /* ============================================================ PRINT STYLES ============================================================ */ @media print { .no-print { display: none !important; } body { background: white !important; } .result-card { box-shadow: none !important; border: none !important; padding: 24px !important; max-width: 100% !important; border-top: 4px solid var(--primary) !important; } .page { display: flex !important; } #page-result { display: flex !important; } } /* ============================================================ RESPONSIVE ============================================================ */ @media (max-width: 700px) { .landing-card { padding: 32px 20px; } .landing-buttons { flex-direction: column; align-items: stretch; } .btn-large { min-width: unset; } .admin-topbar { padding: 12px 16px; } .admin-nav { gap: 2px; } .admin-nav-btn { padding: 6px 10px; font-size: 0.78rem; } .tab-card { padding: 20px 16px; } .form-card { padding: 28px 18px; } .result-card { padding: 24px 16px; } .rc-header { flex-direction: column; align-items: flex-start; } .rc-summary { flex-direction: column; } .fab-container { bottom: 16px; right: 16px; } .fab { padding: 11px 16px; font-size: 0.85rem; } } /* ============================================================ RESULT CARD PORTAL — script.js All data stored in localStorage (no backend needed) ============================================================ */ /* ── EASY CONFIG ── */ const CONFIG = { appName: "Result Card Portal", demoEMIS: "SCH001", /* pre-loaded demo school EMIS */ demoPass: "admin123", /* demo school password */ demoSchool: "Demo Public School", gradingScale: [ /* customize grade thresholds here */ { min: 90, grade: "A+" }, { min: 80, grade: "A" }, { min: 70, grade: "B" }, { min: 60, grade: "C" }, { min: 50, grade: "D" }, { min: 0, grade: "F" }, ], }; /* ============================================================ UTILITY HELPERS ============================================================ */ /* Show/hide pages */ function showPage(id) { document.querySelectorAll(".page").forEach(p => p.classList.remove("active")); document.getElementById(id).classList.add("active"); window.scrollTo(0, 0); } /* Toast notification */ let toastTimer; function showToast(msg, type = "") { const toast = document.getElementById("toast"); const toastMsg = document.getElementById("toast-msg"); toast.className = "toast"; toastMsg.textContent = msg; if (type) toast.classList.add(type); toast.classList.add("show"); clearTimeout(toastTimer); toastTimer = setTimeout(() => toast.classList.remove("show"), 3200); } /* Loading overlay */ function showLoading(show) { document.getElementById("loading-overlay").classList.toggle("show", show); } /* Confirm modal — returns a Promise */ function showConfirm(title, message) { return new Promise(resolve => { document.getElementById("modal-title").textContent = title; document.getElementById("modal-message").textContent = message; document.getElementById("modal-confirm").classList.add("show"); const ok = document.getElementById("modal-confirm-btn"); const cancel = document.getElementById("modal-cancel"); function cleanup(result) { document.getElementById("modal-confirm").classList.remove("show"); ok.removeEventListener("click", onOk); cancel.removeEventListener("click", onCancel); resolve(result); } function onOk() { cleanup(true); } function onCancel() { cleanup(false); } ok.addEventListener("click", onOk); cancel.addEventListener("click", onCancel); }); } /* Grade from percentage */ function getGrade(obtained, total) { if (total === 0) return "—"; const pct = (obtained / total) * 100; for (const g of CONFIG.gradingScale) { if (pct >= g.min) return g.grade; } return "F"; } /* Format date string */ function fmtDate(str) { if (!str) return "—"; const d = new Date(str); return d.toLocaleDateString("en-PK", { day: "2-digit", month: "short", year: "numeric" }); } /* ============================================================ LOCAL STORAGE HELPERS ============================================================ */ function loadDB() { return JSON.parse(localStorage.getItem("rcpDB") || "{}"); } function saveDB(db) { localStorage.setItem("rcpDB", JSON.stringify(db)); } /* Seed demo school if not present */ function seedDemo() { const db = loadDB(); if (!db[CONFIG.demoEMIS]) { db[CONFIG.demoEMIS] = { name: CONFIG.demoSchool, password: CONFIG.demoPass, address: "123 Education Street, City", principal: "Mr. Demo Principal", testInfo: null, students: [], marks: {}, published: false, }; saveDB(db); } } /* ============================================================ SESSION (logged-in school) ============================================================ */ let SESSION = { emis: null }; function sessionLogin(emis) { SESSION.emis = emis; sessionStorage.setItem("rcpSession", emis); } function sessionLogout() { SESSION.emis = null; sessionStorage.removeItem("rcpSession"); } function restoreSession() { const saved = sessionStorage.getItem("rcpSession"); if (saved) SESSION.emis = saved; } function getSchool() { const db = loadDB(); return db[SESSION.emis] || null; } function updateSchool(data) { const db = loadDB(); db[SESSION.emis] = { ...db[SESSION.emis], ...data }; saveDB(db); } /* ============================================================ LANDING PAGE ============================================================ */ document.getElementById("btn-go-login").addEventListener("click", () => { showPage("page-login"); }); document.getElementById("btn-go-generate").addEventListener("click", () => { showPage("page-generate"); }); /* ============================================================ LOGIN ============================================================ */ document.getElementById("btn-back-from-login").addEventListener("click", () => { showPage("page-landing"); }); document.getElementById("form-login").addEventListener("submit", function(e) { e.preventDefault(); const emis = document.getElementById("input-emis").value.trim().toUpperCase(); const pass = document.getElementById("input-password").value.trim(); let valid = true; document.getElementById("error-emis").textContent = ""; document.getElementById("error-password").textContent = ""; if (!emis) { document.getElementById("error-emis").textContent = "Please enter your EMIS code."; valid = false; } if (!pass) { document.getElementById("error-password").textContent = "Please enter your password."; valid = false; } if (!valid) return; showLoading(true); setTimeout(() => { const db = loadDB(); /* First-time login: auto-register school */ if (!db[emis]) { db[emis] = { name: "School " + emis, password: pass, address: "", principal: "", testInfo: null, students: [], marks: {}, published: false, }; saveDB(db); showLoading(false); sessionLogin(emis); openAdminPanel(); showToast("Welcome! New school registered.", "success"); return; } if (db[emis].password !== pass) { showLoading(false); document.getElementById("error-password").textContent = "Incorrect password."; return; } showLoading(false); sessionLogin(emis); openAdminPanel(); showToast("Logged in successfully!", "success"); }, 600); }); /* ============================================================ ADMIN PANEL — OPEN & POPULATE ============================================================ */ function openAdminPanel() { const school = getSchool(); if (!school) return; document.getElementById("admin-school-name").textContent = school.name; document.getElementById("admin-emis-badge").textContent = "EMIS: " + SESSION.emis; /* Restore saved test info fields */ if (school.testInfo) { const t = school.testInfo; document.getElementById("input-test-name").value = t.name || ""; document.getElementById("input-test-date").value = t.date || ""; document.getElementById("input-class").value = t.class || ""; document.getElementById("input-section").value = t.section || ""; document.getElementById("input-school-address").value = school.address || ""; document.getElementById("input-principal").value = school.principal || ""; rebuildSubjectRows(t.subjects || []); } renderStudentsTable(); renderMarksStudentSelect(); renderPreviewTable(); switchAdminTab("tab-test"); showPage("page-admin"); } /* Admin tab switching */ document.querySelectorAll(".admin-nav-btn").forEach(btn => { btn.addEventListener("click", function() { switchAdminTab(this.dataset.tab); }); }); function switchAdminTab(tabId) { document.querySelectorAll(".admin-nav-btn").forEach(b => b.classList.remove("active")); document.querySelectorAll(".admin-tab").forEach(t => t.classList.remove("active")); document.querySelector([data-tab="${tabId}"]).classList.add("active"); document.getElementById(tabId).classList.add("active"); /* Refresh dynamic content when switching */ if (tabId === "tab-students") renderStudentsTable(); if (tabId === "tab-marks") { renderMarksStudentSelect(); renderMarksTable(); } if (tabId === "tab-preview") renderPreviewTable(); } /* Logout */ document.getElementById("btn-logout").addEventListener("click", async () => { const ok = await showConfirm("Logout", "Are you sure you want to logout?"); if (!ok) return; sessionLogout(); document.getElementById("form-login").reset(); showPage("page-landing"); showToast("Logged out."); }); /* ============================================================ TAB 1 — TEST INFO ============================================================ */ /* Add subject row */ document.getElementById("btn-add-subject").addEventListener("click", () => { addSubjectRow(); }); function addSubjectRow(data = {}) { const row = document.createElement("div"); row.className = "subject-row"; row.innerHTML = ` `; row.querySelector(".btn-remove-subject").addEventListener("click", () => row.remove()); document.getElementById("subjects-list").appendChild(row); } function rebuildSubjectRows(subjects) { const list = document.getElementById("subjects-list"); list.innerHTML = ""; if (subjects.length === 0) { addSubjectRow(); return; } subjects.forEach(s => addSubjectRow(s)); /* re-bind existing first row remove button */ list.querySelectorAll(".btn-remove-subject").forEach(btn => { btn.addEventListener("click", () => btn.closest(".subject-row").remove()); }); } /* Bind the first subject row's remove button on page load */ document.querySelector(".btn-remove-subject")?.addEventListener("click", function() { this.closest(".subject-row").remove(); }); /* Save test info */ document.getElementById("form-test-info").addEventListener("submit", function(e) { e.preventDefault(); const subjects = []; document.querySelectorAll(".subject-row").forEach(row => { const name = row.querySelector(".subject-name").value.trim(); const total = parseInt(row.querySelector(".subject-total").value) || 0; const pass = parseInt(row.querySelector(".subject-pass").value) || 0; if (name && total) subjects.push({ name, total, pass }); }); if (subjects.length === 0) { showToast("Add at least one subject.", "error"); return; } const testInfo = { name: document.getElementById("input-test-name").value.trim(), date: document.getElementById("input-test-date").value, class: document.getElementById("input-class").value, section: document.getElementById("input-section").value.trim(), subjects, }; if (!testInfo.name || !testInfo.class) { showToast("Exam name and class are required.", "error"); return; } updateSchool({ testInfo, address: document.getElementById("input-school-address").value.trim(), principal: document.getElementById("input-principal").value.trim(), published: false, }); showToast("Test info saved!", "success"); renderMarksStudentSelect(); renderMarksTable(); }); /* ============================================================ TAB 2 — STUDENTS ============================================================ */ document.getElementById("form-add-student").addEventListener("submit", function(e) { e.preventDefault(); const roll = document.getElementById("input-student-roll").value.trim(); const name = document.getElementById("input-student-name").value.trim(); const father = document.getElementById("input-father-name").value.trim(); if (!roll || !name) { showToast("Roll number and student name are required.", "error"); return; } const school = getSchool(); if (school.students.find(s => s.roll === roll)) { showToast("A student with this roll number already exists.", "error"); return; } const students = [...school.students, { roll, name, fatherName: father }]; updateSchool({ students, published: false }); document.getElementById("form-add-student").reset(); renderStudentsTable(); renderMarksStudentSelect(); renderPreviewTable(); showToast("Student added!", "success"); }); function renderStudentsTable() { const school = getSchool(); if (!school) return; const tbody = document.getElementById("students-tbody"); const empty = document.getElementById("students-empty"); tbody.innerHTML = ""; if (!school.students.length) { empty.style.display = "block"; return; } empty.style.display = "none"; school.students.forEach((s, i) => { const tr = document.createElement("tr"); tr.innerHTML = ` ${i + 1} ${s.roll} ${s.name} ${s.fatherName || "—"} `; tr.querySelector("button").addEventListener("click", () => removeStudent(s.roll)); tbody.appendChild(tr); }); } async function removeStudent(roll) { const ok = await showConfirm("Remove Student", Remove student with roll number ${roll}? Their marks will also be deleted.); if (!ok) return; const school = getSchool(); const students = school.students.filter(s => s.roll !== roll); const marks = { ...school.marks }; delete marks[roll]; updateSchool({ students, marks, published: false }); renderStudentsTable(); renderMarksStudentSelect(); renderPreviewTable(); showToast("Student removed.", "success"); } /* ============================================================ TAB 3 — ENTER MARKS ============================================================ */ function renderMarksStudentSelect() { const school = getSchool(); const select = document.getElementById("marks-student-select"); const current = select.value; select.innerHTML = ; if (!school) return; school.students.forEach(s => { const opt = document.createElement("option"); opt.value = s.roll; opt.textContent = ${s.roll} — ${s.name}; select.appendChild(opt); }); /* Restore selection if still valid */ if (current) select.value = current; } document.getElementById("marks-student-select").addEventListener("change", renderMarksTable); function renderMarksTable() { const school = getSchool(); const roll = document.getElementById("marks-student-select").value; const tbody = document.getElementById("marks-tbody"); const empty = document.getElementById("marks-empty"); const remarks = document.getElementById("input-remarks"); tbody.innerHTML = ""; if (!school || !school.testInfo || !school.students.length || !roll) { empty.style.display = "block"; return; } empty.style.display = "none"; const saved = school.marks[roll] || {}; remarks.value = saved.remarks || ""; school.testInfo.subjects.forEach(sub => { const obtained = saved[sub.name] !== undefined ? saved[sub.name] : ""; const grade = obtained !== "" ? getGrade(obtained, sub.total) : "—"; const tr = document.createElement("tr"); tr.innerHTML = ` ${sub.name} ${sub.total} — `; const input = tr.querySelector(".marks-input"); const cell = tr.querySelector(".grade-cell"); if (obtained !== "") cell.textContent = grade; input.addEventListener("input", function() { const val = parseFloat(this.value); if (!isNaN(val)) { if (val > sub.total) this.value = sub.total; cell.textContent = getGrade(parseFloat(this.value), sub.total); } else { cell.textContent = "—"; } }); tbody.appendChild(tr); }); } document.getElementById("form-marks").addEventListener("submit", function(e) { e.preventDefault(); const roll = document.getElementById("marks-student-select").value; if (!roll) { showToast("Please select a student first.", "error"); return; } const school = getSchool(); const newData = { remarks: document.getElementById("input-remarks").value.trim() }; document.querySelectorAll(".marks-input").forEach(input => { const sub = input.dataset.subject; const val = input.value.trim(); newData[sub] = val !== "" ? parseFloat(val) : ""; }); const marks = { ...school.marks, [roll]: newData }; updateSchool({ marks, published: false }); renderPreviewTable(); showToast("Marks saved!", "success"); }); /* ============================================================ TAB 4 — PREVIEW CARDS ============================================================ */ function renderPreviewTable() { const school = getSchool(); const tbody = document.getElementById("preview-tbody"); const empty = document.getElementById("preview-empty"); tbody.innerHTML = ""; if (!school || !school.students.length) { empty.style.display = "block"; return; } empty.style.display = "none"; school.students.forEach(s => { const saved = school.marks[s.roll] || {}; const subjects = school.testInfo ? school.testInfo.subjects : []; const filled = subjects.filter(sub => saved[sub.name] !== "" && saved[sub.name] !== undefined).length; const status = filled === 0 ? "⚪ Not Entered" : filled < subjects.length ? "🟡 Partial" : "🟢 Complete"; const tr = document.createElement("tr"); tr.innerHTML = ` ${s.roll} ${s.name} ${status} `; tr.querySelector("button").addEventListener("click", () => { buildResultCard(SESSION.emis, s.roll); showPage("page-result"); }); tbody.appendChild(tr); }); } /* Publish all */ document.getElementById("btn-publish-all").addEventListener("click", async () => { const school = getSchool(); if (!school || !school.students.length) { showToast("No students to publish.", "error"); return; } const ok = await showConfirm("Publish Result Cards", "This will make all result cards visible to students and parents. Proceed?"); if (!ok) return; updateSchool({ published: true }); showToast("All result cards published!", "success"); }); /* ============================================================ GENERATE RESULT CARD (Public — students & parents) ============================================================ */ document.getElementById("btn-back-from-generate").addEventListener("click", () => { showPage("page-landing"); }); document.getElementById("form-generate").addEventListener("submit", function(e) { e.preventDefault(); const emis = document.getElementById("gen-emis").value.trim().toUpperCase(); const cls = document.getElementById("gen-class").value; const roll = document.getElementById("gen-roll").value.trim(); /* Clear errors */ ["error-gen-emis","error-gen-class","error-gen-roll"].forEach(id => { document.getElementById(id).textContent = ""; }); let valid = true; if (!emis) { document.getElementById("error-gen-emis").textContent = "Please enter the EMIS code."; valid = false; } if (!cls) { document.getElementById("error-gen-class").textContent = "Please select a class."; valid = false; } if (!roll) { document.getElementById("error-gen-roll").textContent = "Please enter a roll number."; valid = false; } if (!valid) return; showLoading(true); setTimeout(() => { const db = loadDB(); const school = db[emis]; if (!school) { showLoading(false); document.getElementById("error-gen-emis").textContent = "School not found. Check the EMIS code."; return; } if (!school.published) { showLoading(false); showToast("Result cards are not yet published by this school.", "error"); return; } if (!school.testInfo || school.testInfo.class !== cls) { showLoading(false); document.getElementById("error-gen-class").textContent = "No results available for this class."; return; } const student = school.students.find(s => s.roll === roll); if (!student) { showLoading(false); document.getElementById("error-gen-roll").textContent = "Roll number not found in this class."; return; } showLoading(false); buildResultCard(emis, roll); showPage("page-result"); }, 800); }); /* ============================================================ BUILD & DISPLAY RESULT CARD ============================================================ */ function buildResultCard(emis, roll) { const db = loadDB(); const school = db[emis]; const student = school.students.find(s => s.roll === roll); const test = school.testInfo; const saved = school.marks[roll] || {}; /* School info */ document.getElementById("rc-school-name").textContent = school.name; document.getElementById("rc-school-address").textContent = school.address || "—"; document.getElementById("rc-school-emis").textContent = emis; document.getElementById("rc-exam-name").textContent = test.name; document.getElementById("rc-exam-date").textContent = fmtDate(test.date); /* Student info */ document.getElementById("rc-student-name").textContent = student.name; document.getElementById("rc-father-name").textContent = student.fatherName || "—"; document.getElementById("rc-class").textContent = "Class " + test.class; document.getElementById("rc-section").textContent = test.section || "—"; document.getElementById("rc-roll").textContent = student.roll; /* Marks table */ const tbody = document.getElementById("rc-marks-tbody"); tbody.innerHTML = ""; let grandTotal = 0; let grandObtained = 0; let allPass = true; test.subjects.forEach((sub, i) => { const obtained = saved[sub.name] !== undefined && saved[sub.name] !== "" ? parseFloat(saved[sub.name]) : null; const grade = obtained !== null ? getGrade(obtained, sub.total) : "—"; const passed = obtained !== null ? obtained >= sub.pass : false; const statusTxt = obtained === null ? "Absent" : passed ? "Pass" : "Fail"; const statusCls = obtained === null ? "badge-absent" : passed ? "badge-pass" : "badge-fail"; if (obtained !== null) { grandTotal += sub.total; grandObtained += obtained; if (!passed) allPass = false; } else { allPass = false; } const tr = document.createElement("tr"); tr.innerHTML = ` ${i + 1} ${sub.name} ${sub.total} ${obtained !== null ? obtained : "—"} ${sub.pass} ${grade} ${statusTxt} `; tbody.appendChild(tr); }); /* Grand total row */ const finalGrade = grandTotal ? getGrade(grandObtained, grandTotal) : "—"; const finalStatus = allPass ? "Pass" : "Fail"; document.getElementById("rc-grand-total").textContent = grandTotal || "—"; document.getElementById("rc-grand-obtained").textContent = grandObtained || "—"; document.getElementById("rc-final-grade").textContent = finalGrade; document.getElementById("rc-final-status").textContent = finalStatus; document.getElementById("rc-final-status").className = allPass ? "badge-pass" : "badge-fail"; /* Summary */ const pct = grandTotal ? ((grandObtained / grandTotal) * 100).toFixed(1) + "%" : "—"; document.getElementById("rc-percentage").textContent = pct; document.getElementById("rc-overall-grade").textContent = finalGrade; const badge = document.getElementById("rc-result-badge"); badge.textContent = finalStatus; badge.className = "rc-summary-value result-badge " + (allPass ? "pass" : "fail"); /* Class position */ const position = calcPosition(db, emis, roll); document.getElementById("rc-position").textContent = position; /* Remarks & Signatures */ document.getElementById("rc-remarks").textContent = saved.remarks || "—"; document.getElementById("rc-principal-label").textContent = school.principal || "Principal"; /* Generated date */ document.getElementById("rc-generated-date").textContent = new Date().toLocaleDateString("en-PK", { day: "2-digit", month: "short", year: "numeric" }); } /* Calculate position among all students in the same school/class */ function calcPosition(db, emis, roll) { const school = db[emis]; const test = school.testInfo; const subjects = test.subjects; const scores = school.students.map(s => { const saved = school.marks[s.roll] || {}; let total = 0; subjects.forEach(sub => { if (saved[sub.name] !== undefined && saved[sub.name] !== "") total += parseFloat(saved[sub.name]); }); return { roll: s.roll, total }; }); scores.sort((a, b) => b.total - a.total); const pos = scores.findIndex(s => s.roll === roll) + 1; const n = scores.length; const sfx = pos === 1 ? "st" : pos === 2 ? "nd" : pos === 3 ? "rd" : "th"; return ${pos}${sfx} / ${n}; } /* ============================================================ RESULT CARD — BACK BUTTON ============================================================ */ document.getElementById("btn-back-from-result").addEventListener("click", () => { /* Go back to whichever page the user came from */ if (SESSION.emis && document.getElementById("page-admin").classList.contains("active") === false) { showPage("page-generate"); } else { showPage("page-admin"); } }); /* ============================================================ PRINT ============================================================ */ document.getElementById("btn-print").addEventListener("click", () => { window.print(); }); /* ============================================================ DOWNLOAD PDF (html2pdf.js) ============================================================ */ document.getElementById("btn-download-pdf").addEventListener("click", () => { const element = document.getElementById("result-card"); const student = document.getElementById("rc-student-name").textContent || "result"; const filename = ResultCard_${student.replace(/\s+/g,"_")}.pdf; const options = { margin: [8, 8, 8, 8], filename, image: { type: "jpeg", quality: 0.98 }, html2canvas: { scale: 2, useCORS: true }, jsPDF: { unit: "mm", format: "a4", orientation: "portrait" }, }; showLoading(true); html2pdf() .set(options) .from(element) .save() .then(() => { showLoading(false); showToast("PDF downloaded!", "success"); }) .catch(() => { showLoading(false); showToast("PDF generation failed. Try printing instead.", "error"); }); }); /* ============================================================ BACK BUTTONS ============================================================ */ document.getElementById("btn-back-from-login").addEventListener("click", () => { showPage("page-landing"); }); /* ============================================================ INIT ============================================================ */ function init() { seedDemo(); restoreSession(); /* If a session is still alive, go straight to admin */ if (SESSION.emis) { openAdminPanel(); } else { showPage("page-landing"); } } init();

Are you Interested To Get Our Featured Services

Enjoy Your Free Trial Now - Get a Free sample of each service. And Fully free website audit for single website. This Service is Limited time offer.