feat(portal-admin): skeleton app per ADR-0020 #100

Merged
julien merged 1 commits from feat/portal-admin/skeleton into main 2026-05-12 01:49:20 +02:00
Owner

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 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

  • Lint + test + build green across the workspace.
  • 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-statichttp://localhost:4300/en/ and /fr/ show the placeholder with the matching language.
## 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.
julien added 1 commit 2026-05-12 01:47:28 +02:00
feat(portal-admin): skeleton app per ADR-0020
CI / scan (pull_request) Successful in 2m34s
CI / commits (pull_request) Successful in 2m43s
CI / check (pull_request) Successful in 2m49s
CI / a11y (pull_request) Successful in 2m36s
CI / perf (pull_request) Successful in 6m37s
a9b36efb75
First step of the admin track. 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` so module-boundary lint
  treats `portal-admin` distinctly from `portal-shell` and only
  allows it to depend on `scope:portal-admin` + `scope:shared`
  libs.
- Tailwind v4 wired (`.postcssrc.json` + `@import 'tailwindcss'`),
  class-based dark mode (`@custom-variant dark ...`), brand palette
  imported from `libs/shared/tokens/src/brand-tokens.css` —
  identical visual baseline to portal-shell. ADR-0016 a11y baseline
  applies; ADR-0017 perf budget relaxed to **500 KB gzip initial**
  per ADR-0020 §"Performance budgets".
- i18n configured to mirror 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`
  carries the four marked strings used by the placeholder page +
  skip-link + route title.
- Skeleton home page at `/` with a placeholder `<h1>`, intro
  paragraph, and a small "Skeleton — coming soon" status chip in
  the brand-accent palette. Marked for i18n.
- Skip-link landmark (WCAG 2.4.1) at the App level, with the same
  styling as portal-shell (component-scoped scss).
- 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) so both
  can run in parallel during cross-app development.
- `serve-static` target without `spa: true` — locale-prefixed
  routing in production will be handled by the reverse proxy (per
  ADR-0019), same as portal-shell since PR #92.

Production build: 62 KB gzip initial per locale (vs the 500 KB
relaxed budget). Both `dist/apps/portal-admin/browser/{en,fr}/`
emitted with the right `<html lang>` and `<base href>`.

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

- Admin app shell (header / sidebar / footer with "Admin" badge).
- OpenTelemetry tracing setup (will register as
  `service.name=portal-admin` per ADR-0012).
- `environment.ts` wiring (per ADR-0018) for the admin's own BFF
  endpoints.
- BFF `AdminModule` + `AdminRoleGuard` + first `/api/admin/me`
  smoke route (per ADR-0020 §"Auth").
- First functional module (CMS / menu / users / audit viewer).

CLAUDE.md picks up the second app in its naming + commands sections.
julien merged commit d962be838a into main 2026-05-12 01:49:20 +02:00
julien deleted branch feat/portal-admin/skeleton 2026-05-12 01:49:23 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: julien/apf_portal#100