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 (Exams Module)

Every form field, field-by-field: label, widget, keyboard, validation, errors, defaults. All validations mirror the DTO decorators exactly — anything the server does not validate is marked client-only (see 01_Product_Overview.md §10 OQ-2/OQ-3/OQ-6). DTO sources: examination.dto.ts, examination-subject.dto.ts. Error codes per 00-shared/07 §3.


1. Create Exam Form (S8)

Submit: POST /api/v1/examinations (examination.controller.ts:27-29); payload = CreateExaminationDto (examination.dto.ts:4-26). Server forces status: 'draft' (examination.service.ts:48).

#FieldLabelWidgetKeyboardServer validationClient-only validationDefaultError
1academicYearIdAcademic yearAppDropdown@IsMongoId (examination.dto.ts:6-7)none (required)"Select the academic year" / 400 VALIDATION_ERROR
2nameExam nameAppTextFieldtext (autocorrect off)@IsString (examination.dto.ts:10-11)required, ≤100 chars, trim"Enter the exam name"
3typeExam typeAppDropdown@IsString (no enum decorator, examination.dto.ts:13-17; schema enum ['midterm','final','unit_test','quarterly','other'], examination.schema.ts:15-20 — invalid values fail at save() as a validator error)picker constrained to the 5 values"Choose a type"
4startDateStart dateAppDatePicker@IsDateString (examination.dto.ts:19-21)client-only:endDate (OQ-6)"Start date must be on or before end date"
5endDateEnd dateAppDatePicker@IsDateString (examination.dto.ts:23-25)client-only:startDate (OQ-6)"End date must be on or after start date"

Submit behavior: disabled until required fields valid; spinner on submit; on 400 VALIDATION_ERROR → inline field errors (focus first invalid); success → 201 doc → pop to S2 with snackbar "Exam created". Offline: form is online-only; unsaved draft persisted to local draft box on back (optional, (proposed)).

2. Edit Exam Form (S8a)

Submit: PATCH /api/v1/examinations/:id (examination.controller.ts:36-41); payload = UpdateExaminationDto (examination.dto.ts:28-53) — all optional (@IsOptional), only changed fields sent ($set, base.repository.ts:57-66). Prefill from GET /examinations/:id; academicYearId locked (not in DTO).

#FieldServer validationClient rules
1name?@IsOptional @IsStringas §1
2type?@IsOptional @IsStringpicker only
3startDate? / endDate?@IsOptional @IsDateStringwindow rule as §1
4status?@IsOptional @IsStringfree string, schema enum NOT enforced on PATCH (updateById without runValidators, base.repository.ts:57-66; OQ-5)client only ever sends an explicit draft → active transition (OQ-1); never published; once published, edit unreachable (immutability contract)

Errors: 400 field errors; 404 "Examination not found." (examination.service.ts:87) → pop + snackbar "This exam no longer exists."

3. Add Subject Slot Form (S4)

Submit: POST /api/v1/examinations/:id/subjects (examination.controller.ts:45-50; examinationId injected from route, examination.service.ts:49); payload = CreateExaminationSubjectDto (examination-subject.dto.ts:11-45).

#FieldLabelWidgetKeyboardServer validationClient-only validationDefaultError
1subjectIdSubjectAppDropdown@IsMongoId (examination-subject.dto.ts:16-18)client-only: warn "already scheduled" if duplicate (no server dedupe — OQ-3)"Select a subject"
2classIdClassAppDropdown@IsMongoId (examination-subject.dto.ts:20-22)"Select a class"
3dateExam dateAppDatePicker@IsDateString (examination-subject.dto.ts:24-26)client-only: within exam startDate..endDate; warn overlap with existing slot times (OQ-2)"Date must be within the exam window"
4startTimeStart timeAppTextFieldTextInputType.datetime (HH:mm)@IsString (examination-subject.dto.ts:28-30; example '09:00')required, HH:mm pattern09:00"Enter a valid time (HH:mm)"
5endTimeEnd timeAppTextFielddatetime@IsString (examination-subject.dto.ts:31-34)client-only: endTime > startTime (OQ-6)12:00"End time must be after start time"
6maximumMarksMaximum marksAppTextFieldnumber (digits)@IsNumber @Min(1) (examination-subject.dto.ts:36-39)client-only:passingMarks; sane cap 1000100"Maximum marks must be at least 1"
7passingMarksPassing marksAppTextFieldnumber@IsNumber @Min(1) (examination-subject.dto.ts:41-44)client-only:maximumMarks (OQ-6)33"Passing marks cannot exceed maximum marks"

Conflict dialog: on overlap/duplicate detection (fields 1–5), submit shows AppDialog "Overlaps with Subject X 09:00–12:00 — add anyway?" (server won't reject; OQ-2/OQ-3). Errors: 400 VALIDATION_ERROR inline; 404 "Exam subject not found." — n/a here; exam missing → 404 → pop.

4. Enter Marks Form (S3, per row)

Submit: POST /api/v1/results/exam-subject/:examSubjectId/marks (result.controller.ts:26-31); payload = EnterMarksDto (examination-subject.dto.ts:47-66). Upsert: an existing (studentId, examinationSubjectId) doc is updated in place (examination.service.ts:147-175; unique index examination-result.schema.ts:31-33).

#FieldLabelWidgetKeyboardServer validationClient-only validationDefaultError
1studentId(hidden)filled from roster row@IsMongoId (examination-subject.dto.ts:49-51)roster student
2marksObtainedMarksMarksTextFieldnumber, digits only@IsNumber @Min(0) (examination-subject.dto.ts:52-55)critical: marksObtained ≤ maximumMarks of the slot — server rejects with 404 NotFoundException('Marks cannot exceed maximum.') (examination.service.ts:145-146), not 422inline "Marks cannot exceed {max}"; on server 404 → refresh slot max, re-enter
3grade?GradeGradeChip → grade sheet (text)text@IsOptional @IsString (examination-subject.dto.ts:57-60) — free text, never computed server-side per subject (OQ-9)optional; ≤10 chars
4remarks?RemarksAppTextField multiline (2–3 lines)text@IsOptional @IsString (examination-subject.dto.ts:62-65)optional; ≤500 chars

Row submit cadence: save on focus-loss or explicit save tap; retry per failed row; offline → row queued (see 10_Interaction_Specification.md §3, 13_State_Management.md §4). Marks blank → not submitted (no clear endpoint); client offers "Clear" by submitting 0 with confirm ("0 is stored — server treats missing marks as 0 in report cards", result.service.ts:70).

5. Validation error mapping (all forms)

Server codeClient handling
400 VALIDATION_ERRORinline field errors (details[] per 00-shared/07 §3), focus first invalid
401 / 403session flow / permission screen (00-shared/06 §3.6)
404 RESOURCE_NOT_FOUNDresource-gone state (marks: "Marks cannot exceed maximum." is 404 — special-cased, examination.service.ts:146)
409 DUPLICATE_RESOURCEnot expected today (no unique constraints on exams/slots, OQ-3)
429 RATE_LIMITEDbackoff, no auto-retry (00-shared/07 §4)
5xxgeneric + requestId, retry