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

09 — User Behaviour (Attendance Module)

How teachers actually use the marking surface, the mistakes they make, and what the UI must do about each. All corrective behaviour maps to backend semantics — most importantly the upsert-overwrite model (attendance.repository.ts:23-29) and the one-doc-per-student-per-day invariant (attendance.schema.ts:59-62).


1. Fast-marking patterns

PatternFrequency driverUI support
Mark-by-exception (assume present)90%+ of classesDefault-present chips (dashed = unsaved); only changed rows send requests
Sweep all absentsurprise absences (sports day, late bus)FAB "Mark all absent" → bulk confirm → UndoBar
Tap-cycle to correctwalk-by marking1/2/3-tap cycle Present→Absent→Late→Present (06 §2)
Filter to "unmarked"end-of-class sweepStatusFilterChips with counts
Mark-late for stragglerslate arrivalspopover: late + checkIn=now (06 §4)
Batch for a subgrouplab sessions, early dismissalslong-press multi-select → BatchEditSheet

The 60-second rule: the design goal — an entire 40-student class marked in ≤ 60 s. Anything requiring a dialog per student violates it; the only blocking dialog is the overwrite-conflict banner (deliberate, because re-marks silently change data).

2. Mistakes & recovery

2.1 Wrong date

  • What happens: teacher marks today but means yesterday; or opens yesterday to correct and accidentally marks today.
  • System behaviour: each day is independent (docs keyed by date). Nothing merges; wrong-day marks persist as separate docs.
  • UI:
    • Date is pinned in the AppBar subtitle and the grid header ("Mon, 20 Jul 2026") —
    • mark requests embed the pinned date; the chip shows the date in the popover title.
    • A "Marked for Mon 20 Jul" confirmation snackbar on first mark of the session (once per date-switch, not per mark).
    • Correcting: teacher switches date → marks correct → the wrong-day record is found in S4 day view → PATCH status to present/holiday as appropriate (no delete endpoint exists — OQ-11: no DELETE /attendance/:id in attendance.controller.ts; a "wrong day" fix = set holiday or remark, flagged for the product owner).

2.2 Wrong status (mistap)

  • What happens: tap-cycling overshoots (present→absent→late when only absent was meant), or taps the wrong row.
  • System behaviour: single-tap writes immediately (upsert). No server-side undo.
  • UI:
    • The 3-state cycle makes overshoot recoverable in one more tap (back to intended).
    • UndoBar (4 s) after every mark: reverts by re-sending the previous status (safe — upsert overwrite).
    • Haptics lightImpact on save confirm the tap landed on the right row.

2.3 Wrong student (row confusion)

  • What happens: two similar names / misaligned rows on a long grid.
  • UI: rows alternate tint (zebra, surfaceVariant 4% alpha) only when > 30 students; name is titleMedium + rollNumber (mono, student.schema.ts:23-25); popover title always shows full name + roll no; filters collapse the list so targets are adjacent.

2.4 Marking the same student in two classes (same day)

  • What happens: student attends class A period 1 and class B period 2; teacher B marks them; unique index {tenantId, studentId, date} (attendance.schema.ts:59-62) permits one doc per day — remarking from class B overwrites class A's record and keeps class A's classId (attendance.repository.ts:23-33).
  • UI: first grid session of the day shows a one-time inline hint on rows already marked today: chip shows existing status + tooltip "Already marked in Class A — this will overwrite". The overwrite-conflict banner (§3) covers the multi-select case.

2.5 Typos in remarks / check-in times

  • What happens: 09:15 vs 09:50; wrong free-text note.
  • UI: popover shows existing values ("currently 09:12"); PATCH flow (S7/S4) edits in place. Client-side numeric/time formatting; remarks free text ≤ 500 chars.

3. Conflict semantics (must-read)

SituationServer truthUI
Remark same statusno-op upsert (same values re-saved, attendance.repository.ts:25-27)chip flashes, no banner
Remark different status, single rowoverwritechip flips + "was: absent" transient label
Batch containing already-marked rowsoverwrite all in one bulkamber banner "N already marked — will be overwritten"; CTA "Overwrite N" (06 §3)
Offline queue collides with newer online data on flushlast-write-wins per student/date (upsert)flush preview lists collisions; user confirms or drops stale rows
Two devices/teachers raceunique index: one doc; both upserts, last write winsrare; grid refresh on 409

4. Notification-driven behaviour (parents)

  • Alert arrival (when (planned) dispatch lands): parent taps → studylyon://attendance/:date → S7 day view → sees status + source (manual/biometric, attendance.schema.ts:16-21).
  • Copy honesty: "3 absences this month" (worker counts all absent docs, no window — attendance.worker.ts:70-74; OQ-1). Never "consecutive".

5. Data-entry ergonomics (physical)

  • One-hand tablet landscape: chips right side, thumb zone.
  • Landscape keyboard on desktop: arrows navigate rows, P/A/L/H/V/D set status on focused row, Enter opens popover, Esc exits selection/modes.
  • Sticky header (MarkedCountBar + filters) so "done?" is always visible mid-scroll.
  • Auto-scroll to next unmarked row after filter-to-unmarked marking (proposed).

6. Behaviour rules summary

  1. Never block single-row marking with a dialog (exception: overwrite-conflict in batch).
  2. Every write is instantly visible in chip + MarkedCountBar (optimistic + pending indicator, 13_State_Management.md §5).
  3. Every failure is recoverable: snackbar retry; offline queue flush; undo where safe.
  4. Time/date shown in tenant-local time; wire values always YYYY-MM-DD / ISO-8601.
  5. Honest about pipeline gaps (biometric "captured, not applied", alert "planned").