# RD-03 — Move the portal home into its own `overzicht` context Status: done Source: PLAN.md Phase 0 ## Why The dashboard is the portal home, but it lives inside `registratie` — a context that `.dependency-cruiser.ssp.js` declares as `registratie: []`, permitted to import no other context. Three concrete symptoms: - Its six sections span four concerns: registratie data (3), aanvragen (1), cross-context action links to `/herregistratie` `/intake` `/brief` `/concepts` (1), and admin links to `/beheer/*` (1). - The cross-context coupling is **invisible to the linter**, because `wat-wilt-u-doen.section.ts` links by route _string_. `npm run dep:check` passes and gives false assurance on exactly this file. - `beheer-links.section.ts:6` imports `ADMIN_LINKS` from `../../../shell/nav.config` — a context reaching into the app frame. Splitting the dashboard into sections is what makes this fixable at all. That is the refactor's first real payoff. ## Read first - `apps/ssp/src/app/registratie/ui/dashboard.page.ts` (42 lines, pure composition) - `apps/ssp/src/app/registratie/ui/dashboard/` — the six sections - `.dependency-cruiser.ssp.js` — the `CONTEXT_ALLOWED` map (5 lines) - `libs/shared/src/layout/site-header/nav-config.ts` — the `HEADER_ADMIN_LINKS` token - `libs/shared/src/layout/site-header/site-header.component.ts:99` — how to inject it - `apps/ssp/src/app/app.config.ts:72` — where `ADMIN_LINKS` is already provided to the token - `plopfile.mjs`, the `context` generator - CLAUDE.md decision 1 (DDD contexts and layers) ## Decisions (pre-made, don't relitigate) 1. **New context name: `overzicht`.** Dutch, per CLAUDE.md ("domain contexts are Dutch"), and it matches the page's own title, "Mijn overzicht". 2. **Only the page and the two _navigation_ sections move.** These three: - `dashboard.page.ts` → `overzicht/ui/overzicht.page.ts` (class `OverzichtPage`, selector `app-overzicht-page`) - `dashboard/wat-wilt-u-doen.section.ts` → `overzicht/ui/wat-wilt-u-doen.section.ts` - `dashboard/beheer-links.section.ts` → `overzicht/ui/beheer-links.section.ts` 3. **The four data sections STAY in `registratie/ui/dashboard/`** — `mijn-aanvragen`, `wat-moet-ik-regelen`, `mijn-registratie`, `specialismen`. They render registratie data and belong beside `BigProfileStore`/`AanvragenStore`. Do not move them, and do not move their three story files. 4. **Declare `overzicht: ['registratie']`** in `.dependency-cruiser.ssp.js` — the second sanctioned cross-feature edge, mirroring `herregistratie: ['registratie']`. The generator inserts `overzicht: []`; you must change it. 5. **`beheer-links.section.ts` injects `HEADER_ADMIN_LINKS`**, exactly as `site-header.component.ts:99` does. Drop the `shell/nav.config` import entirely. `app.config.ts:72` already provides `ADMIN_LINKS` to that token, and the `AdminLink` doc comment already says it is "Consumed by the site header's admin nav AND (per app) a dashboard's own Beheer section". The direct import was always the anomaly. 6. **KEEP the route at `/dashboard`.** Four e2e specs assert it (`e2e/smoke.spec.ts:18`, `e2e/brief-v2.spec.ts:33`, `e2e/error-state.spec.ts:16,18`), and it is a user-visible URL. Only the `loadComponent` import path and the class name change. Add a one-line comment in `app.routes.ts` saying the path stays `dashboard` deliberately while the context is `overzicht`, so the mismatch reads as a decision and not an oversight. 7. **Use `npm run gen:context`, then correct three things.** The generator gets the folders, the `@overzicht/*` tsconfig alias, and the dep-cruiser entry right. It gets three things wrong for this case: - it writes `overzicht: []` → change to `['registratie']` (decision 4) - it scaffolds a **new** `overzicht.page.ts` from a template → delete it, the real page is the moved `dashboard.page.ts` - it adds a **new** `path: 'overzicht'` route → delete that block, and instead repoint the existing `dashboard` route (decision 6) 8. **Delete the empty layer folders the generator creates.** `overzicht` has no `domain/`, `application/`, `infrastructure/` or `contracts/` — it is composition only. Remove those four `.gitkeep` directories rather than keeping scaffolding for work that does not exist. 9. **The page keeps its 8 imports.** CLAUDE.md has no import-count rule; one `import` per rendered section is exactly right. Do not introduce an `OVERZICHT_SECTIONS` const to get the number down — that trades a self-documenting array for an indirection, and this repo deliberately has no barrels. ## Files Move (use `git mv` so the rename is visible in review): | From | To | | ---------------------------------------------------------------------- | ---------------------------------------------------------- | | `apps/ssp/src/app/registratie/ui/dashboard.page.ts` | `apps/ssp/src/app/overzicht/ui/overzicht.page.ts` | | `apps/ssp/src/app/registratie/ui/dashboard/wat-wilt-u-doen.section.ts` | `apps/ssp/src/app/overzicht/ui/wat-wilt-u-doen.section.ts` | | `apps/ssp/src/app/registratie/ui/dashboard/beheer-links.section.ts` | `apps/ssp/src/app/overzicht/ui/beheer-links.section.ts` | Edit: `apps/ssp/tsconfig.json` (alias), `.dependency-cruiser.ssp.js` (edge), `apps/ssp/src/app/app.routes.ts` (import path + class + comment), `apps/ssp/src/app/overzicht/ui/beheer-links.section.ts` (token), `apps/ssp/src/app/overzicht/ui/overzicht.page.ts` (class, selector, section import paths). ## Steps 1. Run `npm run gen:context` and answer `overzicht`. 2. Delete the generated `overzicht/ui/overzicht.page.ts` and the four empty layer folders. 3. Delete the generated `path: 'overzicht'` route block from `app.routes.ts`. 4. Change the generated `overzicht: []` to `overzicht: ['registratie']`. 5. `git mv` the three files per the table above. 6. Rename the page's class to `OverzichtPage` and its selector to `app-overzicht-page`; fix its six section import paths (four now via `@registratie/ui/dashboard/…`, two local). 7. Repoint the existing `dashboard` route to `@overzicht/ui/overzicht.page` / `OverzichtPage` and add the comment from decision 6. 8. Rewrite `beheer-links.section.ts` to inject `HEADER_ADMIN_LINKS` per decision 5. 9. Update this ticket's `Status:` to `done` and the README's RD-03 row to `done`. 10. Commit all of it together. ## Acceptance criteria ```bash npm run dep:check # exits 0 — proves overzicht: ['registratie'] is accepted npm run ci # exits 0 ``` Then prove the boundary is real, not decorative: ```bash # Add a temporary import of a herregistratie file to overzicht/ui/overzicht.page.ts. # `npm run dep:check` MUST fail (overzicht may reach registratie only). Then revert. ``` And prove the section still renders for an admin: ```bash grep -n "HEADER_ADMIN_LINKS" apps/ssp/src/app/overzicht/ui/beheer-links.section.ts # present grep -rn "shell/nav.config" apps/ssp/src/app/overzicht/ # no match ``` ## Verification `npm run ci`. No story or `.mdx` file moves in this ticket — the three section stories stay in `registratie` — so `--full` is not strictly required. Run it once anyway, because this is a structural move and Storybook globs the app's stories by path. Manual check, since e2e is not in the local GREEN one-liner: `npm start`, open `http://localhost:4200/dashboard`, confirm all six sections render, then `?role=admin` and confirm the Beheer section appears (and is absent without it). ## Out of scope - Renaming the route to `/overzicht`. It needs a redirect and touches four e2e specs. Recorded in PLAN.md as deliberately out of scope. - Moving the four data sections (decision 3). - Story titles — that is RD-04. - Turning `wat-wilt-u-doen`'s action list into a token beside `NAV_ITEMS`. PLAN.md Phase 0 step 6 lists it as a "consider"; leave it for now, since route strings on a landing page are legitimate once the page lives in a context whose job is cross-context linking. ## Risks - **The generator's three wrong outputs** (decision 7). If you skip the corrections, `dep:check` still passes with the boundary silently missing — the exact failure mode `plopfile.mjs`'s own comment warns about for `create-frontend.mjs`. - **`messages.xlf` / `messages.en.xlf` carry `` paths** that these moves make stale. Nothing checks them and `$localize` ids are the identity, so `ng build --localize` stays green. Do **not** hand-edit the xlf files to chase paths. - **Do not change any `$localize` id.** Every id in the three moved files stays byte identical; only the file's location changes. A changed id needs a new English `` or the localize gate fails. - **`git mv`, not delete-and-create**, or the review loses the rename and the diff looks like 300 new lines.