geocrop-platform./apps/nextgen/.harness/changelogs/2026-06-16-teacher-take-att...

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

  1. Seed data missing: demo teacher teacher@school.com was not class_teacher_id of any class; no subjects had teacher_id; no enrollments linked the demo student to a class. Result: teacher dashboard showed 0 classes / 0 students.
  2. Missing endpoint: GET /api/classes/:id/students returned 404, so the AttendanceTake page could not load its roster.
  3. Teacher scope not applied: GET /api/classes returned every class to a teacher, but GET /api/dashboard/teacher filtered to owned classes → mismatch.
  4. No ownership check on POST /api/attendance/bulk: any teacher could write attendance for any class.
  5. Dashboard quick action wrong target: "Take Attendance" navigated to /attendance (overview) instead of /attendance/take (the page that actually marks).
  6. Pre-existing schema drift in transport.controller.js: SELECTs referenced v.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_numberv.registration_number, r.route_titler.name, ta.user_idta.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

  1. Reset DB: cd server && rm -f data/school.db data/school.db-shm data/school.db-wal && node src/database/init.js
  2. Start server: node src/index.js (port 3001)
  3. Build client: cd ../client && npm run build (Vite serves the dist via the server's SPA fallback)
  4. Log in at http://localhost:3000 as teacher@school.com / teacher123
  5. On /dashboard/teacher: greeting, "5 students", "Grade 1 A" in My Classes, 0/0/0 in Today's Overview (clean state).
  6. Click "Take Attendance" → land on /attendance/take (no more 404).
  7. 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.js legacy POST routes (pre-existing schema drift in writes). The new transport-attendance.controller.js is what the page uses, and it works.
  • hostel.controller.js legacy POST /hostels/:id/rooms/attendance/bulk (admin-only, also uses room_id schema correctly). Page uses the new hostel-attendance.controller.js.
  • clubs.controller.js legacy POST /clubs/attendance/bulk — admin/teacher/clubs_head only; uses old column user_id (but bulk endpoint itself isn't called by the take-attendance page).
  • The Mock data injector in attendance.controller.js line 138 — kept as-is (only fires for ?student_id= lookups on student/parent dashboards).

Risk

  • Seed is idempotent, so re-running npm run db:init is safe. Existing data is untouched.
  • New students created in this seed (student2-5@school.com) all use password student123 to match the existing convention.
  • The 3 new teacher accounts (teacher2@school.com, teacher3@school.com) also use teacher123 and are class_teacher of Grade 2/3, 4/5 respectively.