feat(dashboard): restyle Mijn aanvragen / Wat moet ik regelen as a CIBG keuzelijst
Both sections offer a set of choices the user picks between to proceed, matching
designsystem.cibg.nl/componenten/keuzelijst rather than the "aanvragen" row pattern
("Wat wilt u doen?" keeps that look — it's a static nav list, not a choice list).
- New shared/ui molecules: choice-list (heading + keuzelijst__list, wired via
aria-labelledby per CIBG's a11y guidance) and choice-link (one keuzelijst__link
choice; routerLink, imperative-clickable, or a plain non-interactive block).
- choice-link's non-interactive block needed a `--static` modifier: CIBG's
`.keuzelijst__link:after`/`:hover`/`:focus` key off the bare class (keuzelijst
assumes every item is a link), unlike `.applications li a::after` which is scoped
to the anchor — without it, a non-actionable aanvraag row inherited a chevron and
hover accent it shouldn't have.
- task-list.component.ts now composes choice-list/choice-link internally; public
API unchanged except a new required `listHeading` input (the heading moves inside
the list for the aria-labelledby link, so dashboard.page.ts stops rendering it
separately — same fix applied to "Mijn aanvragen").
- aanvraag-block.component.ts moves from application-link to choice-link, combining
its separate status/subtitle text into one instructions paragraph (keuzelijst has
no cta field — the row itself is the action). Only a resumable Concept renders as
a real choice; InBehandeling/Goedgekeurd/Afgewezen stay non-interactive, unchanged
from before.
Verified: lint/check:tokens/build green, 178 tests pass, build-storybook succeeds,
and manually driven end-to-end (dashboard renders both sections as keuzelijst cards,
confirmed via screenshot that non-actionable rows have no chevron after the fix).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { Component, computed, input, output } from '@angular/core';
|
||||
import { ButtonComponent } from '@shared/ui/button/button.component';
|
||||
import { ApplicationLinkComponent } from '@shared/ui/application-link/application-link.component';
|
||||
import { ChoiceLinkComponent } from '@shared/ui/choice-link/choice-link.component';
|
||||
import { Aanvraag, AanvraagType } from '@registratie/domain/aanvraag';
|
||||
import { blockActions } from '@registratie/domain/block-actions';
|
||||
|
||||
@@ -11,29 +11,27 @@ const TYPE_LABELS: Record<AanvraagType, string> = {
|
||||
};
|
||||
|
||||
/** Organism: one application on the dashboard's "Mijn aanvragen" list, rendered as
|
||||
a CIBG Huisstijl "aanvragen" row (application-link). Which text/actions it shows
|
||||
a CIBG Huisstijl "keuzelijst" choice (choice-link). Which text/actions it shows
|
||||
is driven by the pure `blockActions(status)` and the status tag; the block only
|
||||
renders. Only a resumable Concept is clickable — a resolved status has nothing
|
||||
to navigate to, so it renders as a plain (non-anchor) row. */
|
||||
@Component({
|
||||
selector: 'app-aanvraag-block',
|
||||
imports: [ButtonComponent, ApplicationLinkComponent],
|
||||
// display:contents — see application-link.component.ts (keeps <li> a direct <ul> child).
|
||||
imports: [ButtonComponent, ChoiceLinkComponent],
|
||||
// display:contents — see choice-link.component.ts (keeps <li> a direct <ul> child).
|
||||
styles: [`:host{display:contents}`],
|
||||
template: `
|
||||
<app-application-link
|
||||
<app-choice-link
|
||||
[heading]="typeLabel()"
|
||||
[status]="statusText()"
|
||||
[subtitle]="subtitle()"
|
||||
[cta]="actions().includes('resume') ? verderGaan : ''"
|
||||
[instructions]="instructions()"
|
||||
[clickable]="actions().includes('resume')"
|
||||
(activate)="resume.emit()">
|
||||
@if (actions().includes('cancel')) {
|
||||
<div applicationActions>
|
||||
<div choiceActions>
|
||||
<app-button variant="subtle" (click)="cancel.emit()" i18n="@@aanvraagBlock.annuleren">Annuleren</app-button>
|
||||
</div>
|
||||
}
|
||||
</app-application-link>
|
||||
</app-choice-link>
|
||||
`,
|
||||
})
|
||||
export class AanvraagBlockComponent {
|
||||
@@ -42,13 +40,11 @@ export class AanvraagBlockComponent {
|
||||
resume = output<void>();
|
||||
cancel = output<void>();
|
||||
|
||||
protected readonly verderGaan = $localize`:@@aanvraagBlock.verderGaan:Verder gaan`;
|
||||
|
||||
protected typeLabel = computed(() => TYPE_LABELS[this.aanvraag().type]);
|
||||
protected actions = computed(() => blockActions(this.aanvraag().status));
|
||||
|
||||
// Status-tag → row text (the UI's mapping, not a domain rule).
|
||||
protected statusText = computed(() => {
|
||||
// Status-tag → the choice's description line (the UI's mapping, not a domain rule).
|
||||
private statusText = computed(() => {
|
||||
const s = this.aanvraag().status;
|
||||
switch (s.tag) {
|
||||
case 'Concept': return $localize`:@@aanvraagBlock.stap:Stap ${s.stepIndex + 1}:index: van ${s.stepCount}:count:`;
|
||||
@@ -57,13 +53,19 @@ export class AanvraagBlockComponent {
|
||||
case 'Afgewezen': return $localize`:@@aanvraagBlock.afgewezen:Referentie ${s.referentie}:ref:`;
|
||||
}
|
||||
});
|
||||
// Secondary note under the status line, when there's one to show.
|
||||
protected subtitle = computed(() => {
|
||||
// Secondary note appended to the status line, when there's one to show.
|
||||
private subtitle = computed(() => {
|
||||
const s = this.aanvraag().status;
|
||||
if (s.tag === 'InBehandeling' && s.manual) return $localize`:@@aanvraagBlock.manual:Uw aanvraag wordt handmatig beoordeeld in de backoffice.`;
|
||||
if (s.tag === 'Afgewezen') return s.reden;
|
||||
return '';
|
||||
});
|
||||
// The keuzelijst has one description paragraph — combine status + secondary note.
|
||||
protected instructions = computed(() => {
|
||||
const status = this.statusText();
|
||||
const sub = this.subtitle();
|
||||
return sub ? `${status} ${sub}` : status;
|
||||
});
|
||||
}
|
||||
|
||||
function formatNL(iso?: string): string {
|
||||
|
||||
Reference in New Issue
Block a user