Every screen the Academics module needs, mapped to its exact endpoint source.
All endpoints from src/modules/academics/controllers/*. Read/write split per
02_User_Personas.md matrix (intended, RBAC (planned) — OQ-1).
Route /academics/years
Endpoint GET /academic-years (paginated) — academic-year.controller.ts:27-29
Purpose All years, newest first (default sort=createdAt:-1, academic-year.service.ts:47); status badges; current-year flag
Actions Add year (FAB), set current (row menu), edit, delete (soft)
Empty AppEmptyState: "No academic years yet — add your first"
Roles Admin/coordinator full; teacher/student read
Route /academics/years/:id
Endpoint GET /academic-years/:id (academic-year.controller.ts:30-32); classes feed GET /classes/by-year/:academicYearId (class.controller.ts:30-35)
Purpose Dates, status, isCurrent; inline list of the year's classes
Actions set-current, edit dates/status, delete
Route /academics/grades
Endpoint GET /grades (paginated) — grade.controller.ts:27-29; default displayOrder:1 (grade.service.ts:39)
Purpose The tenant's ladder (Grade 1…12) in defined order; optional code (G1), per-grade status (free string, grade.schema.ts:21-22)
Actions Add grade, edit, delete, reorder via displayOrder
Route /academics/grades/:id
Endpoint GET /grades/:id (grade.controller.ts:30-32); sections GET /sections/by-grade/:gradeId (section.controller.ts:30-35)
Purpose Grade metadata + its sections (chips) + classes linking it (client-joined)
Actions Edit grade; add section; jump to classes of grade
Route /academics/classes (optional year filter via year switcher)
Endpoint GET /classes (class.controller.ts:27-29) or GET /classes/by-year/:academicYearId (class.controller.ts:30-35)
Purpose All classes; each row shows joined grade + section chips, capacity, status
Actions Add class (cascading pickers), edit, delete
Route /academics/classes/:id
Endpoint GET /classes/:id (class.controller.ts:36-38); roster GET /subject-assignments/by-class/:classId?academicYearId= (subject-assignment.controller.ts:24-29)
Tabs Overview (meta + chips) · Subjects & teachers (assignment matrix rows) · Students (link to Students module, (planned) read-only count)
Actions Edit class; add subject+teacher; replace/remove assignment; jump to timetable/attendance (consumers)
Route /academics/sections/:id
Endpoint GET /sections/:id (section.controller.ts:36-38); classes of section = client-joined from GET /classes
Purpose Section meta (capacity, classTeacher, room) + classes using it
Actions Edit section; delete
Route /academics/sections
Endpoint GET /sections (section.controller.ts:27-29) or by-grade
Purpose All sections (or of one grade) with grade label
Route /academics/subjects
Endpoint GET /subjects (subject.controller.ts:27-29); insertion order (subject.service.ts:35-47)
Purpose Catalog: code (unique), name, shortName, marks config preview, status
Actions Add subject, edit, delete
Route /academics/subjects/:id
Endpoint GET /subjects/:id (subject.controller.ts:30-32)
Purpose Full marks configuration (max/passing/theory/practical) + usage hint (classes assigning it — client-joined from rosters)
Actions Edit (note: theory/practical not updatable — update-subject.dto.ts:4-42, OQ-5)
Route /academics/classes/:id/assign
Endpoint roster by-class; create POST /subject-assignments (subject-assignment.controller.ts:21-23); delete DELETE /subject-assignments/:id (:36-38)
Purpose Full-screen matrix: rows = assigned (subject, teacher); add = subject picker → teacher picker
Notes No update endpoint → "Replace" = delete+create; duplicates possible server-side (OQ-4)
Route /academics/explore
Endpoint composition of GET /academic-years, /grades, /sections/by-grade, /classes/by-year (client-side tree join; no tree endpoint exists)
Purpose One-screen tree: Year → Grade → Section → Class, with subject count badges; used by coordinators for gap spotting
Roles All (read); admin/coordinator get inline quick-actions
Route /academics/teachers/:teacherId
Endpoint GET /subject-assignments/by-teacher/:teacherId?academicYearId= (subject-assignment.controller.ts:30-35)
Purpose Teacher's teaching load per year: class + subject rows; conflict spotter (same subject in 2 classes)
Roles Teacher (own, year-scoped), coordinator (any)
Routes /…/new and /…/:id/edit for year, grade, section, class, subject — one
screen per entity, field specs in 08_Form_Specifications.md .
Screen Reads Writes
Year list / detail GET /academic-years(,/:id)POST, PATCH /:id, PATCH /:id/set-current, DELETE /:id
Grades list / detail GET /grades(,/:id), GET /sections/by-grade/:gradeIdPOST /grades, PATCH /grades/:id, DELETE /grades/:id
Sections list / detail GET /sections(,/:id)POST /sections, PATCH /sections/:id, DELETE /sections/:id
Classes list / detail GET /classes, GET /classes/by-year/:y, GET /classes/:idPOST /classes, PATCH /classes/:id, DELETE /classes/:id
Subjects list / detail GET /subjects(,/:id)POST /subjects, PATCH /subjects/:id, DELETE /subjects/:id
Assignment matrix / roster GET /subject-assignments/by-class/:c?academicYearId=, by-teacher/:t?academicYearId=POST /subject-assignments, DELETE /subject-assignments/:id
Explorer composition above —