/* ================================================================== Overview → Academics ------------------------------------------------------------------ Master data at a glance: teaching staff, the student roll broken down by class, the class/section/subject registry, and how far syllabus mapping has actually got. Reads /admin/cockpit/academics. Every count comes from the Cockpit database; nothing here is illustrative. ================================================================== */ const AcademicsScreen = ({ setScreen }) => { const CX = window.CX; const state = CX.useCockpitData("/admin/cockpit/academics"); const d = state.data; const statusPill = (s) => s === "present" ? Present : s === "on_leave" ? On leave : No record; const WING_COLOR = { primary: "var(--pink-solid)", middle: "var(--blue-solid)", senior: "var(--green-solid)", }; return (
setScreen("students")}> Manage students } /> }> {d && ( <> {/* ---- Teachers · Students ---- */}
Teachers
{CX.num(d.teachers.total)} active {d.teachers.pending ? ` · ${d.teachers.pending} pending approval` : ""} {d.teachers.inactive ? ` · ${d.teachers.inactive} inactive` : ""}
{d.teachers.list.length === 0 && ( )} {d.teachers.list.map((t) => (
{t.name}
{[t.role, t.subjects, t.classes].filter(Boolean).join(" · ") || "No assignments"}
{statusPill(t.status)}
))}
{d.teachers.pendingResets ? `${d.teachers.pendingResets} password reset request${d.teachers.pendingResets === 1 ? "" : "s"} waiting` : "No password reset requests"}
Students
{CX.num(d.students.total)} enrolled
{CX.num(d.students.boarders)}
Boarders
{CX.num(d.students.dayScholars)}
Day scholars
{/* Suppressed by the API when it equals the whole roll (a bulk import), so the tile falls back to section count. */} {d.students.newThisTerm != null ? (
{CX.num(d.students.newThisTerm)}
New in 90 days
) : (
{CX.num(d.classes.sectionCount)}
Sections
)}
BY CLASS
({ label: c.level, pct: c.barPct, value: CX.num(c.students), color: WING_COLOR[c.wing], }))}/>
{/* ---- Classes & subjects · syllabus ---- */}
Classes & Subjects
{d.classes.classCount} classes · {d.classes.sectionCount} sections
{d.classes.rows.map((c) => (
{c.level}
{c.sections} section{c.sections === 1 ? "" : "s"}
))}
{d.subjects.count} SUBJECTS
{d.subjects.list.map((s) => ( {s.name} ))} {d.subjects.count === 0 && ( No subjects registered yet. )}
Registry drives every test-creation picker
Chapters & Topics
Syllabus mapping
{d.syllabus.length === 0 ? ( ) : (
{d.syllabus.map((s, i) => { const colors = ["var(--green-solid)", "var(--blue-solid)", "var(--pink-solid)", "var(--amber-solid)", "var(--violet-solid)", "var(--grey-solid)"]; return (
{s.subject}{s.classLevel ? ` · ${s.classLevel}` : ""} {s.chapters} chapters · {s.topics} topics ·{" "} {s.coveragePct}%
); })}
)}
Coverage is the share of a subject's chapters that have at least one topic registered against them — it is what the AI question generator reads when building a paper.
Chapters feed test authoring and topic tagging
)}
); }; window.CockpitAcademics = AcademicsScreen;