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

Field-by-field specs. Server column = exact DTO contract (create-timetable-entry.dto.ts). Weak-validation warnings are derived facts (the DTO validates types/ids but not time format or ordering).


F1 — Create Timetable Entry (POST /timetable)

#FieldTypeRequiredServer validationClient inputDefault
1classIdstring (ObjectId)@IsMongoId (create-timetable-entry.dto.ts:6-8)Searchable picker from GET /classes (or prefilled from grid scope)
2subjectIdstring (ObjectId)@IsMongoId (:10-12)Searchable picker from GET /subjects
3teacherIdstring (ObjectId)@IsMongoId (:14-16)Searchable picker from GET /teachers
4roomIdstring (ObjectId)@IsOptional @IsMongoId (:18-21)Picker from GET /rooms (code + name)unset (schema optional, timetable.schema.ts:27-28)
5dayOfWeekstring enum@IsEnum(DayOfWeek) (:23-25)6 chips Mon–Sat from DayOfWeek (timetable.schema.ts:7-14)current day of week (proposed)
6startTimestring@IsString (:27-29) — format NOT validatedAppTimePicker 24 h → HH:MM zero-padded (08:00); regex `^([01]\d2[0-3]):[0-5]\d$ (proposed)`
7endTimestring@IsString (:31-33) — no > start checkAppTimePicker; client enforces end > start (proposed)09:00
8academicYearIdstring (ObjectId)@IsMongoId (:35-37)AppDropdown from GET /academic-years, default isCurrent (academic-year.schema.ts:31-32)current year

Client-side rules (server gaps — derived)

  • Time format is load-bearing: overlap detection compares strings lexically (start1 < end2 && start2 < end1, timetable.service.ts:62-69). It is correct only for zero-padded HH:MM. The server never validates format/order, so the client:
    • forces 24 h HH:MM input (picker-based, no free text),
    • blocks end <= start before submit (a non-ordered pair would make timeOverlaps math meaningless),
    • never sends "8:00" / "9:00" (would silently break conflict detection).
  • end == start is allowed as a boundary (back-to-back slots legal — timeOverlaps returns false, timetable.service.ts:68); client should still prefer ≥ 1-row gaps for readability (proposed).
  • Class-level overlap is not a server error (OQ-1) — the editor must NOT show a class-conflict error; server accepts.
  • Room-less entries: conflict detection reduces to teacher-only (the roomId: undefined query clause is stripped by Mongoose; timetable.service.ts:20).
  • Pre-flight: on picker change, consult cached per-day grids; warn for teacher/room clashes (server 409 remains authoritative — races possible).

Submit payload (exact)

{ "classId": "…", "subjectId": "…", "teacherId": "…", "roomId": "…",
  "dayOfWeek": "monday", "startTime": "08:00", "endTime": "09:00",
  "academicYearId": "…" }

(roomId omitted when unset — never send null/""; DTO whitelist + forbidNonWhitelisted pipes reject unknown keys, main.ts:50-57.)

Server responses

  • 200 data = created entry doc (envelope-unwrapped; timestamps auto, timetable.schema.ts:16).
  • 409 ConflictException "Schedule conflict detected" (timetable.service.ts:28) → ConflictBanner, form values kept. No structured error code — client matches on 409 status.
  • 400 VALIDATION_ERROR: field details mapped (00-shared/07 §3); invalid dayOfWeek (e.g. "sunday") fails @IsEnum (:23-25).
  • Notes: tenantId never sent (from token, base.repository.ts:33-35); forbidNonWhitelisted means the editor sends exactly the 8 fields above.

F2 — Update / Delete Timetable Entry — (planned)

  • No endpoints exist — no PATCH /timetable/:id, no DELETE /timetable/:id (timetable.controller.ts:10 exposes only POST + GET). Any edit/delete UI is (planned) pending backend (roadmap row "Timetable: Bulk create, conflicts, substitution, workload, export", IMPLEMENTATION_PLAN.md:226).
  • Interim UX: "Duplicate & correct" — open F1 prefilled from the slot; original stays until a delete surface exists (OQ-2).