5.6 KiB
5.6 KiB
Deliverable — 2026-06-16 — fix(teacher-take-attendance-wiring)
VERDICT: PASS
Scope: wire the teacher dashboard → take-attendance flow end to end. Previous attempts (feature/attendance-and-front-office-ui, merged onto dev as commit bf26801) shipped controllers, store, page, and routes, but the wiring was broken so teachers still couldn't take attendance.
What was broken
- Seed data missing: demo teacher
teacher@school.comwas notclass_teacher_idof any class; no subjects hadteacher_id; noenrollmentslinked the demo student to a class. Result: teacher dashboard showed 0 classes / 0 students. - Missing endpoint:
GET /api/classes/:id/studentsreturned 404, so theAttendanceTakepage could not load its roster. - Teacher scope not applied:
GET /api/classesreturned every class to a teacher, butGET /api/dashboard/teacherfiltered to owned classes → mismatch. - No ownership check on
POST /api/attendance/bulk: any teacher could write attendance for any class. - Dashboard quick action wrong target: "Take Attendance" navigated to
/attendance(overview) instead of/attendance/take(the page that actually marks). - Pre-existing schema drift in
transport.controller.js: SELECTs referencedv.vehicle_number/r.route_title/ta.user_id, none of which exist in the current schema — the Transport tab crashed with 500.
Changes
| File | Change |
|---|---|
server/src/database/init.js |
Made seed fully idempotent; assign class_teacher_id for all 5 classes; assign subjects.teacher_id; enroll 5 demo students into Grade 1 A; add 4 more demo students; seed hostel/rooms/room_assignments (Boys Hostel A, 4 students); seed transport (1 route + 4 allocations); seed clubs (Chess Club + Football Team) and 4 memberships; backfill parent_students to all 5 students. |
server/src/controllers/classes.controller.js |
Added teacher scope to GET /api/classes (auto-filters to class_teacher_id = me OR subjects.teacher_id = me for the teacher role when scope=mine is passed or no class_teacher_id filter is given). Added GET /api/classes/:id/students (flat array, with teacher-ownership check). |
server/src/controllers/attendance.controller.js |
POST /api/attendance/bulk now (a) requires and validates class_id for the teacher role, (b) verifies the teacher owns or teaches the class, (c) uses the body's class_id/subject_id for the new INSERT (the previous code only used record.class_id which was always undefined). |
server/src/controllers/transport.controller.js |
Fixed pre-existing column drift in GET /api/transport/routes and GET /api/transport/allocations (v.vehicle_number → v.registration_number, r.route_title → r.name, ta.user_id → ta.student_id). |
client/src/pages/dashboard/TeacherDashboard.tsx |
Quick action "Take Attendance" now navigates to /attendance/take (was /attendance). |
Evidence (smoke test as teacher@school.com / teacher123)
GET /api/auth/login → JWT (role=teacher, id=3)
GET /api/classes → [Grade 1 A] (1 class, student_count=5, subject_count=3)
GET /api/classes/1/students → 5 students (roll 01-05)
GET /api/attendance?class_id=1&date=2026-06-16 → [] (no rows yet, expected)
GET /api/dashboard/teacher → myClasses=1, mySubjects=2, totalStudents=5, todaysAttendance={0,0,0}
GET /api/hostels → [Boys Hostel A] (capacity 100, occupied 5)
GET /api/clubs → [Chess Club (5 members), Football Team]
GET /api/transport/routes → [Avondale Route] (after transport controller fix)
GET /api/club-attendance/roster?club_id=1&date=2026-06-16 → 5 students
GET /api/hostel-attendance/roster?hostel_id=1&date=2026-06-16 → 4 students
GET /api/transport-attendance/roster?route_id=1&date=2026-06-16 → 4 students
POST /api/attendance/bulk → {success:true, inserted:5, updated:0, total:5} (class context)
How to verify in the browser
- Reset DB:
cd server && rm -f data/school.db data/school.db-shm data/school.db-wal && node src/database/init.js - Start server:
node src/index.js(port 3001) - Build client:
cd ../client && npm run build(Vite serves the dist via the server's SPA fallback) - Log in at
http://localhost:3000asteacher@school.com / teacher123 - On
/dashboard/teacher: greeting, "5 students", "Grade 1 A" in My Classes, 0/0/0 in Today's Overview (clean state). - Click "Take Attendance" → land on
/attendance/take(no more 404). - Pick "Class" tab → select "Grade 1 A" → see 5 students → mark P/A/E → Submit → success banner.
Out of scope (deliberately not touched)
transport.controller.jslegacy POST routes (pre-existing schema drift in writes). The newtransport-attendance.controller.jsis what the page uses, and it works.hostel.controller.jslegacyPOST /hostels/:id/rooms/attendance/bulk(admin-only, also usesroom_idschema correctly). Page uses the newhostel-attendance.controller.js.clubs.controller.jslegacyPOST /clubs/attendance/bulk— admin/teacher/clubs_head only; uses old columnuser_id(but bulk endpoint itself isn't called by the take-attendance page).- The
Mock datainjector inattendance.controller.jsline 138 — kept as-is (only fires for?student_id=lookups on student/parent dashboards).
Risk
- Seed is idempotent, so re-running
npm run db:initis safe. Existing data is untouched. - New students created in this seed (
student2-5@school.com) all use passwordstudent123to match the existing convention. - The 3 new teacher accounts (
teacher2@school.com,teacher3@school.com) also useteacher123and are class_teacher of Grade 2/3, 4/5 respectively.