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

08 — Form Specifications (Academics Module)

Field-by-field specs for every create/update form. Field metadata (required, type, validation, defaults) is exact from src/modules/academics/dto/*; server validation via class-validator (ValidationPipe → 400 VALIDATION_ERROR with per-field details, 00-shared/07 §3). Shared form rules: full-screen page (> 3 fields), submit button loading state (anti-double-submit), errors inline per field + focus first invalid, offline → blocked with banner (00-shared/08 §6, 00-shared/10 §2). Dates: IsDateString = ISO-8601 YYYY-MM-DD (swagger examples, create-academic-year.dto.ts:9-15).


1. Academic Year — Create (POST /academic-years)

Fields exact from create-academic-year.dto.ts:4-21:

FieldReqControlValidation (server)Notes / UI
nameAppTextFieldIsStringe.g. "2026-2027"; 409 on duplicate (academic-year.service.ts:25-29) — inline banner, server copy
startDateAppDatePickerIsDateStringISO date; helper "e.g. 2026-04-01"
endDateAppDatePickerIsDateStringclient rule: must be > startDate (server does not check — OQ-5 01; still block client-side)
statusAppDropdownIsStringenum values upcoming/active/archived (academic-year.schema.ts:7-11); omitted → server defaults upcoming (academic-year.service.ts:30-33); keep optional, default "Upcoming"
  • Success: 200 doc → navigate to detail; snackbar "Year created".

2. Academic Year — Update (PATCH /academic-years/:id)

From update-academic-year.dto.ts:4-28: all optional — name, startDate, endDate, status, isCurrent (bool).

  • UI rule: isCurrent is not shown as a normal checkbox — edits use "Set as current" on the list/detail (PATCH /:id/set-current, academic-year.service.ts:69-83) which also forces status: active. Inline checkbox would fight that; keep the dedicated action.
  • Server sets only provided fields ($set, academic-year.service.ts:64).

3. Grade — Create (POST /grades)

From create-grade.dto.ts:4-24:

FieldReqControlValidationNotes
nameAppTextFieldIsStringe.g. "Grade 1"; 409 on duplicate (grade.service.ts:21-24)
academicYearIdAppDropdown (year)IsMongoIdschema optional (grade.schema.ts:9-10) — label "Attach to year (optional)"; empty allowed, sent as null/omitted
codeAppTextFieldIsStringe.g. "G1"; auto-uppercase formatter
displayOrderAppTextField (number)IsInt, Min(0), default 0ladder position; default = next max+1 (client pre-fill, editable)
  • Success → list refresh (keeps displayOrder sort, grade.service.ts:39).

4. Grade — Update (PATCH /grades/:id)

From update-grade.dto.ts:4-30: all optional — academicYearId, name, code, displayOrder, status. status shown only for admin; free-string field with a suggested picker of active / archived (schema default 'active', grade.schema.ts:21-22; OQ-2 01).

5. Section — Create (POST /sections)

From create-section.dto.ts:4-28:

FieldReqControlValidationNotes
gradeIdAppDropdown (grade)IsMongoIdpre-selected when arriving from grade detail
nameAppTextFieldIsStringe.g. "A"; client warns if (gradeId, name) duplicates (server does not check — OQ-4)
capacityAppTextField (number)IsInt, Min(1), default 40
classTeacherIdAppDropdown (users, teacher role)IsMongoIdlabel "Class teacher (user)" — create-section.dto.ts:19-21
roomIdAppTextFieldIsStringfree text (OQ-6 01)

6. Section — Update (PATCH /sections/:id)

From update-section.dto.ts:4-35: all optional — gradeId, name, capacity, classTeacherId, roomId, status. Reparenting caution: changing gradeId orphans classes referencing this section (class.schema.ts:18-19 required) — client shows confirm "Classes of this section keep pointing at it".

7. Class — Create (POST /classes)

From create-class.dto.ts:4-41:

FieldReqControlValidationNotes
academicYearIdAppDropdown (year)IsMongoIdcascading picker part 1 (07 §6)
gradeIdAppDropdown (grade)IsMongoIdpart 2; disabled until year picked (UI only — server doesn't cross-check refs)
sectionIdAppDropdown (section, filtered by grade)IsMongoIdpart 3; disabled until grade picked
nameAppTextFieldIsStringauto-suggest "Grade {grade.name} - {section.name}" (editable) — example "Grade 10 - A" (create-class.dto.ts:22)
capacityAppTextField (number)IsInt, Min(1), default 40
campusIdAppDropdown (campus)IsMongoIdhidden (planned) — no campus module/endpoint; schema ref points at Class (OQ-6)
classTeacherIdAppDropdown (users, teacher role)IsMongoId"Class teacher (user)" — create-class.dto.ts:32-35
roomIdAppTextFieldIsStringfree text (OQ-6)
  • No uniqueness on (academicYearId, gradeId, sectionId, name) (class.service.ts:16-18) — client shows a "Duplicate class?" confirm when the exact combo already exists in cache (OQ-4).

8. Class — Update (PATCH /classes/:id)

From update-class.dto.ts:4-50: all optional — academicYearId, campusId, gradeId, sectionId, name, capacity, classTeacherId, roomId, status. Same reparenting caution as section (below, §6).

9. Subject — Create (POST /subjects)

From create-subject.dto.ts:4-47:

FieldReqControlValidationNotes
codeAppTextFieldIsStringe.g. "MATH101"; 409 on duplicate (subject.service.ts:21-25) + DB unique index (subject.schema.ts:38); auto-uppercase
nameAppTextFieldIsStringe.g. "Mathematics"
shortNameAppTextFieldIsStringe.g. "Maths"
creditsAppTextField (number)IsInt, Min(0), default 0
maximumMarksAppTextField (number)IsInt, Min(1), default 100
passingMarksAppTextField (number)IsInt, Min(1), default 33
theoryMarksAppTextField (number)IsInt, Min(0), default 80
practicalMarksAppTextField (number)IsInt, Min(0), default 20
  • Client cross-validation (server has none — OQ-5 01): theoryMarks + practicalMarks ≤ maximumMarks; passingMarks ≤ maximumMarks; error under the offending field, block submit.

10. Subject — Update (PATCH /subjects/:id)

From update-subject.dto.ts:4-42: all optional — code, name, shortName, credits, maximumMarks, passingMarks, status. theoryMarks / practicalMarks are NOT updatable — do not render those fields in edit mode; add a helper line "Theory/Practical marks locked (planned)" (OQ-5). code change → 409 if taken (service checks only on create; a duplicate code update is not checked and will hit the unique index → 500-mapped 409 by the exception filter — client surfaces as "Code already in use" on 409/5xx DUPLICATE per http-exception.filter.ts mapping; flag OQ-2 01).

11. Subject Assignment — Create (POST /subject-assignments)

From create-subject-assignment.dto.ts:4-20:

FieldReqControlValidationNotes
classId(context, hidden)IsMongoIdfrom the class screen — not user-entered
academicYearId(context, hidden)IsMongoIdfrom year switcher
subjectIdAppDropdown (subjects, searchable)IsMongoIdalready-assigned subjects disabled (client guard, OQ-4)
teacherIdAppDropdown (users, teacher role, searchable)IsMongoId(planned) dedicated teachers list

No update form (no endpoint) — "Replace" flow = confirm dialog → DELETE /subject-assignments/:id → reopen create sheet.


Shared submission behaviour (all forms)

  • Submit → button loading (label replaced, 00-shared/08 §6); fields disabled.
  • 400 → per-field errorText from error.details[] (field names match DTO keys); focus first invalid.
  • 401 → silent refresh then retry once; fail → sessionExpired.
  • 409 → inline AppBanner(error) with server message (name/code conflicts).
  • 429 → countdown banner; no auto-retry (00-shared/07 §4).
  • 5xx → generic error + requestId (never server internals).
  • Success → snackbar + navigate (list or detail per entity); no optimistic write (server returns canonical doc — reconcile from response).