import { Component, ElementRef, computed, input, output, viewChild } from '@angular/core'; import { PlaceholderOption } from '@shared/ui/rich-text-editor/rich-text-editor.component'; import { ButtonComponent } from '@shared/ui/button/button.component'; import { HeadingComponent } from '@shared/ui/heading/heading.component'; import { StepperComponent } from '@shared/ui/stepper/stepper.component'; import { Besluit, Brief, CaseContext, LibraryPassage } from '@brief/domain/brief'; import { inferSelection } from '@brief/domain/besluit'; import { Diagnostic } from '@brief/domain/placeholders'; import { OrgTemplate } from '@brief/domain/org-template'; import { BriefMsg } from '@brief/domain/brief.machine'; import { LetterCanvasComponent } from '@brief/ui/letter-canvas/letter-canvas.component'; import { DiagnosticsPanelComponent } from '@brief/ui/diagnostics-panel/diagnostics-panel.component'; import { RejectionCommentsComponent } from '@brief/ui/rejection-comments/rejection-comments.component'; import { LetterEditorComponent } from '@brief/ui/letter-editor/letter-editor.component'; import { BesluitPanelComponent } from '@brief/ui/besluit-panel/besluit-panel.component'; /** Organism: the behandelaar's drafting step. Frames "Brief opstellen" as one step in the case workflow — a case-context header + stepper (Beoordelen → Brief opstellen → Indienen, neighbours stubbed) — with the besluit-driven guidance, the lean letter editor, and an on-demand full-letter preview in a modal. Only ever renders for an editable brief (draft/rejected); the approver's read-only flow stays in letter-composer. Presentational: emits edit/submit/preview intents. */ @Component({ selector: 'app-behandel-scherm', imports: [ ButtonComponent, HeadingComponent, StepperComponent, LetterCanvasComponent, DiagnosticsPanelComponent, RejectionCommentsComponent, LetterEditorComponent, BesluitPanelComponent, ], styles: [ ` :host { display: block; } .case-head { margin-block-end: var(--rhc-space-max-lg); padding: var(--rhc-space-max-md) var(--rhc-space-max-lg); border-inline-start: 4px solid var(--rhc-color-primary, var(--rhc-color-border-strong)); background: var(--rhc-color-background-subtle, transparent); } .case-meta { display: flex; flex-wrap: wrap; gap: var(--rhc-space-max-md); color: var(--rhc-color-foreground-subtle); } .step-body { display: grid; gap: var(--rhc-space-max-xl); margin-block-start: var(--rhc-space-max-lg); } .bar { display: flex; flex-wrap: wrap; gap: var(--rhc-space-max-md); align-items: center; margin-block-start: var(--rhc-space-max-xl); } dialog { border: none; border-radius: var(--rhc-radius-md, 4px); padding: 0; max-width: min(900px, 95vw); width: 100%; } dialog::backdrop { background: rgb(0 0 0 / 45%); /* token-ok: modal scrim, not a palette colour */ } .modal-body { padding: var(--rhc-space-max-lg); max-height: 80vh; overflow: auto; } .modal-bar { display: flex; justify-content: space-between; gap: var(--rhc-space-max-md); padding: var(--rhc-space-max-md) var(--rhc-space-max-lg); border-block-start: 1px solid var(--rhc-color-border); } `, ], template: `
{{ caseHeading() }}
{{ caseContext().aanvraagReferentie }} {{ caseContext().zorgverlenerNaam }} {{ bigLabel() }} {{ caseContext().bigNummer }} @if (canRevealBigNummer() && isMasked()) { {{ revealLabel() }} } {{ caseContext().beroep }}
@if (status() === 'rejected') { }
{{ previewLabel() }} {{ submitLabel() }} @if (!canSubmit()) { {{ submitHint() }} }
`, }) export class BehandelSchermComponent { brief = input.required(); orgTemplate = input.required(); logoUrl = input(null); availablePassages = input([]); diagnostics = input([]); caseContext = input.required(); canSubmit = input(false); busy = input(false); /** Server decision (PRD-0002 §5c): may this actor unmask the case BIG-nummer? */ canRevealBigNummer = input(false); edit = output(); submit = output(); preview = output(); locate = output(); revealBigNummer = output(); /** The BIG-nummer arrives masked (contains `*`); once revealed the swapped value has no `*`, so the reveal action hides itself — no separate "revealed" flag needed. */ protected isMasked = computed(() => this.caseContext().bigNummer.includes('*')); /** Step-up (PRD-0002 §5d) stubbed as a native confirm — the extra verification gesture before an audited PII reveal. ponytail: real systems prompt MFA / recent re-auth. */ protected onReveal() { if (confirm(this.stepUpPrompt())) this.revealBigNummer.emit(); } private previewDialog = viewChild>('previewDialog'); protected status = computed(() => this.brief().status.tag); protected rejectComments = computed(() => { const s = this.brief().status; return s.tag === 'rejected' ? s.comments : ''; }); /** The besluit + redenen the letter currently reflects, read back off the kern's passages — this seeds the panel so it survives reload/undo (no separate storage). */ protected selection = computed(() => { const kern = this.brief().sections.find((s) => s.sectionKey === 'kern'); return inferSelection(kern?.blocks ?? [], this.availablePassages()); }); // Same insert menu as the composer: only valid, fillable, non-deprecated fields. protected menu = computed(() => this.brief() .placeholders.filter((p) => p.fillable !== false && !p.deprecated) .map((p) => ({ key: p.key, label: p.label, autoResolvable: p.autoResolvable })), ); /** Besluit/redenen changed → recompose the kern as one edit (= one undo step). */ protected onSelection(sel: { besluit: Besluit | null; reasons: string[] }) { this.edit.emit({ tag: 'BesluitSelected', besluit: sel.besluit, reasons: sel.reasons }); } protected openPreview() { this.previewDialog()?.nativeElement.showModal(); } protected closePreview() { this.previewDialog()?.nativeElement.close(); } protected submitLabel = computed(() => this.status() === 'rejected' ? $localize`:@@brief.resubmit:Opnieuw indienen` : $localize`:@@brief.submit:Indienen ter beoordeling`, ); protected steps = input([ $localize`:@@brief.step.beoordelen:Beoordelen`, $localize`:@@brief.step.opstellen:Brief opstellen`, $localize`:@@brief.step.indienen:Indienen`, ]); protected processName = input($localize`:@@brief.process:Herregistratie behandelen`); protected stepTitle = input($localize`:@@brief.step.opstellen:Brief opstellen`); protected caseHeading = input($localize`:@@brief.case.heading:Aanvraag herregistratie`); protected bigLabel = input($localize`:@@brief.case.big:BIG-nummer`); protected revealLabel = input($localize`:@@brief.case.reveal:Toon BIG-nummer`); protected stepUpPrompt = input( $localize`:@@brief.case.revealConfirm:Extra verificatie vereist. Het tonen van het BIG-nummer wordt vastgelegd. Doorgaan?`, ); protected previewLabel = input($localize`:@@brief.preview.open:Voorbeeld`); protected openDocumentLabel = input( $localize`:@@brief.preview.openDocument:Openen als document (PDF)`, ); protected closeLabel = input($localize`:@@common.close:Sluiten`); protected submitHint = input( $localize`:@@brief.submitHint:Vul eerst alle verplichte secties en los fouten op.`, ); }