Commit Graph

2 Commits

Author SHA1 Message Date
Julien Gautier 4a707cdb0f feat(portal-admin): spa auth wiring + admin shell skeleton
CI / scan (pull_request) Successful in 3m22s
CI / commits (pull_request) Successful in 3m43s
CI / check (pull_request) Successful in 3m55s
CI / a11y (pull_request) Successful in 2m21s
CI / perf (pull_request) Successful in 7m1s
Phase-3a step per ADR-0020 §"Confirmation" item 4 (entry route +
admin shell). Wires the existing `feature-auth` library against the
distinct admin OIDC routes (`/api/admin/auth/*`) the BFF exposes
since PR #129, ships a lean header/sidebar/footer chrome with an
"Admin" badge so an internal user can never mistake the surface for
portal-shell, and gives the landing page a self-test panel that
confirms the auth chain end-to-end as soon as an `admin` Entra role
gets assigned.

Lib changes (libs/feature/auth)

- New AUTH_PATH_PREFIX injection token. Default factory returns
  `/auth` so portal-shell-shaped hosts keep working without an
  explicit provider. Admin hosts override with `/admin/auth`.
- AuthService composes URLs as `${bffBaseUrl}${pathPrefix}/{me,login,
  logout}`. The interceptors are unchanged — they only care about
  the BFF base URL.

portal-admin wiring

