Mijn aanvragen (F1): ApplicationsStore + dashboard blocks
The payoff — A–E become visible. The dashboard now shows a "Mijn aanvragen"
section at the top with a block per application.
- ApplicationsStore (registratie/application, root): the list as a RemoteData
signal, parsed at the trust boundary; reload() (dashboard revisit reflects
server-computed auto-approval); optimistic cancel (hide → reload / un-hide on fail).
- aanvraag-block (organism): badge (tag → colour/label) + per-status body
("Stap X van Y" / referentie + ingediend-datum / manual note / reden) + actions
from the pure blockActions. Composes card + status-badge + button. Stories per status.
- dashboard: "Mijn aanvragen" section (hidden when empty), sorted Concept → In
behandeling → resolved; Verder gaan deep-links the wizard (?aanvraag=<id>),
Annuleren cancels via the store.
Deferred to F2: document-chip preview/download affordance.
Gates green: vitest 128, lint, build, check:tokens; backend dotnet 56.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
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 { LinkComponent } from '@shared/ui/link/link.component';
|
||||
@@ -11,8 +12,11 @@ import { SideNavComponent, NavItem } from '@shared/layout/side-nav/side-nav.comp
|
||||
import { ASYNC } from '@shared/ui/async/async.component';
|
||||
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 { tasksFromProfile } from '@registratie/domain/tasks';
|
||||
|
||||
/** Page: "Mijn overzicht" — the portal home, following the NL Design System
|
||||
@@ -22,7 +26,7 @@ import { tasksFromProfile } from '@registratie/domain/tasks';
|
||||
imports: [
|
||||
PageShellComponent, HeadingComponent, LinkComponent, AlertComponent, SkeletonComponent,
|
||||
DataRowComponent, CardComponent, TaskListComponent, SideNavComponent, ...ASYNC,
|
||||
RegistrationSummaryComponent, RegistrationTableComponent,
|
||||
RegistrationSummaryComponent, RegistrationTableComponent, AanvraagBlockComponent,
|
||||
],
|
||||
template: `
|
||||
<app-page-shell i18n-heading="@@dashboard.heading" heading="Mijn overzicht" i18n-intro="@@dashboard.intro" intro="Welkom in uw persoonlijke omgeving van het BIG-register. Hier ziet u uw registratie en regelt u uw zaken.">
|
||||
@@ -30,6 +34,17 @@ import { tasksFromProfile } from '@registratie/domain/tasks';
|
||||
<app-side-nav [items]="nav" />
|
||||
|
||||
<div class="app-stack">
|
||||
@if (aanvragen().length) {
|
||||
<section>
|
||||
<app-heading [level]="2" i18n="@@dashboard.mijnAanvragen">Mijn aanvragen</app-heading>
|
||||
<div class="app-stack app-section">
|
||||
@for (a of aanvragen(); track a.id) {
|
||||
<app-aanvraag-block [aanvraag]="a" (resume)="resume(a)" (cancel)="cancelAanvraag(a)" />
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
|
||||
@if (store.pendingHerregistratie()) {
|
||||
<app-alert type="info" i18n="@@dashboard.pendingHerregistratie">Uw herregistratie-aanvraag is in behandeling.</app-alert>
|
||||
}
|
||||
@@ -103,6 +118,35 @@ import { tasksFromProfile } from '@registratie/domain/tasks';
|
||||
})
|
||||
export class DashboardPage {
|
||||
protected store = inject(BigProfileStore);
|
||||
private apps = inject(ApplicationsStore);
|
||||
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.apps.reload();
|
||||
}
|
||||
|
||||
/** The user's applications, sorted Concept → In behandeling → resolved. Empty →
|
||||
the "Mijn aanvragen" section is hidden (see template). */
|
||||
protected aanvragen = computed<Aanvraag[]>(() => {
|
||||
const rd = this.apps.applications();
|
||||
if (rd.tag !== 'Success') return [];
|
||||
const order: Record<Aanvraag['status']['tag'], number> = { Concept: 0, InBehandeling: 1, Goedgekeurd: 2, Afgewezen: 2 };
|
||||
return rd.value.slice().sort((a, b) => order[a.status.tag] - order[b.status.tag]);
|
||||
});
|
||||
|
||||
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 cancelAanvraag(a: Aanvraag) {
|
||||
void this.apps.cancel(a.id);
|
||||
}
|
||||
|
||||
/** Server-computed eligibility (rendered, not recomputed). */
|
||||
private readonly eligible = computed(() => {
|
||||
|
||||
Reference in New Issue
Block a user