05 — Screen Inventory (Attendance Module)
- S1 — Today's Roster (class picker)
- S2 — Marking Grid (hero screen, fully speced in 06)
- S3 — Batch Edit Sheet
- S4 — History / Calendar View
- S5 — Monthly Report (summary)
- S6 — Report Detail
- S7 — Student Attendance Profile (tab)
- S8 — Biometric Device Status
Eight screens. Purpose, entry, primary tasks, components, API, and states for each. API refs are exact (see 12_API_Mapping.md); flags:
(planned)= backend gap.
S1 — Today's Roster (class picker)
| Purpose | Pick a class (my classes this academic year) + date (default today) → enter marking. Surfaces completion: "23/40 marked". |
| Entry | /attendance (teacher landing, 00-shared/05 §2); timetable class card ("Mark attendance", END_TO_END_USER_FLOWS.md:27); dashboard KPI tap |
| Primary tasks | pick class, switch date (yesterday corrections), start marking, view unmarked count |
| Data | classes via GET /api/v1/classes (academics module); existing marks need class+date → S2 loads them |
| Components | AppListTile (class cards + completion ring), AppDatePicker, AppSectionHeader ("Marked today"), AppBadge (unmarked count), AppFAB |
| States | skeleton → class list (grouped by grade/section) → empty (no classes assigned) |
| Notes | Teacher's classes = classes where classTeacherId = me (class.schema.ts:28) or timetable assignment — resolve via timetable module (planned); fallback: all classes the user can read |
S2 — Marking Grid (hero screen, fully speced in 06)
| Purpose | Mark/overlay one status per student for a class+date; the fastest daily capture surface. |
| Entry | S1 pick → push; deep-link /attendance/class/:id/date/:date |
| Primary tasks | tap-cycle status, sweep-all-absent, batch apply, mark-late w/ check-in, remark existing day (overwrite semantics), undo |
| Data | GET /attendance/class/:classId?date= (existing docs); roster = students with classId (student.schema.ts:38-39); writes POST /attendance or POST /attendance/bulk |
| Components | AttendanceGrid, StatusChip, MarkedCountBar, StatusFilterChips, BatchEditSheet, UndoBar (see 07) |
| States | loading skeleton → grid (rows default-present, existing marks overlaid) → offline (cached roster + queued marks) |
| Rules | one doc per student/day enforced by unique index attendance.schema.ts:59-62; re-mark = overwrite (attendance.repository.ts:23-29) |
S3 — Batch Edit Sheet
| Purpose | Apply one status to many selected rows in a single POST /attendance/bulk. |
| Entry | S2: long-press row (multi-select) → sheet; or "Sweep all to absent" quick action |
| Primary tasks | multi-select rows, choose target status, confirm, see per-record result |
| Data | BulkMarkAttendanceDto {records[]} (mark-attendance.dto.ts:51-54) → sequential marks, one AttendanceMarked per record (attendance.service.ts:48-54) |
| Components | BatchEditSheet (half-height), status chips grid, selection counter, AppButton "Apply to N" |
| Notes | Partial failure handling per OQ-2 (server returns docs array; failures would throw — client shows snackbar with count; retry-safe because upsert) |
S4 — History / Calendar View
| Purpose | Monthly heatmap per class (or per student from S7): status colors per day, tap day → day summary. |
| Entry | /attendance/class/:id/history; S2 app-bar calendar icon; S6 drill-down |
| Primary tasks | month navigation, status-filter heatmap, day detail, jump to S2 read-only for a date |
| Data | GET /attendance/class/:classId?date= per day (N requests, OQ-4 unpaginated) or GET /attendance/student/:studentId?startDate&endDate for S7 |
| Components | CalendarHeatmap, StatusLegend, DaySummaryCard, AppDatePicker |
| Notes | Class-level month = up to ~30 daily fetches — cache aggressively (see 13_State_Management.md); consider server aggregation (planned) when OQ-4 resolves |
S5 — Monthly Report (summary)
| Purpose | Class attendance stats for a range: totals + per-status counts; trigger async report. |
| Entry | /attendance/class/:id/report/:month (admin) — teacher lacks report.* (role.schema.ts:31, OQ) |
| Primary tasks | pick class + range, view summary, request generated report, open detail |
| Data | GET /attendance/summary?classId&startDate&endDate (attendance.service.ts:103-113 — {total, summary:{status:count}}); POST /reports/generate {type:'attendance_summary'} → GET /reports/:jobId (reports.controller.ts:14-24) |
| Components | SummaryDonut (or AppCharts bar), AppStatTile per status, CalendarHeatmap (rate by week), ReportJobCard (queued/processing/completed/failed — report-job.schema.ts:13-18) |
| Notes | Both summary paths aggregate in memory (attendance.service.ts:103-113, reports.service.ts:110-137) — UI must handle slow responses at scale (OQ-8) |
S6 — Report Detail
| Purpose | Full generated attendance_summary result: totals, period, per-status breakdown; drill into students/days. |
| Entry | S5 report card tap; /attendance/reports/:jobId |
| Primary tasks | read result, filter status, drill to S4/S7, re-run for new range |
| Data | GET /reports/:jobId → {status, result:{total, summary, classId, period}} (reports.service.ts:131-136) |
| Components | AppInfoRow (period, class), AppCharts bar/donut, StatusLegend, AppEmptyState (no data) |
| States | queued → processing (progress) → completed / failed (error from report-job.schema.ts:38-39) |
S7 — Student Attendance Profile (tab)
| Purpose | One student's history: month heatmap, % present, per-status counts, day detail. Used by self (student), child (parent), and class teacher (context). |
| Entry | /students/:id → "Attendance" tab; parent alert deep link studylyon://attendance/:date |
| Primary tasks | month nav, % present, day drill-down, mark/remark (teacher only) |
| Data | GET /attendance/student/:studentId?startDate&endDate (attendance.service.ts:63-73) |
| Components | CalendarHeatmap, AppStatTile (% present, absent, late), StatusLegend, AbsenceAlertCard |
| Notes | Role-gating open (OQ-7): parent/student roles have student.read only (role.schema.ts:55,63); END_TO_END_USER_FLOWS.md:324-325,378-379 assume read access |
S8 — Biometric Device Status
| Purpose | Device health + ingest visibility: status (active/inactive/offline, biometric-device.schema.ts:7-11), last punch, ingest count, pipeline state. |
| Entry | /attendance/devices (admin/operator) |
| Primary tasks | list devices, check last-punch freshness, view today's ingest count, understand derivation status |
| Data | devices via biometric module repository (no list endpoint today — (planned), OQ-3); logs GET-able via biometric.log.read (permissions.constants.ts:42) |
| Components | BiometricDeviceTile, AppBadge (status), IngestTrendChart (proposed), AppBanner ("punches captured — not yet applied", OQ-3) |
| Notes | Ingest exists (POST /biometric/ingest, biometric.controller.ts:14-18); biometric-sync cron registered (scheduler.service.ts:71-76) but no worker → nothing becomes attendance (OQ-3) |