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.
124 lines
3.1 KiB
JSON
124 lines
3.1 KiB
JSON
{
|
|
"name": "portal-admin",
|
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
"projectType": "application",
|
|
"prefix": "app",
|
|
"sourceRoot": "apps/portal-admin/src",
|
|
"tags": ["scope:portal-admin", "type:app"],
|
|
"i18n": {
|
|
"sourceLocale": {
|
|
"code": "en",
|
|
"baseHref": "/en/"
|
|
},
|
|
"locales": {
|
|
"fr": {
|
|
"translation": "apps/portal-admin/src/locale/messages.fr.xlf",
|
|
"baseHref": "/fr/"
|
|
}
|
|
}
|
|
},
|
|
"targets": {
|
|
"build": {
|
|
"executor": "@angular/build:application",
|
|
"outputs": ["{options.outputPath}"],
|
|
"options": {
|
|
"outputPath": "dist/apps/portal-admin",
|
|
"browser": "apps/portal-admin/src/main.ts",
|
|
"polyfills": ["@angular/localize/init"],
|
|
"tsConfig": "apps/portal-admin/tsconfig.app.json",
|
|
"inlineStyleLanguage": "scss",
|
|
"assets": [
|
|
{
|
|
"glob": "**/*",
|
|
"input": "apps/portal-admin/public"
|
|
}
|
|
],
|
|
"styles": ["apps/portal-admin/src/styles.css"]
|
|
},
|
|
"configurations": {
|
|
"production": {
|
|
"localize": ["en", "fr"],
|
|
"i18nMissingTranslation": "error",
|
|
"budgets": [
|
|
{
|
|
"type": "initial",
|
|
"maximumWarning": "1.5mb",
|
|
"maximumError": "1.5mb"
|
|
},
|
|
{
|
|
"type": "anyScript",
|
|
"maximumWarning": "500kb",
|
|
"maximumError": "500kb"
|
|
},
|
|
{
|
|
"type": "anyComponentStyle",
|
|
"maximumWarning": "5kb",
|
|
"maximumError": "6kb"
|
|
},
|
|
{
|
|
"type": "bundle",
|
|
"name": "styles",
|
|
"maximumWarning": "150kb",
|
|
"maximumError": "150kb"
|
|
}
|
|
],
|
|
"outputHashing": "all"
|
|
},
|
|
"development": {
|
|
"optimization": false,
|
|
"extractLicenses": false,
|
|
"sourceMap": true
|
|
}
|
|
},
|
|
"defaultConfiguration": "production"
|
|
},
|
|
"extract-i18n": {
|
|
"executor": "@angular/build:extract-i18n",
|
|
"options": {
|
|
"buildTarget": "portal-admin:build",
|
|
"outputPath": "apps/portal-admin/src/locale",
|
|
"outFile": "messages.xlf"
|
|
}
|
|
},
|
|
"serve": {
|
|
"continuous": true,
|
|
"executor": "@angular/build:dev-server",
|
|
"options": {
|
|
"port": 4300
|
|
},
|
|
"configurations": {
|
|
"production": {
|
|
"buildTarget": "portal-admin:build:production"
|
|
},
|
|
"development": {
|
|
"buildTarget": "portal-admin:build:development"
|
|
}
|
|
},
|
|
"defaultConfiguration": "development"
|
|
},
|
|
"lint": {
|
|
"executor": "@nx/eslint:lint"
|
|
},
|
|
"test": {
|
|
"executor": "@angular/build:unit-test",
|
|
"options": {
|
|
"watch": false
|
|
},
|
|
"configurations": {
|
|
"watch": {
|
|
"watch": true
|
|
}
|
|
}
|
|
},
|
|
"serve-static": {
|
|
"continuous": true,
|
|
"executor": "@nx/web:file-server",
|
|
"options": {
|
|
"buildTarget": "portal-admin:build",
|
|
"port": 4300,
|
|
"staticFilePath": "dist/apps/portal-admin/browser"
|
|
}
|
|
}
|
|
}
|
|
}
|