import { Component, computed, inject } from '@angular/core'; import { Router } from '@angular/router'; import { PageShellComponent } from '@shared/layout/page-shell/page-shell.component'; import { HeadingComponent } from '@shared/ui/heading/heading.component'; import { AlertComponent } from '@shared/ui/alert/alert.component'; import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component'; import { DataRowComponent } from '@shared/ui/data-row/data-row.component'; import { DataBlockComponent } from '@shared/ui/data-block/data-block.component'; 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 { FeatureFlagStore } from '@shared/application/feature-flags.store'; import { FLAG_INSCHRIJVING_OPEN } from '@shared/domain/feature-flag'; 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'; import { BigProfileStore } from '@registratie/application/big-profile.store'; import { ApplicationsStore } from '@registratie/application/applications.store'; import { Registration } from '@registratie/domain/registration'; import { Aanvraag, AanvraagType } from '@registratie/domain/aanvraag'; import { submittedRow } from '@registratie/domain/aanvraag-view'; import { tasksFromProfile } from '@registratie/domain/tasks'; /** Page:"Mijn overzicht" — the portal home, following the NL Design System "Mijn omgeving" pattern (side nav +"Wat moet ik regelen" +"Mijn zaken"). */ @Component({ selector: 'app-dashboard-page', imports: [ PageShellComponent, HeadingComponent, AlertComponent, SkeletonComponent, DataRowComponent, DataBlockComponent, TaskListComponent, ApplicationListComponent, ApplicationLinkComponent, ...ASYNC, RegistrationSummaryComponent, RegistrationTableComponent, AanvraagBlockComponent, ], template: `
@if (aanvragen().length) {
@for (a of concepten(); track a.id) { } @if (ingediend().length) { Mijn aanvragen @for (a of ingediend(); track a.id) { @let row = submittedRow(a);
  • }
    }
    } @if (store.pendingHerregistratie()) { Uw herregistratie-aanvraag is in behandeling. } @if (profile(); as p) { @let tasks = tasksFor(p.registration);
    @if (tasks.length) { } @else { Wat moet ik regelen

    U heeft op dit moment niets openstaan.

    }
    Mijn registratie
    }
    Specialismen en aantekeningen
    @if (aantekeningen(); as r) { }

    U heeft nog geen specialismen of aantekeningen.

    Wat wilt u doen? @for (a of acties(); track a.to) {
  • }
    @if (adminLinks().length) {
    Beheer @for (link of adminLinks(); track link.to) {
  • }
    }
    `, }) export class DashboardPage { protected store = inject(BigProfileStore); private apps = inject(ApplicationsStore); private access = inject(AccessStore); private flags = inject(FeatureFlagStore); 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; constructor() { // Re-fetch on each visit so server-computed auto-approval transitions show up // (Concept → In behandeling → Goedgekeurd after the processing window). this.apps.reload(); } /** The user's applications, sorted Concept → In behandeling → resolved. Empty → the"Mijn aanvragen" section is hidden (see template). */ protected aanvragen = computed(() => { const rd = this.apps.applications(); if (rd.tag !== 'Success') return []; const order: Record = { Concept: 0, InBehandeling: 1, Goedgekeurd: 2, Afgewezen: 2, }; return rd.value.slice().sort((a, b) => order[a.status.tag] - order[b.status.tag]); }); /** A Concept ("lopende aanvraag") renders as a melding above the list; the rest as keuzelijst items — the two shapes need different HTML contexts. */ protected concepten = computed(() => this.aanvragen().filter((a) => a.status.tag === 'Concept')); protected ingediend = computed(() => this.aanvragen().filter((a) => a.status.tag !== 'Concept')); private readonly resumeRoutes: Record = { registratie: '/registreren', herregistratie: '/herregistratie', intake: '/intake', }; protected resume(a: Aanvraag) { void this.router.navigate([this.resumeRoutes[a.type]], { queryParams: { aanvraag: a.id } }); } protected cancelAanvraag(a: Aanvraag) { void this.apps.cancel(a.id); } /** Server-computed eligibility (rendered, not recomputed). */ private readonly eligible = computed(() => { const d = this.store.decisions(); return d.tag === 'Success' && d.value.eligibleForHerregistratie; }); protected tasksFor(reg: Registration) { return tasksFromProfile(reg, this.eligible()); } /** Typed narrowing for the `` loaded slot — ``'s own context can't inherit a generic from a sibling host input (Angular only infers a structural directive's type parameter from an input on that same node), so the Success value is unwrapped here instead of through `let-`. */ protected readonly profile = computed(() => { const rd = this.store.profile(); return rd.tag === 'Success' ? rd.value : undefined; }); protected readonly aantekeningen = computed(() => { const rd = this.store.aantekeningen(); return rd.tag === 'Success' ? rd.value : undefined; }); /** Primary transactional actions, as an "aanvragen" list (see CIBG's componenten/aanvragen). The core portal sections live in the header nav now; the teaching pages (concepts/brief) are only reachable from here. */ private readonly allActies = [ { to: '/registreren', titel: $localize`:@@dashboard.actie.inschrijven.titel:Inschrijven`, tekst: $localize`:@@dashboard.actie.inschrijven.tekst:Schrijf u in in het BIG-register via de registratiewizard.`, actie: $localize`:@@dashboard.actie.inschrijven.actie:Start inschrijving`, }, { to: '/herregistratie', titel: $localize`:@@dashboard.actie.herregistratie.titel:Herregistratie aanvragen`, tekst: $localize`:@@dashboard.actie.herregistratie.tekst:Verleng uw registratie voor de komende periode.`, actie: $localize`:@@dashboard.actie.herregistratie.actie:Vraag aan`, }, { to: '/intake', titel: $localize`:@@dashboard.actie.intake.titel:Herregistratie-intake`, tekst: $localize`:@@dashboard.actie.intake.tekst:Vragenlijst met vertakkingen.`, actie: $localize`:@@dashboard.actie.intake.actie:Start intake`, }, { to: '/registratie', titel: $localize`:@@dashboard.actie.wijzigen.titel:Gegevens wijzigen`, tekst: $localize`:@@dashboard.actie.wijzigen.tekst:Bekijk uw gegevens of geef een wijziging door.`, actie: $localize`:@@dashboard.actie.wijzigen.actie:Bekijk gegevens`, }, { to: '/concepts', titel: $localize`:@@dashboard.actie.concepten.titel:Functionele patronen`, tekst: $localize`:@@dashboard.actie.concepten.tekst:Bekijk de FP/TEA-bouwstenen van deze POC.`, actie: $localize`:@@dashboard.actie.concepten.actie:Bekijk patronen`, }, { to: '/brief', titel: $localize`:@@dashboard.actie.brief.titel:Brief opstellen`, tekst: $localize`:@@dashboard.actie.brief.tekst:Stel een brief samen uit vaste en vrije onderdelen.`, actie: $localize`:@@dashboard.actie.brief.actie:Start brief`, }, ]; /** Hide the "Inschrijven" action when self-service registration is flagged off (WP-47). */ protected readonly acties = computed(() => this.allActies.filter( (a) => a.to !== '/registreren' || this.flags.enabled(FLAG_INSCHRIJVING_OPEN), ), ); }