Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

07 — Component Library (Attendance Module)

Module-specific components; all shared primitives (AppCard, AppListTile, AppBottomSheet, …) come from 00-shared/03 and are referenced by name. Token references per 00-shared/02; status colors follow the standalone roles (success / warning / error, 02 §1.1).


Status model (shared by all components)

StatusToken roleIcon (Material Symbols, filled)Label (i18n key)
presentsuccesscheck_circleatt.status.present
absenterrorblockatt.status.absent
latewarning (tertiary)scheduleatt.status.late
half_daywarning (tertiary)right_half_circleatt.status.halfDay
leaveneutral surfaceVariantbeach_accessatt.status.leave
holidayneutral surfaceVariantwb_sunnyatt.status.holiday
unmarkedoutlineVariantradio_button_uncheckedatt.status.unmarked

Statuses/enum from attendance.schema.ts:7-14. Never color-only: icon + label always (00-shared/09 §9).


1. AttendanceGrid

  • Purpose: the marking surface — virtualized roster rows with status chips.
  • Props: rows: List<AttendanceRow> (student + current doc), statuses: Map<studentId, Status>, onCycle(row) (tap-cycle), onOpenPopover(row), selection: SelectionState?, enabled (read-only mode), filter (from StatusFilterChips).
  • Structure: header row (sticky) + ListView.builder rows (56 dp, itemExtent fixed — perf, see 15_Flutter_Implementation_Guide.md); optional empty state slot.
  • Semantics: table role (00-shared/09 §7), row summary "Aarav Sharma, absent".
  • Golden tests: 3 sizes × light/dark; 60-row build < 8 ms frame budget.

2. StatusChip

  • Purpose: single status affordance on a row; tap = cycle (Present→Absent→Late→Present).
  • Props: status, saved (dashed outline when unsaved/queued), dense (44 dp a11y toggle), onTap, loading, selected.
  • Behaviour: Mo.m-fast color/icon morph; mini spinner when in-flight; long-press → popover (owner: row gesture arbitration).
  • A11y: Semantics(toggled: status != unmarked), label = status name + "double-tap to change"; state announced via live region on change.

3. StatusChipPopover (bottom sheet)

  • Purpose: full status picker + optional checkIn/checkOut/remarks for one row.
  • Props: row, existingDoc? (prefill, GET /attendance/:id), onSave(patch).
  • Contents: 6 status chips (grid 3×2), AppTimePicker check-in (default now), check-out optional, AppTextField remarks, AppButton "Save".
  • Semantics: sheet title "Mark Aarav Sharma — 20 Jul".

4. BatchEditSheet

  • Purpose: apply one status to N selected rows (POST /attendance/bulk, mark-attendance.dto.ts:51-54).
  • Props: selection, conflicts (rows already marked differently), onApply(status).
  • Contents: preview list (5 rows + "+29 more"), current→target status chips, conflict AppBanner (amber, "will be overwritten"), CTA "Apply to N" / "Overwrite N".
  • A11y: counter announced; banner is a live region.

5. MarkedCountBar

  • Purpose: sticky live summary of the day ("23 marked · 17 unmarked").
  • Props: counts: Map<Status,int>, total.
  • Behaviour: animates on change (AnimatedSwitcher, Mo.m-fast); compact on phone (horizontal scroll). Live region for a11y.

6. StatusFilterChips

  • Purpose: client-side filter of grid rows.
  • Props: selected, counts (badge per status), onChanged.
  • Rules: "All" default; chips show counts as AppBadge; filtering never refetches (roster in memory).

7. CalendarHeatmap

  • Purpose: month grid of day cells colored by status mix; used in S4 (class) and S7 (student).
  • Props: days: Map<date, DayStats>, selectedDate, onDayTap, disabledBefore/future.
  • Cell: 40×40 min; color = dominant status (ties: worst status wins — absent > late > half_day > leave > present); empty = neutral.
  • A11y: each cell label "20 Jul — 28 present, 2 absent"; cells are buttons ≥ 44 dp.
  • Perf: cell = CustomPaint-free plain Container/InkWell; month builds via GridView.builder (see 15).

8. DaySummaryCard

  • Purpose: per-day counts + drill actions (S4 tap).
  • Props: date, counts, onOpenGrid (S2 read-only), onOpenStudent (S7).
  • Contents: status legend row with counts, source mix note (manual/biometric/import/api from attendance.schema.ts:16-21).

9. SummaryDonut (S5/S6)

  • Purpose: per-status share of a range (GET /attendance/summary{total, summary:{present:n,...}}, attendance.service.ts:103-113).
  • Props: summary, total, onStatusTap (filter).
  • A11y: Semantics summary text "Total 812: 750 present, 34 absent, …" (00-shared/09:60); tabular fallback list when summary empty (total: 0).

10. AbsenceAlertCard (S7, (planned) dispatch — OQ-1)

  • Purpose: surface absence pattern to parents: "3 absences this month".
  • Props: count, window (visible month), onViewDays.
  • Copy rules: count from worker semantics = total absent docs in window (attendance.worker.ts:70-74); never claim "consecutive". Banner tone warning.

11. BiometricDeviceTile (S8)

  • Purpose: device row: health + ingest state.
  • Props: device (name, model, location, status from biometric-device.schema.ts:15-31), lastPunchAt, todayCount, onTap (detail).
  • States: status badge (active success / inactive neutral / offline error); "no punches in 24 h" warn chip; pipeline AppBanner per 06 §S8.
  • A11y: label "Entrance gate — active — 412 punches today".

12. UndoBar

  • Purpose: 4 s undo window after sweep/batch applies (00-shared/03 undo pattern).
  • Props: description, onUndo.
  • Behaviour: undo re-sends previous statuses via bulk (upsert-safe); expires after 4 s (00-shared/03 §F).

13. ReportJobCard (S5/S6)

  • Purpose: async report job state (report-job.schema.ts:13-18).
  • Props: status (queued/processing/completed/failed), type (attendance_summary, report-job.schema.ts:8-9), result?, error?, onOpen.
  • Behaviour: queued/processing → LinearProgressIndicator; failed → AppErrorState compact with server error string; completed → open S6.

Shared-component usage map

NeedsShared component
Class picker rowsAppListTile + AppAvatar (initials from name)
Date/month pickersAppDatePicker / AppTimePicker
Empty/error/loadingAppEmptyState, AppErrorState, AppSkeleton
ConfirmationsAppDialog (batch overwrite confirm)
Selection bars, sheetsAppBottomSheet (S3), AppMenu (row context)
Status legend & statsAppBadge, AppStatTile, AppCharts (S5/S6 bars)
KPI entryAppBanner (pipeline warning, conflict)