03 — User Journeys (Staff Module)
- J1 — Create a staff member (Org Admin / HR)
- J2 — Manage departments (Org Admin / HR)
- J3 — Manage designations (Org Admin / HR)
- J4 — Edit a staff profile (HR)
- J5 — Deactivate a staff member (HR / Org Admin)
- J6 — Browse the staff directory (read-only
(proposed))
End-to-end journeys mapped to the exact API surface. All backend behavior cited from source; flows marked
(proposed)for client-only decisions.
J1 — Create a staff member (Org Admin / HR)
Backend steps: POST /staff (staff.controller.ts:24) → duplicate guard
(staff.service.ts:31-36) → create with status: ACTIVE, employmentType
defaulting to full_time (staff.service.ts:37-43) → StaffCreated event
(staff.service.ts:44-54 → in-app queue, event-queue-map.ts:34).
flowchart LR
A[Admin opens Staff list] --> B[Taps FAB Add staff]
B --> C[Create Staff form]
C -->|user selected from Users list| D[POST /api/v1/staff]
D --> E{employeeNumber unique?}
E -- no --> F[409 DUPLICATE_RESOURCE<br/>inline error on employeeNumber]
F --> C
E -- yes --> G[201 + staff record<br/>status=active default]
G --> H[StaffCreated event<br/>in-app notification]
H --> I[Snackbar 'Staff created'<br/>navigate to detail]
- Form fields:
userId(required picker),employeeNumber(required),departmentId,designationId,employmentType,joiningDate,salaryGrade(create-staff.dto.ts:5-38). statusis not settable at creation (staff.service.ts:39).- Haptic:
lightImpacton success (00-shared/08 §3).
J2 — Manage departments (Org Admin / HR)
Backend steps: POST /departments duplicate-name guard (department.service.ts:22-30);
list via GET /departments (department.controller.ts:29); update
PATCH /departments/:id; delete DELETE /departments/:id (soft).
No events emitted by the catalog services (department.service.ts:19-20).
flowchart TD
A[Departments list] --> B[FAB Add department]
B --> C[POST /api/v1/departments<br/>name required, code?, headId?]
C --> D{name duplicates?}
D -- yes --> E[409 inline error]
E --> C
D -- no --> F[201 department<br/>status=active]
F --> G[Row appears in list]
A --> H[Row menu: Edit / Deactivate]
H --> I[PATCH /api/v1/departments/:id<br/>name, code, headId, status]
H --> J[DELETE /api/v1/departments/:id]
J --> K[404 if already deleted]
K --> L[Removed from list<br/>no member-count check OQ-4]
headIdpicker sources staff (department.schema.ts:15-16); a soft-deleted head renders as unlinked ((proposed)— backend stores the bare ref).- Dept→staff counts and "reassign members before deleting" are not enforced server-side — see OQ-4; the client must present the risk in the confirm dialog.
J3 — Manage designations (Org Admin / HR)
Backend steps: POST /designations duplicate-name guard (designation.service.ts:25-28);
level is IsInt (designation.dto.ts:14-17); optional departmentId
(designation.schema.ts:9-10).
flowchart LR
A[Designations list] --> B[FAB Add designation]
B --> C[POST /api/v1/designations<br/>name required, level?, departmentId?]
C --> D{name duplicates?}
D -- yes --> E[409 inline error]
D -- no --> F[201 designation<br/>level default 0, status active]
F --> G[List, sorted by level<br/>client-side proposed]
A --> H[Edit: PATCH /api/v1/designations/:id]
H --> I[Update name/level/dept/status]
A --> J[Delete: DELETE /api/v1/designations/:id]
leveldefaults to0(designation.schema.ts:15-16); sort-by-level is(proposed)— the API returns insertion order (designation.service.ts:41-50).
J4 — Edit a staff profile (HR)
Backend steps: PATCH /staff/:id (staff.controller.ts:33) → existence check 404
(staff.service.ts:79) → $set + version++ (base.repository.ts:57-66) →
StaffUpdated with changed keys (staff.service.ts:82-90).
flowchart LR
A[Staff detail] --> B[Edit button]
B --> C[Edit form prefilled from GET /staff/:id]
C --> D[PATCH /api/v1/staff/:id]
D --> E{exists?}
E -- no --> F[404 RESOURCE_NOT_FOUND]
F --> G[Empty-state 'not found']
E -- yes --> H[200 updated record]
H --> I[StaffUpdated → audit-write]
I --> J[Snackbar 'Saved'<br/>detail refreshes]
- All fields optional on update (
update-staff.dto.ts:5-42) — send only changed fields;changeslist is the audit delta (staff.service.ts:88). statusmay be set here — this is the sanctioned way to move a member toon_leave/inactive/terminated(update-staff.dto.ts:35-38).
J5 — Deactivate a staff member (HR / Org Admin)
Backend steps: DELETE /staff/:id (staff.controller.ts:36) → soft delete sets
isDeleted + deletedAt + deletedBy and bumps version (base.repository.ts:68-74)
→ 404 when already deleted (staff.service.ts:94-95) → StaffDeleted audit event
(staff.service.ts:96-103).
flowchart LR
A[Staff detail or row menu] --> B[Deactivate]
B --> C{Confirm dialog<br/>'This removes the staff member from all lists'}
C -- cancel --> A
C -- confirm --> D[DELETE /api/v1/staff/:id]
D --> E{found & active?}
E -- no --> F[404 RESOURCE_NOT_FOUND]
E -- yes --> G[200 empty data<br/>StaffDeleted → audit-write]
G --> H[Navigate back to list<br/>row gone; snackbar]
- No restore path exists (no un-delete endpoint) — the dialog must say so
(
base.repository.ts:68-74; OQ-8 for a future restore). - The list refetches because soft-deleted rows are excluded from every query
(
base.repository.ts:20-30).
J6 — Browse the staff directory (read-only (proposed))
GET /staff?page=&limit= (staff.controller.ts:27) — no server-side q/sort
for staff (staff.service.ts:64-76, OQ-2). Journey: open Staff → list loads →
pull-to-refresh → infinite scroll / page controls → tap row → detail with tabs.
Status badges map 1:1 to StaffStatus (staff.schema.ts:7-12).