refactor: ui/dashboard becomes ui/overzicht-secties (RD-36)
RD-03 moved the dashboard page to overzicht/ui/overzicht.page.ts and left four sections in registratie/ui/dashboard/. The folder was named after a page that lives in another context. A reader who opened it found four sections that are not the dashboard. The folder is now overzicht-secties/ — registratie's sections for the overzicht page. The alias does not change, because the sections stay in the registratie context. The story titles do not change, because they name the context. Five documents cited registratie/ui/dashboard.page.ts, a file that RD-03 renamed. They now name overzicht.page.ts, or the section that owns the behaviour they describe. The /dashboard route keeps its path. It is a user-visible URL. npm run ci --full passes: 67 and 45 storybook suites, 306 axe tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { applicationConfig } from '@storybook/angular';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { MijnAanvragenSection } from './mijn-aanvragen.section';
|
||||
import { AanvragenStore } from '@registratie/application/aanvragen.store';
|
||||
import { Aanvraag } from '@registratie/domain/aanvraag';
|
||||
import { RemoteData } from '@shared/application/remote-data';
|
||||
import { loading, success, failure } from '@shared/testing/remote-data';
|
||||
|
||||
const base = {
|
||||
id: 'a1',
|
||||
type: 'herregistratie',
|
||||
documentIds: [],
|
||||
createdAt: '2026-06-28T10:00:00Z',
|
||||
updatedAt: '2026-06-28T10:05:00Z',
|
||||
submittedAt: '2026-06-28T10:05:00Z',
|
||||
} satisfies Omit<Aanvraag, 'status'>;
|
||||
|
||||
const concept: Aanvraag = { ...base, status: { tag: 'Concept', stepIndex: 1, stepCount: 3 } };
|
||||
const ingediend: Aanvraag = {
|
||||
...base,
|
||||
id: 'a2',
|
||||
status: { tag: 'InBehandeling', referentie: 'BIG-2026-456789', manual: false },
|
||||
};
|
||||
|
||||
/** Minimal store stand-in — only the members the section's template reads. */
|
||||
function storeStub(aanvragen: RemoteData<Error | undefined, Aanvraag[]>, lastError = '') {
|
||||
return {
|
||||
aanvragen: () => aanvragen,
|
||||
reload: () => {},
|
||||
cancel: async () => {},
|
||||
lastError: () => lastError,
|
||||
};
|
||||
}
|
||||
|
||||
const meta: Meta<MijnAanvragenSection> = {
|
||||
title: 'Domein/Registratie/Mijn Aanvragen',
|
||||
component: MijnAanvragenSection,
|
||||
decorators: [applicationConfig({ providers: [provideRouter([])] })],
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<MijnAanvragenSection>;
|
||||
|
||||
export const Loading: Story = {
|
||||
decorators: [
|
||||
applicationConfig({ providers: [{ provide: AanvragenStore, useValue: storeStub(loading()) }] }),
|
||||
],
|
||||
};
|
||||
export const WithConceptAndSubmitted: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [{ provide: AanvragenStore, useValue: storeStub(success([concept, ingediend])) }],
|
||||
}),
|
||||
],
|
||||
};
|
||||
export const Empty: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [{ provide: AanvragenStore, useValue: storeStub(success([])) }],
|
||||
}),
|
||||
],
|
||||
};
|
||||
export const CancelFailed: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [
|
||||
{
|
||||
provide: AanvragenStore,
|
||||
useValue: storeStub(
|
||||
success([concept]),
|
||||
$localize`:@@dashboard.cancel.failed:Verwijderen is niet gelukt.`,
|
||||
),
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
export const Failed: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [{ provide: AanvragenStore, useValue: storeStub(failure(new Error('offline'))) }],
|
||||
}),
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
|
||||
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
|
||||
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
|
||||
import { ApplicationListComponent } from '@shared/ui/molecules/application-list/application-list.component';
|
||||
import { ApplicationLinkComponent } from '@shared/ui/molecules/application-link/application-link.component';
|
||||
import { ASYNC } from '@shared/ui/molecules/async/async.component';
|
||||
import { successOr } from '@shared/application/remote-data';
|
||||
import { AanvragenStore } from '@registratie/application/aanvragen.store';
|
||||
import { Aanvraag, AanvraagType } from '@registratie/domain/aanvraag';
|
||||
import {
|
||||
submittedRow,
|
||||
sortForDashboard,
|
||||
concepten,
|
||||
ingediend,
|
||||
} from '@registratie/domain/aanvraag-view';
|
||||
import { AanvraagBlockComponent } from '@registratie/ui/aanvraag-block/aanvraag-block.component';
|
||||
|
||||
/** Section: "Mijn aanvragen" — the user's own aanvragen (concepten as resumable
|
||||
meldingen, submitted ones as keuzelijst rows), owning its own fetch, sort and
|
||||
the resume/cancel actions. Empty → renders nothing, same as the aanvraag-block
|
||||
convention (see AanvraagBlockComponent). */
|
||||
@Component({
|
||||
selector: 'app-mijn-aanvragen-section',
|
||||
imports: [
|
||||
AlertComponent,
|
||||
SkeletonComponent,
|
||||
HeadingComponent,
|
||||
ApplicationListComponent,
|
||||
ApplicationLinkComponent,
|
||||
AanvraagBlockComponent,
|
||||
...ASYNC,
|
||||
],
|
||||
template: `
|
||||
@if (cancelError(); as err) {
|
||||
<app-alert type="error">{{ err }}</app-alert>
|
||||
}
|
||||
<app-async [data]="store.aanvragen()" (retryClicked)="store.reload()">
|
||||
<ng-template appAsyncLoaded>
|
||||
@if (aanvragen().length) {
|
||||
<section>
|
||||
@for (a of concepten_(); track a.id) {
|
||||
<app-aanvraag-block
|
||||
animate.enter="app-item-enter"
|
||||
animate.leave="app-item-leave"
|
||||
[aanvraag]="a"
|
||||
(resume)="resume(a)"
|
||||
(cancel)="cancel(a)"
|
||||
/>
|
||||
}
|
||||
@if (ingediend_().length) {
|
||||
<app-heading [level]="2" class="app-section" i18n="@@dashboard.mijnAanvragen"
|
||||
>Mijn aanvragen</app-heading
|
||||
>
|
||||
<app-application-list>
|
||||
@for (a of ingediend_(); track a.id) {
|
||||
@let row = submittedRow(a);
|
||||
<li
|
||||
app-application-link
|
||||
animate.enter="app-item-enter"
|
||||
animate.leave="app-item-leave"
|
||||
[heading]="row.heading"
|
||||
[subtitle]="row.subtitle"
|
||||
[status]="row.status"
|
||||
[to]="'/aanvraag/' + a.id"
|
||||
></li>
|
||||
}
|
||||
</app-application-list>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
</ng-template>
|
||||
<ng-template appAsyncLoading>
|
||||
<app-skeleton height="2.5rem" [count]="2" />
|
||||
</ng-template>
|
||||
</app-async>
|
||||
`,
|
||||
})
|
||||
export class MijnAanvragenSection {
|
||||
protected store = inject(AanvragenStore);
|
||||
private router = inject(Router);
|
||||
|
||||
constructor() {
|
||||
// Re-fetch on each visit so server-computed auto-approval transitions show up
|
||||
// (Concept → In behandeling → Goedgekeurd after the processing window).
|
||||
this.store.reload();
|
||||
}
|
||||
|
||||
protected submittedRow = submittedRow;
|
||||
|
||||
protected aanvragen = computed<Aanvraag[]>(() =>
|
||||
sortForDashboard(successOr(this.store.aanvragen(), [])),
|
||||
);
|
||||
protected concepten_ = computed(() => concepten(this.aanvragen()));
|
||||
protected ingediend_ = computed(() => ingediend(this.aanvragen()));
|
||||
|
||||
private readonly resumeRoutes: Record<AanvraagType, string> = {
|
||||
registratie: '/registreren',
|
||||
herregistratie: '/herregistratie',
|
||||
intake: '/intake',
|
||||
};
|
||||
protected resume(a: Aanvraag) {
|
||||
void this.router.navigate([this.resumeRoutes[a.type]], { queryParams: { aanvraag: a.id } });
|
||||
}
|
||||
protected cancel(a: Aanvraag) {
|
||||
void this.store.cancel(a.id);
|
||||
}
|
||||
|
||||
/** The message from a failed cancel, rendered above the list. */
|
||||
protected cancelError = computed(() => this.store.lastError());
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { applicationConfig } from '@storybook/angular';
|
||||
import { MijnRegistratieSection } from './mijn-registratie.section';
|
||||
import { BigProfileStore } from '@registratie/application/big-profile.store';
|
||||
import { BigProfile } from '@registratie/domain/big-profile';
|
||||
import { RemoteData } from '@shared/application/remote-data';
|
||||
import { loading, success, failure } from '@shared/testing/remote-data';
|
||||
|
||||
const profile: BigProfile = {
|
||||
registration: {
|
||||
bigNummer: '19012345601',
|
||||
naam: 'Dr. A. (Anna) de Vries',
|
||||
beroep: 'Arts',
|
||||
registratiedatum: '2012-09-01',
|
||||
geboortedatum: '1985-03-14',
|
||||
status: { tag: 'Geregistreerd', herregistratieDatum: '2027-09-01' },
|
||||
},
|
||||
person: {
|
||||
naam: 'Dr. A. (Anna) de Vries',
|
||||
geboortedatum: '1985-03-14',
|
||||
adres: { straat: 'Rijksweg 1', postcode: '2514 EA', woonplaats: 'Den Haag' },
|
||||
},
|
||||
};
|
||||
|
||||
/** Minimal store stand-in — only the members the section's template reads. */
|
||||
function storeStub(profileRd: RemoteData<Error | undefined, BigProfile>) {
|
||||
return { profile: () => profileRd, reloadProfile: () => {} };
|
||||
}
|
||||
|
||||
const meta: Meta<MijnRegistratieSection> = {
|
||||
title: 'Domein/Registratie/Mijn Registratie',
|
||||
component: MijnRegistratieSection,
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<MijnRegistratieSection>;
|
||||
|
||||
export const Loading: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [{ provide: BigProfileStore, useValue: storeStub(loading()) }],
|
||||
}),
|
||||
],
|
||||
};
|
||||
export const Loaded: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [{ provide: BigProfileStore, useValue: storeStub(success(profile)) }],
|
||||
}),
|
||||
],
|
||||
};
|
||||
export const Failed: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [{ provide: BigProfileStore, useValue: storeStub(failure(new Error('offline'))) }],
|
||||
}),
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { successOf } from '@shared/application/remote-data';
|
||||
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
|
||||
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
|
||||
import { DataBlockComponent } from '@shared/ui/molecules/data-block/data-block.component';
|
||||
import { DataRowComponent } from '@shared/ui/molecules/data-row/data-row.component';
|
||||
import { ASYNC } from '@shared/ui/molecules/async/async.component';
|
||||
import { BigProfileStore } from '@registratie/application/big-profile.store';
|
||||
import { RegistrationSummaryComponent } from '@registratie/ui/registration-summary/registration-summary.component';
|
||||
|
||||
/** Section: "Mijn registratie" — the BIG-register summary plus the BRP
|
||||
persoonsgegevens, both from the one screen-shaped dashboard call
|
||||
(BigProfileStore). */
|
||||
@Component({
|
||||
selector: 'app-mijn-registratie-section',
|
||||
imports: [
|
||||
HeadingComponent,
|
||||
SkeletonComponent,
|
||||
DataBlockComponent,
|
||||
DataRowComponent,
|
||||
RegistrationSummaryComponent,
|
||||
...ASYNC,
|
||||
],
|
||||
template: `
|
||||
<app-async [data]="store.profile()" (retryClicked)="store.reloadProfile()">
|
||||
<ng-template appAsyncLoaded>
|
||||
@if (profile(); as p) {
|
||||
<section>
|
||||
<app-heading [level]="2" i18n="@@dashboard.mijnRegistratie"
|
||||
>Mijn registratie</app-heading
|
||||
>
|
||||
<div class="app-section">
|
||||
<app-registration-summary [reg]="p.registration" />
|
||||
</div>
|
||||
<app-data-block
|
||||
class="app-section"
|
||||
i18n-heading="@@dashboard.persoonsgegevens"
|
||||
heading="Persoonsgegevens (BRP)"
|
||||
>
|
||||
<div
|
||||
app-data-row
|
||||
i18n-key="@@dashboard.straat"
|
||||
key="Straat"
|
||||
[value]="p.person.adres.straat"
|
||||
></div>
|
||||
<div
|
||||
app-data-row
|
||||
i18n-key="@@dashboard.postcode"
|
||||
key="Postcode"
|
||||
[value]="p.person.adres.postcode"
|
||||
></div>
|
||||
<div
|
||||
app-data-row
|
||||
i18n-key="@@dashboard.woonplaats"
|
||||
key="Woonplaats"
|
||||
[value]="p.person.adres.woonplaats"
|
||||
></div>
|
||||
</app-data-block>
|
||||
</section>
|
||||
}
|
||||
</ng-template>
|
||||
<ng-template appAsyncLoading>
|
||||
<app-skeleton height="2.5rem" [count]="4" />
|
||||
</ng-template>
|
||||
</app-async>
|
||||
`,
|
||||
})
|
||||
export class MijnRegistratieSection {
|
||||
protected store = inject(BigProfileStore);
|
||||
protected profile = () => successOf(this.store.profile());
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { applicationConfig } from '@storybook/angular';
|
||||
import { SpecialismenSection } from './specialismen.section';
|
||||
import { BigProfileStore } from '@registratie/application/big-profile.store';
|
||||
import { Aantekening } from '@registratie/domain/registration';
|
||||
import { RemoteData } from '@shared/application/remote-data';
|
||||
import { loading, success, empty, failure } from '@shared/testing/remote-data';
|
||||
|
||||
const rows: Aantekening[] = [
|
||||
{ type: 'Specialisme', omschrijving: 'Huisartsgeneeskunde', datum: '2016-04-12' },
|
||||
{ type: 'Aantekening', omschrijving: 'Erkend opleider huisartsgeneeskunde', datum: '2019-01-08' },
|
||||
];
|
||||
|
||||
/** Minimal store stand-in — only the members the section's template reads. */
|
||||
function storeStub(aantekeningen: RemoteData<Error | undefined, Aantekening[]>) {
|
||||
return { aantekeningen: () => aantekeningen, reloadAantekeningen: () => {} };
|
||||
}
|
||||
|
||||
const meta: Meta<SpecialismenSection> = {
|
||||
title: 'Domein/Registratie/Specialismen',
|
||||
component: SpecialismenSection,
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<SpecialismenSection>;
|
||||
|
||||
export const Loading: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [{ provide: BigProfileStore, useValue: storeStub(loading()) }],
|
||||
}),
|
||||
],
|
||||
};
|
||||
export const Loaded: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [{ provide: BigProfileStore, useValue: storeStub(success(rows)) }],
|
||||
}),
|
||||
],
|
||||
};
|
||||
export const Empty: Story = {
|
||||
decorators: [
|
||||
applicationConfig({ providers: [{ provide: BigProfileStore, useValue: storeStub(empty()) }] }),
|
||||
],
|
||||
};
|
||||
export const Failed: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [{ provide: BigProfileStore, useValue: storeStub(failure(new Error('offline'))) }],
|
||||
}),
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { successOf } from '@shared/application/remote-data';
|
||||
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
|
||||
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
|
||||
import { ASYNC } from '@shared/ui/molecules/async/async.component';
|
||||
import { BigProfileStore } from '@registratie/application/big-profile.store';
|
||||
import { RegistrationTableComponent } from '@registratie/ui/registration-table/registration-table.component';
|
||||
|
||||
/** Section: "Specialismen en aantekeningen" — a separate resource from the rest of
|
||||
the dashboard (own load/empty/error state), because it is genuinely a second
|
||||
endpoint (`big-register.adapter.ts`), not part of the BFF-lite view call. */
|
||||
@Component({
|
||||
selector: 'app-specialismen-section',
|
||||
imports: [HeadingComponent, SkeletonComponent, RegistrationTableComponent, ...ASYNC],
|
||||
template: `
|
||||
<section>
|
||||
<app-heading [level]="2" i18n="@@dashboard.specialismen"
|
||||
>Specialismen en aantekeningen</app-heading
|
||||
>
|
||||
<div class="app-section">
|
||||
<app-async [data]="store.aantekeningen()" (retryClicked)="store.reloadAantekeningen()">
|
||||
<ng-template appAsyncLoaded>
|
||||
@if (aantekeningen(); as r) {
|
||||
<app-registration-table [rows]="r" />
|
||||
}
|
||||
</ng-template>
|
||||
<ng-template appAsyncLoading>
|
||||
<app-skeleton height="2.5rem" [count]="3" />
|
||||
</ng-template>
|
||||
<ng-template appAsyncEmpty>
|
||||
<p class="app-text-subtle" i18n="@@dashboard.geenSpecialismen">
|
||||
U heeft nog geen specialismen of aantekeningen.
|
||||
</p>
|
||||
</ng-template>
|
||||
</app-async>
|
||||
</div>
|
||||
</section>
|
||||
`,
|
||||
})
|
||||
export class SpecialismenSection {
|
||||
protected store = inject(BigProfileStore);
|
||||
protected aantekeningen = () => successOf(this.store.aantekeningen());
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { applicationConfig } from '@storybook/angular';
|
||||
import { WatMoetIkRegelenSection } from './wat-moet-ik-regelen.section';
|
||||
import { BigProfileStore } from '@registratie/application/big-profile.store';
|
||||
import { BigProfile } from '@registratie/domain/big-profile';
|
||||
import { HerregistratieDecisions } from '@registratie/domain/registration';
|
||||
import { RemoteData } from '@shared/application/remote-data';
|
||||
import { loading, success } from '@shared/testing/remote-data';
|
||||
|
||||
const profile: BigProfile = {
|
||||
registration: {
|
||||
bigNummer: '19012345601',
|
||||
naam: 'Dr. A. (Anna) de Vries',
|
||||
beroep: 'Arts',
|
||||
registratiedatum: '2012-09-01',
|
||||
geboortedatum: '1985-03-14',
|
||||
status: { tag: 'Geregistreerd', herregistratieDatum: '2027-09-01' },
|
||||
},
|
||||
person: {
|
||||
naam: 'Dr. A. (Anna) de Vries',
|
||||
geboortedatum: '1985-03-14',
|
||||
adres: { straat: 'Rijksweg 1', postcode: '2514 EA', woonplaats: 'Den Haag' },
|
||||
},
|
||||
};
|
||||
|
||||
/** Minimal store stand-in — only the members the section's template and class read. */
|
||||
function storeStub(
|
||||
profileRd: RemoteData<Error | undefined, BigProfile>,
|
||||
decisionsRd: RemoteData<Error | undefined, HerregistratieDecisions>,
|
||||
pendingHerregistratie: boolean,
|
||||
) {
|
||||
return {
|
||||
profile: () => profileRd,
|
||||
decisions: () => decisionsRd,
|
||||
pendingHerregistratie: () => pendingHerregistratie,
|
||||
reloadProfile: () => {},
|
||||
};
|
||||
}
|
||||
|
||||
const meta: Meta<WatMoetIkRegelenSection> = {
|
||||
title: 'Domein/Registratie/Wat Moet Ik Regelen',
|
||||
component: WatMoetIkRegelenSection,
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<WatMoetIkRegelenSection>;
|
||||
|
||||
export const Loading: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [{ provide: BigProfileStore, useValue: storeStub(loading(), loading(), false) }],
|
||||
}),
|
||||
],
|
||||
};
|
||||
export const MetTaken: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [
|
||||
{
|
||||
provide: BigProfileStore,
|
||||
useValue: storeStub(
|
||||
success(profile),
|
||||
success({ eligibleForHerregistratie: true }),
|
||||
false,
|
||||
),
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
export const NietsOpenstaand: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [
|
||||
{
|
||||
provide: BigProfileStore,
|
||||
useValue: storeStub(
|
||||
success(profile),
|
||||
success({ eligibleForHerregistratie: false }),
|
||||
false,
|
||||
),
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
export const InBehandeling: Story = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [
|
||||
{
|
||||
provide: BigProfileStore,
|
||||
useValue: storeStub(
|
||||
success(profile),
|
||||
success({ eligibleForHerregistratie: false }),
|
||||
true,
|
||||
),
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { successOf } from '@shared/application/remote-data';
|
||||
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
|
||||
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
|
||||
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
|
||||
import { TaskListComponent } from '@shared/ui/molecules/task-list/task-list.component';
|
||||
import { ASYNC } from '@shared/ui/molecules/async/async.component';
|
||||
import { BigProfileStore } from '@registratie/application/big-profile.store';
|
||||
import { tasksFromProfile } from '@registratie/domain/tasks';
|
||||
|
||||
/** Section: "Wat moet ik regelen" — the open tasks derived from the registration
|
||||
+ the server-computed herregistratie eligibility (rendered, never recomputed;
|
||||
ADR-0001). Empty task list → a plain "niets openstaan" message, not an empty
|
||||
async state (the registration itself did load). */
|
||||
@Component({
|
||||
selector: 'app-wat-moet-ik-regelen-section',
|
||||
imports: [HeadingComponent, AlertComponent, SkeletonComponent, TaskListComponent, ...ASYNC],
|
||||
template: `
|
||||
@if (store.pendingHerregistratie()) {
|
||||
<app-alert type="info" i18n="@@dashboard.pendingHerregistratie"
|
||||
>Uw herregistratie-aanvraag is in behandeling.</app-alert
|
||||
>
|
||||
}
|
||||
<app-async [data]="store.profile()" (retryClicked)="store.reloadProfile()">
|
||||
<ng-template appAsyncLoaded>
|
||||
@if (tasks(); as t) {
|
||||
<section>
|
||||
@if (t.length) {
|
||||
<app-task-list
|
||||
i18n-listHeading="@@dashboard.watMoetIkRegelen"
|
||||
listHeading="Wat moet ik regelen"
|
||||
[tasks]="t"
|
||||
/>
|
||||
} @else {
|
||||
<app-heading [level]="2" i18n="@@dashboard.watMoetIkRegelen"
|
||||
>Wat moet ik regelen</app-heading
|
||||
>
|
||||
<p class="app-text-subtle" i18n="@@dashboard.nietsOpenstaan">
|
||||
U heeft op dit moment niets openstaan.
|
||||
</p>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
</ng-template>
|
||||
<ng-template appAsyncLoading>
|
||||
<app-skeleton height="2.5rem" [count]="2" />
|
||||
</ng-template>
|
||||
</app-async>
|
||||
`,
|
||||
})
|
||||
export class WatMoetIkRegelenSection {
|
||||
protected store = inject(BigProfileStore);
|
||||
|
||||
private eligible = computed(() => {
|
||||
const d = successOf(this.store.decisions());
|
||||
return d?.eligibleForHerregistratie ?? false;
|
||||
});
|
||||
|
||||
protected tasks = computed(() => {
|
||||
const p = successOf(this.store.profile());
|
||||
return p ? tasksFromProfile(p.registration, this.eligible()) : undefined;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user