Final PR of the portal-admin User-list chantier per ADR-0020 §"v1
scope — User list (read-only)". Ships the SPA viewer at /users that
consumes GET /api/admin/users (PR #141) and renders a filter form
+ paginated table mirroring the audit viewer's shape (PR #136).
What lands
- AdminUsersService (apps/portal-admin/src/app/pages/users/
admin-users.service.ts): thin HttpClient wrapper around
GET /api/admin/users. Drops empty-string filter values (Nest's
ValidationPipe rejects ?foo= as ''). providedIn 'root' — the
users page is the single v1 consumer.
- UsersPage component (apps/portal-admin/src/app/pages/users/
users.ts): signal-driven page composing:
- Filter form: username (prefix), displayName (contains),
audience enum, lastSeenAt range (datetime-local → ISO),
page-size selector (25 / 50 / 100 / 200 matching BFF MAX_LIMIT).
- Result table: displayName, username, audience badge, firstSeen,
lastSeen (locale-formatted), oid (monospaced).
- Pagination: previous/next disabled at boundaries; offset
resets to 0 on Apply Filters or Reset.
- States: loading line, empty state, error with 403 vs 5xx
differentiation matching the audit viewer's posture.
Route + sidebar
- /users route lazy-loaded in app.routes.ts with the
route.users.title i18n marker. messages.fr.xlf updated with the
French translation ('Utilisateurs — Administration APF Portal');
the prod build's i18nMissingTranslation=error policy fails
otherwise.
- AdminSidebar 'User list' entry promoted from aria-disabled
placeholder ('Soon' badge) to a live RouterLink at /users. The
matching spec is updated.
Tests: +15 specs (AdminUsersService 3 covering URL params + empty
+ undefined; UsersPage 11 covering initial fetch, result range,
empty/error states, filter forwarding, Apply offset reset, Reset,
pagination boundaries, display badges; sidebar 1 for the promoted
link). 45 portal-admin specs total.
Build verified: 14.59 KB raw / 3.76 KB gzip for the lazy users
chunk — well under any per-chunk budget. The /audit chunk
remains 18.16 KB / 4.35 KB; the two viewers ship as separate
lazy bundles since each pulls its own service and components.
Chantier portal-admin User-list closed — 3 PRs total (140 schema
+ write, 141 read endpoint, this PR for the viewer).