/* ================================================================== Overview → Staff Attendance ------------------------------------------------------------------ The heat-map from the reference design, driven by teacher_attendance. ONE THING TO UNDERSTAND BEFORE READING A NUMBER HERE: rows in teacher_attendance are written when a teacher checks in on the mobile app. A day with no row therefore means "nothing was recorded", which is NOT the same as "absent" — and the backend keeps the two apart ('unrecorded' vs 'leave'). This screen never colours an unrecorded day red, and it suppresses the headline average entirely when check-in coverage is too thin to average honestly. A confident 43% would be a lie about people's jobs. ================================================================== */ const StaffAttendanceScreen = ({ setScreen }) => { const CX = window.CX; const [period, setPeriod] = useState("monthly"); const state = CX.useCockpitData(`/admin/cockpit/staff-attendance?period=${period}`); const d = state.data; const exportCsv = () => { if (!d) return; const head = ["Name", "Role", "Subjects", "Days present", "Days on leave", "Holidays", "Working days in window", "Recorded %"]; const body = d.rows.map((r) => [ r.name, r.role, r.subjects || "", r.presentDays, r.leaveDays, r.holidayDays ?? 0, r.workDays, `${r.recordedPct}%`, ]); CX.exportCsv(`staff-attendance-${period}.csv`, [head, ...body]); }; const LEGEND = [ { label: "Present", cls: "present" }, { label: "On leave", cls: "leave" }, { label: "No record", cls: "unrecorded" }, { label: "Holiday", cls: "holiday" }, { label: "Weekend", cls: "weekend" }, ]; return (
setScreen("dashboard")} actions={ <> } /> }> {d && ( <>
Teaching staff
{CX.num(d.staffTotal)} on the roll
Checked in today
{CX.num(d.presentToday)} of {d.staffTotal}
Days with any data
{CX.num(d.daysWithData)} in this window
Check-ins recorded
{CX.num(d.marksInWindow)} {d.coverageMeaningful ? "usable" : "too sparse to average"}
{!d.coverageMeaningful && (
Attendance percentages are hidden for this window.{" "} Only {d.marksInWindow} check-in{d.marksInWindow === 1 ? " has" : "s have"} been recorded across {d.rows.length} staff, so any average would describe app adoption rather than who came to work. Days with no record are drawn as outlines below — never as absences.
)}
{period === "daily" ? "Last 14 days" : period === "monthly" ? "Last 31 days" : "Last 12 months (weekly)"}
{LEGEND.map((l) => ( {l.label} ))}
{d.rows.length === 0 && ( )} {d.rows.map((r) => (
{r.name}
{[r.role, r.subjects].filter(Boolean).join(" · ")}
{r.cells.map((c, i) => ( ))}
{d.coverageMeaningful ? `${r.recordedPct}%` : `${r.presentDays}/${r.workDays}`}
))}
{d.rows.length} staff · {d.coverageMeaningful ? "percentage is days present ÷ working days in window" : "showing days present ÷ working days, not a percentage"} Source · teacher check-in app
)}
); }; window.CockpitStaffAttendance = StaffAttendanceScreen;