Module · Attendance
Daily attendance marking by teachers. The smallest module — but the one teachers use most. Get the UX right.
Attendance is the module teachers touch every single day. The whole user experience hinges on how few taps it takes to mark a full section. Plan ~8 hours.
By the end:
/teachershows the teacher's assigned section(s)- Tapping a section opens today's attendance — a list of students with quick Present / Absent toggles
- Submit saves all rows in one Supabase call
/teacher/attendance/historyshows past 30 days- Parents see attendance history for their child on
/parent/{child-id} - The admin view shows attendance % per section over time
1. The Attendance Table
2. The Mark-Attendance Screen (Teacher View)
This is the most-tapped screen in the whole system. Optimise it ruthlessly.
Design choices that matter:
- One row per student, tap to toggle. No checkboxes — visual radio-style buttons.
- "Mark All Present" defaults the obvious case. ~85% of students are present; teacher then toggles the absentees.
- One save at the end, not save-per-row. Avoids 26 separate insert round-trips.
- Roll number visible. Teachers know students by roll number.
- Today's date prominent. Stops mistakes when marking the wrong day's attendance.
3. The Code Pattern
src/app/teacher/sections/[id]/page.tsx (Client Component):
Key things to notice:
.upsert()withonConflict: 'student_id,date'— re-marking is idempotent. Teacher can correct mistakes and re-save without errors.- Defaults everyone to 'present' on load. Teacher only changes the few who are absent. Fewer taps.
- Single mutation saves all 26 rows in one call. Network round-trip count = 1.
4. Filling marked_by and branch_id Server-Side
You don't want the client sending these fields — they should be derived from the authenticated user. Use a Postgres trigger:
Now whatever the client sends (or doesn't send), marked_by and branch_id are always correct.
5. The Parent View
/parent/[childId]/attendance shows a calendar grid of the child's last 90 days. Each day is a coloured square — green for present, red for absent, yellow for late, grey for weekends.
This is one query — select date, status from attendance where student_id = $1 and date >= now() - interval '90 days' — and a small calendar grid component. ~150 lines. Skip optimistic mutations; this view is read-only.
6. The Admin View
/admin/attendance shows attendance % by section and by month. Use a database RPC to do the aggregation in one query:
Call from the client:
One round-trip. RLS still applies — branch admin sees only their branch's sections.
7. What to Verify
- Teacher logs in → can ONLY see attendance for their assigned section(s).
- Marking all 26 students saves in under 300ms.
- Re-marking the same day works without errors.
- Parent sees the calendar with their child's data only.
- Branch admin's analytics view shows their branch only.
- Super admin sees a switcher to view any branch.
If any of these is wrong, fix it before Exams — every later module follows this pattern.