/** * patient-table — domain types * ---------------------------------------------------------------------------- * Type surface for the `patient-table` Domain-Specific component, a thin * composition over the kit's `data-table` engine (AG Grid Community). * * CONVENTIONS (CI-blocking — see src/docs/23-constraints.mdx + 26-agent-readiness.mdx): * * • MONEY IS INTEGER CENTS. Every monetary field on `PatientRow` * (`unpaidAmount`, `billableAmount`) and on the financial thresholds is the * smallest currency unit (cents). Format to a display currency only at the * render boundary — never store fractional units. * * • PHI MUST NEVER REACH THE DOM. Only `PatientRow.id` may appear in DOM * `data-*` attributes (PRS 26 §6). Name, date of birth, email, phone, fiscal * code and every other identifying field stay out of attribute hooks, agent * snapshots and selectors. Renderers surface them as text content only. * * These types are the public contract for `buildPatientColumns(t)`, the patient * cell renderers, and the `PatientTable` component (Phase 2). They carry no * runtime values — pure type declarations. * * GROUND-TRUTH NOTE: `ColDef` / `ColGroupDef` are NOT re-exported from the * data-table barrel; they are imported here straight from `ag-grid-community`, * exactly as `data-table.tsx` does. `DataTableHandle` IS re-exported from the * barrel and is imported from there. */ import type { ColDef, ColGroupDef } from 'ag-grid-community'; import type { DataTableHandle, DataTableSortEntry } from '../data-table'; /** Tri-state marketing consent: `0` = no, `1` = yes, `2` = not specified. */ export type PatientMarketingConsent = 0 | 1 | 2; /** Biological-sex marker used for the demographic column / gender filter. */ export type PatientGender = 'm' | 'f' | 'x'; /** * A per-practice custom field descriptor. Drives the dynamic `custom:*` columns * appended after the standard ones by `buildPatientColumns`. The `label` is * practice-defined display copy — NOT i18n-keyed (it is the header verbatim). */ export interface PatientCustomFieldDef { /** Stable descriptor id. */ id: string; /** Key into {@link PatientRow.customFields}. */ fieldName: string; /** Practice-defined header label — rendered verbatim, never i18n-keyed. */ label: string; /** Renderer + filter shape: plain text, a yes/no checkbox, or a chip. */ type: 'text' | 'checkbox' | 'dropdown'; /** Closed value set for `dropdown` fields (feeds the SelectFilter options). */ options?: string[]; } /** Care-plan acceptance state surfaced by `CarePlanStatusCell`. */ export type PatientCarePlanStatus = 'accepted' | 'estimate' | 'none'; /** A single phone number plus its SMS-reminder eligibility. */ export interface PatientPhoneNumber { /** Display string, e.g. `"+39 055 123 4567"`. Never a DOM attribute value. */ number: string; /** Whether SMS reminders can be sent to this number. */ smsEnabled: boolean; } /** * The patient row model rendered by `patient-table`. * * Mirrors the story-only mock schema in the workflow plan. `id` is the ONLY * field permitted in DOM `data-*` hooks (PHI rule); every other field is * display/content-only. Monetary amounts (`unpaidAmount`, `billableAmount`) * are integer cents. */ export interface PatientRow { /** Stable identifier — the ONLY field allowed in DOM data-* (PHI rule). */ id: string; firstName: string; lastName: string; avatarUrl?: string; email?: string; /** Whether email contact is enabled — gates the email-reminder action. */ emailEnabled: boolean; /** Whether the stored email passed validation. */ emailValid?: boolean; /** PEC (certified email) address. */ pecAddress?: string; phoneNumbers: PatientPhoneNumber[]; /** Whether the patient is eligible to receive SMS reminders. */ smsReminderEligible?: boolean; /** Whether the patient is eligible to receive email reminders. */ emailReminderEligible?: boolean; /** ISO-8601 date string. Age is computed at the render boundary. */ dateOfBirth?: string; gender?: PatientGender; /** Place (comune) of birth. */ placeOfBirth?: string; /** ISO-3166-1 alpha-2 country of birth. */ countryOfBirth?: string; street?: string; postcode?: string; city?: string; province?: string; /** ISO-3166-1 alpha-2 country code (residence). */ countryCode?: string; italianFiscalCode?: string; /** Acquisition source (the practice-configured channel). */ source?: string; /** Free-text source override when `source` is `'Other'`. */ customSource?: string; defaultInsurance?: string; /** Default discount percentage applied to this patient's plans. */ defaultDiscount?: number; /** Primary clinical diagnosis note. */ diagnosis?: string; /** Patient's occupation. */ job?: string; /** Operator / personal assistant the patient is assigned to. */ personalAssistant?: string; marketingConsent: PatientMarketingConsent; /** Year component of the yearly patient numbering (`YYYY`). */ yearlyNumberingYear?: number; /** Sequence component of the yearly patient numbering (`N`). */ yearlyNumberingNumber?: number; /** Whether the patient has a MyDentista portal account. */ mydentista?: boolean; nextAppointment?: string; lastAppointment?: string; /** Outstanding balance in cents. */ unpaidAmount: number; /** Billable plan value in cents. */ billableAmount: number; carePlanStatus?: PatientCarePlanStatus; tags?: string[]; /** ISO-8601 creation timestamp. */ createdAt: string; isArchived?: boolean; /** * Per-practice custom-field values, keyed by `PatientCustomFieldDef.fieldName`. * Strings for text/dropdown fields; booleans for checkbox fields. Surfaced by * the dynamic `custom:*` columns. Never a DOM attribute (PHI rule). */ customFields?: Record; } /** * Stable column ids for the patient grid. Drives `ColumnToggle`, the * responsive-priority map, view presets and the agent column address. * Keep in sync with `buildPatientColumns(t)`. */ export type PatientColumnId = 'select' | 'avatar' | 'lastName' | 'actions' | 'firstName' | 'fiscalCode' | 'age' | 'gender' | 'dateOfBirth' | 'placeOfBirth' | 'countryOfBirth' | 'phone' | 'email' | 'smsReminders' | 'emailReminders' | 'pec' | 'nextAppointment' | 'lastVisit' | 'unpaidBalance' | 'billableAmount' | 'discount' | 'carePlanStatus' | 'diagnosis' | 'insurance' | 'assignedTo' | 'job' | 'source' | 'marketingConsent' | 'patientNumber' | 'portal' | 'createdAt' | 'city' | 'province' | 'postcode' | 'country' | 'street' | `custom:${string}`; /** * Responsive priority tier. Lower numbers survive longer as the grid narrows: * `P0` always visible, `P3` first to hide. The responsive hook toggles * column `hide` by tier; an explicit user ColumnToggle override wins and is * what gets persisted. */ export type ResponsiveTier = 'P0' | 'P1' | 'P2' | 'P3'; /** Closed set of per-row action keys. */ export type PatientRowActionKey = 'show' | 'edit' | 'sendEmailReminder' | 'sendSmsReminder' | 'reassign' | 'delete'; /** * A per-row action descriptor. `id` is the stable key; the display fields feed * the `ActionsCellRenderer` (icon + accessible label + intent). Visibility and * disablement are resolved per-row against the `PatientRow` at render time. */ export interface PatientRowAction { /** Stable action key — the value passed back to `onRowAction`. */ id: PatientRowActionKey; /** Localised accessible label / tooltip. */ label: string; /** Lucide-style icon node (sized by the renderer). */ icon?: import('react').ReactNode; /** `destructive` swaps the button intent (e.g. delete). */ variant?: 'default' | 'destructive'; /** Hide the action for a given row (e.g. SMS reminder with no SMS phone). */ hidden?: boolean | ((patient: PatientRow) => boolean); /** Disable the action for a given row. */ disabled?: boolean | ((patient: PatientRow) => boolean); /** Human-readable reason shown in the tooltip when disabled. */ disabledReason?: string; } /** Closed set of bulk-action keys (operate on the current selection). */ export type PatientBulkActionKey = 'exportCsv' | 'exportPdf' | 'reassign' | 'tag' | 'sendReminder' | 'delete'; /** * A bulk-action descriptor for the toolbar. Invoked with the selected row ids * (resolved from `DataTableHandle.getSelection()`); only ids cross this * boundary — never PHI. */ export interface PatientBulkAction { /** Stable bulk-action key. */ id: PatientBulkActionKey; /** Localised button label. */ label: string; /** Lucide-style icon node. */ icon?: import('react').ReactNode; /** `destructive` swaps the button intent (e.g. bulk delete). */ variant?: 'default' | 'destructive'; /** Invoked with the ids of the currently-selected rows. */ onSelect: (selectedIds: string[]) => void; } /** * A saved view: a named column/filter/sort preset applied to the grid. * `columns` is the ordered, visible subset; `filterModel` and `sortModel` are * fed straight to `DataTableHandle.setFilter` / `setSort`. */ export interface PatientView { /** Stable view id. */ id: string; /** Localised view label shown in the view switcher. */ label: string; /** Ordered list of visible columns for this view. */ columns: PatientColumnId[]; /** AG Grid filter model passed to `DataTableHandle.setFilter`. */ filterModel: Record; /** Sort model passed to `DataTableHandle.setSort`. */ sortModel: DataTableSortEntry[]; } /** * The curated imperative handle exposed by `PatientTable` via `forwardRef`, * specialised to `PatientRow`. Re-exported so column/renderer/component files * import a single patient-typed handle. */ export type PatientTableHandle = DataTableHandle; /** Column definition typed to `PatientRow` (AG Grid Community). */ export type PatientColDef = ColDef; /** Column-group (two-row header) definition typed to `PatientRow`. */ export type PatientColGroupDef = ColGroupDef; /** Mixed leaf / group column-definition array, as `DataTable.columnDefs` expects. */ export type PatientColumnDef = PatientColDef | PatientColGroupDef; //# sourceMappingURL=types.d.ts.map