import { Component, computed, inject } from '@angular/core'; import { PageShellComponent } from '@shared/layout/page-shell/page-shell.component'; import { AlertComponent } from '@shared/ui/atoms/alert/alert.component'; import { ButtonComponent } from '@shared/ui/atoms/button/button.component'; import { ASYNC } from '@shared/ui/molecules/async/async.component'; import { BriefStore } from '@brief/application/brief.store'; import { LetterComposerComponent } from '@brief/ui/letter-composer/letter-composer.component'; import { BehandelSchermComponent } from '@brief/ui/behandel-scherm/behandel-scherm.component'; /** Page: thin container. Injects the root store, kicks off the load, and passes its derived read-model to the composer. Business/UI logic lives below in pure pieces; this just wires signals to the organism and events back to store commands. */ @Component({ selector: 'app-brief-page', imports: [ PageShellComponent, AlertComponent, ButtonComponent, ...ASYNC, LetterComposerComponent, BehandelSchermComponent, ], host: { '(document:keydown)': 'onKey($event)' }, styles: [ ` .brief-toolbar { display: flex; justify-content: space-between; align-items: center; gap: var(--rhc-space-max-md); margin-block-end: var(--rhc-space-max-lg); } .toolbar-start { display: flex; align-items: center; gap: var(--rhc-space-max-md); } .save { color: var(--rhc-color-foreground-subtle); font-size: 0.9em; } `, ], template: ` @if (lastError(); as err) { {{ err }} } {{ failedText }} {{ retryText }} @if (loaded(); as s) { @if (store.orgTemplate(); as orgTemplate) { @if (store.canEdit()) { {{ undoLabel }} {{ redoLabel }} } {{ saveText() }} @if (store.saveState().tag === 'Error') { {{ retrySaveLabel }} } {{ resetLabel }} @if (store.canEdit() && store.caseContext(); as caseContext) { } @else { } } } `, }) export class BriefPage { protected store = inject(BriefStore); protected model = this.store.model; protected lastError = this.store.lastError; protected heading = $localize`:@@brief.page.heading:Brief opstellen`; protected intro = $localize`:@@brief.page.intro:Stel de brief aan de zorgverlener samen uit standaardteksten en vrije tekst.`; protected failedText = $localize`:@@brief.page.failed:De brief kon niet worden geladen.`; protected retryText = $localize`:@@brief.page.retry:Opnieuw proberen`; protected resetLabel = $localize`:@@brief.page.reset:Opnieuw beginnen (demo)`; protected undoLabel = $localize`:@@brief.page.undo:Ongedaan maken`; protected redoLabel = $localize`:@@brief.page.redo:Opnieuw uitvoeren`; protected retrySaveLabel = $localize`:@@brief.page.retrySave:Opnieuw proberen`; private savingText = $localize`:@@brief.page.saving:Concept opslaan…`; private savedText = $localize`:@@brief.page.saved:Concept opgeslagen`; private saveErrorText = $localize`:@@brief.page.saveError:Niet opgeslagen — opnieuw proberen`; /** Debounced-save state, surfaced in a polite live region. */ protected saveText = computed(() => { switch (this.store.saveState().tag) { case 'Saving': return this.savingText; case 'Saved': return this.savedText; case 'Error': return this.saveErrorText; default: return ''; } }); constructor() { void this.store.load(); } protected resetDemo() { void this.store.resetDemo(); } /** Typed narrowing for the `` loaded slot: a structural directive's context can't inherit a generic from a sibling host input, so the Success value is unwrapped here instead of through `let-`. */ protected readonly loaded = computed(() => { const s = this.model(); return s.tag === 'Loaded' ? s : undefined; }); protected reload() { void this.store.load(); } /** Ctrl/Cmd+Z = undo, Ctrl/Cmd+Shift+Z = redo. Ignored while focus is in the rich-text editor or a form control, so the browser's own text undo keeps working there — our shell-level undo is for structural edits (add/remove/reorder blocks). */ protected onKey(e: KeyboardEvent) { if (!(e.ctrlKey || e.metaKey) || e.key.toLowerCase() !== 'z' || !this.store.canEdit()) return; const t = e.target as HTMLElement | null; if (t && (t.isContentEditable || t.tagName === 'INPUT' || t.tagName === 'TEXTAREA')) return; e.preventDefault(); if (e.shiftKey) this.store.redo(); else this.store.undo(); } }