From 63fdacf622706ff3940d8924f96d3b4ac7639d41 Mon Sep 17 00:00:00 2001 From: Edwin van den Houdt Date: Thu, 23 Jul 2026 12:51:00 +0200 Subject: [PATCH] feat(dashboard): admin "Beheer" section linking the admin pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a capability-gated Beheer section to the dashboard listing the admin pages (Huisstijl, Stamdata, Aanvragen), each shown when the principal holds its capability — so admin pages are discoverable, not URL-only. Extract the admin link list to shared/layout/admin-links.ts as one source of truth, reused by the site header (was ADMIN_NAV_ITEMS) and the new section. Capability-gated, never role-derived (PRD-0002). Co-Authored-By: Claude Opus 4.8 --- src/app/registratie/ui/dashboard.page.ts | 23 +++ src/app/shared/layout/admin-links.ts | 34 +++++ .../site-header/site-header.component.ts | 24 +-- src/locale/messages.en.xlf | 16 ++ src/locale/messages.xlf | 144 +++++++++++------- 5 files changed, 161 insertions(+), 80 deletions(-) create mode 100644 src/app/shared/layout/admin-links.ts diff --git a/src/app/registratie/ui/dashboard.page.ts b/src/app/registratie/ui/dashboard.page.ts index d1c7f6f..fdb81e5 100644 --- a/src/app/registratie/ui/dashboard.page.ts +++ b/src/app/registratie/ui/dashboard.page.ts @@ -10,6 +10,8 @@ import { TaskListComponent } from '@shared/ui/task-list/task-list.component'; import { ApplicationListComponent } from '@shared/ui/application-list/application-list.component'; import { ApplicationLinkComponent } from '@shared/ui/application-link/application-link.component'; import { ASYNC } from '@shared/ui/async/async.component'; +import { AccessStore } from '@shared/application/access.store'; +import { ADMIN_LINKS } from '@shared/layout/admin-links'; import { RegistrationSummaryComponent } from '@registratie/ui/registration-summary/registration-summary.component'; import { RegistrationTableComponent } from '@registratie/ui/registration-table/registration-table.component'; import { AanvraagBlockComponent } from '@registratie/ui/aanvraag-block/aanvraag-block.component'; @@ -185,6 +187,22 @@ import { tasksFromProfile } from '@registratie/domain/tasks'; } + + @if (adminLinks().length) { +
+ Beheer + + @for (link of adminLinks(); track link.to) { +
  • + } +
    +
    + } `, @@ -192,8 +210,13 @@ import { tasksFromProfile } from '@registratie/domain/tasks'; export class DashboardPage { protected store = inject(BigProfileStore); private apps = inject(ApplicationsStore); + private access = inject(AccessStore); private router = inject(Router); + /** Admin pages the current principal may reach — capability-gated (never role-derived), + the same source + filter the site header uses. Empty for a non-admin → section hidden. */ + protected adminLinks = computed(() => ADMIN_LINKS.filter((l) => this.access.can(l.cap))); + /** Pure view mapping for a submitted aanvraag → CIBG aanvragen-row fields. */ protected submittedRow = submittedRow; diff --git a/src/app/shared/layout/admin-links.ts b/src/app/shared/layout/admin-links.ts new file mode 100644 index 0000000..5ede2e2 --- /dev/null +++ b/src/app/shared/layout/admin-links.ts @@ -0,0 +1,34 @@ +import { Capability } from '@shared/domain/capability'; + +/** One admin page: its label, a short description, its route, and the capability that + gates it. Single source of truth for "which admin pages exist" — consumed by the site + header's admin nav AND the dashboard's Beheer section, both filtered by `AccessStore.can`. + Capability-gated, never role-derived (PRD-0002 §6): the FE only mirrors server-resolved + capabilities, so this stays correct when real authz replaces the X-Role stub. */ +export interface AdminLink { + readonly label: string; + readonly description: string; + readonly to: string; + readonly cap: Capability; +} + +export const ADMIN_LINKS: readonly AdminLink[] = [ + { + label: $localize`:@@header.nav.huisstijl:Huisstijl`, + description: $localize`:@@admin.link.huisstijl.desc:Organisatiesjablonen voor brieven beheren`, + to: '/brief/huisstijl', + cap: 'orgtemplate:edit', + }, + { + label: $localize`:@@header.nav.stamdata:Stamdata`, + description: $localize`:@@admin.link.stamdata.desc:Business-tabellen onderhouden`, + to: '/beheer/stamdata', + cap: 'stamdata:edit', + }, + { + label: $localize`:@@header.nav.zaken:Aanvragen`, + description: $localize`:@@admin.link.zaken.desc:Alle aanvragen bekijken en beheren`, + to: '/beheer/zaken', + cap: 'cases:manage', + }, +]; diff --git a/src/app/shared/layout/site-header/site-header.component.ts b/src/app/shared/layout/site-header/site-header.component.ts index a735aa1..53935d8 100644 --- a/src/app/shared/layout/site-header/site-header.component.ts +++ b/src/app/shared/layout/site-header/site-header.component.ts @@ -4,7 +4,7 @@ import { NavigationEnd, Router, RouterLink, RouterLinkActive } from '@angular/ro import { filter, map } from 'rxjs/operators'; import { SESSION_PORT } from '@shared/application/session.port'; import { AccessStore } from '@shared/application/access.store'; -import { Capability } from '@shared/domain/capability'; +import { ADMIN_LINKS } from '@shared/layout/admin-links'; import { BreadcrumbComponent } from '@shared/layout/breadcrumb/breadcrumb.component'; import { trailFor } from '@shared/layout/breadcrumb/breadcrumb-trail'; @@ -20,26 +20,6 @@ const NAV_ITEMS: readonly HeaderNavItem[] = [ { label: $localize`:@@header.nav.inschrijven:Inschrijven`, to: '/registreren' }, ]; -/** Admin-only nav, shown only when `/me` grants the matching capability — the pages - are otherwise reachable by URL alone. */ -const ADMIN_NAV_ITEMS: readonly (HeaderNavItem & { readonly cap: Capability })[] = [ - { - label: $localize`:@@header.nav.huisstijl:Huisstijl`, - to: '/brief/huisstijl', - cap: 'orgtemplate:edit', - }, - { - label: $localize`:@@header.nav.stamdata:Stamdata`, - to: '/beheer/stamdata', - cap: 'stamdata:edit', - }, - { - label: $localize`:@@header.nav.zaken:Aanvragen`, - to: '/beheer/zaken', - cap: 'cases:manage', - }, -]; - /** Organism: CIBG Huisstijl site header — logo block, robijn titlebar (breadcrumb + user menu), horizontal nav. ponytail: text wordmark, not the licensed Rijksoverheid beeldmerk; no search box (no search feature yet). */ @@ -131,7 +111,7 @@ export class SiteHeaderComponent { private access = inject(AccessStore); /** Injecting AccessStore here also warms `/me` at app start (the header renders on every page), so the admin routes' guard usually finds caps already resolved. */ - protected adminItems = computed(() => ADMIN_NAV_ITEMS.filter((i) => this.access.can(i.cap))); + protected adminItems = computed(() => ADMIN_LINKS.filter((i) => this.access.can(i.cap))); readonly session = computed(() => this.sessionPort?.session() ?? null); private url = toSignal( diff --git a/src/locale/messages.en.xlf b/src/locale/messages.en.xlf index 7615d7d..0d7aea5 100644 --- a/src/locale/messages.en.xlf +++ b/src/locale/messages.en.xlf @@ -3642,6 +3642,22 @@ Aanvragen Cases + + Beheer + Administration + + + Organisatiesjablonen voor brieven beheren + Manage organisation letter templates + + + Business-tabellen onderhouden + Maintain business reference tables + + + Alle aanvragen bekijken en beheren + View and manage all cases + Aanvragen beheren Manage cases diff --git a/src/locale/messages.xlf b/src/locale/messages.xlf index 464df89..dc7b2d6 100644 --- a/src/locale/messages.xlf +++ b/src/locale/messages.xlf @@ -1959,228 +1959,235 @@ Mijn overzicht src/app/registratie/ui/dashboard.page.ts - 45,46 + 47,48 Welkom in uw persoonlijke omgeving van het BIG-register. Hier ziet u uw registratie en regelt u uw zaken. src/app/registratie/ui/dashboard.page.ts - 47,49 + 49,51 Mijn aanvragen src/app/registratie/ui/dashboard.page.ts - 63,65 + 65,67 Uw herregistratie-aanvraag is in behandeling. src/app/registratie/ui/dashboard.page.ts - 85,89 + 87,91 Wat moet ik regelen src/app/registratie/ui/dashboard.page.ts - 99,101 + 101,103 src/app/registratie/ui/dashboard.page.ts - 104,106 + 106,108 U heeft op dit moment niets openstaan. src/app/registratie/ui/dashboard.page.ts - 107,108 + 109,110 Mijn registratie src/app/registratie/ui/dashboard.page.ts - 114,116 + 116,118 Persoonsgegevens (BRP) src/app/registratie/ui/dashboard.page.ts - 122,124 + 124,126 Straat src/app/registratie/ui/dashboard.page.ts - 128 + 130 Postcode src/app/registratie/ui/dashboard.page.ts - 133,134 + 135,136 Woonplaats src/app/registratie/ui/dashboard.page.ts - 139,140 + 141,142 Specialismen en aantekeningen src/app/registratie/ui/dashboard.page.ts - 153,155 + 155,157 U heeft nog geen specialismen of aantekeningen. src/app/registratie/ui/dashboard.page.ts - 167,169 + 169,171 Wat wilt u doen? src/app/registratie/ui/dashboard.page.ts - 175,176 + 177,178 + + + + Beheer + + src/app/registratie/ui/dashboard.page.ts + 193,194 Inschrijven src/app/registratie/ui/dashboard.page.ts - 265 + 288 Schrijf u in in het BIG-register via de registratiewizard. src/app/registratie/ui/dashboard.page.ts - 266 + 289 Start inschrijving src/app/registratie/ui/dashboard.page.ts - 267 + 290 Herregistratie aanvragen src/app/registratie/ui/dashboard.page.ts - 271 + 294 Verleng uw registratie voor de komende periode. src/app/registratie/ui/dashboard.page.ts - 272 + 295 Vraag aan src/app/registratie/ui/dashboard.page.ts - 273 + 296 Herregistratie-intake src/app/registratie/ui/dashboard.page.ts - 277 + 300 Vragenlijst met vertakkingen. src/app/registratie/ui/dashboard.page.ts - 278 + 301 Start intake src/app/registratie/ui/dashboard.page.ts - 279 + 302 Gegevens wijzigen src/app/registratie/ui/dashboard.page.ts - 283 + 306 Bekijk uw gegevens of geef een wijziging door. src/app/registratie/ui/dashboard.page.ts - 284 + 307 Bekijk gegevens src/app/registratie/ui/dashboard.page.ts - 285 + 308 Functionele patronen src/app/registratie/ui/dashboard.page.ts - 289 + 312 Bekijk de FP/TEA-bouwstenen van deze POC. src/app/registratie/ui/dashboard.page.ts - 290 + 313 Bekijk patronen src/app/registratie/ui/dashboard.page.ts - 291 + 314 Brief opstellen src/app/registratie/ui/dashboard.page.ts - 295 + 318 Stel een brief samen uit vaste en vrije onderdelen. src/app/registratie/ui/dashboard.page.ts - 296 + 319 Start brief src/app/registratie/ui/dashboard.page.ts - 297 + 320 @@ -2607,6 +2614,48 @@ 28 + + Huisstijl + + src/app/shared/layout/admin-links.ts + 17 + + + + Organisatiesjablonen voor brieven beheren + + src/app/shared/layout/admin-links.ts + 18 + + + + Stamdata + + src/app/shared/layout/admin-links.ts + 23 + + + + Business-tabellen onderhouden + + src/app/shared/layout/admin-links.ts + 24 + + + + Aanvragen + + src/app/shared/layout/admin-links.ts + 29 + + + + Alle aanvragen bekijken en beheren + + src/app/shared/layout/admin-links.ts + 30 + + Mijn overzicht @@ -2761,53 +2810,32 @@ 20 - - Huisstijl - - src/app/shared/layout/site-header/site-header.component.ts - 27 - - - - Stamdata - - src/app/shared/layout/site-header/site-header.component.ts - 32 - - - - Aanvragen - - src/app/shared/layout/site-header/site-header.component.ts - 37 - - BIG-register src/app/shared/layout/site-header/site-header.component.ts - 75,76 + 55,56 Ministerie van Volksgezondheid, Welzijn en Sport src/app/shared/layout/site-header/site-header.component.ts - 77,79 + 57,59 Uitloggen src/app/shared/layout/site-header/site-header.component.ts - 99,100 + 79,80 Hoofdnavigatie src/app/shared/layout/site-header/site-header.component.ts - 107,108 + 87,88