Some checks failed
CI / lint (pull_request) Successful in 1m5s
CI / build (pull_request) Successful in 52s
CI / unit (pull_request) Successful in 1m2s
CI / frontend (pull_request) Successful in 1m31s
CI / mutation (pull_request) Successful in 3m55s
CI / verify-stack (pull_request) Has been cancelled
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.3 KiB
6.3 KiB
Frontend decisions
A running log of frontend tooling and component decisions (CLAUDE.md §10). One entry per decision; record why, and note any deviation from NL Design System.
Workspace & tooling (S-08a, #65)
The portals live in an Nx monorepo at the repository root, alongside the .NET services/.
- Package manager: pnpm. Native build scripts are approved explicitly in
pnpm-workspace.yamlunderallowBuilds(pnpm 11 fails the install otherwise). Node 24, pnpm 11. - Angular, standalone components + signals, no NgModules (§10). Apps are generated with
@nx/angular:application. - Unit tests: Vitest via Angular's built-in
@angular/build:unit-test(thevitest-angularrunner). Angular Testing Library is added for component tests when the first real components land (S-08c); the S-08a placeholder uses a plainTestBedrender assertion. - Lint: ESLint (flat config,
@nx/eslint). - Nx is scoped to
apps/+libs/only. The@nx/dockerand@nx/dotnetplugins are not installed — the .NET services are built bydotnet/the Makefile, and@nx/dockerwould otherwise infer everyservices/*/Dockerfileas an unnamed Nx project and break the project graph. - No Nx Cloud.
nxCloudIdis stripped fromnx.json; remote caching would depend on an external service, and the repo is Gitea-only (§8.7). Nx's "configure-ai-agents" additions (.claude/settings.json, a CLAUDE.md section referencing a GitHub marketplace) are not committed for the same reason. - CI: a
frontendjob (make frontend→pnpm install --frozen-lockfile+nx run-many -t lint test build) runs on pnpm + Node, with pinned action URLs (§15).
NL Design System: not yet introduced — the S-08a app is a placeholder. NL DS components arrive with the submit form (S-08c, #67); any deviation from NL DS will be recorded here.
API client generator (S-08b, #66)
libs/api-client is generated from services/bff/openapi.json — never hand-written (§10).
- Generator: orval (
client: 'angular'), a node-based generator (no Java, unlikeopenapi-generator), so it runs in the pnpm/Node CI lane. It emits an injectableBffApiV1Serviceusing Angular'sHttpClient— which means the DigiD bearer token can be attached by anHttpInterceptor(S-08c), the idiomatic Angular approach; a fetch-based SDK would bypass the interceptor pipeline. - Config:
libs/api-client/orval.config.ts(single-file output intosrc/lib/generated/,clean: true, prettier). Regenerate withnx run api-client:generateafter the BFF spec changes; the output is deterministic (idempotent), andsrc/lib/generated/is never hand-edited. - Tested against a mocked BFF via
HttpClientTesting(libs/api-client/src/lib/bff-api.spec.ts). - The BFF endpoints carry no
operationId, so orval synthesises method names (postSelfServiceRegistrations,getOpenbaarRegister); adding explicit operation ids to the BFF is a possible later polish.
Self-service form: NL DS, DigiD auth, testing (S-08c, #67)
- NL Design System via
@utrecht/component-library-angular(libs/ui) +@utrecht/design-tokens(imported once inapps/self-service/src/styles.css). Utrecht is NL DS's reference Angular implementation. Its v3 components are NgModule-based, not standalone, solibs/uire-exportsUtrechtComponentsModule(and the component classes, so the AOT compiler resolves the template directives through the barrel); standalone components consume it viaimports: [UtrechtComponentsModule]. §10's "no NgModules in new code" governs our code — consuming a third-party module is fine. - DigiD login via
angular-auth-oidc-client(libs/auth): auth-code + PKCE against the Keycloakdigidrealm (public clientbig-portal). A smallAuthServiceabstraction (bsn / isAuthenticated / login) wraps the library so components and theauthenticatedGuarddepend on a mockable surface; a tokenHttpInterceptorattaches the bearer to BFF calls (secure route). The OIDCauthority/secureApiOriginare dev defaults inapp.config.ts; the compose-served app overrides them (S-08d), and the browser-vs-container issuer alignment is handled there (ADR-0010). - Testing: component tests use
@testing-library/angular(§10) withAuthServiceand the api-client mocked; the axe (vitest-axe) check runs scoped to WCAG 2.1 AA tags (wcag2a/2aa/21a/21aa) with the documentlangset, asserting zero violations on the submit page. The real DigiD browser round-trip is exercised in S-08d (Playwright). - Module boundaries: replaced the demo eslint
depConstraints(scope:shop/scope:shared, left over from the Nx angular template) with a permissive*default; scope/type tags can be introduced when the portal set grows.
Serving + e2e (S-08d, #68)
- Served by nginx, same-origin as the BFF. The compose
self-serviceimage serves the built app and reverse-proxies/self-service/*+/openbaar/*to thebffservice. Because the api-client uses relative URLs, the browser calls the app's own origin → nginx forwards to the BFF: no CORS, and the DigiD token (same-origin) is attached by the interceptor. nginx resolves the BFF at request time (aresolver+ variableproxy_pass) so it starts before the BFF is up. - Runtime config. The app fetches
/config.jsonbefore bootstrap (main.ts);appConfigis a factory. The dev default (public/config.json) points atlocalhost:8180; the Docker image bakes the compose value (keycloak:8080). One build, per-environment OIDC authority. - e2e runs inside the compose network.
infra/run-e2e-check.shruns Playwright in anodecontainer oncg, so the browser reaches Keycloak askeycloak:8080— the same issuer the BFF validates against (resolves the browser-vs-container mismatch, ADR-0010). Chromium is installed at runtime, so there's no Playwright-image-version pinning to keep in sync. The spec is copied in (docker cp), not mounted, so it leaves nothing root-owned on the host. Wired asverify-e2ein theverify-stackCI job. tests/e2eis a standalone Playwright project (its ownpackage.json), not an Nx project — it's a live-stack check like the otherverify-*runners, not part of thefrontendunit lane.