03 — User Journeys (Teachers Module)
- Journey 1 — Create a teacher (Org Admin / HR)
- Journey 2 — Assign subjects/classes (Org Admin / Academic Coordinator)
- Journey 3 — Edit teacher profile (HR / Admin)
- Journey 4 — Deactivate a teacher (HR / Admin)
- Journey 5 — Teacher self-view: profile, assignments, schedule (Teacher)
Five journeys mapped 1:1 to the implemented API. Endpoint citations:
teacher.controller.ts:24-38,subject-assignment.controller.ts:21-38,timetable.controller.ts:21-27.(planned)= roadmap/documented but not in code.
Journey 1 — Create a teacher (Org Admin / HR)
flowchart TD
A[Start: Users list] --> B[Find/create User account]
B --> C[Open Teachers list]
C --> D[FAB 'Add teacher' → Create form]
D --> E{Fill form: userId, employeeNumber, department,\n designation, joiningDate, status, subjects, classes}
E --> F[Submit POST /teachers]
F --> G{Server}
G -->|409 DUPLICATE_RESOURCE| H[Inline error: user already has profile\n OR employeeNumber exists]
G -->|400 VALIDATION_ERROR| I[Field errors per DTO]
G -->|200| J[TeacherCreated event]
J --> K[In-app notification: teacher-created]
J --> L[Search index: Teacher]
J --> M[Success snackbar → navigate to detail]
- Steps:
POST /teacherswithCreateTeacherDto(create-teacher.dto.ts:4-52). - Business rules:
findByUserId→ 409 (teacher.service.ts:27-31);findByEmployeeNumber→ 409 (teacher.service.ts:32-38). - Side-effects:
TeacherCreated→in-appqueue jobteacher-created(event-queue-map.ts:31) → notification (inapp.worker.ts:46-53); search index upsert (search-indexer.service.ts:10,90-96). - Note:
PLAN.md:36says "→ email queue → welcome email" — the code routes toin-app, notemails(see01 §6, QA-12). - Failure exits: 401 (expired token → refresh), 429 (backoff), 5xx (generic + requestId).
Journey 2 — Assign subjects/classes (Org Admin / Academic Coordinator)
flowchart TD
A[Teacher detail → Assignments tab] --> B[Pick academic year\ndefault = current]
B --> C[Load matrix GET /subject-assignments/by-teacher/:id?academicYearId=]
C --> D[Empty state: 'No assignments this year'\n CTA 'Add assignment']
D --> E[Assignment editor: subject × class × year]
E --> F{Duplicate check client-side\n teacher+subject+class+year exists?}
F -->|yes| G[Block: inline conflict message]
F -->|no| H[POST /subject-assignments]
H --> I{Server}
I -->|200| J[Append row to matrix]
I -->|400/404| K[Error banner, no state change]
I -->|500| L[Generic error + requestId]
J --> M[Optional: DELETE /subject-assignments/:id\n to remove mis-assignment]
- Sources:
subject-assignment.service.ts:13-17(create — no server dup check),subject-assignment.service.ts:26-31(byTeacher, requiresacademicYearId),subject-assignment.schema.ts:24-25(non-unique indexes). academicYearIdcomes fromGET /academic-years(academic-year.controller.ts:27-30) withisCurrentflag (academic-year.schema.ts:31-32).- Class picker:
GET /classes?academicYearId(class.controller.ts:27-33); subject picker:GET /subjects(subject.controller.ts:27-30).
Journey 3 — Edit teacher profile (HR / Admin)
flowchart TD
A[Teachers list → row] --> B[Teacher detail → Profile tab]
B --> C[Edit button → Edit form prefilled from GET /teachers/:id]
C --> D[Change fields: department, designation, joiningDate,\n status, qualification, experienceYears, subjects, classes]
D --> E[Submit PATCH /teachers/:id]
E --> F{Server}
F -->|200| G[TeacherUpdated → audit-write log-teacher-updated]
F -->|404| H['Teacher not found' → return to list]
F -->|400| I[Field errors]
G --> J[Snackbar saved → detail refresh]
UpdateTeacherDto(update-teacher.dto.ts:4-53) —userIdimmutable,metadataadded (only in update DTO,update-teacher.dto.ts:51-53).- Side-effect:
TeacherUpdated→audit-write(event-queue-map.ts:32) + search re-index (search-indexer.service.ts:17). - Server pre-check
findByIdbefore$set(teacher.service.ts:80-83) → PATCH of a soft-deleted teacher returns 404 automatically (soft-delete scope).
Journey 4 — Deactivate a teacher (HR / Admin)
flowchart TD
A[Teacher detail → overflow menu 'Deactivate'] --> B[Confirm dialog:\n 'Deactivate {name}?\n Profile will be hidden from lists.\n Assignments remain in history.']
B --> C[Confirm → DELETE /teachers/:id]
C --> D{Server}
D -->|200| E[TeacherDeleted → audit-write log-teacher-deleted\n + search index removal]
D -->|404| F['Teacher not found' → treat as already deactivated\n → return to list]
E --> G[Snackbar 'Teacher deactivated'\n → navigate back to list (row gone)]
- Soft-delete mechanics:
base.repository.ts:68-74setsisDeleted/deletedAt/deletedBy, all reads exclude (base.repository.ts:20-30). - No server-side guard on active assignments/timetable/substitutions (OQ-5) — dialog copy must warn that schedule data is not cleaned up.
- No re-activation endpoint exists → deactivate is irreversible via API (manual DB restore only). Dialog states this.
Journey 5 — Teacher self-view: profile, assignments, schedule (Teacher)
flowchart TD
A[Login as teacher] --> B[Nav 'My Teaching' (client-side surface)]
B --> C[GET /subject-assignments/by-teacher/:teacherId?academicYearId=]
B --> D[GET /timetable?teacherId=<me>]
B --> E[GET /teachers/:id (own record)]
C --> F[Term overview: subject × class cards]
D --> G[Weekly grid: dayOfWeek × startTime, sorted\n timetable.service.ts:55-60]
E --> H[Profile summary: department, designation,\n status, qualification]
F --> I[If status on_leave → banner 'On leave'\n emphasis off]
G --> J[Tap period → context: attendance/homework\n (other modules, no navigation from backend)]
teacherIdfor self: client resolves fromGET /teachers/:id? There is no/teachers/me— client needs the teacher record linked to the logged-in user (OQ-2). Options: resolve viaGET /teacherslist scan (O(n) — bad), or aGET /teachers/:idper recorded id; backend support for "me" is(planned).- Schedule source:
GET /timetable?teacherId=(timetable.controller.ts:21-27) — note the controller's query param isteacherId, soteacherId=mestring fromEND_TO_END_USER_FLOWS.md:266is doc-fiction; the client must send the real id. - Sort is server-side:
{dayOfWeek:1, startTime:1}(timetable.service.ts:56-59).