/** * Calendar pattern — the JOURNEY contract: the domain state and every * mutating handler the workspace, its panels and its surface consume * (rows, cog display settings, grid focus, waiting list, modification * history, signature requests, patient notes, the editor / Backlog / * finder seeds, the draft-slot mirror, the goto seam and the * availability projection). * Transcribed member by member from the demo's `useCalendarJourneyState` * so the host owns the implementation: the demos keep their fixture-backed * hook, the platform island wires the same shape to `/internalApi/...`. * No implementation lives here. */ import type { CalendarEventStatus } from '../../components/calendar'; import type { Dispatch, SetStateAction } from 'react'; import type { NotesPanelNote, NotesPanelProps, NotesPanelSubmission, NotesPanelWriteFailure, AiConsentGate } from '../../components/notes-panel'; import type { PatientNotesSeam } from './calendar.sheets'; import type { AvailabilityContext, BacklogFilter, BacklogItem, CalendarFreeSlot, CalendarRow, DisplaySettings, DraftSlot, EditorDraft, EditorPrefill, FindSlotsSeed, HistoryInput, RailFocus, SlotPatch, WaitingEntryDraft } from './calendar.model'; import type { DeleteOptions, EditorDraftExtras, RecurringScope } from './calendar-workspace.model'; import type { CalendarHistoryEntry, CalendarSignChannel, CalendarTask, CalendarWaitingEntry, TaskDraft } from './calendar.types'; /** The host's notes read for one patient (N-C3); `writable` follows the * notes, not the appointment-edit right. */ export type PatientNotesLoad = { status: 'loading' | 'ready' | 'error'; total: number | null; complete: boolean; canAddClinicalNote: boolean; writable: boolean; error?: string; }; /** A grid move of a host recurring parent's occurrence beyond itself (S-C8). */ export interface CalendarOccurrenceMove { eventId: string; scope: Exclude; oldStart: Date; newStart: Date; newEnd?: Date; chairId?: string; operatorId?: string; } export interface CalendarJourney { rows: CalendarRow[]; setRows: Dispatch>; display: DisplaySettings; setDisplay: Dispatch>; focus: RailFocus; setFocus: Dispatch>; waitingList: CalendarWaitingEntry[]; /** A dialog host restores a removed entry from its Undo toast. */ setWaitingList: Dispatch>; history: Record; /** * The patient notes the host holds for this page load, by patient id. * Seeded from `CalendarData.patientNotes(id)` and owned here after * that, exactly like `history`: a patient absent from this map has * not been written to yet and the panel falls back to the read model. * The notes sub-view RENDERS this — it keeps no copy of its own — so * an add, edit, delete or pin outlives the panel unmounting instead * of being silently dropped. */ notesByPatient: Record; /** Each patient's notes read; absent, the notes count as loaded. */ notesLoadByPatient?: Record; signRequests: Record; editorSeed: { tick: number; prefill?: EditorPrefill; }; backlogSeed: { tick: number; type: BacklogFilter; }; visibleRange: { start: number; end: number; } | null; backlogItems: BacklogItem[]; draftSlot: DraftSlot | null; setDraftSlot: Dispatch>; slotPatch: SlotPatch | null; gotoRequest: { tick: number; date: Date; } | null; availability: CalendarFreeSlot[] | null; availabilityContext: AvailabilityContext | null; findSlotsSeed: FindSlotsSeed | null; logHistory: (eventId: string, entry: HistoryInput) => void; handleRequestSignature: (eventId: string, channel: CalendarSignChannel) => void; handleComposeNew: () => void; handleScheduleWaiting: (entry: CalendarWaitingEntry) => void; handleRemoveWaiting: (entry: CalendarWaitingEntry) => void; handleUpsertWaiting: (draft: WaitingEntryDraft) => void; handleConfirmAppointment: (eventId: string) => void; handleOpenBacklog: (type: BacklogFilter) => void; handleVisibleRangeChange: (range: { start: number; end: number; }) => void; /** `options` carry the delete dialog's scope and care-plan choice; * absent options mean 'this'. */ handleDeleteAppointment: (eventId: string, options?: DeleteOptions) => void; /** Called before the kit moves the one dragged row through `setRows`. * Reject with a user-facing Error and the kit puts that row back. * Omit it and every move of a recurring parent stays 'this'. */ handleMoveOccurrence?: (move: CalendarOccurrenceMove) => Promise; /** Saves a new status for one occurrence from the details panel. Reject * with a user-facing Error and the panel keeps the old status and shows * the message; resolve and the kit updates that row's status on the * grid. Omit it and the details panel shows the status read-only. */ handleStatusChange?: (eventId: string, status: CalendarEventStatus) => Promise; /** Saves a new description for one occurrence from the details panel's * editor. Reject with a user-facing Error and the editor stays open with * it. Omit it and the description is read-only. */ handleDescriptionChange?: (eventId: string, description: string) => Promise; handleDuplicateAppointment: (prefill: EditorPrefill) => void; handleDraftMoved: (next: { date: Date; time: { hours: number; minutes: number; }; chairId?: string; operatorId?: string; }) => void; handleDraftResized: (durationMin: number) => void; handleBookSlot: (prefill: EditorPrefill) => void; handleShowAvailability: (slots: CalendarFreeSlot[] | null, fromDate?: Date, context?: AvailabilityContext) => void; handleFocusEvent: (eventId: string) => void; handleRevealAppointment: (eventId: string) => void; handleFindSlotsForWaiting: (entry: CalendarWaitingEntry) => void; clearFindSlotsSeed: () => void; handleBookSlotFromFinder: (prefill: EditorPrefill) => void; /** Resolve only after the write is accepted. Reject with a safe, * user-facing Error message to keep the editor open with its draft. * `extras` arrive untouched unless the kit expands a demo series row. */ handleCreateAppointment: (draft: EditorDraft, extras?: EditorDraftExtras) => void | Promise; /** * Task seams — mirroring the platform's DoctorReminder routes * (`ic_dcp_task_create` / `ic_dcp_task_edit` / a `done` toggle / delete). * Every seam here is OPTIONAL and each is independent: absent, the * corresponding action is withheld from the task editor (kit convention * — withheld, not disabled). A host with no task backend at all simply * omits every one of them and the "New task" toolbar member opens a * form nothing can save. */ /** * ONE promise contract across every task seam: reject with a * user-facing Error to keep the editor open with its draft (and, for * delete/done, to roll the confirm dialog / the Switch back rather than * leave the form locked on a write that never landed); resolve and the * editor treats the write as accepted. `handleCreateTask` alone must * resolve WITH the saved record — the host assigns the new id, and * nothing else can tell the editor what it is. The others resolve with * nothing: an update or a delete names its subject by the id already in * hand, so there is nothing new to report back. */ handleCreateTask?: (draft: TaskDraft) => Promise; /** Full re-send, not a patch — mirrors `handleCreateAppointment`. */ handleUpdateTask?: (id: string, draft: TaskDraft) => Promise; handleDeleteTask?: (id: string) => Promise; /** The task editor's Done switch — a dedicated seam rather than routing * through `handleUpdateTask` so a host whose `done` toggle is a * separate lightweight endpoint (the platform's is) can wire it * directly instead of resending the whole record. */ handleSetTaskDone?: (id: string, done: boolean) => Promise; /** Loads one task for the editor's edit mode — the same hydration * seam shape as `loadAppointmentEditor` (`CalendarEditorHydration`). */ loadTask?: (id: string) => Promise; /** * Commit a new note on the patient's record. The HOST builds the * stored note — its id, its timestamp and its AUTHOR, who is the * signed-in operator the host knows and the kit does not. The panel * hands over only what was typed, so no name the kit invented can * ever be attributed to a clinical note. Reject with a translated * Error: the draft stays in the composer with the message while the * Notes tool is open, and `handleNoteWriteError` hears a refusal that * lands after the reader left it. */ handleAddNote: (patientId: string, submission: NotesPanelSubmission) => void | Promise; /** Replace a note's body: HTML for an HTML note, else plain text. A * rejected write keeps the draft in the composer with its message, as * `handleAddNote` does. */ handleEditNote: (patientId: string, noteId: string, content: string) => void | Promise; /** * Delete a note. Called on the click that confirms it. Resolve once the * delete has landed. A rejection brings the row back and the notes * panel shows the error's message, so reject with a translated Error * and do not also toast it. * * A clinical note is taken as kept for the record: its row waits in * place, paused, until `notesByPatient` marks it `deleted`. Any other * note fades and stays hidden until `notesByPatient` drops it. Set * `keptOnDelete` on a note to say otherwise. The panel holds this only * while the Notes tool stays open for that patient: reopened, it shows * the note as `notesByPatient` does, with a live delete. So return the * same promise for a second delete of a note already being deleted. */ handleDeleteNote: (patientId: string, noteId: string) => void | Promise; /** * Pin or unpin a note. `next` is the value the note moves to, so set it * rather than flipping what you hold. A returned promise holds the new * value until the notes agree; a rejection rolls it back and the notes * panel shows the message. Do not also toast it. */ handleToggleNotePin: (patientId: string, noteId: string, next: boolean) => void | Promise; /** As `handleToggleNotePin`, for the flag. Omit it and the flag control * is not offered. */ handleToggleNoteFlag?: (patientId: string, noteId: string, next: boolean) => void | Promise; /** * Hears every refused note write: a save, a delete, a pin or a flag. * `failure.shown` says whether the notes panel told the reader. Toast * only the ones with `shown: false`, for example a refusal that arrives * after the reader switched tool or patient; a save among them has lost * its draft. Wire it in the same change that stops you toasting every * refusal yourself: without it, those late refusals reach no one. */ handleNoteWriteError?: (patientId: string, failure: NotesPanelWriteFailure) => void; /** Read a patient's notes: the latest few, or every note. */ handleLoadNotes?: (patientId: string, scope: 'preview' | 'all') => void; /** * The practice's OWN drafting endpoint, behind the composer's AI * affordance. Omit it and the affordance is not offered: the kit * ships no draft prose of its own, and must never put words it * invented into a patient's record. */ requestNoteAiDraft?: NotesPanelProps['onRequestAiDraft']; /** * The host's live transcription. Omit it and no microphone is * offered — the kit transcribes nothing. */ dictateNote?: NotesPanelProps['onDictate']; /** * The patient's recorded AI-consent state for the notes composer, read * per patient as the details panel opens them. Omit it and the composer * is ungated (today's behaviour) — the platform wires this to the * patient's `PatientAiConsent` record and the * `ic_internal_api_post_patient_ai_consent` grant route. */ noteAiConsentFor?: (patientId: string) => AiConsentGate | undefined; /** * The notes composer's template-variable, Help-me-write and image-upload * seams for one patient, read as the details panel opens them — one hook * instead of a prop each, mirroring `noteAiConsentFor`. Omit any key (or the * whole hook) and that affordance is not offered: the kit ships no * variables, no bring-your-own Help-me-write surface and no image upload * of its own. Typically backed by the host's own `useHelpMeWriteTemplates` * call, built once per patient and passed straight through. */ noteComposerExtrasFor?: (patientId: string) => Partial> | undefined; } //# sourceMappingURL=calendar.journey.d.ts.map