/** * Calendar pattern — the DOMAIN TYPES and the pure helpers over them: * patient, phone, care, care entry, care link, care plan (+ entry), price * list, sign channel / document, appointment meta, task detail, base * event, booking contact, business hours, closure, holiday, operator * group / filter group, waiting entry, history entry, practice features * and branch. Nothing here reads fixture data: every helper takes what it * needs as an argument, so the demos and the platform host share one * vocabulary. The read model built from these lives in `calendar.data.ts`. * * Type names keep their `Demo*` prefix for now — a later sweep renames * them `Calendar*` in one go. */ import type { CalendarEvent, CalendarResource } from '../../components/calendar'; import type { CarePlanState } from '../../components/_shared/entity-card'; import type { SignatureParty } from '../../components/signature-status-chip'; import type { TaskEditorDraft, TaskEditorTask } from '../../components/task-editor'; import type { AppointmentAnamnesisAnswer, AppointmentDetailsPatient, AppointmentHistoryEntry, AppointmentHistoryField, AppointmentHistoryChange, AppointmentHistoryKind, AppointmentPhoneLabel, AppointmentPhoneNumber, AppointmentSignChannel } from '../../components/appointment-details-panel'; /** Platform `DoctorPatientPhoneNumber` carries a free-text label; the * vocabulary is fixed so the printed call sheet can localise it. */ export type CalendarPhoneLabel = AppointmentPhoneLabel; export type CalendarPhoneNumber = AppointmentPhoneNumber; /** One highlighted anamnesis answer (display strings, host-localised). */ export type CalendarAnamnesisAnswer = AppointmentAnamnesisAnswer; /** * A patient as the HOST knows them. Only the identity — `id` and `name` — * is required: a host whose appointment feed carries a patient id and a * display name and nothing else supplies exactly those two, and every * reader below treats each remaining field as "the host did not tell us". * * The rule for a reader: never render a row, chip or label that asserts a * value for a field the host omitted — an empty fiscal code printed under * a "Fiscal code" label is a claim about the record, not a blank. Omit the * row; where every row of a section would go, omit the section. */ export interface CalendarPatient extends AppointmentDetailsPatient { id: string; name: string; dobIso?: string; phone?: string; email?: string; smsEnabled?: boolean; emailEnabled?: boolean; city?: string; /** Optional profile photo — Avatar falls back to the initials circle. */ avatarSrc?: string; sex?: 'f' | 'm'; fiscalCode?: string; address?: string; province?: string; postalCode?: string; patientSinceIso?: string; lastVisitIso?: string; priceList?: string; discountPct?: number; unpaidAmount?: number; allergies?: string; diagnosis?: string; highlightedAnamnesisCount?: number; expiredAnamnesisCount?: number; /** The highlighted answers themselves — the platform details dialog * lists these, the popover only counts them. */ highlightedAnamnesisAnswers?: CalendarAnamnesisAnswer[]; /** Every labelled number for the call sheet; absent = `phone` is the * mobile (see `patientPhones`). */ phones?: CalendarPhoneNumber[]; } /** A public holiday (platform `GetHolidaysService`): a local ISO date plus * its display name — the host localises it (the platform's holiday * service already returns a named day). */ export interface CalendarHoliday { dateIso: string; name: string; } /** Practice-level calendar flags (platform `keep_open_on_holidays`). */ export interface CalendarPractice { openOnHolidays: boolean; } /** An operator group (platform `OperatorGroup`) — the operator filter * lists members under their group with a per-group select-all. */ export interface CalendarOperatorGroup { id: string; name: string; operatorIds: readonly string[]; } /** One section of the grouped operator filter. */ export interface CalendarOperatorFilterGroup { id: string; name: string; operators: CalendarResource[]; } /** Virtual bucket id for operators outside every group (platform id 0). */ export declare const OPERATOR_GROUP_NONE_ID = "grp-none"; /** * The platform's `GetGroupedOperatorService` rule: unknown member ids are * dropped, empty groups omitted, ungrouped operators fall into the virtual * bucket (only when it has members), members sort active-first then by * `sequenceNumber`, and every group — bucket included — sorts by name in * the given locale. */ export declare function buildOperatorFilterGroups(args: { groups: readonly CalendarOperatorGroup[]; operators: readonly CalendarResource[]; inactiveIds: readonly string[]; withoutGroupLabel: string; locale: string; }): CalendarOperatorFilterGroup[]; /** One care/treatment recorded on an appointment. Catalogued when `careId` * resolves in the host's care catalogue; ad hoc otherwise (a typed name, no code). */ export interface CalendarCareEntry { careId?: string; name: string; /** Tariff code — catalogue entries only. */ code?: string; /** FDI two-digit tooth codes, e.g. ['26']. */ elements?: string[]; /** Phase labels, rendered as [a, b] after the name. */ phases?: string[]; } /** Platform `appointmentCareVisualization` — disabled / name / code / * name_and_code: how the card describes its cares. */ export type CareDescriptionMode = 'off' | 'name' | 'code' | 'nameCode'; /** Platform `color_association_type_*`: which colour a NEW or MOVED * appointment inherits. Stored colours never repaint on a switch. */ export type ColorAssociation = 'care' | 'operator' | 'chair'; /** * Platform care/treatment slice for the find-free-slots tool: picking a * care derives the slot duration, the eligible operators/chairs, and the * appointment colour (platform `FreeSlotController` care-driven search). */ export interface CalendarCare { id: string; name: string; /** Tariff code (Italian price-list style) — unique per care. */ code: string; durationMin: number; operatorIds: string[]; chairIds: string[]; colorHex: string; priceCents: number; /** Catalogue phases (platform `care.carePhases`) — a price-list row * books a subset of them, exactly as a plan line does. */ phases?: string[]; } /** * The card's care line (platform `getAppointmentNameSuffixAndPopoverName`): * the free-text description leads, the cares follow joined with ', ', * phases in [brackets]. Under Code an uncoded ad hoc care shows its name * (the platform prints a blank). `undefined` when nothing remains. */ export declare function careSuffix(input: { description?: string; cares?: CalendarCareEntry[]; }, mode: CareDescriptionMode): string | undefined; /** The always-on derived label — what `meta.care` and `event.suffix` * carry regardless of the user's care-description setting. */ export declare function careLabel(cares: CalendarCareEntry[]): string; /** What `associationColor` resolves its ids against — the practice's * operators, chairs and care catalogue. `CalendarData` satisfies it. */ export interface AssociationColorSources { operators: readonly CalendarResource[]; chairs: readonly CalendarResource[]; cares: readonly CalendarCare[]; } /** The colour an appointment inherits under an association (platform * `determineColorIdForResource`): the operator's, the chair's, or the * first catalogued care's. `undefined` = keep whatever it has. */ export declare function associationColor(association: ColorAssociation, ctx: { operatorId?: string; chairId?: string; cares?: CalendarCareEntry[]; }, sources: AssociationColorSources): string | undefined; /** A practice price list (platform `PriceList`). */ export interface CalendarPriceList { id: string; name: string; } /** One treatment line of a care plan (platform `CarePlanEntry`). */ export interface CalendarCarePlanEntry { id: string; careId: string; /** FDI two-digit tooth codes. */ elements?: string[]; done: boolean; executionDateIso?: string; billed?: boolean; /** Phase labels — an appointment may book a subset of them. */ phases?: string[]; /** Overrides the care's catalogue duration. */ durationMin?: number; /** Overrides the care's catalogue price. */ priceCents?: number; } /** A patient's care plan (platform `CarePlan`) in the kit CarePlanCard's * state vocabulary; amounts in cents. */ export interface CalendarCarePlan { id: string; patientId: string; number: string; name: string; state: CarePlanState; acceptedIso?: string; amounts?: { priceCents?: number; billedAmountCents?: number; dueCents?: number; }; entries: CalendarCarePlanEntry[]; } /** What a saved appointment carries about where its treatments came * from (platform `carePlanId` + entry ids, or price-list picks). */ export type CalendarCareLink = { kind: 'plan'; planId: string; /** Plan order, never tick order. */ entryIds: string[]; phasesByEntry?: Record; } | { kind: 'priceList'; priceListId: string; /** `phases` are the booked subset of the care's catalogue phases; * `billed` locks the row (the platform refuses to touch an * already-invoiced treatment). */ items: Array<{ careId: string; priceCents: number; phases?: string[]; billed?: boolean; }>; }; /** Channel a signature request went out on (platform `requiredObjectsSignMode`). */ export type CalendarSignChannel = AppointmentSignChannel; /** One document to sign on an appointment — each party's standing. */ export interface CalendarSignDocument { id: string; name: string; operator: SignatureParty; patient: SignatureParty; } /** * Per-appointment extras the CalendarEvent shape does not carry — the * details panel and the peek card read these by event id. */ export interface CalendarAppointmentMeta { patientId: string; /** Absent = nobody assigned yet — a Backlog "No operator" item. */ operatorId?: string; /** Absent = a desk-created slot with no chair yet (chairs are optional * on the platform) — the calendar backlog's "Senza poltrona" gap. */ chairId?: string; /** Derived care label — `careLabel(cares)`, tasks keep the task title. * Also the event's always-on `suffix`. */ care: string; /** The care entries the label derives from (tasks: empty). */ cares: CalendarCareEntry[]; /** Outstanding billable amount in EUR, when any. */ billableAmount?: number; /** Where the cares came from — a care plan's lines or a price list. */ careLink?: CalendarCareLink; /** Telehealth session to join (platform `telehealthSessionId`). */ telehealthSessionCode?: string; /** Documents attached for signature, with each party's standing. */ documentsToSign?: CalendarSignDocument[]; /** Channel the signature request already went out on, if any. */ signRequestedVia?: CalendarSignChannel; /** When the online booking was made (platform `createdAt`). */ bookedAtIso?: string; /** Note the patient typed on the booking form. */ bookingNote?: string; } /** * Platform-shaped task detail (the `TaskDTO` slice the legacy task page * shows) — the demo's task-details rail panel reads these by event id. */ export interface CalendarTaskDetail { /** SystemTaskType key, or 'custom' with `typeLabel` (platform TaskType). */ type: 'recall' | 'payment' | 'collection' | 'other' | 'custom'; /** Practice-defined type name — custom types only. */ typeLabel?: string; /** Free-text body (platform `note`). */ note?: string; /** Lifecycle flag (platform `done`). */ done: boolean; /** Assignee display name (platform `practiceUser`). */ assigneeName?: string; /** Linked company name (platform `doctorCompany`). */ companyName?: string; /** Provenance — created by hand or by a workflow automation. */ createdBy: 'user' | 'workflow'; /** Creator display name when `createdBy === 'user'`. */ createdByName?: string; createdAtIso: string; } /** Base events WITHOUT `resourceId` — the surface maps each event onto the * chair or the operator axis depending on the active view. */ export interface CalendarBaseEvent { event: CalendarEvent; meta: CalendarAppointmentMeta | null; /** Care entries even without meta (online bookings); tasks carry []. */ cares: CalendarCareEntry[]; } /** * What an online booking actually carries (platform parity): the details * the person typed on the booking website — name, SMS-verified phone, * email and a free-text note — but NO patient record. Assigning one is * the triage job (the platform's `BookingPatientDetails` + * `appointment_form.search_patient_for_booking_appointment_below`). */ export interface CalendarBookingContact { typedName: string; phone: string; email: string; note: string; /** When the booking was made (platform `createdAt`). */ bookedAtIso: string; } /** A calendar task (platform `DoctorReminder`): the calendar's name for the * published TaskEditor's record. See `TaskEditorTask` for every field, * including why `dateIso` is an instant and the one-counterpart rule. */ export type CalendarTask = TaskEditorTask; /** The same shape without an identity: what the create seam receives and * what an edit save re-sends in full. See `TaskEditorDraft`. Published as * `CalendarTaskDraft`. */ export type TaskDraft = TaskEditorDraft; /** One contiguous working range, minutes from local midnight. */ export interface CalendarDayHours { startMin: number; endMin: number; } /** * Weekly hours keyed by JS `getDay()` (0 = Sunday … 6 = Saturday). A * missing weekday means closed all day; one range per day is the demo's * deliberate simplification (the platform allows split shifts). */ export type CalendarWeeklyHours = Partial>; export interface CalendarBusinessHours { /** The practice's own opening hours. */ practice: CalendarWeeklyHours; /** Per-operator weekly hours — intersect with `practice` when used. */ operators: Record; } /** Why a date (or, given an operator, that column) cannot take a booking. * Precedence closed > holiday > absence: a practice-level closure applies * to every column, so it wins over one operator's day off. */ export type CalendarClosure = { kind: 'closed'; } | { kind: 'holiday'; name: string; } | { kind: 'absence'; operatorId: string; title?: string; }; /** * Platform `WaitingListEntry` shape — exactly ONE of the three mutually * exclusive connections per entry (the platform enforces this): * - `appointmentId`: the patient already HAS an appointment; the entry * means "bring it forward if a slot frees up" (schedule = move mode); * - `patientId` (+ optional description): waiting for a first free slot * (schedule = create mode, pre-seeded with operator/colour); * - `description` only: a free-text hold with no patient linked. * `operatorId` and `colorHex` (platform `colorId`) are the DEFAULTS the * future appointment is created with, not display decoration. */ export interface CalendarWaitingEntry { id: string; patientId?: string; appointmentId?: string; description?: string; operatorId?: string; colorHex?: string; createdDay: number; /** Story-only: the patient's number can receive WhatsApp messages. */ whatsappCapable?: boolean; /** Host: the number WhatsApp can reach (platform `whatsappPhoneNumber`). * Set, it IS the WhatsApp number and `whatsappCapable` is not read. */ whatsappPhoneNumber?: string; /** The row's photo (its own patient's, or the linked appointment's) * when the host's `patientById` record carries no `avatarSrc`. */ patientAvatarUrl?: string; } export type CalendarHistoryKind = AppointmentHistoryKind; /** Form fields an edit can touch (the platform's audit vocabulary). */ export type CalendarHistoryField = AppointmentHistoryField; /** One value an edit changed, before and after. */ export type CalendarHistoryChange = AppointmentHistoryChange; /** One ledger line: who did what to the appointment, and when. */ export type CalendarHistoryEntry = AppointmentHistoryEntry; /** Every labelled phone number; see the shared helper. */ export { patientPhones } from '../../components/_shared/appointment-format'; /** * A patient as the calendar can see them at one point: the id a row, * an event or a waiting entry carries, plus the record if the host's * `patientById` could answer. */ export interface PatientRef { /** The id, as a string. `''` — and ONLY `''` — means nobody is booked. */ readonly id: string; /** The record, when the host could resolve the id. */ readonly record?: CalendarPatient; /** Someone is booked here, resolved or not. The existence test. */ readonly assigned: boolean; /** Someone is booked and their record did not load — state A. */ readonly unresolved: boolean; } /** A patient id as a string, `''` when there is none. A host id of `0` * IS an id, so the test is `!= null` and never truthiness. */ export declare function patientIdOf(id: string | number | null | undefined): string; /** Pair the id a row carries with whatever the host made of it. */ export declare function patientRefOf(id: string | number | null | undefined, record: CalendarPatient | undefined): PatientRef; /** What to CALL this patient: the record's name, else the title the row * already carries, else the caller's localised "record not loaded". * Never blank — a blank name renders an initials-less avatar — and * never the "no patient assigned" of an unbooked slot. */ export declare function patientRefName(ref: PatientRef, rowTitle: string | undefined, notLoadedLabel: string): string; /** Why a notify / signing channel is or is not usable for a patient. */ export type PatientChannelState = /** On file, and the patient takes messages there. */ 'ready' /** On file, and the patient opted this channel out. */ | 'optedOut' /** The record loaded and really carries nothing here — state C, the * only state in which "none on file" may be said. */ | 'none' /** We were not told: no record at all, or the record omits the field * — states A and B. Says nothing about what the practice holds. */ | 'unknown'; /** The state of one channel for one patient reference. */ export declare function patientChannelState(ref: PatientRef, channel: 'sms' | 'email'): PatientChannelState; /** A channel the kit can hand a destination to right now — the test for * seeding a checkbox ON, or for defaulting a sign mode to it. */ export declare function channelReady(state: PatientChannelState): boolean; /** A channel the kit must leave OFFERED. `unknown` belongs here: the * host may well succeed on send, and disabling the control would claim * the practice holds nothing — a claim about a record we never saw. */ export declare function channelOfferable(state: PatientChannelState): boolean; /** The reason a channel is not offered — `null` when it IS offered, so * a caller never has to invent a string for a state it cannot reach. */ export declare function channelReasonKey(state: PatientChannelState, keys: { optedOut: string; none: string; }): string | null; /** Whether a host-supplied catalogue can back a control. */ export type CatalogueState = /** At least one entry — a picker can be filled and defaulted. */ 'ready' /** The host handed us the collection and it is empty: the practice * really has none. The only state in which "none" may be said. */ | 'none' /** The host never handed us the collection. Says nothing about what * the practice holds. */ | 'unknown'; /** The state of one host-supplied catalogue. Takes the raw member, so * `undefined` reaches it intact — never `?? []` at the call site. */ export declare function catalogueStateOf(items: readonly unknown[] | undefined): CatalogueState; /** A catalogue that can back a picker right now. `unknown` is NOT * offerable here — unlike `channelOfferable`, see the note above. */ export declare function catalogueUsable(state: CatalogueState): boolean; /** Practice-level toggles the platform gates the editor and popover on. */ export interface CalendarPracticeFeatures { /** `FIND_FREE_SLOTS_BY_CARE` — devices per care, the device line. */ findFreeSlotsByCare: boolean; /** `RECURRING_PRICE_LIST_CARE_PLAN_ENTRIES` — the care-plan cleanup. */ recurringPriceListCarePlanEntries: boolean; /** Payment automation active on the practice (no upsell when true). */ paymentAutomation: boolean; /** `FeatureToggle::PAU_UPSELL` — show the upsell hook when PAU is off. */ pauUpsell: boolean; } /** A practice branch (platform `CarePlanBranch`) — the select shows only * when the practice has more than one. */ export interface CalendarBranch { id: string; /** Display name — the host localises it. */ name: string; } /** * One free-slot question for a host that answers SERVER-side * (`CalendarData.searchFreeSlots`). The scope is already narrowed to what * the chosen care makes eligible, so the host asks its own finder for * exactly this and nothing wider. `from` / `to` are local wall-clock * instants. The STEP is the host's — the kit names none, so the server * answers on whatever grid the practice's calendar interval implies. */ export interface FreeSlotQuery { /** Search from here — clamped to midnight of the host's `nowIso` day. */ from: Date; /** Last date to look at (inclusive). */ to: Date; /** The appointment length the answer must fit. */ durationMin: number; /** Operator ids in scope — never empty. */ operatorIds: readonly string[]; /** Chair ids in scope — never empty. */ chairIds: readonly string[]; /** The care the search was scoped to, when one was chosen. */ careId?: string; /** The host's own medical-device strings, round-tripped untouched. */ medicalDeviceIds?: readonly string[]; } //# sourceMappingURL=calendar.types.d.ts.map