feat(WP-67): merge behandelportal into this repo as a monorepo

Restructures into apps/ssp + apps/behandelportal (two Angular projects)
plus libs/shared + libs/beheer (cross-app libraries), replacing WP-61's
separate sibling repo. That split had already produced real drift: a
hand-vendored copy of the backend's OpenAPI doc, a shared/ui+layout tree
forked and silently diverging (7 files), and beheer + the styles.scss
token bridge duplicated byte-for-byte across both repos.

- git mv the SSP's src/app/* into apps/ssp/; fold shared/, beheer/,
  environments/, the Storybook docs/*.mdx, and styles.scss into
  libs/shared + libs/beheer (all confirmed identical between the two
  repos before merging). auth stays deliberately duplicated per
  ADR-0002 (actor-specific, expected to diverge) - amended there.
- One generated API client (libs/shared), no more vendored swagger.json.
- .dependency-cruiser split into a base factory + one config per app,
  and Storybook into .storybook-ssp/.storybook-behandelportal - both
  forced by the @auth/* alias resolving to different directories per app.
- SiteHeaderComponent/ShellComponent gained HEADER_NAV_ITEMS/
  HEADER_ADMIN_LINKS/DEBUG_PANEL injection tokens so each app supplies
  its own nav/admin-links/dev-panel instead of one being hardcoded.
- CLAUDE.md, ARCHITECTURE.md, dependencies.md, and ADR-0002 updated;
  WP-67 backlog entry documents the full decision trail.

npm run ci green (lint, dep:check x2, 360 tests across ssp/
behandelportal/shared/beheer, both localized builds, backend tests,
snippet + api-client drift); both dev servers, both Storybook
instances, and docker compose verified working.

The old sibling repo (/home/eho/repos/behandelportal) is left
untouched, not deleted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-08-02 21:01:57 +02:00
co-authored by Claude Sonnet 5
parent d3f3b13345
commit e7156c5132
403 changed files with 7103 additions and 60917 deletions
+45
View File
@@ -0,0 +1,45 @@
import {
DOCUMENT,
ENVIRONMENT_INITIALIZER,
EnvironmentInjector,
afterNextRender,
inject,
} from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
/** Template-layer wiring (not a component): on every route change after the
initial load, moves focus to the new page's `<h1>` (page-shell always
renders one) so screen-reader/keyboard users land on the new content
instead of wherever focus happened to be. Falls back to `#main` (the
shell's landmark) if a page has no heading. Deferred via `afterNextRender`
so it doesn't race Angular's view-transition DOM swap. */
export function provideRouteFocus() {
return {
provide: ENVIRONMENT_INITIALIZER,
multi: true,
useValue: () => {
const router = inject(Router);
const document = inject(DOCUMENT);
const injector = inject(EnvironmentInjector);
let isInitialLoad = true;
router.events.subscribe((event) => {
if (!(event instanceof NavigationEnd)) return;
if (isInitialLoad) {
isInitialLoad = false;
return;
}
afterNextRender(
() => {
const target =
document.querySelector<HTMLElement>('#main h1') ?? document.getElementById('main');
if (!target) return;
target.setAttribute('tabindex', '-1');
target.focus({ preventScroll: true });
},
{ injector },
);
});
},
};
}