diff --git a/public/mock/registration.json b/public/mock/registration.json index 5452106..e1192fa 100644 --- a/public/mock/registration.json +++ b/public/mock/registration.json @@ -2,8 +2,7 @@ "bigNummer": "19012345601", "naam": "Dr. A. (Anna) de Vries", "beroep": "Arts", - "status": "Geregistreerd", "registratiedatum": "2012-09-01", - "herregistratieDatum": "2027-09-01", - "geboortedatum": "1985-03-14" + "geboortedatum": "1985-03-14", + "status": { "tag": "Geregistreerd", "herregistratieDatum": "2027-09-01" } } diff --git a/src/app/atoms/status-badge/status-badge.component.ts b/src/app/atoms/status-badge/status-badge.component.ts index 9944301..2e47a09 100644 --- a/src/app/atoms/status-badge/status-badge.component.ts +++ b/src/app/atoms/status-badge/status-badge.component.ts @@ -1,8 +1,10 @@ import { Component, computed, input } from '@angular/core'; -import { RegistrationStatus } from '../../core/models'; +import { StatusTag } from '../../core/models'; +import { assertNever } from '../../core/fp'; -/** Atom: status badge = colored RHC dot-badge + label. Hand-built to show how - you compose a new atom from design tokens (dot color driven per status). */ +/** Atom: status badge = colored RHC dot-badge + label. The color is a total + function of the status tag — assertNever makes a new status fail to compile + until a color is chosen for it. */ @Component({ selector: 'app-status-badge', template: ` @@ -13,10 +15,18 @@ import { RegistrationStatus } from '../../core/models'; `, }) export class StatusBadgeComponent { - status = input.required(); - color = computed(() => ({ - Geregistreerd: 'var(--rhc-color-groen-500)', - Doorgehaald: 'var(--rhc-color-rood-500)', - Geschorst: 'var(--rhc-color-oranje-500)', - }[this.status()])); + status = input.required(); + color = computed(() => { + const tag = this.status(); + switch (tag) { + case 'Geregistreerd': + return 'var(--rhc-color-groen-500)'; + case 'Doorgehaald': + return 'var(--rhc-color-rood-500)'; + case 'Geschorst': + return 'var(--rhc-color-oranje-500)'; + default: + return assertNever(tag); + } + }); } diff --git a/src/app/core/models.ts b/src/app/core/models.ts index 116db65..fa5dd4d 100644 --- a/src/app/core/models.ts +++ b/src/app/core/models.ts @@ -1,17 +1,29 @@ -export type RegistrationStatus = 'Geregistreerd' | 'Doorgehaald' | 'Geschorst'; +/** + * Registration status as a discriminated union: each variant owns exactly the + * data that makes sense for it. Only an active (Geregistreerd) registration has + * a herregistratie date; a struck-off (Doorgehaald) one cannot carry one. The + * old flat interface allowed that impossible combination — this makes it + * unrepresentable. + */ +export type RegistrationStatus = + | { tag: 'Geregistreerd'; herregistratieDatum: string } // ISO date + | { tag: 'Geschorst'; geschorstTot: string; reden: string } + | { tag: 'Doorgehaald'; doorgehaaldOp: string; reden: string }; + +/** Just the discriminant — for atoms that only need the label/color. */ +export type StatusTag = RegistrationStatus['tag']; export interface Registration { bigNummer: string; naam: string; - beroep: string; // arts, verpleegkundige, apotheker, ... - status: RegistrationStatus; - registratiedatum: string; // ISO date - herregistratieDatum: string; + beroep: string; // arts, verpleegkundige, apotheker, ... + registratiedatum: string; // ISO date geboortedatum: string; + status: RegistrationStatus; } export interface Aantekening { - type: string; // specialisme of aantekening + type: string; // specialisme of aantekening omschrijving: string; datum: string; } diff --git a/src/app/organisms/registration-summary/registration-summary.component.ts b/src/app/organisms/registration-summary/registration-summary.component.ts index 4fbcdbf..bf6802c 100644 --- a/src/app/organisms/registration-summary/registration-summary.component.ts +++ b/src/app/organisms/registration-summary/registration-summary.component.ts @@ -15,10 +15,23 @@ import { StatusBadgeComponent } from '../../atoms/status-badge/status-badge.comp - + - + + @switch (reg().status.tag) { + @case ('Geregistreerd') { + + } + @case ('Geschorst') { + + + } + @case ('Doorgehaald') { + + + } + } `, diff --git a/src/app/organisms/registration-summary/registration-summary.stories.ts b/src/app/organisms/registration-summary/registration-summary.stories.ts index 92a048f..43fdd0b 100644 --- a/src/app/organisms/registration-summary/registration-summary.stories.ts +++ b/src/app/organisms/registration-summary/registration-summary.stories.ts @@ -2,15 +2,13 @@ import type { Meta, StoryObj } from '@storybook/angular'; import { RegistrationSummaryComponent } from './registration-summary.component'; import { Registration } from '../../core/models'; -const sample: Registration = { +const base = { bigNummer: '19012345601', naam: 'Dr. A. (Anna) de Vries', beroep: 'Arts', - status: 'Geregistreerd', registratiedatum: '2012-09-01', - herregistratieDatum: '2027-09-01', geboortedatum: '1985-03-14', -}; +} satisfies Omit; const meta: Meta = { title: 'Organisms/Registration Summary', @@ -19,5 +17,14 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Default: Story = { args: { reg: sample } }; -export const Geschorst: Story = { args: { reg: { ...sample, status: 'Geschorst' } } }; +// Each story feeds a different union variant; the card renders only the rows +// that variant's data supports (note Doorgehaald has no herregistratie date). +export const Geregistreerd: Story = { + args: { reg: { ...base, status: { tag: 'Geregistreerd', herregistratieDatum: '2027-09-01' } } }, +}; +export const Geschorst: Story = { + args: { reg: { ...base, status: { tag: 'Geschorst', geschorstTot: '2026-12-31', reden: 'Lopend tuchtonderzoek' } } }, +}; +export const Doorgehaald: Story = { + args: { reg: { ...base, status: { tag: 'Doorgehaald', doorgehaaldOp: '2024-05-01', reden: 'Op eigen verzoek' } } }, +};