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

05 — Screen Inventory (Users Module)

Every screen the module owns, with purpose, entry, data sources, and states. Detailed specs in 06_Screen_Specifications.md. Screens are (planned) (no client repo); data source citations are backend-exact.


S1 — Users list (master)

Route/users
EntrySettings group → "Users & Roles"
PurposeFind, filter, and act on every person in the tenant
Data sourceGET /api/v1/users?page&limit&sort&q (users.controller.ts:42-46; users.service.ts:90-115) + GET /api/v1/rbac/members for role chips (rbac.controller.ts:57-61)
Query contractq$or regex on firstName/lastName/email/displayName (users.service.ts:92-99); default sort -createdAt (:103); paginated envelope meta {page,limit,totalItems,totalPages,hasNext,hasPrevious} (pagination-query.dto.ts:32-55)
StatesInitial → skeleton rows; success+empty → AppEmptyState ("No users — add your first user"); error → AppErrorState (404 never for lists; 429 → backoff copy)
Key elementsAppSearchBar (debounce 300 ms) · filter chips row (status, role — (planned) server params, client-side filter meanwhile, 04_IA §10) · user rows: AppAvatar + name/displayName + email + status badge + role chips + last-login · pagination (infinite scroll on phone, page controls on desktop)
ActionsRow tap → S3 detail · row menu: Edit / Deactivate

S2 — Create user (wizard-lite form)

Route/users/new
EntryList FAB
Data sourcePOST /api/v1/users with CreateUserDto (create-user.dto.ts:5-62) + POST /api/v1/rbac/members after success
ElementsIdentity section (firstName, middleName?, lastName, displayName?, email, phone?) · demographics (gender?, dateOfBirth?) · locale (language, timezone) · status (default active) · membership section (roles from GET /rbac/roles)
Submissioncreate user → on success immediately create membership; if membership fails show explicit "created without roles" banner + retry (OQ-5)
StatesForm validation per field; 409 email/phone → inline conflict; success → snackbar + navigate to detail

S3 — User detail

Route/users/:id
Data sourcesGET /users/:id (users.controller.ts:48-53) · GET /rbac/members (join by userId) · GET /users/:id/preferences (:78-83)
ElementsHeader: avatar + name + status badge + email + roles · tabs: Profile (all fields incl. lastLoginAt, emailVerified, phoneVerified, metadata) · Membership (roles + member status) · Preferences (manager view) · Audit (planned) (audit-write events exist — event-queue-map.ts:11-12; no read endpoint in scope)
ActionsEdit · status menu · Delete / GDPR erase (top-bar overflow)
States404 → "User not found" empty state (cross-tenant ids resolve 404 — base.repository.ts:20-30)

S4 — Bulk import wizard (upload → preview → result)

Route/users/import
EntryList toolbar "Import CSV"
Data sourcePOST /api/v1/users/import (multipart file, users.controller.ts:104-110); future bulk-module adapter path POST /api/v1/bulk/import/:entity (planned) for a users entity (bulk-import.service.ts:17-20)
Steps1 Upload — file picker (.csv only), drag-drop on desktop, size guard, template download · 2 Preview — client-side parse in isolate: header map, row count, first rows table, column mapping (firstname/first_name), warnings (missing optional cols, quotes detected) · 3 Result — imported/errors counts, error table Row N: message (users.service.ts:281), download-error CSV, "Import more"
Progressstage-based client progress; server phase indeterminate (sync endpoint, 03_User_Journey.md §2)
StatesUpload invalid file → inline error; malformed (no header/data) → server returns errors ["CSV must have a header row and at least one data row"] (users.service.ts:238-243); partial success → result screen

S5 — Edit user

Route/users/:id/edit
Data sourcePATCH /users/:id with UpdateUserDto (update-user.dto.ts:5-70)
Elementssame fields as S2 minus roles (roles edited in S3 Membership via PATCH /rbac/members/:idrbac.controller.ts:69-73); email/phone conflicts → 409 inline (users.service.ts:120-134)
NotesdisplayName recomputed server-side when names change (users.service.ts:136-139) — show resulting displayName

S6 — Preferences (self + manager)

Route/settings/profile/preferences (self) · /users/:id/preferences (manager)
Data sourceGET/PATCH /api/v1/users/:id/preferences (users.controller.ts:78-93)
ElementsNotifications: email/push/sms switches · Theme: mode radio `light
StatesLoad → skeleton; save → button spinner; 404 → not found

S7 — Self profile

Route/settings/profile
Data sourceGET /users/:id with :id = jwt sub (client-resolved; server self-guard (planned) — OQ-3)
Elementsavatar (POST /users/:id/avatar, users.controller.ts:95-102) · name/email/phone/gender/DOB/language/timezone · status read-only (self) · link to S6 preferences · link to password/2FA/sessions (auth module surface, auth.controller.ts:109-165)
NotesemailVerified/phoneVerified read-only; see OQ-4 (user-level emailVerified flag not maintained by verifyEmailauth.service.ts:217-223)

S8 — Confirm dialogs (delete / erase / deactivate)

ScopeModal overlays on S1/S3
VariantsDeactivate (plain confirm + consequence copy: still log-in capable if credentials exist) · Delete (typed confirm "delete"; 30-day purge note — tenant-purge.worker.ts:32-43) · GDPR erase (typed confirm "ERASE"; irreversible — anonymization users.service.ts:188-197 + purge job :200-209)

S9 — Import result / error report

ScopeStep 3 of S4 (also standalone after server-side error-only response)
Elementsheadline counts (imported / failed), error table (row number, backend message), download-error-CSV, "Import more"
Copy ruleerror strings rendered as-is from server (Row N: …users.service.ts:256-278); prefixed with a static i18n title, never interpreted client-side

Screen → endpoint matrix

ScreenEndpoint(s)
S1GET /users · GET /rbac/members
S2POST /users · POST /rbac/members · GET /rbac/roles
S3GET /users/:id · GET /rbac/members · GET /users/:id/preferences
S4POST /users/import (future: POST /bulk/import/users (planned))
S5PATCH /users/:id
S6GET/PATCH /users/:id/preferences
S7GET /users/:id · POST /users/:id/avatar
S8(client-only dialogs)
S9(client-only; feeds retry of POST /users/import)