- environment.ts: same shape as portal-shell, same BFF base URL
  (both SPAs talk to one BFF per ADR-0020 §"Where does the admin
  app live"), same CSRF cookie name in v1.
- app.config.ts: HttpClient with the standard interceptor chain
  (credentials → csrf → unauthorized), AUTH_BFF_BASE_URL from env,
  AUTH_PATH_PREFIX = '/admin/auth', AUTH_CSRF_COOKIE_NAME from env.

Admin shell components

- AdminHeader: APF wordmark + persistent "Admin" badge + auth widget.
  No global search / notifications / help cluster — admins land on
  tabular workloads, not discovery. Per ADR-0020 §"UX style is
  data-dense".
- AdminSidebar: static menu listing the four ADR-0020 v1 modules.
  Audit log is a live router link (target of PR 8); the others are
  aria-disabled placeholders with a "Soon" badge so the navigation
  shape is visible even before they ship.
- AdminFooter: copyright + persistent "Admin surface" tag — the
  badge stays in view for long workloads where the header may have
  scrolled off.

Home page

- Auth self-test panel: signed-in payload (display name, tenant,
  oid) when authenticated, Sign in button when anonymous, error
  state when the BFF is unreachable.
- Roadmap list quoting ADR-0020's v1 catalogue so a returning
  contributor sees what's pending without grepping.

Notes

- LayoutStateService isn't consumed yet (no collapse toggle in v1),
  but the theme preference still threads through because both apps
  read the same localStorage key — toggle in portal-shell, see it
  honoured in portal-admin.
- Strings are plain English in v1. The admin app's $localize plumbing
  is wired but adding markers to the shell chrome here would require
  regenerating messages.fr.xlf and i18nMissingTranslation=error fails
  the prod build on every gap. Full i18n is its own follow-up.
- v1 reuses the shared `portal_csrf` cookie name. A user with both
  portals open could see CSRF cookie overwrites on session creation;
  splitting to `portal_admin_csrf` is a follow-up if the pattern
  becomes common.
- Build verified locally: 86.65 KB gzip initial / 300 KB budget,
  4.46 KB CSS / 150 KB budget.

Tests: +15 specs (AdminHeader 5, AdminSidebar 3, AdminFooter 2,
Home 4, App 1 expanded). 1 new test in feature-auth AuthService for
the AUTH_PATH_PREFIX override path.
2026-05-14 16:54:10 +02:00
julien d962be838a feat(portal-admin): skeleton app per ADR-0020 (#100)
CI / check (push) Successful in 2m49s
CI / commits (push) Has been skipped
CI / scan (push) Successful in 2m32s
CI / a11y (push) Successful in 1m26s
CI / perf (push) Successful in 4m17s
## Summary

First step of the admin track per ADR-0020. Scaffold the second Angular SPA in the workspace alongside `portal-shell`, with the architectural decisions from the v1 ADRs already wired so subsequent feature PRs can drop straight into modules.

## What lands

- **App generated** via `nx g @nx/angular:application` (with the matching e2e project skeleton). Standalone, zoneless-ready, prefix `app`, scss component styles, css root stylesheet, no SSR.
- **Tags** `scope:portal-admin, type:app` — module-boundary lint now treats `portal-admin` distinctly from `portal-shell` and lets it depend only on `scope:portal-admin` + `scope:shared` libs.
- **Tailwind v4 + brand tokens**: `.postcssrc.json`, `@import 'tailwindcss'`, class-based dark variant, and `@import '../../../libs/shared/tokens/src/brand-tokens.css'` — identical visual baseline to portal-shell.
- **i18n config mirrors portal-shell** (per ADR-0019): sourceLocale `en`, target `fr`, baseHref `/{locale}/` per locale, `@angular/localize/init` polyfill, `localize: ["en", "fr"]` on production, `i18nMissingTranslation: "error"` so CI blocks any PR that adds a marker without translating it. Seed [`messages.fr.xlf`](apps/portal-admin/src/locale/messages.fr.xlf) carries the four marked strings used today.
- **Budgets** relaxed to **500 KB gzip initial** per ADR-0020 §"Performance budgets" (vs 300 KB for portal-shell).
- **Skeleton home page** at `/` — `<h1>` + intro paragraph + "Skeleton — coming soon" chip in the brand-accent palette. All marked for i18n.
- **Skip-link landmark** (WCAG 2.4.1) with the same component-scoped scss as portal-shell.
- **Wildcard catch-all route** → bounces unknown paths to home (same defensive pattern as PR #96 on portal-shell).
- **Dev server on port 4300** (vs 4200 for portal-shell) — both can run in parallel.
- **`serve-static` target without `spa: true`** — locale-prefixed routing in production is handled by the reverse proxy (per ADR-0019), same as portal-shell since PR #92.

## CLAUDE.md

Picked up the second app in the **Naming** entry and the **Commands** snippet (`<app>` is now one of `portal-shell`, `portal-admin`, `portal-bff`).

## Verification

- `pnpm exec nx run-many -t lint test build --projects=portal-shell,portal-admin,shared-ui,shared-state,shared-tokens` — green.
- `portal-admin` test: 1 spec (skip-link + main landmark present).
- Production build of `portal-admin`: **62 KB gzip initial per locale** (vs 500 KB budget — plenty of headroom for the upcoming modules).
- Both `dist/apps/portal-admin/browser/{en,fr}/` emit with the right `<html lang>` and `<base href>`. FR bundle contains `"Administration"` / `"Squelette"` / `"bientôt disponible"` — translations applied.
- portal-shell production build unchanged.

## Out of scope (each its own follow-up PR)

- Admin app shell (header / sidebar / footer with "Admin" badge — likely sharing graduated primitives plus admin-specific bits).
- OpenTelemetry tracing setup (`service.name=portal-admin` per ADR-0012).
- `environment.ts` wiring (per ADR-0018) for admin-specific endpoints.
- BFF `AdminModule` + `AdminRoleGuard` + smoke `/api/admin/me` (per ADR-0020 §"Auth").
- First functional module — CMS / menu / users / audit viewer.

## Test plan

- [x] Lint + test + build green across the workspace.
- [x] Per-locale production build emits both folders with correct metadata.
- [ ] Manual: `pnpm exec nx serve portal-admin` boots on `:4300`, smoke page renders.
- [ ] Manual: prod build + `pnpm exec nx run portal-admin:serve-static` → `http://localhost:4300/en/` and `/fr/` show the placeholder with the matching language.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #100
2026-05-12 01:49:19 +02:00