The dashboard page lived inside registratie, a context with no permitted outbound edges. Two of its sections do cross-context navigation: action links to herregistratie/intake/brief/concepts, and an admin links section that imported ADMIN_LINKS directly from the app shell. Move the page and these two navigation sections into a new overzicht context. Declare overzicht: ['registratie'] as the one sanctioned edge, so the four data sections that stay in registratie/ui/dashboard/ remain reachable. The route stays at /dashboard: it is a user-visible URL and four e2e specs assert it, so only the import path and class name change. beheer-links.section.ts now injects HEADER_ADMIN_LINKS, the same token the site header uses, instead of importing the app shell's nav config directly. Used npm run gen:context and corrected its three known-wrong outputs: the overzicht: [] edge, the scaffolded overzicht.page.ts (the real page is the moved dashboard.page.ts), and the scaffolded path: 'overzicht' route. Verified the boundary is enforced, not decorative: a temporary import of a herregistratie file into overzicht.page.ts makes dep:check fail, as expected, then reverted. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
8.9 KiB
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.tslinks by route string.npm run dep:checkpasses and gives false assurance on exactly this file. beheer-links.section.ts:6importsADMIN_LINKSfrom../../../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— theCONTEXT_ALLOWEDmap (5 lines)libs/shared/src/layout/site-header/nav-config.ts— theHEADER_ADMIN_LINKStokenlibs/shared/src/layout/site-header/site-header.component.ts:99— how to inject itapps/ssp/src/app/app.config.ts:72— whereADMIN_LINKSis already provided to the tokenplopfile.mjs, thecontextgenerator- CLAUDE.md decision 1 (DDD contexts and layers)
Decisions (pre-made, don't relitigate)
-
New context name:
overzicht. Dutch, per CLAUDE.md ("domain contexts are Dutch"), and it matches the page's own title, "Mijn overzicht". -
Only the page and the two navigation sections move. These three:
dashboard.page.ts→overzicht/ui/overzicht.page.ts(classOverzichtPage, selectorapp-overzicht-page)dashboard/wat-wilt-u-doen.section.ts→overzicht/ui/wat-wilt-u-doen.section.tsdashboard/beheer-links.section.ts→overzicht/ui/beheer-links.section.ts
-
The four data sections STAY in
registratie/ui/dashboard/—mijn-aanvragen,wat-moet-ik-regelen,mijn-registratie,specialismen. They render registratie data and belong besideBigProfileStore/AanvragenStore. Do not move them, and do not move their three story files. -
Declare
overzicht: ['registratie']in.dependency-cruiser.ssp.js— the second sanctioned cross-feature edge, mirroringherregistratie: ['registratie']. The generator insertsoverzicht: []; you must change it. -
beheer-links.section.tsinjectsHEADER_ADMIN_LINKS, exactly assite-header.component.ts:99does. Drop theshell/nav.configimport entirely.app.config.ts:72already providesADMIN_LINKSto that token, and theAdminLinkdoc 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. -
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 theloadComponentimport path and the class name change. Add a one-line comment inapp.routes.tssaying the path staysdashboarddeliberately while the context isoverzicht, so the mismatch reads as a decision and not an oversight. -
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.tsfrom a template → delete it, the real page is the moveddashboard.page.ts - it adds a new
path: 'overzicht'route → delete that block, and instead repoint the existingdashboardroute (decision 6)
- it writes
-
Delete the empty layer folders the generator creates.
overzichthas nodomain/,application/,infrastructure/orcontracts/— it is composition only. Remove those four.gitkeepdirectories rather than keeping scaffolding for work that does not exist. -
The page keeps its 8 imports. CLAUDE.md has no import-count rule; one
importper rendered section is exactly right. Do not introduce anOVERZICHT_SECTIONSconst 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
- Run
npm run gen:contextand answeroverzicht. - Delete the generated
overzicht/ui/overzicht.page.tsand the four empty layer folders. - Delete the generated
path: 'overzicht'route block fromapp.routes.ts. - Change the generated
overzicht: []tooverzicht: ['registratie']. git mvthe three files per the table above.- Rename the page's class to
OverzichtPageand its selector toapp-overzicht-page; fix its six section import paths (four now via@registratie/ui/dashboard/…, two local). - Repoint the existing
dashboardroute to@overzicht/ui/overzicht.page/OverzichtPageand add the comment from decision 6. - Rewrite
beheer-links.section.tsto injectHEADER_ADMIN_LINKSper decision 5. - Update this ticket's
Status:todoneand the README's RD-03 row todone. - Commit all of it together.
Acceptance criteria
npm run dep:check # exits 0 — proves overzicht: ['registratie'] is accepted
npm run ci # exits 0
Then prove the boundary is real, not decorative:
# 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:
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 besideNAV_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:checkstill passes with the boundary silently missing — the exact failure modeplopfile.mjs's own comment warns about forcreate-frontend.mjs. messages.xlf/messages.en.xlfcarry<context context-type="sourcefile">paths that these moves make stale. Nothing checks them and$localizeids are the identity, song build --localizestays green. Do not hand-edit the xlf files to chase paths.- Do not change any
$localizeid. Every id in the three moved files stays byte identical; only the file's location changes. A changed id needs a new English<target>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.