07 — Component Library (Users Module)
- 1. Module component list
- 2. UserListTile
- 3. StatusBadge
- 4. RoleChips
- 5. FilterBar
- 6. AvatarUploader
- 7. ImportWizard
- 8. CsvPreviewTable
- 9. ImportErrorList
- 10. PreferencesPanel
- 11. PermissionScoped
- 12. Reused shared components (no new code)
- Golden-test expectation (00-shared/03 §G)
Module-specific components. Shared building blocks referenced from 00-shared/03_Component_Library.md are not redefined. All components are
(planned)(no client repo). Backend-derived constraints cited.
1. Module component list
| Component | Purpose | Builds on |
|---|---|---|
UserListTile | List row for S1 | AppListTile + AppAvatar + AppBadge |
StatusBadge | User status pill | AppBadge |
RoleChips | Role display/selection | AppChips |
FilterBar | Search + status/role chips | AppSearchBar + AppChips |
AvatarUploader | Avatar pick/crop/upload | AppAvatar + AppDialog |
ImportWizard | 3-step bulk import shell | AppStepper + AppProgress |
CsvPreviewTable | Parsed-row preview | table (00-shared/04 §8 desktop) |
ImportErrorList | Row-numbered error table | AppListTile dense + AppBadge |
TemplateDownloadButton | CSV template export (client-generated) | AppButton outlined |
PreferencesPanel | Notification/theme controls | AppSwitch + AppRadio + AppDropdown |
SelfProfileHeader | Avatar + identity header | AppAvatar + AppInfoRow |
PermissionScoped | Conditional render/guard wrapper | route guards (00-shared/05 §9) |
TypedConfirmDialog | Delete/erase typed confirm | AppDialog |
2. UserListTile
- Props:
user,roles?,onTap,onEdit,onDeactivate,onDelete,onErase,selected. - Render: avatar 40 (initials fallback — 00-shared/03 AppAvatar), title =
displayName(users.service.ts:62guarantees non-empty), subtitle =email, trailing =StatusBadge+AppMenu(View · Edit · Deactivate/ Activate · Delete · GDPR erase). - States: selected (master-detail), loading avatar placeholder.
- A11y: single button semantics; badge text included in summary.
3. StatusBadge
- Maps
UserStatusexactly (user.schema.ts:7-12):
| Status | Token | Icon+label (never color-only, 00-shared/09 §9) |
|---|---|---|
active | success/secondary | ✓ Active |
inactive | neutral onSurfaceVariant outlined | ◌ Inactive |
suspended | error/tertiary | ⊘ Suspended |
invited | primaryContainer | ✉ Invited |
- Unknown value (server could add) → neutral "Unknown" badge, no crash (forward-compat).
4. RoleChips
- Props:
roles[],onToggle,editable,loading. - Data:
GET /rbac/roles(rbac.controller.ts:27-31) — list mode shows member roles fromGET /rbac/membersjoined byuserId(rbac.controller.ts:57-61); empty when the viewer lacksorg_admin(that endpoint is role-gated:21-22) — show "—" with tooltip, never fake. - Selected chip =
secondaryContainer(00-shared/04 §2).
5. FilterBar
- Search:
AppSearchBar, debounce 300 ms, server-sideq(users.service.ts:92-99). - Status chip group (
StatusBadgevalues) + role chip group: client-side filter of loaded pages with explicit caption "(planned) server filter — applies to loaded results only" (04_IA §10, OQ-9). - Clear-all button resets chips + query.
6. AvatarUploader
- Flow: tap avatar → pick image (
file_picker) → square-crop dialog → multipartPOST /users/:id/avatar(users.controller.ts:95-102) withfield name = file(FileInterceptor('file'):96). - Response
{avatarFileId}(users.service.ts:230) — display via storage URL resolution(planned)(no URL from this endpoint; file id only). - States: uploading (avatar overlay spinner), error (revert + snackbar),
success (
HapticFeedback.lightImpact). - Size guidance: keep ≤ 2 MB (no backend cap in code — OQ-10).
7. ImportWizard
AppSteppersteps: 1 Upload → 2 Preview → 3 Result (00-shared/03 AppStepper).- Owns
BulkImportCubitstate (13_State_Management.md §6); back-nav from step 2 → step 1 keeps parsed file. - Destructive exit (leave mid-flow) → unsaved-changes dialog (parse state only; nothing server-side pending).
8. CsvPreviewTable
- Columns from parsed header (
users.service.ts:244-247); row numbers 1-based, header = row 1 — error rows areindex + 2(bulk-import.service.ts:46precedent) andRow Nin users import refers to the physical line (users.service.ts:257) — the preview must display physical line numbers to match error strings. - Highlight rows flagged client-side (missing email, dup email, quotes).
9. ImportErrorList
- Rows: physical row number (mono font, 00-shared/02 §2) + message
verbatim; badge count = errors length; "Download errors" button exports
row,messageCSV client-side. - Empty state when
errors.length === 0→ success check icon.
10. PreferencesPanel
AppSwitch× 3 (email/push/sms —update-user-preferences.dto.ts:8-12),AppRadiotheme mode × 3 (light|dark|system—:16-20),AppDropdownlanguage.- Save = merged full object (
users.service.ts:161-163replaces wholesale).
11. PermissionScoped
- Wrapper taking a permission string (e.g.
user.delete) → hides/rebuilds subtree; route-level equivalent viapermissionGuard(00-shared/11 §6). - Caution: server users controller currently enforces JWT only
(
users.controller.ts:31); RBAC guard on those routes is(planned)— the client still gates by permission so behavior is correct the day the guard lands (OQ-11).
12. Reused shared components (no new code)
AppSearchBar, AppChips, AppStepper, AppProgress, AppEmptyState,
AppErrorState, AppSnackbar, AppDialog, AppBottomSheet, AppMenu,
AppDatePicker, AppAvatar, AppBadge, AppListTile, AppInfoRow,
AppFAB, AppTabs, AppSkeleton.
Golden-test expectation (00-shared/03 §G)
UserListTile, StatusBadge (4 states), RoleChips, ImportErrorList,
PreferencesPanel at 3 sizes + light/dark.