@alfadocs/ui-kit-debug 1.19.0 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/_chunks/{anamnesis-summary-rows-CRWO8x8g.js → anamnesis-summary-rows-DwjqZgR5.js} +3 -3
  2. package/dist/_chunks/{anamnesis-summary-rows-CRWO8x8g.js.map → anamnesis-summary-rows-DwjqZgR5.js.map} +1 -1
  3. package/dist/_chunks/{antenatal-schedule-timeline-B1k8QkA6.js → antenatal-schedule-timeline-sXfIp5lp.js} +3 -3
  4. package/dist/_chunks/{antenatal-schedule-timeline-B1k8QkA6.js.map → antenatal-schedule-timeline-sXfIp5lp.js.map} +1 -1
  5. package/dist/_chunks/{appointment-timeline-DCn0Jx55.js → appointment-timeline-DhHwmHo1.js} +2 -2
  6. package/dist/_chunks/{appointment-timeline-DCn0Jx55.js.map → appointment-timeline-DhHwmHo1.js.map} +1 -1
  7. package/dist/_chunks/{balance-badge-cell-DNjHODfP.js → balance-badge-cell-Bh2QbxsZ.js} +47 -46
  8. package/dist/_chunks/balance-badge-cell-Bh2QbxsZ.js.map +1 -0
  9. package/dist/_chunks/{booking-DQbo6mQF.js → booking-WXKbA69C.js} +4 -4
  10. package/dist/_chunks/{booking-DQbo6mQF.js.map → booking-WXKbA69C.js.map} +1 -1
  11. package/dist/_chunks/{calendar-workspace-BAouFiA5.js → calendar-workspace-CB6KLoF6.js} +87 -73
  12. package/dist/_chunks/{calendar-workspace-BAouFiA5.js.map → calendar-workspace-CB6KLoF6.js.map} +1 -1
  13. package/dist/_chunks/{care-plan-card-D-FMfDKH.js → care-plan-card-CQVboTn1.js} +2 -2
  14. package/dist/_chunks/{care-plan-card-D-FMfDKH.js.map → care-plan-card-CQVboTn1.js.map} +1 -1
  15. package/dist/_chunks/{care-plan-entry-card-Bvw_9T-d.js → care-plan-entry-card-jxHkzez5.js} +2 -2
  16. package/dist/_chunks/{care-plan-entry-card-Bvw_9T-d.js.map → care-plan-entry-card-jxHkzez5.js.map} +1 -1
  17. package/dist/_chunks/{cycle-calculator-DmtYGhqq.js → cycle-calculator-DVgxBfP0.js} +3 -3
  18. package/dist/_chunks/{cycle-calculator-DmtYGhqq.js.map → cycle-calculator-DVgxBfP0.js.map} +1 -1
  19. package/dist/_chunks/{data-table-DC5rO3Ty.js → data-table-Bw3i0753.js} +253 -249
  20. package/dist/_chunks/{data-table-DC5rO3Ty.js.map → data-table-Bw3i0753.js.map} +1 -1
  21. package/dist/_chunks/{data-table-tabs-D24qj89A.js → data-table-tabs-DVIFkqx_.js} +2 -2
  22. package/dist/_chunks/{data-table-tabs-D24qj89A.js.map → data-table-tabs-DVIFkqx_.js.map} +1 -1
  23. package/dist/_chunks/{date-picker-CepI_oI2.js → date-picker-B6NW_PS5.js} +3 -3
  24. package/dist/_chunks/{date-picker-CepI_oI2.js.map → date-picker-B6NW_PS5.js.map} +1 -1
  25. package/dist/_chunks/{date-picker-variants-DfuooXEK.js → date-picker-variants-B9NLAfUK.js} +5 -2
  26. package/dist/_chunks/{date-picker-variants-DfuooXEK.js.map → date-picker-variants-B9NLAfUK.js.map} +1 -1
  27. package/dist/_chunks/{date-range-picker-BD8P_rJq.js → date-range-picker-TpmECKRh.js} +197 -196
  28. package/dist/_chunks/date-range-picker-TpmECKRh.js.map +1 -0
  29. package/dist/_chunks/{date-time-picker-3I2vVKGV.js → date-time-picker-DijEae25.js} +2 -2
  30. package/dist/_chunks/{date-time-picker-3I2vVKGV.js.map → date-time-picker-DijEae25.js.map} +1 -1
  31. package/dist/_chunks/{due-date-calculator-BF_Ikofx.js → due-date-calculator-BtgWeFqp.js} +4 -4
  32. package/dist/_chunks/{due-date-calculator-BF_Ikofx.js.map → due-date-calculator-BtgWeFqp.js.map} +1 -1
  33. package/dist/_chunks/{field-value-D-vkX9uL.js → field-value-BetdWMA2.js} +2 -2
  34. package/dist/_chunks/{field-value-D-vkX9uL.js.map → field-value-BetdWMA2.js.map} +1 -1
  35. package/dist/_chunks/{file-manager-spc3_khe.js → file-manager-BdXjZNIc.js} +2 -2
  36. package/dist/_chunks/{file-manager-spc3_khe.js.map → file-manager-BdXjZNIc.js.map} +1 -1
  37. package/dist/_chunks/{gestational-age-calculator-CiOQjzgx.js → gestational-age-calculator-KdMg9z9H.js} +5 -5
  38. package/dist/_chunks/{gestational-age-calculator-CiOQjzgx.js.map → gestational-age-calculator-KdMg9z9H.js.map} +1 -1
  39. package/dist/_chunks/{hcg-doubling-CpU7H5rQ.js → hcg-doubling-C2fQxfXd.js} +3 -3
  40. package/dist/_chunks/{hcg-doubling-CpU7H5rQ.js.map → hcg-doubling-C2fQxfXd.js.map} +1 -1
  41. package/dist/_chunks/{header-settings-J-ZCAfir.js → header-settings-DvjZNMhD.js} +2 -2
  42. package/dist/_chunks/{header-settings-J-ZCAfir.js.map → header-settings-DvjZNMhD.js.map} +1 -1
  43. package/dist/_chunks/{icon-picker-ChzpCnZ-.js → icon-picker-DOa89vn3.js} +2 -2
  44. package/dist/_chunks/{icon-picker-ChzpCnZ-.js.map → icon-picker-DOa89vn3.js.map} +1 -1
  45. package/dist/_chunks/{info-tip-C119cwQl.js → info-tip-CADik6WN.js} +2 -2
  46. package/dist/_chunks/{info-tip-C119cwQl.js.map → info-tip-CADik6WN.js.map} +1 -1
  47. package/dist/_chunks/{link-cell-renderer-B-xrUPew.js → link-cell-renderer-tWEOv3Sc.js} +6 -4
  48. package/dist/_chunks/link-cell-renderer-tWEOv3Sc.js.map +1 -0
  49. package/dist/_chunks/{locale-picker-BvbVjDQ-.js → locale-picker-CwFtqSkt.js} +2 -2
  50. package/dist/_chunks/{locale-picker-BvbVjDQ-.js.map → locale-picker-CwFtqSkt.js.map} +1 -1
  51. package/dist/_chunks/{marketplace-app-shell-DDQXQ8hX.js → marketplace-app-shell-C4AjQmvz.js} +2 -2
  52. package/dist/_chunks/{marketplace-app-shell-DDQXQ8hX.js.map → marketplace-app-shell-C4AjQmvz.js.map} +1 -1
  53. package/dist/_chunks/{notes-panel-DYAtlq_i.js → notes-panel-CTNpsByN.js} +2 -2
  54. package/dist/_chunks/{notes-panel-DYAtlq_i.js.map → notes-panel-CTNpsByN.js.map} +1 -1
  55. package/dist/_chunks/{parse-MN9bGkrY.js → parse-DPqj4JtD.js} +2 -2
  56. package/dist/_chunks/{parse-MN9bGkrY.js.map → parse-DPqj4JtD.js.map} +1 -1
  57. package/dist/_chunks/{patient-details-BfzvFnxQ.js → patient-details-DYAmq76G.js} +3 -3
  58. package/dist/_chunks/{patient-details-BfzvFnxQ.js.map → patient-details-DYAmq76G.js.map} +1 -1
  59. package/dist/_chunks/{patient-summary-card-D5Vfa321.js → patient-summary-card-C9WBwct-.js} +2 -2
  60. package/dist/_chunks/{patient-summary-card-D5Vfa321.js.map → patient-summary-card-C9WBwct-.js.map} +1 -1
  61. package/dist/_chunks/{popover-Cqcjx-Ny.js → popover-DP6WCVwm.js} +2 -2
  62. package/dist/_chunks/{popover-Cqcjx-Ny.js.map → popover-DP6WCVwm.js.map} +1 -1
  63. package/dist/_chunks/{pregnancy-dating-D2wchD11.js → pregnancy-dating-m-c05DOI.js} +5 -5
  64. package/dist/_chunks/{pregnancy-dating-D2wchD11.js.map → pregnancy-dating-m-c05DOI.js.map} +1 -1
  65. package/dist/_chunks/{radiograph-panel.agent-C3trfWqB.js → radiograph-panel.agent-ByM9M3qm.js} +3 -3
  66. package/dist/_chunks/{radiograph-panel.agent-C3trfWqB.js.map → radiograph-panel.agent-ByM9M3qm.js.map} +1 -1
  67. package/dist/_chunks/{rich-text-editor-DtbcA6s1.js → rich-text-editor-lSCiE0Zz.js} +2 -2
  68. package/dist/_chunks/{rich-text-editor-DtbcA6s1.js.map → rich-text-editor-lSCiE0Zz.js.map} +1 -1
  69. package/dist/_chunks/{signature-request-CUe903hj.js → signature-request-CwlAmVLw.js} +2 -2
  70. package/dist/_chunks/{signature-request-CUe903hj.js.map → signature-request-CwlAmVLw.js.map} +1 -1
  71. package/dist/_chunks/subDays-HPvBuvq3.js +8 -0
  72. package/dist/_chunks/{subDays-BLl3y3pd.js.map → subDays-HPvBuvq3.js.map} +1 -1
  73. package/dist/_chunks/{tooth-scheme-2IsmbE6w.js → tooth-scheme-C9jssOmy.js} +2 -2
  74. package/dist/_chunks/{tooth-scheme-2IsmbE6w.js.map → tooth-scheme-C9jssOmy.js.map} +1 -1
  75. package/dist/agent-catalog.json +1 -1
  76. package/dist/components/_shared/calendar-class-names.d.ts.map +1 -1
  77. package/dist/components/appointment-timeline/index.js +1 -1
  78. package/dist/components/booking/index.js +1 -1
  79. package/dist/components/care-plan-card/index.js +1 -1
  80. package/dist/components/care-plan-entry-card/index.js +1 -1
  81. package/dist/components/cycle-calculator/index.js +1 -1
  82. package/dist/components/data-table/data-table.d.ts.map +1 -1
  83. package/dist/components/data-table/filters/date-range-filter.d.ts.map +1 -1
  84. package/dist/components/data-table/index.js +2 -2
  85. package/dist/components/data-table/toolbar.d.ts.map +1 -1
  86. package/dist/components/data-table-tabs/index.js +1 -1
  87. package/dist/components/date-picker/index.js +1 -1
  88. package/dist/components/date-range-picker/date-range-picker.d.ts +6 -0
  89. package/dist/components/date-range-picker/date-range-picker.d.ts.map +1 -1
  90. package/dist/components/date-range-picker/index.js +1 -1
  91. package/dist/components/date-time-picker/index.js +1 -1
  92. package/dist/components/due-date-calculator/index.js +2 -2
  93. package/dist/components/file-manager/index.js +1 -1
  94. package/dist/components/gestational-age-calculator/index.js +1 -1
  95. package/dist/components/hcg-doubling/index.js +1 -1
  96. package/dist/components/header-settings/index.js +1 -1
  97. package/dist/components/icon-picker/index.js +1 -1
  98. package/dist/components/info-tip/index.js +1 -1
  99. package/dist/components/locale-picker/index.js +1 -1
  100. package/dist/components/notes-panel/index.js +1 -1
  101. package/dist/components/patient-details/index.js +1 -1
  102. package/dist/components/patient-summary-card/index.js +1 -1
  103. package/dist/components/patient-table/columns.d.ts +10 -3
  104. package/dist/components/patient-table/columns.d.ts.map +1 -1
  105. package/dist/components/patient-table/index.js +1 -1
  106. package/dist/components/popover/index.js +1 -1
  107. package/dist/components/popover/popover.d.ts +1 -1
  108. package/dist/components/popover/popover.d.ts.map +1 -1
  109. package/dist/components/pregnancy-dating/index.js +1 -1
  110. package/dist/components/radiograph-panel/index.js +1 -1
  111. package/dist/components/rich-text-editor/index.js +1 -1
  112. package/dist/components/tooth-scheme/index.js +1 -1
  113. package/dist/index.js +31 -31
  114. package/dist/patterns/anamnesis/index.js +1 -1
  115. package/dist/patterns/calendar/calendar.surface.d.ts.map +1 -1
  116. package/dist/patterns/calendar/index.js +1 -1
  117. package/dist/patterns/marketplace-app-shell/index.js +1 -1
  118. package/dist/patterns/signature-request/index.js +1 -1
  119. package/package.json +1 -1
  120. package/dist/_chunks/balance-badge-cell-DNjHODfP.js.map +0 -1
  121. package/dist/_chunks/date-range-picker-BD8P_rJq.js.map +0 -1
  122. package/dist/_chunks/link-cell-renderer-B-xrUPew.js.map +0 -1
  123. package/dist/_chunks/subDays-BLl3y3pd.js +0 -8
@@ -1 +0,0 @@
1
- {"version":3,"file":"balance-badge-cell-DNjHODfP.js","sources":["../../src/components/patient-table/cell-renderers/transaction-chip-cell.tsx","../../src/components/patient-table/cell-renderers/care-plan-status-cell.tsx","../../src/components/patient-table/cell-renderers/next-appointment-cell.tsx","../../src/components/patient-table/columns.ts","../../src/components/patient-table/use-responsive-columns.ts","../../src/components/patient-table/patient-table.tsx","../../src/components/patient-table/cell-renderers/balance-badge-cell.tsx"],"sourcesContent":["import type { CustomCellRendererProps } from 'ag-grid-react';\nimport { TransactionChip } from '../../transaction-chip/transaction-chip';\nimport type { TransactionState } from '../../transaction-chip/transaction-chip';\nimport type { PatientRow } from '../types';\n\nexport interface TransactionChipCellParams {\n /** State shown for a positive amount; zero always reads as `settled`. */\n positiveState: Extract<TransactionState, 'debt' | 'credit' | 'to-invoice'>;\n /** Show the state label beside the amount. Default `true` — a table cell has no other caption. */\n showLabel?: boolean;\n /**\n * ISO 4217 code for the amounts in this column. Defaults to the chip's own\n * `EUR`; a practice billing in another currency must pass its own, or every\n * cell claims euros.\n */\n currency?: string;\n}\n\n/**\n * Money column → `TransactionChip`, the kit's one financial-state pill (PRS\n * §27: \"Financial transaction state pill (debt / credit / settled)\"). Takes the\n * cell VALUE in integer cents — the `PatientRow` contract — and hands the chip\n * major units, so the row model and the chip each keep their own convention.\n */\nexport function TransactionChipCell(\n props: CustomCellRendererProps<PatientRow, number | null | undefined> &\n TransactionChipCellParams,\n) {\n const { value, positiveState, showLabel = true, currency } = props;\n const cents =\n typeof value === 'number' && Number.isFinite(value) ? value : null;\n if (cents === null) return null;\n const state: TransactionState = cents > 0 ? positiveState : 'settled';\n return (\n <TransactionChip\n state={state}\n amount={cents / 100}\n showLabel={showLabel}\n currency={currency}\n />\n );\n}\n","/**\n * care-plan-status-cell — patient-table cell renderer\n * ----------------------------------------------------------------------------\n * A thin domain preset over the data-table `StatusCellRenderer` / kit `Badge`:\n * it bakes the patient care-plan lifecycle `variantMap` so consumers don't have\n * to re-declare it on every column, and localises the label via the `ui`\n * namespace (`ui.patientTable.carePlanStatus.*`).\n *\n * Reads `PatientRow.carePlanStatus` (preferring `data.carePlanStatus`, falling\n * back to AG Grid's resolved `value`), maps it to a Badge variant:\n *\n * accepted → success estimate → info none → neutral (muted)\n *\n * GROUND-TRUTH NOTES (vs. the task brief):\n * • `StatusCellRenderer` is not exported from a barrel and is a tiny wrapper\n * around `Badge`; this preset composes `Badge` directly (same primitive,\n * no extra indirection) rather than wrapping the wrapper.\n * • The brand says the \"muted\" variant is `none`; `Badge`'s neutral variant is\n * already the muted grey chip, so `none → 'neutral'`.\n * • No event handling: a status badge is presentational, so there are no\n * provided callbacks to wire.\n */\nimport type { CustomCellRendererProps } from 'ag-grid-react';\nimport { useTranslation } from 'react-i18next';\nimport { Badge } from '../../badge/badge';\nimport type { PatientCarePlanStatus, PatientRow } from '../types';\n\n/** Badge variant baked per care-plan lifecycle state. */\ntype CarePlanBadgeVariant = 'success' | 'info' | 'neutral';\n\n/**\n * Lifecycle → Badge-variant map. Frozen so it can't be mutated by a consumer\n * holding the reference.\n */\nconst CARE_PLAN_VARIANT_MAP: Readonly<\n Record<PatientCarePlanStatus, CarePlanBadgeVariant>\n> = {\n accepted: 'success',\n estimate: 'info',\n none: 'neutral',\n};\n\n/**\n * `cellRendererParams` for `CarePlanStatusCell`.\n *\n * The lifecycle status, label source and variant map are all baked in, so the\n * only knob is the Badge size — defaults to the compact `sm` chip that fits the\n * standard AG Grid row height.\n */\nexport interface CarePlanStatusCellParams {\n /** Badge size. Defaults to `'sm'` to fit the standard row height. */\n size?: 'sm' | 'md' | 'lg';\n}\n\nfunction isCarePlanStatus(value: unknown): value is PatientCarePlanStatus {\n return value === 'accepted' || value === 'estimate' || value === 'none';\n}\n\n/**\n * Patient care-plan lifecycle badge cell.\n *\n * Pass as `cellRenderer` on the `carePlanStatus` column; the patient\n * `variantMap` and localisation are handled internally.\n */\nexport function CarePlanStatusCell(\n props: CustomCellRendererProps<PatientRow, PatientCarePlanStatus> &\n CarePlanStatusCellParams,\n) {\n const { t } = useTranslation('ui');\n const { data, value, size = 'sm' } = props;\n\n // Prefer the typed row field; fall back to AG Grid's resolved cell value.\n const raw = data?.carePlanStatus ?? value;\n if (!isCarePlanStatus(raw)) return null;\n\n const variant = CARE_PLAN_VARIANT_MAP[raw];\n\n return (\n <Badge variant={variant} withDot size={size}>\n {t(`patientTable.carePlanStatus.${raw}`)}\n </Badge>\n );\n}\n","/**\n * next-appointment-cell — patient-table cell renderer\n * ----------------------------------------------------------------------------\n * Smart upcoming-appointment cell. Renders `PatientRow.nextAppointment` as a\n * locale-aware *relative* phrase (\"in 3 days\", \"tomorrow\") via the kit\n * `Timestamp`, mirroring the `relative` preset of the data-table\n * `DateCellRenderer`. When the appointment falls within a configurable window\n * (default 7 days from now), the cell takes a token-bound emphasis treatment so\n * imminent visits stand out at a glance. When `nextAppointment` is absent it\n * renders a muted \"no upcoming\" empty-state label.\n *\n * Reads `PatientRow.nextAppointment` (preferring the typed `data.nextAppointment`\n * field, falling back to AG Grid's resolved `value`).\n *\n * GROUND-TRUTH NOTES (vs. the task brief):\n * • The brief asked for a highlight \"within 7 days\". That window is exposed as\n * `soonWithinDays` (default 7) so a column can tune it without a new renderer,\n * matching the param-driven shape of the other patient renderers.\n * • The brief named `Avatar`, `Tag`, `Icon`, `IconButton`, `Tooltip` as\n * candidate primitives. This cell is purely presentational (a date + optional\n * emphasis), so it composes only `Timestamp` plus a decorative lucide glyph —\n * the same minimal-primitive approach the analogous `CarePlanStatusCell`\n * takes. There is no `Icon`/`IconButton` standalone component in the kit\n * (renderers use `lucide-react` glyphs directly), and no row callback is\n * provided, so no interactive affordance is wired.\n * • The emphasis is a CVA `tone` variant bound entirely to design tokens\n * (`--warning-foreground`); NO colour is hardcoded and nothing is pushed into\n * AG Grid `cellStyle`/`cellClass`.\n * • PHI rule (PRS 26 §6): no row field reaches a DOM `data-*` attribute here.\n */\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { CalendarClock } from 'lucide-react';\nimport type { CustomCellRendererProps } from 'ag-grid-react';\nimport { useTranslation } from 'react-i18next';\nimport { Timestamp } from '../../timestamp';\nimport type { PatientRow } from '../types';\n\n/* ------------------------------------------------------------------ */\n/* CVA — cell wrapper */\n/* */\n/* `tone` is the only stylistic axis: `soon` lifts the row to a */\n/* token-bound warning emphasis, `default` inherits the cell colour. */\n/* ------------------------------------------------------------------ */\n\nconst nextAppointmentVariants = cva(\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)] ds:whitespace-nowrap',\n {\n variants: {\n tone: {\n default: '',\n // Imminent visit: warning foreground token + medium weight. Fully\n // token-bound — no literal colour, no AG Grid cellStyle.\n soon: 'ds:text-[color:var(--warning-foreground)] ds:font-medium',\n // Absent appointment: muted empty state.\n empty: 'ds:text-[color:var(--muted-foreground)]',\n },\n },\n defaultVariants: { tone: 'default' },\n },\n);\n\ntype NextAppointmentVariantProps = VariantProps<typeof nextAppointmentVariants>;\n\n/* ------------------------------------------------------------------ */\n/* Params */\n/* ------------------------------------------------------------------ */\n\n/**\n * `cellRendererParams` for {@link NextAppointmentCell}.\n *\n * Localisation and the relative-date formatting are handled internally; the\n * only knob is the imminence window.\n */\nexport interface NextAppointmentCellParams {\n /**\n * Highlight the cell when the appointment is at most this many days away\n * (and not in the past). Defaults to `7`. A value `<= 0` disables the\n * highlight.\n */\n soonWithinDays?: number;\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_SOON_WITHIN_DAYS = 7;\n\nfunction toTime(value: unknown): number | null {\n if (value == null || value === '') return null;\n const t =\n value instanceof Date\n ? value.getTime()\n : typeof value === 'number'\n ? value\n : new Date(String(value)).getTime();\n return Number.isNaN(t) ? null : t;\n}\n\n/**\n * Resolve the emphasis tone for a parsed appointment time. `soon` covers any\n * upcoming visit within the window (including today); past appointments fall\n * back to `default`.\n */\nfunction resolveTone(\n time: number,\n soonWithinDays: number,\n): NonNullable<NextAppointmentVariantProps['tone']> {\n if (soonWithinDays <= 0) return 'default';\n const deltaMs = time - Date.now();\n if (deltaMs < 0) return 'default';\n return deltaMs <= soonWithinDays * DAY_MS ? 'soon' : 'default';\n}\n\n/* ------------------------------------------------------------------ */\n/* NextAppointmentCell */\n/* ------------------------------------------------------------------ */\n\n/**\n * Smart upcoming-appointment cell.\n *\n * Pass as `cellRenderer` on the `nextAppointment` column; the relative\n * formatting, imminence emphasis and empty state are handled internally.\n */\nexport function NextAppointmentCell(\n props: CustomCellRendererProps<PatientRow, string> &\n NextAppointmentCellParams,\n) {\n const { t } = useTranslation('ui');\n const { data, value, soonWithinDays = DEFAULT_SOON_WITHIN_DAYS } = props;\n\n // Prefer the typed row field; fall back to AG Grid's resolved cell value.\n const time = toTime(data?.nextAppointment ?? value);\n\n if (time === null) {\n return (\n <span className={nextAppointmentVariants({ tone: 'empty' })}>\n {t('patientTable.nextAppointment.none')}\n </span>\n );\n }\n\n const tone = resolveTone(time, soonWithinDays);\n\n return (\n <span className={nextAppointmentVariants({ tone })}>\n {tone === 'soon' ? (\n <CalendarClock aria-hidden=\"true\" className=\"ds:size-3.5 ds:shrink-0\" />\n ) : null}\n <Timestamp value={time} format=\"relative\" shape=\"bare\" />\n </span>\n );\n}\n","/**\n * patient-table — column definitions\n * ----------------------------------------------------------------------------\n * `buildPatientColumns(t, opts)` returns the ordered `ColDef<PatientRow>[]` for\n * the patient grid. Every patient field is its OWN column — sortable, filterable\n * and individually hideable via ColumnToggle — rather than several fields jumbled\n * into one composite cell. Each column wires the right kit cell renderer + the\n * right kit filter + floatingFilter, a localised `headerName` (from\n * `ui.patientTable.column.*`) and a stable `colId`.\n *\n * Identity is SPLIT across three pinned-start columns (the spec's\n * \"Avatar · First · Last\"):\n * • `avatar` — ImageCellRenderer (photo, initials fallback), sr-only header.\n * • `firstName` — plain text.\n * • `lastName` — LinkCellRenderer (the patient-detail link, `identityHref`).\n * All three are pinned-start P0 anchors and never auto-hide.\n *\n * COLUMN VISIBILITY\n * -----------------\n * `●` default-visible columns carry a `responsiveTier`; the responsive hook hides\n * them by tier as the grid narrows. Every other (grouped) column ships\n * `hide: true` and NO tier, so the hook leaves it alone and a user ColumnToggle\n * override wins. The anchors (avatar / firstName / lastName / actions) omit a\n * tier so they are never auto-hidden.\n *\n * RESPONSIVE TIER MECHANISM\n * -------------------------\n * Each column may carry a custom `responsiveTier` (`P0`–`P3`) property. AG Grid's\n * `ColDef` is an open/extensible shape — extra properties pass through untouched\n * at runtime — but its TS type is closed, so we type each entry as\n * `PatientColDefWithTier` and read the tier back via {@link getResponsiveTier}\n * (no `any`, no reliance on AG Grid's loose `context`).\n *\n * CUSTOM FIELDS\n * -------------\n * `opts.customFields` appends one dynamic column per descriptor after the\n * standard columns: `colId: \\`custom:${fieldName}\\``, a LITERAL `headerName`\n * (the practice-defined label — never i18n-keyed), a `valueGetter` reading\n * `row.customFields?.[fieldName]`, and a renderer + filter chosen by `type`\n * (`text` → plain text · `checkbox` → ✓/— · `dropdown` → Tag chip). All ship\n * `hide: true` and no tier.\n *\n * CONSTRAINTS:\n * • No colour / spacing / radius / font literal in any `cellStyle`/`cellClass`\n * here — only token-bound `ds:` utility classes — so the AG Grid theme bridge\n * stays in control of theming.\n * • CSS logical properties only.\n * • PHI rule (PRS 26 §6): no `PatientRow` field other than `id` is ever placed\n * in a DOM `data-*` attribute. Renderers surface name/contact/DOB as text\n * content only; these defs pass values, not attributes.\n */\nimport type { TFunction } from 'i18next';\nimport { Eye, Mail, MessageSquare, Pencil, Trash2, Users } from 'lucide-react';\nimport { createElement } from 'react';\nimport {\n ActionsCellRenderer,\n actionsColumnWidth,\n DateCellRenderer,\n DateRangeFilter,\n DateRangeFloatingFilter,\n ImageCellRenderer,\n LinkCellRenderer,\n NumberFilter,\n NumberFloatingFilter,\n SelectFilter,\n SelectFloatingFilter,\n StatusCellRenderer,\n TagListCellRenderer,\n TextFilter,\n TextFloatingFilter,\n TypeaheadFilter,\n TypeaheadFloatingFilter,\n UserCellRenderer,\n type ActionDef,\n type StatusCellRendererParams,\n type UserCellValue,\n} from '../data-table';\nimport { fromIsoDate } from '../data-table/calendar-day';\nimport { TransactionChipCell } from './cell-renderers/transaction-chip-cell';\nimport { CarePlanStatusCell } from './cell-renderers/care-plan-status-cell';\nimport { NextAppointmentCell } from './cell-renderers/next-appointment-cell';\nimport type {\n PatientColDef,\n PatientColumnDef,\n PatientColumnId,\n PatientCustomFieldDef,\n PatientRow,\n PatientRowAction,\n ResponsiveTier,\n} from './types';\n\n/* -------------------------------------------------------------------------- */\n/* Responsive-tier extension */\n/* -------------------------------------------------------------------------- */\n\n/**\n * A patient leaf column carrying its responsive-priority tier. AG Grid passes\n * the extra `responsiveTier` key through untouched; we keep it strongly typed\n * (no `any`, no reliance on AG Grid's `context: any`) so the responsive hook can\n * read it back via {@link getResponsiveTier}.\n */\nexport type PatientColDefWithTier = PatientColDef & {\n /**\n * Responsive priority. Lower numbers survive longer as the grid narrows:\n * `P0` always visible, `P3` first to hide. Consumed by `useResponsiveColumns`.\n */\n responsiveTier?: ResponsiveTier;\n};\n\n/**\n * Read the responsive tier off a column definition. Returns `undefined` for\n * columns that opt out of responsive toggling — the pinned anchors and every\n * `hide: true` grouped column deliberately omit a tier so they are never\n * auto-managed by the responsive hook.\n */\nexport function getResponsiveTier(\n colDef: PatientColumnDef,\n): ResponsiveTier | undefined {\n return (colDef as PatientColDefWithTier).responsiveTier;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Options */\n/* -------------------------------------------------------------------------- */\n\n/** Options threaded into {@link buildPatientColumns}. */\nexport interface BuildPatientColumnsOptions {\n /**\n * Mask sensitive contact details (email) — when `true` the `email` column\n * ships hidden. Forwarded from `PatientTable`'s `hidePatientDetails`. Default\n * `false`.\n */\n hideDetails?: boolean;\n /**\n * Live getter for the actions-column density. When it returns `true`, the\n * `ActionsCellRenderer` collapses its inline buttons into a single \"…\" overflow\n * menu (the spec's 180→88px compact density). Reading via a getter lets a\n * density toggle flip without rebuilding column defs (preserves column state) —\n * call `api.refreshCells({ force: true })` after flipping the backing value.\n * Defaults to a getter that always returns `false`.\n */\n getActionsCompact?: () => boolean;\n /**\n * Invoked when a per-row action button is activated, with the resolved action\n * descriptor and the patient row. Only `row.id` ever leaves this boundary into\n * persistence / agent surfaces — never PHI.\n */\n onRowAction?: (action: PatientRowAction, patient: PatientRow) => void;\n /**\n * Derive the patient-detail href for the `lastName` link column, e.g.\n * `(row) => \\`/patients/${row.id}\\``. When omitted the lastName cell renders a\n * button that calls `onRowAction` with the `show` action instead.\n */\n identityHref?: string | ((row: PatientRow) => string | undefined);\n /**\n * Per-practice custom-field descriptors. Each appends one dynamic `custom:*`\n * column (hidden by default) after the standard columns. See\n * {@link PatientCustomFieldDef}.\n */\n customFields?: PatientCustomFieldDef[];\n /**\n * ISO 4217 code for the money columns (`unpaidBalance`, `billableAmount`).\n * Defaults to `EUR`; a practice billing in another currency must pass its own,\n * or every amount in the grid claims euros.\n */\n currency?: string;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Row actions */\n/* -------------------------------------------------------------------------- */\n\n/**\n * The fixed number of per-row actions the patient grid builds (show · edit ·\n * email reminder · sms reminder · reassign · delete — see {@link buildRowActions}).\n * Exposed so the component can size the actions column to its EXPANDED footprint\n * (`actionsColumnWidth(PATIENT_ROW_ACTION_COUNT)`) when flipping the runtime\n * compact-actions toggle off, without rebuilding the column defs. Keep in sync\n * with the array {@link buildRowActions} returns.\n */\nexport const PATIENT_ROW_ACTION_COUNT = 6;\n\n/**\n * Build the per-row `ActionDef`s for the actions column. Each is mapped from the\n * spec's closed action set; `hidden` / `disabled` predicates encode the spec's\n * per-row rules (e.g. `sendEmailReminder` hidden when `!emailEnabled`).\n *\n * Bridges the kit `ActionDef<PatientRow>` shape (icon/label/onClick) to the\n * patient `onRowAction(action, patient)` contract by re-deriving a\n * `PatientRowAction` descriptor on click.\n */\nfunction buildRowActions(\n t: TFunction,\n onRowAction: BuildPatientColumnsOptions['onRowAction'],\n): ActionDef<PatientRow>[] {\n const dispatch =\n (action: PatientRowAction) =>\n (patient: PatientRow): void => {\n onRowAction?.(action, patient);\n };\n\n // Each entry pairs the kit ActionDef (what the renderer needs) with the\n // patient-typed descriptor handed back to `onRowAction`.\n const make = (\n action: PatientRowAction,\n overrides: Omit<ActionDef<PatientRow>, 'label' | 'onClick' | 'icon'>,\n ): ActionDef<PatientRow> => ({\n icon: action.icon,\n label: action.label,\n variant: action.variant,\n onClick: dispatch(action),\n ...overrides,\n });\n\n return [\n make(\n {\n id: 'show',\n label: t('patientTable.actions.show'),\n icon: createElement(Eye, { 'aria-hidden': 'true' }),\n },\n {},\n ),\n make(\n {\n id: 'edit',\n label: t('patientTable.actions.edit'),\n icon: createElement(Pencil, { 'aria-hidden': 'true' }),\n },\n {},\n ),\n make(\n {\n id: 'sendEmailReminder',\n label: t('patientTable.actions.sendEmailReminder'),\n icon: createElement(Mail, { 'aria-hidden': 'true' }),\n },\n // Hidden when the patient has email contact disabled (spec).\n { hidden: (patient) => !patient.emailEnabled },\n ),\n make(\n {\n id: 'sendSmsReminder',\n label: t('patientTable.actions.sendSmsReminder'),\n icon: createElement(MessageSquare, { 'aria-hidden': 'true' }),\n },\n // Hidden when no phone number is SMS-enabled (spec).\n {\n hidden: (patient) =>\n !(patient.phoneNumbers ?? []).some((p) => p.smsEnabled),\n },\n ),\n make(\n {\n id: 'reassign',\n label: t('patientTable.actions.reassign'),\n icon: createElement(Users, { 'aria-hidden': 'true' }),\n },\n {},\n ),\n make(\n {\n id: 'delete',\n label: t('patientTable.actions.delete'),\n icon: createElement(Trash2, { 'aria-hidden': 'true' }),\n variant: 'destructive',\n },\n // Disabled (with a reason) for archived patients — the row model's\n // closest \"cannot delete\" signal. Surfaced as the inline tooltip.\n {\n disabled: (patient) => Boolean(patient.isArchived),\n disabledReason: t('patientTable.actions.deleteLocked'),\n },\n ),\n ];\n}\n\n/* -------------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------------- */\n\nconst NO = () => false;\n\n/** Glyph for boolean / eligibility valueFormatter (✓ when true, — otherwise). */\nconst CHECK = '✓';\nconst DASH = '—';\n\n/** Compute integer age (years) from an ISO date-of-birth string. */\n/**\n * A country's display name in the kit's current language (`'it'` → Italia /\n * Italy / …). Falls back to the code where the runtime lacks `DisplayNames`\n * or the code is unknown, so the cell is never blank for a stored value.\n */\nfunction countryName(code: string | null | undefined): string {\n if (!code) return '';\n const upper = code.toUpperCase();\n try {\n const lang =\n (typeof document !== 'undefined' && document.documentElement.lang) ||\n 'en';\n return new Intl.DisplayNames([lang], { type: 'region' }).of(upper) ?? upper;\n } catch {\n return upper;\n }\n}\n\nfunction ageFromDob(dob: string | undefined): number | null {\n if (!dob) return null;\n // A date of birth is a calendar day: `new Date('2008-09-06')` is UTC midnight,\n // which the local getters below then read back as the 5th west of UTC, making\n // the age a year out on the day before a birthday.\n const birth = fromIsoDate(dob);\n if (!birth) return null;\n const now = new Date();\n let age = now.getFullYear() - birth.getFullYear();\n const m = now.getMonth() - birth.getMonth();\n if (m < 0 || (m === 0 && now.getDate() < birth.getDate())) age -= 1;\n return age >= 0 ? age : null;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Custom-field columns */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Build the dynamic `custom:*` columns from the practice descriptors. Each is\n * appended hidden (no tier) after the standard columns: a literal `headerName`\n * (the descriptor's `label`, never i18n-keyed), a `valueGetter` reading\n * `row.customFields?.[fieldName]`, and a renderer + filter chosen by `type`.\n */\nfunction buildCustomFieldColumns(\n fields: PatientCustomFieldDef[],\n): PatientColDefWithTier[] {\n return fields.map((field): PatientColDefWithTier => {\n const colId: PatientColumnId = `custom:${field.fieldName}`;\n const base: PatientColDefWithTier = {\n colId,\n headerName: field.label,\n hide: true,\n // No tier — custom fields are opt-in via ColumnToggle only.\n };\n\n if (field.type === 'checkbox') {\n return {\n ...base,\n valueGetter: (params) =>\n params.data?.customFields?.[field.fieldName] === true,\n valueFormatter: (params) => (params.value === true ? CHECK : DASH),\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n { value: true, label: CHECK },\n { value: false, label: DASH },\n ],\n },\n minWidth: 110,\n width: 130,\n };\n }\n\n if (field.type === 'dropdown') {\n return {\n ...base,\n // TagListCellRenderer expects an array; wrap the single value.\n valueGetter: (params) => {\n const raw = params.data?.customFields?.[field.fieldName];\n return typeof raw === 'string' && raw ? [raw] : [];\n },\n cellRenderer: TagListCellRenderer,\n filterValueGetter: (params) => {\n const raw = params.data?.customFields?.[field.fieldName];\n return typeof raw === 'string' ? raw : '';\n },\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n // Undefined (not []) when the field declares no options, so the\n // filter derives its choices from the rows. An empty array would\n // mean \"this column has no facets\" and leave the filter unusable.\n options: field.options?.map((opt) => ({\n value: opt,\n label: opt,\n })),\n },\n sortable: false,\n minWidth: 130,\n width: 160,\n };\n }\n\n // text\n return {\n ...base,\n valueGetter: (params) => {\n const raw = params.data?.customFields?.[field.fieldName];\n return typeof raw === 'string' ? raw : '';\n },\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 140,\n width: 170,\n };\n });\n}\n\n/* -------------------------------------------------------------------------- */\n/* buildPatientColumns */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Build the ordered patient-grid column definitions — one field per column.\n *\n * @param t `react-i18next` translation fn (default `ui` namespace).\n * @param opts See {@link BuildPatientColumnsOptions} — density getter, row-action\n * callback, detail masking, identity href, custom fields.\n * @returns Ordered `PatientColumnDef[]`. Default-visible columns carry a\n * `responsiveTier` (read via {@link getResponsiveTier}); the pinned\n * anchors and every grouped (`hide: true`) column omit it so they are\n * never auto-hidden.\n */\nexport function buildPatientColumns(\n t: TFunction,\n opts: BuildPatientColumnsOptions = {},\n): PatientColumnDef[] {\n const {\n hideDetails = false,\n getActionsCompact = NO,\n onRowAction,\n identityHref,\n customFields = [],\n currency,\n } = opts;\n\n const actions = buildRowActions(t, onRowAction);\n\n const genderLabels: Record<NonNullable<PatientRow['gender']>, string> = {\n m: t('patientTable.gender.m'),\n f: t('patientTable.gender.f'),\n x: t('patientTable.gender.x'),\n };\n\n // Tri-state marketing-consent → label + Badge variant (StatusCellRenderer).\n const consentLabels = {\n yes: t('patientTable.consent.yes'),\n no: t('patientTable.consent.no'),\n notSpecified: t('patientTable.consent.notSpecified'),\n };\n const consentVariantMap: StatusCellRendererParams['variantMap'] = {\n [consentLabels.yes]: 'success',\n [consentLabels.no]: 'error',\n [consentLabels.notSpecified]: 'neutral',\n };\n\n const header = (id: PatientColumnId): string =>\n t(`patientTable.column.${id}`);\n\n const columns: PatientColDefWithTier[] = [\n /* ── Avatar (P0, pinned-start) ─────────────────────────────────── */\n {\n colId: 'avatar',\n // sr-only header — the photo column needs no visible label, but AG Grid\n // and AT still want a name. `headerName` reads \"Photo\"; the visible header\n // TEXT is clipped via the kit's `sr-only-th` convention (clips only\n // `.ag-header-cell-text`, keeping the header-cell box in the grid chrome\n // and the name in the a11y tree — see ag-grid-theme.css).\n headerName: header('avatar'),\n headerClass: 'sr-only-th',\n pinned: 'left',\n cellRenderer: ImageCellRenderer,\n cellRendererParams: {\n srcField: 'avatarUrl',\n fallbackField: 'lastName',\n size: 'sm',\n shape: 'circle',\n },\n sortable: false,\n filter: false,\n resizable: false,\n cellClass: 'ag-cell-graphic',\n // Hug the 32px (size=\"sm\") avatar + cell padding — a tight fixed column,\n // not a wide empty one. width === minWidth === maxWidth pins it to size so\n // the pinned group can't stretch it (pinned columns never flex).\n //\n // 48 = 32 (avatar) + 16 (--spacing-sm each side, the reduced padding the\n // theme gives graphic-only cells). The old 52 assumed 20px of padding\n // against a cell that actually had 30px, so the avatar clipped by 10px.\n width: 48,\n minWidth: 48,\n maxWidth: 48,\n // Anchor — never auto-hidden (no tier).\n },\n\n /* ── First name (P0, pinned-start) — plain text ────────────────── */\n {\n colId: 'firstName',\n headerName: header('firstName'),\n pinned: 'left',\n field: 'firstName',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n // Size to content: width === minWidth so the pinned column sits tight with\n // no dead space before the next pinned column (pinned columns can't flex).\n width: 140,\n minWidth: 120,\n // Anchor — never auto-hidden (no tier).\n },\n\n /* ── Last name (P0, pinned-start) — the patient-detail link ────── */\n {\n colId: 'lastName',\n headerName: header('lastName'),\n pinned: 'left',\n field: 'lastName',\n cellRenderer: LinkCellRenderer,\n cellRendererParams: {\n href: identityHref,\n // When no detail href is supplied the cell renders a button; route its\n // click through the same `show` row action.\n onClick: onRowAction\n ? (row: PatientRow) =>\n onRowAction(\n { id: 'show', label: t('patientTable.actions.show') },\n row,\n )\n : undefined,\n },\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n // Size to content: a modest pinned width with no stretched default, so the\n // avatar + first + last group ends tight against the scrollable columns.\n width: 150,\n minWidth: 130,\n // Anchor — never auto-hidden (no tier).\n },\n\n /* ── Phone (P1) ────────────────────────────────────────────────── */\n {\n colId: 'phone',\n headerName: header('phone'),\n responsiveTier: 'P1',\n // The primary phone number string (prefix + number).\n valueGetter: (params) => params.data?.phoneNumbers?.[0]?.number ?? '',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 160,\n width: 180,\n },\n\n /* ── Next appointment (P1) ─────────────────────────────────────── */\n {\n colId: 'nextAppointment',\n headerName: header('nextAppointment'),\n responsiveTier: 'P1',\n field: 'nextAppointment',\n cellRenderer: NextAppointmentCell,\n filter: DateRangeFilter,\n floatingFilterComponent: DateRangeFloatingFilter,\n minWidth: 160,\n width: 180,\n },\n\n /* ── Unpaid balance (P1) ───────────────────────────────────────── */\n {\n colId: 'unpaidBalance',\n headerName: header('unpaidBalance'),\n responsiveTier: 'P1',\n field: 'unpaidAmount',\n type: 'numericColumn',\n // The kit's financial-state pill: `debt` while anything is owed, `settled` at zero.\n cellRenderer: TransactionChipCell,\n cellRendererParams: { positiveState: 'debt', currency },\n // The row carries integer cents; the filter reads euros, as typed.\n filterValueGetter: (params) => (params.data?.unpaidAmount ?? 0) / 100,\n filter: NumberFilter,\n floatingFilterComponent: NumberFloatingFilter,\n // Money is integer cents; the inRange filter compares cents directly.\n filterParams: { defaultOperator: 'inRange', min: 0 },\n minWidth: 150,\n width: 170,\n },\n\n /* ── Email (P2) — hidden when masking ──────────────────────────── */\n {\n colId: 'email',\n headerName: header('email'),\n responsiveTier: 'P2',\n field: 'email',\n // Emit `hide` ONLY when masking (never a literal `hide: false`). A static\n // `hide: false` is re-applied by AG Grid on every `columnDefs` reconcile,\n // clobbering a runtime hide from the ViewSwitcher / responsive hook.\n hide: hideDetails || undefined,\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 200,\n width: 240,\n },\n\n /* ── Care-plan status (P2) ─────────────────────────────────────── */\n {\n colId: 'carePlanStatus',\n headerName: header('carePlanStatus'),\n responsiveTier: 'P2',\n field: 'carePlanStatus',\n cellRenderer: CarePlanStatusCell,\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n {\n value: 'accepted',\n label: t('patientTable.carePlanStatus.accepted'),\n },\n {\n value: 'estimate',\n label: t('patientTable.carePlanStatus.estimate'),\n },\n { value: 'none', label: t('patientTable.carePlanStatus.none') },\n ],\n },\n sortable: false,\n minWidth: 130,\n width: 150,\n },\n\n /* ── Fiscal code (P3, text) ────────────────────────────────────── */\n {\n colId: 'fiscalCode',\n headerName: header('fiscalCode'),\n responsiveTier: 'P3',\n field: 'italianFiscalCode',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 160,\n width: 180,\n },\n\n /* ── Age (P3, derived numeric) ─────────────────────────────────── */\n {\n colId: 'age',\n headerName: header('age'),\n responsiveTier: 'P3',\n // Left-aligned like the text columns — `numericColumn` (which right-aligns\n // header + cell) is for ledger figures, not a 2-digit age. Sort stays\n // numeric because the valueGetter returns a number.\n valueGetter: (params) =>\n ageFromDob(params.data?.dateOfBirth) ?? undefined,\n filter: NumberFilter,\n floatingFilterComponent: NumberFloatingFilter,\n minWidth: 90,\n width: 110,\n },\n\n /* ── Gender (P3) ───────────────────────────────────────────────── */\n {\n colId: 'gender',\n headerName: header('gender'),\n responsiveTier: 'P3',\n // Map the raw code to a localised label for both cell + set filter.\n valueGetter: (params) =>\n params.data?.gender ? genderLabels[params.data.gender] : DASH,\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n { value: genderLabels.m, label: genderLabels.m },\n { value: genderLabels.f, label: genderLabels.f },\n { value: genderLabels.x, label: genderLabels.x },\n ],\n },\n minWidth: 110,\n width: 130,\n },\n\n /* ── Source (P3) ───────────────────────────────────────────────── */\n {\n colId: 'source',\n headerName: header('source'),\n responsiveTier: 'P3',\n // TagListCellRenderer expects an array; wrap the resolved source string.\n valueGetter: (params) => {\n const s = params.data?.source ?? params.data?.customSource;\n return s ? [s] : [];\n },\n cellRenderer: TagListCellRenderer,\n filterValueGetter: (params) =>\n params.data?.source ?? params.data?.customSource ?? '',\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n sortable: false,\n minWidth: 120,\n width: 150,\n },\n\n /* ── Grouped: Identity — hidden by default ─────────────────────── */\n {\n colId: 'dateOfBirth',\n headerName: header('dateOfBirth'),\n hide: true,\n field: 'dateOfBirth',\n cellRenderer: DateCellRenderer,\n cellRendererParams: { format: 'date' },\n filter: DateRangeFilter,\n floatingFilterComponent: DateRangeFloatingFilter,\n minWidth: 140,\n width: 160,\n },\n {\n colId: 'placeOfBirth',\n headerName: header('placeOfBirth'),\n hide: true,\n field: 'placeOfBirth',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 140,\n width: 160,\n },\n {\n colId: 'countryOfBirth',\n headerName: header('countryOfBirth'),\n hide: true,\n // ISO code in the row, the name in the cell — and in the text filter.\n valueGetter: (params) => countryName(params.data?.countryOfBirth),\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 130,\n width: 150,\n },\n\n /* ── Grouped: Contact — hidden by default ──────────────────────── */\n {\n colId: 'smsReminders',\n headerName: header('smsReminders'),\n hide: true,\n valueGetter: (params) => Boolean(params.data?.smsReminderEligible),\n valueFormatter: (params) => (params.value === true ? CHECK : DASH),\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n { value: true, label: CHECK },\n { value: false, label: DASH },\n ],\n },\n minWidth: 120,\n width: 140,\n },\n {\n colId: 'emailReminders',\n headerName: header('emailReminders'),\n hide: true,\n valueGetter: (params) => Boolean(params.data?.emailReminderEligible),\n valueFormatter: (params) => (params.value === true ? CHECK : DASH),\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n { value: true, label: CHECK },\n { value: false, label: DASH },\n ],\n },\n minWidth: 120,\n width: 140,\n },\n {\n colId: 'pec',\n headerName: header('pec'),\n hide: true,\n field: 'pecAddress',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 200,\n width: 240,\n },\n\n /* ── Grouped: Appointments — hidden by default ─────────────────── */\n {\n colId: 'lastVisit',\n headerName: header('lastVisit'),\n hide: true,\n field: 'lastAppointment',\n cellRenderer: DateCellRenderer,\n cellRendererParams: { format: 'relative' },\n filter: DateRangeFilter,\n floatingFilterComponent: DateRangeFloatingFilter,\n minWidth: 140,\n width: 160,\n },\n\n /* ── Grouped: Financial — hidden by default ────────────────────── */\n {\n colId: 'billableAmount',\n headerName: header('billableAmount'),\n hide: true,\n type: 'numericColumn',\n field: 'billableAmount',\n // \"Fatturabile\" is the chip's own `to-invoice` state; `settled` at zero.\n // Same shape as unpaidBalance: the cell value stays in cents and the\n // renderer converts, so the inRange filter compares cents directly.\n cellRenderer: TransactionChipCell,\n cellRendererParams: { positiveState: 'to-invoice', currency },\n // The row carries integer cents; the filter reads euros, as typed.\n filterValueGetter: (params) => (params.data?.billableAmount ?? 0) / 100,\n filter: NumberFilter,\n floatingFilterComponent: NumberFloatingFilter,\n filterParams: { defaultOperator: 'inRange', min: 0 },\n minWidth: 140,\n width: 160,\n },\n {\n colId: 'discount',\n headerName: header('discount'),\n hide: true,\n field: 'defaultDiscount',\n type: 'numericColumn',\n valueFormatter: (params) =>\n params.value == null ? '' : `${params.value}%`,\n filter: NumberFilter,\n floatingFilterComponent: NumberFloatingFilter,\n minWidth: 110,\n width: 130,\n },\n\n /* ── Grouped: Clinical — hidden by default ─────────────────────── */\n {\n colId: 'diagnosis',\n headerName: header('diagnosis'),\n hide: true,\n field: 'diagnosis',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 180,\n width: 220,\n },\n {\n colId: 'insurance',\n headerName: header('insurance'),\n hide: true,\n field: 'defaultInsurance',\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n minWidth: 140,\n width: 160,\n },\n {\n colId: 'assignedTo',\n headerName: header('assignedTo'),\n hide: true,\n cellRenderer: UserCellRenderer,\n valueGetter: (params) =>\n params.data?.personalAssistant\n ? ({ name: params.data.personalAssistant } satisfies UserCellValue)\n : null,\n comparator: (a: UserCellValue | null, b: UserCellValue | null) =>\n (a?.name ?? '').localeCompare(b?.name ?? ''),\n filterValueGetter: (params) => params.data?.personalAssistant ?? '',\n filter: TypeaheadFilter,\n floatingFilterComponent: TypeaheadFloatingFilter,\n minWidth: 160,\n width: 180,\n },\n {\n colId: 'job',\n headerName: header('job'),\n hide: true,\n field: 'job',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 140,\n width: 160,\n },\n\n /* ── Grouped: Admin — hidden by default ────────────────────────── */\n {\n colId: 'marketingConsent',\n headerName: header('marketingConsent'),\n hide: true,\n cellRenderer: StatusCellRenderer,\n cellRendererParams: {\n variantMap: consentVariantMap,\n } satisfies StatusCellRendererParams,\n valueGetter: (params) => {\n switch (params.data?.marketingConsent) {\n case 1:\n return consentLabels.yes;\n case 0:\n return consentLabels.no;\n default:\n return consentLabels.notSpecified;\n }\n },\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n { value: consentLabels.yes, label: consentLabels.yes },\n { value: consentLabels.no, label: consentLabels.no },\n {\n value: consentLabels.notSpecified,\n label: consentLabels.notSpecified,\n },\n ],\n },\n sortable: false,\n minWidth: 150,\n width: 170,\n },\n {\n colId: 'patientNumber',\n headerName: header('patientNumber'),\n hide: true,\n // `YYYY/N` yearly numbering.\n valueGetter: (params) => {\n const year = params.data?.yearlyNumberingYear;\n const num = params.data?.yearlyNumberingNumber;\n return year != null && num != null ? `${year}/${num}` : '';\n },\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 120,\n width: 140,\n },\n {\n colId: 'portal',\n headerName: header('portal'),\n hide: true,\n valueGetter: (params) => Boolean(params.data?.mydentista),\n valueFormatter: (params) => (params.value === true ? CHECK : DASH),\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n { value: true, label: CHECK },\n { value: false, label: DASH },\n ],\n },\n minWidth: 110,\n width: 130,\n },\n {\n colId: 'createdAt',\n headerName: header('createdAt'),\n hide: true,\n field: 'createdAt',\n cellRenderer: DateCellRenderer,\n cellRendererParams: { format: 'date' },\n filter: DateRangeFilter,\n floatingFilterComponent: DateRangeFloatingFilter,\n minWidth: 140,\n width: 160,\n },\n\n /* ── Grouped: Address — hidden by default ──────────────────────── */\n {\n colId: 'city',\n headerName: header('city'),\n hide: true,\n field: 'city',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 120,\n width: 150,\n },\n {\n colId: 'province',\n headerName: header('province'),\n hide: true,\n field: 'province',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 100,\n width: 120,\n },\n {\n colId: 'postcode',\n headerName: header('postcode'),\n hide: true,\n field: 'postcode',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 110,\n width: 130,\n },\n {\n colId: 'country',\n headerName: header('country'),\n hide: true,\n valueGetter: (params) => countryName(params.data?.countryCode),\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 110,\n width: 130,\n },\n {\n colId: 'street',\n headerName: header('street'),\n hide: true,\n field: 'street',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 180,\n width: 220,\n },\n\n /* ── Custom fields (per-practice, dynamic) — appended, hidden ──── */\n ...buildCustomFieldColumns(customFields),\n\n /* ── Actions (P0, pinned-end) ──────────────────────────────────── */\n {\n colId: 'actions',\n headerName: header('actions'),\n // Pinned to the inline-end edge so actions stay reachable while the field\n // columns scroll. AG Grid `pinned` is physical — the grid's `enableRtl`\n // flips this to the start edge under RTL. Locked: neither a header drag\n // nor a restored column-state snapshot may move it off the edge.\n pinned: 'right',\n lockPinned: true,\n suppressMovable: true,\n // Header + action group left-aligned (justify-start), consistent with every\n // other column — no right-aligned header (the actions still sit at the\n // pinned inline-end edge, the group just starts at the cell's leading edge).\n cellClass: 'ds:justify-start ag-cell-graphic',\n cellRenderer: ActionsCellRenderer,\n cellRendererParams: {\n actions,\n intent: 'ghost',\n group: true,\n label: t('patientTable.actions.group'),\n collapsed: getActionsCompact,\n },\n sortable: false,\n filter: false,\n resizable: false,\n // The renderer collapses to a single overflow trigger via\n // `getActionsCompact`; the expanded width is DERIVED from the action count\n // so a row showing every action can't clip — 88px collapsed → fits-all.\n width: actionsColumnWidth(actions.length),\n minWidth: 88,\n // Anchor — never auto-hidden (no tier).\n },\n ];\n\n return columns;\n}\n","/**\n * patient-table — responsive column hook\n * ----------------------------------------------------------------------------\n * `useResponsiveColumns(handleRef, opts)` toggles column visibility by the\n * `responsiveTier` (`P0`–`P3`) baked onto each `ColDef` by `buildPatientColumns`,\n * driven by the *grid container's* width — measured with a `ResizeObserver`, NOT\n * the viewport — so the table responds correctly when embedded in a narrow panel,\n * split pane or sidebar rather than only at browser-window breakpoints.\n *\n * BREAKPOINTS (container inline-size → highest tier kept visible):\n *\n * < 640px → P0 only (pinned avatar / firstName / lastName / actions)\n * < 900px → P0 + P1 (+ phone, nextAppointment, balance)\n * < 1200px → P0 + P1 + P2 (+ email, carePlanStatus)\n * ≥ 1200px → all tiers (P0–P3) (+ fiscalCode, age, gender, source)\n *\n * Columns without a tier (the pinned avatar / firstName / lastName / actions\n * anchors, and every grouped `hide: true` column) are never touched — the\n * anchors stay visible at every width; the grouped columns are owned by\n * ColumnToggle only.\n *\n * RESPONSIVE ↔ PERSISTENCE RECONCILIATION\n * ---------------------------------------\n * `data-table.tsx` persists the full AG Grid column state to\n * `localStorage[\"data-table-col-state:<gridId>\"]` (load on grid-ready, save on\n * unmount). A user's explicit `ColumnToggle` choice lives in that persisted state\n * and MUST win over — and survive — the responsive baseline. Reconciliation:\n *\n * 1. The responsive baseline is exactly that — a *baseline*. We only auto\n * show/hide a column the user has NOT manually toggled this session.\n * 2. We track a `Set<colId>` of user-touched columns. We learn of a manual\n * toggle from the grid's `columnVisible` event: any such event whose\n * `source` is NOT `'api'` is user-driven (ColumnToggle tool panel, column\n * menu, context menu, drag), so we add those colIds to the set. Our own\n * responsive calls go through `setColumnsVisible(..., 'api')`, so they never\n * pollute the set (and we additionally guard with a re-entrancy flag).\n * 3. Once a column is user-touched, the hook leaves it alone forever this\n * session — its visibility is whatever the user chose, which is what\n * `data-table.tsx` then persists. The persisted choice therefore wins on\n * the next mount too: the DataTable persists the user-touched set with\n * its column state (`getUserTouchedColumns` / `markColumnsUserTouched` on\n * the handle), and every reconcile pass reads it live.\n * 4. A programmatic visibility write by someone else (the DataTable restoring\n * persisted state, a columnDefs update) schedules one reconcile pass, so\n * the baseline is re-asserted for untouched columns without a resize.\n *\n * CONSTRAINTS: no `any` (AG Grid's `ColumnEventType` is read off the typed event,\n * tier read via the typed `getResponsiveTier` helper). No DOM `data-*` attribute\n * is written here (PHI rule is moot — this hook only reads colIds and widths).\n */\nimport { useCallback, useEffect, useRef, type RefObject } from 'react';\nimport type { Column, ColumnVisibleEvent } from 'ag-grid-community';\nimport { getResponsiveTier } from './columns';\nimport type { PatientRow, PatientTableHandle, ResponsiveTier } from './types';\n\n/**\n * Column events the grid raises for its own or a program's reasons. Anything\n * else (`toolPanelUi`, `columnMenu`, `contextMenu`, `uiColumnDragged`, …) is\n * the user, whose choice the baseline must respect from then on.\n */\nconst PROGRAMMATIC_SOURCES = new Set<string>([\n 'api',\n 'flex',\n 'gridInitializing',\n 'gridOptionsChanged',\n 'autosizeColumns',\n 'sizeColumnsToFit',\n 'rowDataUpdated',\n 'rowModelUpdated',\n]);\n\n/* -------------------------------------------------------------------------- */\n/* Breakpoint model */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Container inline-size breakpoints. Each entry is the *minimum* container width\n * (px) at which the named tier becomes visible. Below the first threshold only\n * `P0` shows; at or above the last, everything shows.\n */\ninterface Breakpoint {\n /** Minimum container inline-size (px) for this tier to be shown. */\n minWidth: number;\n /** The tier this breakpoint reveals. */\n tier: ResponsiveTier;\n}\n\nconst DEFAULT_BREAKPOINTS: readonly Breakpoint[] = [\n { minWidth: 0, tier: 'P0' },\n { minWidth: 640, tier: 'P1' },\n { minWidth: 900, tier: 'P2' },\n { minWidth: 1200, tier: 'P3' },\n];\n\n/** Resolve the set of tiers that should be visible at a given container width. */\nfunction visibleTiersForWidth(\n width: number,\n breakpoints: readonly Breakpoint[],\n): Set<ResponsiveTier> {\n const visible = new Set<ResponsiveTier>();\n for (const bp of breakpoints) {\n if (width >= bp.minWidth) visible.add(bp.tier);\n }\n // P0 is always visible regardless of width.\n visible.add('P0');\n return visible;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Options */\n/* -------------------------------------------------------------------------- */\n\nexport interface UseResponsiveColumnsOptions {\n /**\n * The grid container element to observe. The hook measures THIS element's\n * inline-size (not the viewport) so it works embedded. When the ref is unset\n * the hook is inert.\n */\n containerRef: RefObject<HTMLElement | null>;\n /**\n * Disable responsive toggling entirely (the `responsive={false}` escape hatch\n * on `PatientTable`). When `false` the hook unobserves and makes no visibility\n * changes — column visibility is left entirely to persistence / ColumnToggle.\n * Default `true`.\n */\n enabled?: boolean;\n /**\n * Override the container-width breakpoints. Ordered low→high `minWidth`; each\n * reveals its `tier`. Defaults to 640 / 900 / 1200 (P1 / P2 / P3).\n */\n breakpoints?: readonly Breakpoint[];\n}\n\n/* -------------------------------------------------------------------------- */\n/* Return shape */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Imperative surface returned by {@link useResponsiveColumns}. The responsive\n * effect runs internally; `lockColumns` lets a caller (the view switcher) mark\n * columns as user-touched so the responsive baseline stops auto-managing them.\n */\nexport interface UseResponsiveColumnsResult {\n /**\n * Mark the given colIds as \"user-touched\" so the responsive baseline leaves\n * their visibility alone from here on — exactly as if the user had toggled\n * them via `ColumnToggle`. The view switcher calls this when it applies a\n * view's column visibility, so its programmatic (`source: 'api'`) visibility\n * writes are not immediately re-hidden/re-shown by the next responsive\n * reconcile pass. No-op (with the colIds still recorded for a later mount of\n * the effect) when the hook is disabled.\n */\n lockColumns: (colIds: readonly string[]) => void;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Hook */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Wire responsive, tier-driven column visibility to the patient grid.\n *\n * @param handleRef The `PatientTable` imperative handle ref — the hook reaches\n * the raw AG Grid API via `handleRef.current.getRawApi()`.\n * @param opts Container ref to observe, plus enable flag / breakpoint\n * overrides. See {@link UseResponsiveColumnsOptions}.\n * @returns {@link UseResponsiveColumnsResult} — `lockColumns(colIds)`\n * to mark columns user-touched (see the view-switcher\n * coordination note in patient-table.tsx).\n */\nexport function useResponsiveColumns(\n handleRef: RefObject<PatientTableHandle | null>,\n opts: UseResponsiveColumnsOptions,\n): UseResponsiveColumnsResult {\n const {\n containerRef,\n enabled = true,\n breakpoints = DEFAULT_BREAKPOINTS,\n } = opts;\n\n // colIds the user has explicitly toggled this session — responsive auto-toggle\n // must never override these (their persisted visibility wins). A ref, not\n // state: mutating it must not re-run the effect or re-render. Shared between\n // the responsive effect (which seeds + grows it from `columnVisible` events)\n // and the exported `lockColumns` (which the view switcher uses to register a\n // view-driven visibility change as a user override).\n const userTouchedRef = useRef<Set<string>>(new Set());\n // Re-entrancy guard: true while WE are applying a responsive change, so the\n // resulting `columnVisible` event is not misread as a user toggle even if a\n // future AG Grid version reports a non-`api` source for it.\n const applyingRef = useRef(false);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!enabled || !container) return;\n\n // Resolve the live API lazily on each tick — the grid may not be ready on\n // the first observer callback.\n const getApi = () => handleRef.current?.getRawApi();\n\n /**\n * Record a user-driven visibility change so the column is left alone from\n * here on. `source: 'api'` is the only programmatic source the hook itself\n * uses; everything else (tool panel, column menu, context menu, drag) is the\n * user. The re-entrancy flag covers our own calls defensively.\n */\n let reconcileTimer: number | undefined;\n /** Re-run the baseline on the next tick, coalescing a burst of events. */\n const scheduleReconcile = () => {\n if (reconcileTimer !== undefined) return;\n reconcileTimer = window.setTimeout(() => {\n reconcileTimer = undefined;\n reconcile();\n }, 0);\n };\n\n const onColumnVisible = (event: ColumnVisibleEvent<PatientRow>) => {\n if (applyingRef.current) return;\n if (PROGRAMMATIC_SOURCES.has(event.source)) {\n // Someone else's programmatic write — the DataTable restoring its\n // persisted state, a columnDefs update. Re-assert the baseline for\n // the columns the user has not taken over; theirs are left as written.\n scheduleReconcile();\n return;\n }\n const touched = event.columns ?? (event.column ? [event.column] : []);\n const colIds = touched.map((col) => col.getColId());\n for (const colId of colIds) userTouchedRef.current.add(colId);\n // The DataTable persists the set with its column state.\n handleRef.current?.markColumnsUserTouched(colIds);\n };\n\n // The grid API is frequently not ready on the first effect tick (AG Grid\n // mounts asynchronously). `wired` ensures the one-time seed + listener\n // attach happens the first time we actually have an API, no matter whether\n // that is here on mount or later from a ResizeObserver tick.\n let wired = false;\n\n /**\n * One-time wiring once the API is live: seed the user-touched set from the\n * persisted/current column state, then subscribe to `columnVisible` so later\n * manual toggles are recorded. Seeding treats an already-hidden tiered\n * column that *would* be visible at the current width as a deliberate user\n * hide (restored from localStorage by data-table.tsx) so the baseline does\n * not clobber it. We only positively detect *hidden* overrides — auto-show\n * would otherwise fight a deliberate hide.\n */\n const ensureWired = (api: NonNullable<ReturnType<typeof getApi>>) => {\n if (wired) return;\n wired = true;\n // The DataTable restores the user-touched set from its persisted\n // envelope before this runs; it is read live in every reconcile pass.\n api.addEventListener('columnVisible', onColumnVisible);\n };\n\n /** Apply the responsive baseline for the current container width. */\n const reconcile = () => {\n const api = getApi();\n if (!api) return;\n ensureWired(api);\n const columns = api.getColumns();\n if (!columns) return;\n\n const width = container.getBoundingClientRect().width;\n const visibleTiers = visibleTiersForWidth(width, breakpoints);\n // Union of what this hook saw and what the DataTable restored/persisted.\n const persistedTouched = new Set(\n handleRef.current?.getUserTouchedColumns() ?? [],\n );\n\n // Compute the show / hide buckets, skipping user-touched and untiered\n // columns. Batch the calls so AG Grid fires one event per direction.\n const toShow: Column[] = [];\n const toHide: Column[] = [];\n\n for (const col of columns) {\n const colId = col.getColId();\n // The user owns this column's visibility — never auto-manage it.\n if (userTouchedRef.current.has(colId) || persistedTouched.has(colId))\n continue;\n\n const tier = getResponsiveTier(col.getColDef());\n // No tier ⇒ anchor column (Identity / Actions): always left visible.\n if (!tier) continue;\n\n const shouldShow = visibleTiers.has(tier);\n if (shouldShow && !col.isVisible()) toShow.push(col);\n else if (!shouldShow && col.isVisible()) toHide.push(col);\n }\n\n if (toShow.length === 0 && toHide.length === 0) return;\n\n applyingRef.current = true;\n try {\n // AG Grid tags API-driven changes with `source: 'api'`, so the\n // resulting `columnVisible` events are ignored by `onColumnVisible`\n // (they are the baseline, not user intent). `applyingRef` is the belt\n // to that braces in case a future version reports a different source.\n if (toShow.length > 0) api.setColumnsVisible(toShow, true);\n if (toHide.length > 0) api.setColumnsVisible(toHide, false);\n } finally {\n applyingRef.current = false;\n }\n };\n\n // Observe the GRID CONTAINER (not the viewport) so embedded tables respond\n // to their own box. The first observer tick fires synchronously on observe,\n // and `reconcile` lazily wires + seeds the moment the grid API is live.\n const observer = new ResizeObserver(() => reconcile());\n observer.observe(container);\n reconcile();\n\n return () => {\n observer.disconnect();\n if (reconcileTimer !== undefined) window.clearTimeout(reconcileTimer);\n // The API may have torn down already on unmount; guard the removal.\n getApi()?.removeEventListener('columnVisible', onColumnVisible);\n };\n }, [containerRef, enabled, breakpoints, handleRef]);\n\n // Stable across renders — `userTouchedRef` is a ref, so adding to it never\n // re-runs the responsive effect. Recording a colId here makes the next\n // `reconcile` pass skip it (it `continue`s on `userTouchedRef.has(colId)`),\n // which is exactly how a manual ColumnToggle override is honoured. The view\n // switcher calls this for every column it shows/hides so the responsive\n // baseline doesn't immediately fight the applied view.\n const lockColumns = useCallback(\n (colIds: readonly string[]) => {\n for (const id of colIds) userTouchedRef.current.add(id);\n handleRef.current?.markColumnsUserTouched(colIds);\n },\n [handleRef],\n );\n\n return { lockColumns };\n}\n","/**\n * patient-table — Domain-Specific component\n * ----------------------------------------------------------------------------\n * `PatientTable` is a thin composition over the kit's `data-table` engine\n * (AG Grid Community) specialised for the AlfaDocs patient list. It bakes in:\n *\n * • `buildPatientColumns(t, …)` (one field per column — split identity,\n * grouped hidden columns, dynamic custom fields) merged with a\n * consumer-supplied `columns` override.\n * • the kit data-table built-in renderers (Image / Link / Date / Currency /\n * Status / TagList / User) plus the three patient renderers kept for the\n * domain columns (NextAppointment / BalanceBadge / CarePlanStatus).\n * • a default toolbar — `QuickSearch` + `FilterChips` + `ColumnToggle` +\n * `ExportMenu` + a `BulkAction` per `props.bulkActions` — overridable\n * wholesale via `props.toolbar`.\n * • `useResponsiveColumns` (container-width column-priority tiers) when\n * `props.responsive` (default `true`).\n * • multiple row selection, pagination, archived-row tint, loading / empty\n * overlays.\n * • `useAgentRegistration(dataTableAgent, handle, gridId)` — the SAME agent\n * adapter `data-table` registers, keyed by `gridId`.\n *\n * The `forwardRef` exposes the inner `DataTableHandle<PatientRow>` so consumers\n * (e.g. the `patients` pattern's WarningStack \"View\" actions) can drive the\n * grid imperatively — `setFilter`, `setSort`, `getSelection`, etc.\n *\n * CONSTRAINTS (CI-blocking — src/docs/23-constraints.mdx, 26-agent-readiness.mdx):\n * • No hardcoded colour / spacing / radius / shadow / font — token-bound `ds:`\n * utilities only. Row tint goes through the token-bound\n * `data-table-row-archived` class on the AG Grid theme bridge, never an\n * inline `cellStyle`.\n * • CSS logical properties only (`ms/me/ps/pe`, `text-start/text-end`).\n * • All user-visible strings via `useTranslation('ui')` → `ui.patientTable.*`.\n * • TypeScript strict — no `any`. `forwardRef` exposes the curated handle.\n * • PHI rule (PRS 26 §6): only `row.id` reaches DOM `data-*`. This component\n * writes no PHI attribute; AG Grid surfaces ids via its native `row-id`.\n */\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n type RefObject,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport type { GetRowIdParams, RowClassParams } from 'ag-grid-community';\nimport {\n actionsColumnWidth,\n DataTable,\n dataTableAgent,\n type DataTableHandle,\n} from '../data-table';\nimport { Select } from '../select';\nimport { useAgentRegistration } from '../../agent';\nimport {\n buildPatientColumns,\n getResponsiveTier,\n PATIENT_ROW_ACTION_COUNT,\n type BuildPatientColumnsOptions,\n} from './columns';\nimport {\n useResponsiveColumns,\n type UseResponsiveColumnsResult,\n} from './use-responsive-columns';\nimport type {\n PatientBulkAction,\n PatientColumnDef,\n PatientCustomFieldDef,\n PatientRow,\n PatientRowAction,\n PatientTableHandle,\n PatientView,\n} from './types';\n\n/* -------------------------------------------------------------------------- */\n/* Props */\n/* -------------------------------------------------------------------------- */\n\nexport interface PatientTableProps {\n /**\n * Row data. `undefined` ⇒ the grid renders its loading overlay (the\n * data-table contract: `rowData === undefined` is the loading sentinel). Pass\n * `[]` for an explicit empty state.\n */\n rowData?: PatientRow[];\n /**\n * Override / extend the baked patient columns. When provided, these column\n * definitions REPLACE the kit defaults entirely (the consumer is expected to\n * compose `buildPatientColumns(t, …)` — re-exported from the barrel — with\n * their own additions). When omitted, the kit's `buildPatientColumns` output\n * is used.\n */\n columns?: PatientColumnDef[];\n /**\n * REQUIRED. Persistence key (column state → `localStorage`) AND the agent\n * instance address (`data-component-id`). Two tables on one page need\n * distinct `gridId`s.\n */\n gridId: string;\n /** Row-height preset. Defaults to the engine default (`'default'`). */\n density?: 'compact' | 'default' | 'expanded';\n /**\n * Collapse the per-row actions column into a single \"…\" overflow trigger\n * independently of row `density`. When `true` the actions cells render the\n * collapsed menu and the column shrinks to its 88px compact footprint; when\n * `false` they render the inline button group at the full expanded width.\n * When `undefined` (the default) compactness FOLLOWS `density` — `compact`\n * density collapses the actions, every other density keeps them inline — so\n * existing consumers are unaffected. Flipping this at runtime re-reads the\n * actions cells via a stable ref-backed getter (no `columnDefs` rebuild, so\n * column state is preserved) and resizes the actions column to match.\n */\n actionsCompact?: boolean;\n /**\n * Grid height — forwarded to the underlying `DataTable.heightClass` (a Tailwind\n * height class). Pass `\"ds:h-full\"` to fill a sized parent, or an explicit\n * value like `\"ds:h-[75vh]\"`. Omit to use the engine default (`ds:h-[500px]`).\n */\n heightClass?: string;\n /**\n * Enable container-width column-priority responsiveness (lower-priority\n * columns hide as the grid narrows). Default `true`. Set `false` to leave\n * column visibility entirely to persistence / `ColumnToggle`.\n */\n responsive?: boolean;\n /**\n * Saved column/filter/sort presets. When non-empty AND no custom `toolbar`\n * is supplied, the default toolbar renders a `Select`-based view switcher in\n * the FilterBar; choosing a view applies its column visibility (via the\n * responsive-hook lock so the choice is honoured like a manual ColumnToggle),\n * `filterModel` (`setFilter`) and `sortModel` (`setSort`). Omitted entirely\n * when empty / undefined or when `toolbar` is supplied (a custom toolbar owns\n * its own view UI).\n */\n views?: PatientView[];\n /**\n * Invoked when a per-row action button is activated, with the resolved action\n * descriptor and the patient row. Only `row.id` ever leaves this boundary into\n * persistence / agent surfaces — never PHI.\n */\n onRowAction?: (action: PatientRowAction, patient: PatientRow) => void;\n /**\n * Toolbar bulk actions (operate on the current selection). Each renders a\n * `DataTable.Toolbar.BulkAction` that surfaces only while rows are selected;\n * its `onSelect` receives the selected row ids (never PHI).\n */\n bulkActions?: PatientBulkAction[];\n /**\n * Force the loading overlay independently of `rowData`. When `true` the grid\n * shows the loading skeleton even if `rowData` is an array (e.g. a background\n * refresh). `rowData === undefined` also triggers loading.\n */\n loading?: boolean;\n /**\n * Replace the default toolbar wholesale. Pass a `<DataTable.Toolbar>…</…>`\n * tree (it has access to the same toolbar context). When omitted the default\n * QuickSearch + FilterChips + ColumnToggle + ExportMenu + BulkActions toolbar\n * is rendered.\n */\n toolbar?: ReactNode;\n /**\n * Omit the `QuickSearch` field from the DEFAULT toolbar. The rest of the\n * default toolbar (view switcher when `views` is supplied, FilterChips,\n * ColumnToggle, ExportMenu, BulkActions) is unaffected. Ignored when a custom\n * `toolbar` is supplied (that toolbar owns its own composition). Default\n * `false` (search shown). Use for surfaces that provide their own search\n * affordance elsewhere on the page.\n */\n hideQuickSearch?: boolean;\n /**\n * Enable the grid's client-side pagination. When `false` (the default), every\n * row loads into a single virtualised, scrollable view and filter / sort act\n * over the WHOLE set (no pager) — the right model for a \"load and filter all\"\n * list, which is what a patient list is. Pass `true` for a surface that really\n * wants a pager.\n */\n pagination?: boolean;\n /**\n * Page-size dropdown options shown in the pagination panel — forwarded to the\n * underlying `DataTable.paginationPageSizeSelector`. Pass `false` to hide the\n * page-size chooser entirely (pagination navigation is unaffected). Default\n * `[10, 25, 50, 100]` (the engine default).\n */\n paginationPageSizeSelector?: number[] | false;\n /**\n * Mask sensitive contact details (email) in the Contact / Email columns.\n * Forwarded to `buildPatientColumns`' `hideDetails`. Ignored when `columns`\n * is supplied (the consumer owns their columns then). Default `false`.\n */\n hideDetails?: boolean;\n /**\n * Derive the patient-detail href for the Identity cell, e.g.\n * `(row) => \\`/patients/${row.id}\\``. Forwarded to `buildPatientColumns`.\n * Ignored when `columns` is supplied. When omitted the Identity cell routes\n * its click through `onRowAction` with the `show` action.\n */\n identityHref?: BuildPatientColumnsOptions['identityHref'];\n /**\n * Per-practice custom-field descriptors. Each appends one dynamic `custom:*`\n * column (hidden by default, toggleable via ColumnToggle) after the standard\n * columns. Forwarded to `buildPatientColumns`. Ignored when `columns` is\n * supplied (the consumer owns their columns then).\n */\n customFields?: PatientCustomFieldDef[];\n /**\n * ISO 4217 code for the money columns. Forwarded to `buildPatientColumns`,\n * ignored when `columns` is supplied. Defaults to `EUR`; a practice billing in\n * another currency must pass its own, or every amount claims euros.\n */\n currency?: string;\n /** Override the default region aria-label (`ui.patientTable.regionLabel`). */\n 'aria-label'?: string;\n /** Extra class names merged onto the inner grid wrapper. */\n className?: string;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Row class — archived tint */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Token-bound archived-row tint. The class is defined on the AG Grid theme\n * bridge (`src/tokens/ag-grid-theme.css` → `.data-table-row-archived`) so the\n * recessive muted fill stays under token control — no colour ever enters a\n * `cellStyle` / `cellClass` literal here.\n */\nfunction getPatientRowClass(\n params: RowClassParams<PatientRow>,\n): string | undefined {\n return params.data?.isArchived ? 'data-table-row-archived' : undefined;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Row id — key selection / bulk actions on the real patient id */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Use the stable `PatientRow.id` as AG Grid's node id. Without this AG Grid\n * keys nodes by row index, so `getSelectedNodes().map(n => n.id)`,\n * `setSelection(ids)`, the bulk-action bridge and the agent adapter would all\n * receive index strings (`\"0\"`, `\"1\"`) instead of patient ids. Forwarded to the\n * engine via `gridOptions.getRowId` (the path `data-table.tsx` spreads through\n * untouched). `id` is the ONLY field permitted on a DOM hook (PHI rule).\n */\nfunction getPatientRowId(params: GetRowIdParams<PatientRow>): string {\n return params.data.id;\n}\n\n/* -------------------------------------------------------------------------- */\n/* View switcher — default-toolbar control for the `views` prop */\n/* -------------------------------------------------------------------------- */\n\ninterface ViewSwitcherProps {\n /** Saved views to choose from (already known non-empty by the caller). */\n views: PatientView[];\n /** The live patient-table handle ref (filter / sort / raw API access). */\n handleRef: RefObject<PatientTableHandle | null>;\n /**\n * Mark a view's visibility writes as user-touched so `useResponsiveColumns`\n * stops auto-managing them — see the coordination note at the call site.\n */\n lockColumns: UseResponsiveColumnsResult['lockColumns'];\n /** Accessible label for the control (`ui.patientTable.views.label`). */\n label: string;\n}\n\n/**\n * A minimal, accessible saved-view switcher rendered in the DEFAULT toolbar's\n * FilterBar. Uses the kit `Select` (convenience `options` form) — a small,\n * labelled single-choice control that fits the toolbar's compact density and\n * carries Radix's listbox a11y for free.\n *\n * On selection it applies the chosen {@link PatientView} through the patient\n * handle + raw AG Grid API:\n * • column visibility — show the view's listed toggleable columns, hide the\n * rest of the toggleable (tiered) ones. Untiered columns (the pinned\n * Identity / Actions anchors) are NEVER hidden.\n * • `filterModel` via `handle.setFilter`.\n * • `sortModel` via `handle.setSort`.\n *\n * RESPONSIVE COORDINATION (critical): a view-applied visibility change must not\n * be immediately re-hidden/re-shown by `useResponsiveColumns`. We route every\n * colId we touch through `lockColumns(colIds)`, which adds them to the same\n * `userTouchedRef` set the hook grows from non-`api` `columnVisible` events —\n * so from then on the responsive baseline treats them as a user override and\n * leaves them alone, exactly as a manual ColumnToggle would.\n */\nfunction ViewSwitcher({\n views,\n handleRef,\n lockColumns,\n label,\n}: ViewSwitcherProps) {\n const [activeViewId, setActiveViewId] = useState<string>('');\n\n const options = useMemo(\n () => views.map((view) => ({ value: view.id, label: view.label })),\n [views],\n );\n\n const applyView = useCallback(\n (viewId: string) => {\n const view = views.find((v) => v.id === viewId);\n const handle = handleRef.current;\n if (!view || !handle) return;\n\n const api = handle.getRawApi();\n if (api) {\n const wanted = new Set<string>(view.columns);\n const toShow: string[] = [];\n const toHide: string[] = [];\n for (const col of api.getColumns() ?? []) {\n // Only the tiered (auto-hideable) field columns participate; the\n // pinned Identity / Actions anchors carry no tier and are never\n // hidden by a view.\n if (getResponsiveTier(col.getColDef()) === undefined) continue;\n const colId = col.getColId();\n if (wanted.has(colId)) toShow.push(colId);\n else toHide.push(colId);\n }\n // Lock BEFORE the visibility write so a synchronous responsive\n // reconcile (e.g. a ResizeObserver tick triggered by the layout shift)\n // already sees these colIds as user-touched.\n lockColumns([...toShow, ...toHide]);\n if (toShow.length > 0) api.setColumnsVisible(toShow, true);\n if (toHide.length > 0) api.setColumnsVisible(toHide, false);\n }\n\n handle.setFilter(view.filterModel);\n handle.setSort(view.sortModel);\n api?.onFilterChanged();\n },\n [views, handleRef, lockColumns],\n );\n\n const handleValueChange = useCallback(\n (next: string) => {\n setActiveViewId(next);\n if (next) applyView(next);\n },\n [applyView],\n );\n\n return (\n // Width-constraining wrapper (same pattern as the toolbar's QuickSearch) so\n // the Select's intrinsic `w-full` trigger sizes to a compact, legible width\n // instead of absorbing the whole FilterBar row.\n <div className=\"ds:w-[12rem] ds:max-w-full ds:min-w-0 ds:shrink-0\">\n <Select\n options={options}\n value={activeViewId}\n onValueChange={handleValueChange}\n size=\"sm\"\n aria-label={label}\n />\n </div>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* PatientTable */\n/* -------------------------------------------------------------------------- */\n\nexport const PatientTable = forwardRef<PatientTableHandle, PatientTableProps>(\n function PatientTable(\n {\n rowData,\n columns,\n gridId,\n density,\n actionsCompact,\n heightClass,\n responsive = true,\n views,\n onRowAction,\n bulkActions,\n loading = false,\n toolbar,\n hideQuickSearch = false,\n pagination = false,\n paginationPageSizeSelector,\n hideDetails = false,\n identityHref,\n customFields,\n currency,\n 'aria-label': ariaLabel,\n className,\n },\n ref,\n ) {\n const { t } = useTranslation('ui');\n\n // Internal handle ref forwarded BOTH to the consumer's ref (below) and to\n // `useResponsiveColumns`, which reaches the live AG Grid API via\n // `handle.getRawApi()`.\n const handleRef = useRef<PatientTableHandle | null>(null);\n\n // Container the responsive hook observes (its inline-size drives the\n // column-priority tiers). Observing the grid's own box — not the viewport —\n // is what makes the table responsive when embedded in a narrow panel.\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n // Mirror the handle into state once the inner DataTable mounts so the\n // agent-registration effect (which keys off `handle` reactively) fires with\n // a live handle. A bare ref mutation would not re-run the effect.\n const [registrableHandle, setRegistrableHandle] =\n useState<PatientTableHandle | null>(null);\n\n // Expose the inner DataTable handle to the consumer ref. Re-runs when the\n // mirrored handle becomes available so a consumer reading the ref in an\n // effect sees the live handle, not the initial `null`.\n useImperativeHandle(\n ref,\n () => (registrableHandle ?? handleRef.current) as PatientTableHandle,\n [registrableHandle],\n );\n\n // Responsive column-priority tiers, driven by the grid container's width.\n // Inert when `responsive` is false (the hook unobserves and makes no calls).\n // The container ref is widened to the hook's `HTMLElement` param at the call\n // site (RefObject is invariant on its element type) — same cast pattern as\n // `useDirection` in tooltip.tsx. `lockColumns` lets the ViewSwitcher mark a\n // view's visibility writes as user-touched (see ViewSwitcher's coordination\n // note) — called before the hook value is consumed in the default toolbar.\n const { lockColumns } = useResponsiveColumns(handleRef, {\n containerRef: containerRef as RefObject<HTMLElement | null>,\n enabled: responsive,\n });\n\n // Actions-column compactness. Explicit `actionsCompact` wins; otherwise it\n // follows `density` (`compact` collapses, every other density stays inline)\n // — the legacy coupling, preserved as the default so existing consumers are\n // unaffected.\n const compact = actionsCompact ?? density === 'compact';\n\n // Ref-backed live getter for the compactness flag. The actions cell renderer\n // calls this every render, so flipping `compact` need only update the ref +\n // refresh the cells — it never rebuilds `columnDefs` (which would discard the\n // user's column state). The getter identity is stable, so it is NOT a\n // `columnDefs` dependency.\n const actionsCompactRef = useRef(compact);\n const getActionsCompact = useCallback(() => actionsCompactRef.current, []);\n\n // On a `compact` change — OR once the grid handle first becomes live — sync\n // the ref the renderer reads, then drive the LIVE grid: set the actions\n // column to its collapsed (88px) or expanded (derived from the row-action\n // count) width and force the actions cells to re-read the getter. Depending\n // on `registrableHandle` makes the initial compact state apply as soon as the\n // DataTable mirrors its handle in (the api is null on first paint), so a\n // page mounting already-compact lands at 88px without a column-defs rebuild.\n useEffect(() => {\n actionsCompactRef.current = compact;\n const api = handleRef.current?.getRawApi();\n if (!api) return;\n api.setColumnWidths([\n {\n key: 'actions',\n newWidth: compact ? 88 : actionsColumnWidth(PATIENT_ROW_ACTION_COUNT),\n },\n ]);\n api.refreshCells({ force: true });\n }, [compact, registrableHandle]);\n\n // Columns: consumer override wins wholesale; otherwise the baked patient\n // columns. Memoised on the inputs the builder reads — `density` is NO LONGER\n // among them: the actions column reads its compactness through the stable\n // `getActionsCompact` getter, so a density (or `actionsCompact`) flip resizes\n // + refreshes the live grid without rebuilding the defs (preserving column\n // state). `density` still drives row height via the DataTable prop below.\n const columnDefs = useMemo<PatientColumnDef[]>(\n () =>\n columns ??\n buildPatientColumns(t, {\n hideDetails,\n onRowAction,\n identityHref,\n customFields,\n currency,\n getActionsCompact,\n }),\n [\n columns,\n t,\n hideDetails,\n onRowAction,\n identityHref,\n customFields,\n currency,\n getActionsCompact,\n ],\n );\n\n // Default toolbar — an optional ViewSwitcher + QuickSearch + FilterChips\n // (FilterBar slot) and ColumnToggle + ExportMenu + one BulkAction per\n // `bulkActions` (Actions slot). Overridable wholesale via `props.toolbar`;\n // when a custom `toolbar` is supplied the ViewSwitcher is NOT injected — a\n // custom toolbar owns its own view UI. The switcher is also omitted when\n // `views` is empty / undefined. All toolbar parts translate their own labels\n // through the `dataTable.*` namespace; the BulkAction labels come from the\n // consumer-supplied descriptors.\n const hasViews = (views?.length ?? 0) > 0;\n const defaultToolbar = (\n <DataTable.Toolbar>\n <DataTable.Toolbar.FilterBar>\n {hasViews && views ? (\n <ViewSwitcher\n views={views}\n handleRef={handleRef}\n lockColumns={lockColumns}\n label={t('patientTable.views.label')}\n />\n ) : null}\n {hideQuickSearch ? null : <DataTable.Toolbar.QuickSearch />}\n <DataTable.Toolbar.FilterChips />\n </DataTable.Toolbar.FilterBar>\n <DataTable.Toolbar.Actions>\n {bulkActions?.map((action) => (\n <DataTable.Toolbar.BulkAction\n key={action.id}\n label={action.label}\n icon={action.icon}\n variant={action.variant}\n onClick={(api) => {\n // Resolve the selection via the live grid API → row ids only\n // (never PHI). Falls back to an empty list if the API is unset.\n const ids =\n api\n ?.getSelectedNodes()\n .map((node) => node.id)\n .filter((id): id is string => id != null) ?? [];\n action.onSelect(ids);\n }}\n />\n ))}\n <DataTable.Toolbar.ColumnToggle />\n <DataTable.Toolbar.ExportMenu />\n </DataTable.Toolbar.Actions>\n </DataTable.Toolbar>\n );\n\n const toolbarContent = toolbar ?? defaultToolbar;\n\n // `rowData === undefined` is the data-table loading sentinel; `loading`\n // forces it independently (e.g. a background refresh over existing rows).\n const effectiveRowData = loading ? undefined : rowData;\n\n // Stable `gridOptions` forwarding `getRowId` so AG Grid keys nodes by the\n // real `PatientRow.id` (see getPatientRowId) — selection, bulk actions and\n // the agent adapter all then return patient ids, not row-index strings.\n const gridOptions = useMemo(() => ({ getRowId: getPatientRowId }), []);\n\n // Register the data-table agent adapter keyed by `gridId` — the same\n // adapter `data-table` registers internally. Idempotent overwrite with the\n // identical handle under the same `(adapterId, gridId)` key; both share the\n // unmount lifecycle so the dedup is harmless and the registration stays\n // addressable as `patient-table`'s `gridId` for the duration.\n useAgentRegistration(\n dataTableAgent,\n registrableHandle as DataTableHandle | null,\n gridId,\n );\n\n return (\n <div\n ref={containerRef}\n role=\"region\"\n aria-label={ariaLabel ?? t('patientTable.regionLabel')}\n data-component=\"patient-table\"\n data-component-id={gridId}\n className=\"ds:flex ds:w-full ds:min-h-0 ds:flex-1 ds:flex-col\"\n >\n <DataTable<PatientRow>\n ref={(instance) => {\n handleRef.current = instance;\n // Mirror into state once (or when the instance identity changes) so\n // the registration effect picks up the live handle.\n setRegistrableHandle((prev) =>\n prev === instance ? prev : instance,\n );\n }}\n gridId={gridId}\n columnDefs={columnDefs}\n rowData={effectiveRowData}\n rowSelection=\"multiple\"\n pagination={pagination}\n {...(heightClass !== undefined ? { heightClass } : {})}\n {...(paginationPageSizeSelector !== undefined\n ? { paginationPageSizeSelector }\n : {})}\n density={density}\n getRowClass={getPatientRowClass}\n gridOptions={gridOptions}\n className={className}\n >\n {toolbarContent}\n </DataTable>\n </div>\n );\n },\n);\n\nPatientTable.displayName = 'PatientTable';\n","import type { CustomCellRendererProps } from 'ag-grid-react';\nimport { useTranslation } from 'react-i18next';\nimport { Badge } from '../../badge/badge';\nimport type { PatientRow } from '../types';\n\n/**\n * patient-table — BalanceBadgeCell\n * ----------------------------------------------------------------------------\n * Threshold-coloured unpaid-balance badge for the patient grid.\n *\n * Reads {@link PatientRow.unpaidAmount} (integer cents), formats it to the\n * display currency via `Intl.NumberFormat` (cents → major unit at the render\n * boundary — see types.ts), and wraps the result in a kit {@link Badge} whose\n * semantic variant buckets by outstanding-balance threshold:\n *\n * | cents bucket | meaning | Badge variant |\n * | ----------------------- | ------------------ | ------------- |\n * | `0` | settled | `success` |\n * | `> 0` and `<= 50000` | small balance | `warning` |\n * | `> 50000` | overdue / large | `error` |\n *\n * NOTE on the variant name: the task brief calls the red bucket\n * `destructive`; the kit `Badge` exposes that semantic tier as the `error`\n * variant (backed by the `--destructive` / `--error-foreground` tokens). There\n * is no `destructive` Badge variant, so `error` is the correct destructive-tier\n * mapping — recorded here so the brief↔code discrepancy is intentional.\n *\n * The bucket meaning is colour-coded, so it is also surfaced as an sr-only\n * status word inside the badge (alongside the `withDot` indicator) — the\n * threshold tier is never conveyed by colour alone.\n *\n * Returns `null` when `unpaidAmount` is missing / non-finite (empty cell), per\n * the data-table renderer convention.\n */\n\n/** Display + threshold-classification result for a single balance value. */\ntype BalanceBucket = {\n variant: 'success' | 'warning' | 'error';\n /** i18n key suffix under `ui.patientTable.balance.status.*`. */\n statusKey: 'settled' | 'pending' | 'overdue';\n};\n\n/** Cents at or below which a non-zero balance is treated as a small balance. */\nconst SMALL_BALANCE_CENTS_MAX = 50000;\n\nfunction classifyBalance(cents: number): BalanceBucket {\n if (cents === 0) return { variant: 'success', statusKey: 'settled' };\n if (cents <= SMALL_BALANCE_CENTS_MAX)\n return { variant: 'warning', statusKey: 'pending' };\n return { variant: 'error', statusKey: 'overdue' };\n}\n\nfunction readUnpaidCents(value: unknown): number | null {\n if (value == null || value === '') return null;\n const n = typeof value === 'number' ? value : Number(value);\n return Number.isFinite(n) ? n : null;\n}\n\nexport interface BalanceBadgeCellParams {\n /** ISO 4217 currency code used to format the balance. Default `'EUR'`. */\n currency?: string;\n /**\n * `Intl.NumberFormat` escape hatch, merged on top of the currency defaults.\n * E.g. `{ maximumFractionDigits: 0 }` for whole-currency display.\n */\n options?: Intl.NumberFormatOptions;\n}\n\nexport function BalanceBadgeCell(\n props: CustomCellRendererProps<PatientRow> & BalanceBadgeCellParams,\n) {\n const { value, currency = 'EUR', options } = props;\n const { t, i18n } = useTranslation('ui');\n\n const cents = readUnpaidCents(value);\n if (cents === null) return null;\n\n const locale = i18n.language || 'en';\n const formatted = new Intl.NumberFormat(locale, {\n style: 'currency',\n currency,\n ...options,\n }).format(cents / 100);\n\n const { variant, statusKey } = classifyBalance(cents);\n const status = t(`patientTable.balance.status.${statusKey}`);\n\n return (\n <Badge variant={variant} withDot size=\"sm\" className=\"ds:tabular-nums\">\n {/* Visible: the formatted amount. The threshold meaning is colour-coded,\n so the status word is surfaced to AT as sr-only text — the bucket is\n never conveyed by colour alone. */}\n <span>{formatted}</span>\n <span className=\"ds:sr-only\">{status}</span>\n </Badge>\n );\n}\n"],"names":["TransactionChipCell","props","value","positiveState","showLabel","currency","cents","state","jsx","TransactionChip","CARE_PLAN_VARIANT_MAP","isCarePlanStatus","CarePlanStatusCell","t","useTranslation","data","size","raw","variant","Badge","nextAppointmentVariants","cva","DAY_MS","DEFAULT_SOON_WITHIN_DAYS","toTime","resolveTone","time","soonWithinDays","deltaMs","NextAppointmentCell","tone","CalendarClock","Timestamp","getResponsiveTier","colDef","PATIENT_ROW_ACTION_COUNT","buildRowActions","onRowAction","dispatch","action","patient","make","overrides","createElement","Eye","Pencil","Mail","MessageSquare","p","Users","Trash2","NO","CHECK","DASH","countryName","code","upper","lang","ageFromDob","dob","birth","fromIsoDate","now","age","m","buildCustomFieldColumns","fields","field","base","params","_b","_a","SelectFilter","SelectFloatingFilter","TagListCellRenderer","opt","TextFilter","TextFloatingFilter","buildPatientColumns","opts","hideDetails","getActionsCompact","identityHref","customFields","actions","genderLabels","consentLabels","consentVariantMap","header","id","ImageCellRenderer","LinkCellRenderer","row","_c","DateRangeFilter","DateRangeFloatingFilter","NumberFilter","NumberFloatingFilter","s","DateCellRenderer","UserCellRenderer","a","b","TypeaheadFilter","TypeaheadFloatingFilter","StatusCellRenderer","year","num","ActionsCellRenderer","actionsColumnWidth","PROGRAMMATIC_SOURCES","DEFAULT_BREAKPOINTS","visibleTiersForWidth","width","breakpoints","visible","bp","useResponsiveColumns","handleRef","containerRef","enabled","userTouchedRef","useRef","applyingRef","useEffect","container","getApi","reconcileTimer","scheduleReconcile","reconcile","onColumnVisible","event","colIds","col","colId","wired","ensureWired","api","columns","visibleTiers","persistedTouched","toShow","toHide","tier","shouldShow","observer","useCallback","getPatientRowClass","getPatientRowId","ViewSwitcher","views","lockColumns","label","activeViewId","setActiveViewId","useState","options","useMemo","view","applyView","viewId","v","handle","wanted","handleValueChange","next","Select","PatientTable","forwardRef","rowData","gridId","density","actionsCompact","heightClass","responsive","bulkActions","loading","toolbar","hideQuickSearch","pagination","paginationPageSizeSelector","ariaLabel","className","ref","registrableHandle","setRegistrableHandle","useImperativeHandle","compact","actionsCompactRef","columnDefs","hasViews","defaultToolbar","jsxs","DataTable","ids","node","toolbarContent","effectiveRowData","gridOptions","useAgentRegistration","dataTableAgent","instance","prev","SMALL_BALANCE_CENTS_MAX","classifyBalance","readUnpaidCents","n","BalanceBadgeCell","i18n","locale","formatted","statusKey","status"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"date-range-picker-BD8P_rJq.js","sources":["../../node_modules/date-fns/subWeeks.js","../../node_modules/date-fns/subYears.js","../../src/components/date-range-picker/date-range-picker.agent.ts","../../src/components/date-range-picker/date-range-picker.tsx"],"sourcesContent":["import { addWeeks } from \"./addWeeks.js\";\n\n/**\n * The {@link subWeeks} function options.\n */\n\n/**\n * @name subWeeks\n * @category Week Helpers\n * @summary Subtract the specified number of weeks from the given date.\n *\n * @description\n * Subtract the specified number of weeks from the given date.\n *\n * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).\n * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments.\n *\n * @param date - The date to be changed\n * @param amount - The amount of weeks to be subtracted.\n * @param options - An object with options\n *\n * @returns The new date with the weeks subtracted\n *\n * @example\n * // Subtract 4 weeks from 1 September 2014:\n * const result = subWeeks(new Date(2014, 8, 1), 4)\n * //=> Mon Aug 04 2014 00:00:00\n */\nexport function subWeeks(date, amount, options) {\n return addWeeks(date, -amount, options);\n}\n\n// Fallback for modularized imports:\nexport default subWeeks;\n","import { addYears } from \"./addYears.js\";\n\n/**\n * The {@link subYears} function options.\n */\n\n/**\n * @name subYears\n * @category Year Helpers\n * @summary Subtract the specified number of years from the given date.\n *\n * @description\n * Subtract the specified number of years from the given date.\n *\n * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).\n * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments.\n *\n * @param date - The date to be changed\n * @param amount - The amount of years to be subtracted.\n * @param options - An object with options\n *\n * @returns The new date with the years subtracted\n *\n * @example\n * // Subtract 5 years from 1 September 2014:\n * const result = subYears(new Date(2014, 8, 1), 5)\n * //=> Tue Sep 01 2009 00:00:00\n */\nexport function subYears(date, amount, options) {\n return addYears(date, -amount, options);\n}\n\n// Fallback for modularized imports:\nexport default subYears;\n","/* -------------------------------------------------------------------- */\n/* Agent adapter — DateRangePicker. */\n/* */\n/* DateRangePicker wraps react-day-picker (range mode) inside a Radix */\n/* Popover. The agent surface curates the trigger + popover into range */\n/* operations — set / clear / open / close, commit (apply / cancel), and */\n/* the Range Cockpit config (set_granularity / toggle_compare + read-only */\n/* granularity + compareRange state). */\n/* */\n/* See `src/docs/26-agent-readiness.mdx` §15. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { DateRangePickerHandle } from './date-range-picker';\n\nexport const dateRangePickerAgent: AgentAdapter<DateRangePickerHandle> = {\n id: 'date-range-picker',\n capabilities: [\n 'range_pick',\n 'open',\n 'close',\n 'confirm', // apply — commit the draft range\n 'dismiss', // cancel — discard the draft\n 'set_value', // set_granularity + compare_toggle\n ],\n state: {\n range: {\n type: '{ from: iso-date | null, to: iso-date | null }',\n descriptionKey: 'ui.agent.dateRangePicker.state.range',\n description:\n 'Currently-selected ISO date range endpoints, each possibly null.',\n read: (handle) => {\n const range = handle.getRange();\n return {\n from: range.from?.toISOString() ?? null,\n to: range.to?.toISOString() ?? null,\n };\n },\n },\n isOpen: {\n type: 'boolean',\n descriptionKey: 'ui.agent.dateRangePicker.state.isOpen',\n description: 'Whether the range calendar popover is open.',\n read: (handle) => handle.isOpen(),\n },\n granularity: {\n type: \"'day' | 'week' | 'month' | 'quarter' | null\",\n descriptionKey: 'ui.agent.dateRangePicker.state.granularity',\n description:\n 'Selected time granularity (Range Cockpit), or null when the control is disabled.',\n read: (handle) => handle.getGranularity() ?? null,\n },\n compareRange: {\n type: '{ from: iso-date | null, to: iso-date | null } | null',\n descriptionKey: 'ui.agent.dateRangePicker.state.compareRange',\n description:\n 'Resolved comparison period when compare-to-previous-period is on, else null.',\n read: (handle) => {\n const compare = handle.getCompareRange();\n if (!compare) return null;\n return {\n from: compare.from?.toISOString() ?? null,\n to: compare.to?.toISOString() ?? null,\n };\n },\n },\n },\n actions: {\n set_range: {\n safety: 'write',\n argsType: '{ from: string, to: string }',\n argsSchema: {\n type: 'object',\n properties: {\n from: {\n type: 'string',\n format: 'date',\n description: 'ISO start date (inclusive).',\n },\n to: {\n type: 'string',\n format: 'date',\n description: 'ISO end date (inclusive).',\n },\n },\n required: ['from', 'to'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.dateRangePicker.actions.setRange',\n description: 'Replace the selected range with the given ISO endpoints.',\n examples: [\n {\n args: { from: '2026-06-01', to: '2026-06-30' },\n note: 'Select all of June 2026.',\n },\n ],\n invoke: (handle, args: { from: string; to: string }) => {\n handle.setRange({ from: new Date(args.from), to: new Date(args.to) });\n },\n },\n clear: {\n // Discards the selected range — loses the user's input.\n safety: 'destructive',\n idempotent: true,\n descriptionKey: 'ui.agent.dateRangePicker.actions.clear',\n description: 'Clear the selected range.',\n invoke: (handle) => {\n handle.clear();\n },\n },\n open: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.dateRangePicker.actions.open',\n description: 'Open the range calendar popover.',\n invoke: (handle) => {\n handle.open();\n },\n },\n close: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.dateRangePicker.actions.close',\n description: 'Close the range calendar popover.',\n invoke: (handle) => {\n handle.close();\n },\n },\n apply: {\n // Commits the current draft — in `commitMode=\"apply\"` this is what runs\n // the downstream query, so it is a write, not read.\n safety: 'write',\n idempotent: true,\n descriptionKey: 'ui.agent.dateRangePicker.actions.apply',\n description:\n 'Commit the current draft range (as the Apply / Done button).',\n invoke: (handle) => {\n handle.apply();\n },\n },\n cancel: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.dateRangePicker.actions.cancel',\n description: 'Discard the draft and close without committing.',\n invoke: (handle) => {\n handle.cancel();\n },\n },\n set_granularity: {\n safety: 'write',\n argsType: \"{ granularity: 'day' | 'week' | 'month' | 'quarter' }\",\n argsSchema: {\n type: 'object',\n properties: {\n granularity: {\n type: 'string',\n enum: ['day', 'week', 'month', 'quarter'],\n description: 'Time grain to query the range at.',\n },\n },\n required: ['granularity'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.dateRangePicker.actions.setGranularity',\n description: 'Set the time granularity (Range Cockpit).',\n examples: [{ args: { granularity: 'month' }, note: 'Query by month.' }],\n invoke: (\n handle,\n args: { granularity: 'day' | 'week' | 'month' | 'quarter' },\n ) => {\n handle.setGranularity(args.granularity);\n },\n },\n toggle_compare: {\n safety: 'write',\n argsType: '{ enabled: boolean }',\n argsSchema: {\n type: 'object',\n properties: {\n enabled: {\n type: 'boolean',\n description: 'Whether to compare against the previous period.',\n },\n },\n required: ['enabled'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.dateRangePicker.actions.toggleCompare',\n description: 'Turn compare-to-previous-period on or off (Range Cockpit).',\n examples: [\n { args: { enabled: true }, note: 'Enable period comparison.' },\n ],\n invoke: (handle, args: { enabled: boolean }) => {\n handle.setCompare(args.enabled);\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'date-range-picker',\n description: 'Marks the DateRangePicker wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import {\n forwardRef,\n Fragment,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type KeyboardEvent,\n} from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport * as Popover from '@radix-ui/react-popover';\nimport * as RadioGroup from '@radix-ui/react-radio-group';\nimport { DayPicker, type DateRange } from 'react-day-picker';\nimport {\n format as fnsFormat,\n parse as fnsParse,\n isValid,\n isAfter,\n isBefore,\n differenceInCalendarDays,\n isSameDay,\n addDays,\n subDays,\n subWeeks,\n subYears,\n startOfWeek,\n endOfWeek,\n startOfMonth,\n endOfMonth,\n} from 'date-fns';\nimport { CalendarDays, Check } from 'lucide-react';\nimport { useControllableState } from '../../hooks/use-controllable-state';\nimport { useFormField } from '../form-field/form-field-context';\nimport {\n useDateFnsLocale,\n getShortDateFormat,\n buildDisabledMatcher,\n CalendarChevron,\n calendarClassNames,\n calendarCompareModifierClassNames,\n triggerVariants,\n calendarIconVariants,\n popoverContentVariants,\n presetButtonVariants,\n rangeFieldVariants,\n rangeActionVariants,\n granularityGroupVariants,\n granularityItemVariants,\n presetGroupHeadingVariants,\n} from '../_shared';\nimport { Switch } from '../switch';\nimport { useAgentRegistration } from '../../agent';\nimport { dateRangePickerAgent } from './date-range-picker.agent';\n\n/* -------------------------------------------------------------------------- */\n/* Props */\n/* -------------------------------------------------------------------------- */\n\nexport interface DateRangePreset {\n label: string;\n range: () => { from: Date; to: Date };\n /** Optional group name — presets sharing a group render under one sub-heading. */\n group?: string;\n}\n\nexport interface DateRangeValue {\n from?: Date;\n to?: Date;\n}\n\n/** Time grain the range is queried at (Range Cockpit). */\nexport type DateRangeGranularity = 'day' | 'week' | 'month' | 'quarter';\n\n/** How the comparison period is derived from the selected range. */\nexport type DateRangeCompareMode = 'previous' | 'lastYear';\n\n/** Extra data emitted alongside a committed range (Range Cockpit). */\nexport interface DateRangeApplyMeta {\n granularity?: DateRangeGranularity;\n compareRange?: DateRangeValue;\n}\n\n/** Curated imperative handle exposed via `ref` — also consumed by the agent adapter. */\nexport interface DateRangePickerHandle {\n /** Get the current selected range. */\n getRange: () => DateRangeValue;\n /** Replace the selected range. */\n setRange: (range: DateRangeValue) => void;\n /** Clear the selected range. */\n clear: () => void;\n /** Whether the popover is open. */\n isOpen: () => boolean;\n /** Open the range calendar popover. */\n open: () => void;\n /** Close the range calendar popover. */\n close: () => void;\n /** Current granularity, or undefined when the control is disabled. */\n getGranularity: () => DateRangeGranularity | undefined;\n /** Set the time granularity. */\n setGranularity: (granularity: DateRangeGranularity) => void;\n /** Current comparison period, or undefined when compare is off/incomplete. */\n getCompareRange: () => DateRangeValue | undefined;\n /** Turn compare-to-previous-period on or off. */\n setCompare: (enabled: boolean) => void;\n /** Label of the preset matching the committed range, or undefined for a custom range. */\n getActivePreset: () => string | undefined;\n /** Which endpoint the next calendar click will set, or null when the draft is complete. */\n getActiveEnd: () => 'start' | 'end' | null;\n /** Commit the current draft (as the Apply / Done button does). */\n apply: () => void;\n /** Discard the draft and close (as Cancel / Escape does). */\n cancel: () => void;\n}\n\nexport interface DateRangePickerProps extends Pick<\n VariantProps<typeof triggerVariants>,\n 'size'\n> {\n /** Current range — controlled. */\n value?: DateRangeValue;\n /** Default range — uncontrolled. */\n defaultValue?: DateRangeValue;\n /**\n * Called when the range is committed. In `commitMode=\"auto\"` (default) this\n * fires the instant the range becomes COMPLETE (both ends) — never on a\n * start-only partial. In `commitMode=\"apply\"` it fires once, on Apply.\n */\n onChange?: (range: DateRangeValue) => void;\n /**\n * Commit model.\n * - `'auto'` (default): manual selection updates a popover-local draft and\n * commits (`onChange`) the moment both ends exist; the popover stays open so\n * the user can refine, and the footer's Done button dismisses it. A preset\n * commits and closes. Right for cheap client-side filters.\n * - `'apply'`: every edit (presets, calendar, typed fields) mutates the draft\n * only; nothing commits until the Apply button. Escape / outside-click /\n * Cancel revert to the last committed value. Right for expensive queries\n * that must not fire on a half-picked range.\n */\n commitMode?: 'auto' | 'apply';\n /**\n * Called when the range is committed by a decisive action that CLOSES the\n * popover — a preset click, or the Apply button. Completing a range on the\n * calendar in `'auto'` mode fires `onChange` but keeps the popover open, so it\n * does not fire `onApply`. The optional second argument carries the Range\n * Cockpit metadata (granularity + resolved comparison range) when enabled.\n */\n onApply?: (range: DateRangeValue, meta?: DateRangeApplyMeta) => void;\n /** Called when an `'apply'`-mode edit is cancelled (Cancel / Escape / dismiss). */\n onCancel?: () => void;\n /** Show the footer Clear action. Defaults to `true`. */\n showClear?: boolean;\n /** Number of month grids to render. Defaults to `2`. */\n numberOfMonths?: number;\n /**\n * Range Cockpit — granularity. Passing `granularity` (controlled) or\n * `defaultGranularity` (uncontrolled) shows a Day / Week / Month / Quarter\n * segmented control in the header and names the queried grain.\n */\n granularity?: DateRangeGranularity;\n defaultGranularity?: DateRangeGranularity;\n onGranularityChange?: (granularity: DateRangeGranularity) => void;\n /**\n * Restrict the granularity segmented control to a subset of\n * `'day' | 'week' | 'month' | 'quarter'` — e.g. `['month', 'quarter']` for\n * data with a coarser grain. Options always render in canonical\n * Day → Quarter order regardless of the array order passed. Defaults to all\n * four (an empty array is treated as all four). Only meaningful when the\n * Cockpit granularity control is enabled (via `granularity` /\n * `defaultGranularity`) — it does not enable the control on its own. A\n * `defaultGranularity` or controlled `granularity` outside the allowed set is\n * clamped to the first allowed option.\n */\n allowedGranularities?: DateRangeGranularity[];\n /**\n * Range Cockpit — compare. When `true`, a compare-to-previous-period toggle\n * appears; enabling it overlays a neutral secondary band on the calendar and\n * (on Apply) reports the resolved comparison range via `onApply` meta and\n * `onCompareChange`.\n */\n compare?: boolean;\n /**\n * Controlled checked state of the compare toggle — the APPLIED compare\n * on/off value. Pair with `onCompareChange` and drive it from the consumer\n * so a reopened picker reflects the applied state. When omitted the toggle\n * is uncontrolled and seeded from `defaultCompare`. This is distinct from\n * `compare`, which only decides whether the control renders. Only\n * meaningful when `compare` is set.\n */\n compareEnabled?: boolean;\n /** Start with compare enabled — uncontrolled initial checked state (only meaningful when `compare` is set). */\n defaultCompare?: boolean;\n defaultCompareMode?: DateRangeCompareMode;\n onCompareChange?: (enabled: boolean, compareRange?: DateRangeValue) => void;\n /** Week start (0 = Sunday). Defaults to the locale's first day. */\n weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\n /** Minimum selectable date. */\n minDate?: Date;\n /** Maximum selectable date. */\n maxDate?: Date;\n /** Custom presets. Replaces defaults unless mergePresets is true. */\n presets?: DateRangePreset[];\n /** Merge custom presets with defaults instead of replacing. */\n mergePresets?: boolean;\n /** Component size. */\n size?: 'sm' | 'md' | 'lg';\n /**\n * Trigger chrome. `'input'` (default) is the soft input look — fill + the\n * `--shadow-input` halo — that matches other form fields. `'button'` renders\n * the trigger as an outline Button (flat, `--radius-md`, no halo, `md` = 40px)\n * so the picker sits flush among Buttons in a toolbar. Use `'button'` only in\n * toolbars, not in forms: the input halo is a11y-load-bearing (it lifts the\n * field border to the WCAG 1.4.11 3:1 composite), whereas a button-chrome\n * trigger relies on its value/label like a Button does.\n */\n chrome?: 'input' | 'button';\n /** Disabled state. */\n disabled?: boolean;\n /** Placeholder text. */\n placeholder?: string;\n /** Additional class names. */\n className?: string;\n /** id override. */\n id?: string;\n}\n\nconst GRANULARITY_OPTIONS: {\n value: DateRangeGranularity;\n fallback: string;\n}[] = [\n { value: 'day', fallback: 'Day' },\n { value: 'week', fallback: 'Week' },\n { value: 'month', fallback: 'Month' },\n { value: 'quarter', fallback: 'Quarter' },\n];\n\nconst COMPARE_MODE_OPTIONS: {\n value: DateRangeCompareMode;\n fallback: string;\n}[] = [\n { value: 'previous', fallback: 'Previous period' },\n { value: 'lastYear', fallback: 'Same period last year' },\n];\n\n/* -------------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------------- */\n\nexport const DateRangePicker = forwardRef<\n DateRangePickerHandle,\n DateRangePickerProps\n>(\n (\n {\n value,\n defaultValue,\n onChange,\n commitMode = 'auto',\n onApply,\n onCancel,\n showClear = true,\n numberOfMonths = 2,\n granularity: granularityProp,\n defaultGranularity,\n onGranularityChange,\n allowedGranularities,\n compare = false,\n compareEnabled,\n defaultCompare = false,\n defaultCompareMode = 'previous',\n onCompareChange,\n weekStartsOn,\n minDate,\n maxDate,\n presets: presetsProp,\n mergePresets = false,\n size = 'md',\n chrome = 'input',\n disabled,\n placeholder,\n className,\n id,\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const ctx = useFormField();\n const effectiveId = id ?? ctx.id;\n const effectiveDisabled = ctx.disabled || disabled;\n const effectiveTone = ctx.invalid ? 'error' : 'default';\n\n const locale = useDateFnsLocale(i18n.language);\n const dateFormat = getShortDateFormat(i18n.language);\n const placeholderText =\n placeholder ??\n t('inputs.dateRangePicker.placeholder', 'Select date range');\n\n const [currentValueRaw, setDateRangeValue] =\n useControllableState<DateRangeValue>({\n value,\n defaultValue: defaultValue ?? {},\n onChange,\n });\n const currentValue: DateRangeValue = currentValueRaw ?? {};\n\n const [open, setOpen] = useState(false);\n // Popover-local draft — the working range while the popover is open. In\n // `auto` it commits to `currentValue` the moment it is complete; in `apply`\n // it stays local until Apply. Seeded from the committed value on open.\n const [draft, setDraft] = useState<DateRangeValue>(currentValue);\n const [month, setMonth] = useState<Date>(currentValue.from ?? new Date());\n\n // Editable endpoint field text + parse-error flags.\n const [startText, setStartText] = useState('');\n const [endText, setEndText] = useState('');\n const [startInvalid, setStartInvalid] = useState(false);\n const [endInvalid, setEndInvalid] = useState(false);\n\n // Range Cockpit state (all opt-in; the default picker never renders these).\n const granularityEnabled =\n granularityProp !== undefined || defaultGranularity !== undefined;\n // Restrict the granularity control to an allowed subset (default = all four;\n // an empty array is treated as \"all\"). Filtering GRANULARITY_OPTIONS keeps\n // the canonical Day → Quarter order no matter what order the consumer passes.\n const allowedGranularitySet =\n allowedGranularities && allowedGranularities.length > 0\n ? allowedGranularities\n : undefined;\n const visibleGranularityOptions = allowedGranularitySet\n ? GRANULARITY_OPTIONS.filter((o) =>\n allowedGranularitySet.includes(o.value),\n )\n : GRANULARITY_OPTIONS;\n const clampGranularity = (g: DateRangeGranularity): DateRangeGranularity =>\n !allowedGranularitySet || allowedGranularitySet.includes(g)\n ? g\n : (visibleGranularityOptions[0]?.value ?? g);\n const [granularity, setGranularity] =\n useControllableState<DateRangeGranularity>({\n value: granularityProp,\n defaultValue: clampGranularity(defaultGranularity ?? 'day'),\n onChange: onGranularityChange,\n });\n // The clamped granularity actually shown / reported — guards a controlled\n // `granularity` prop that sits outside the allowed set. Equal to\n // `granularity` when unrestricted, so default output is unchanged.\n const resolvedGranularity = clampGranularity(granularity ?? 'day');\n // Committed compare state — controllable, mirrors `value`/`granularity`.\n // This is the APPLIED compare on/off; the consumer drives it via the\n // `compareEnabled` prop so a reopened picker reflects the applied state.\n const [committedCompare, setCommittedCompare] =\n useControllableState<boolean>({\n value: compareEnabled,\n defaultValue: defaultCompare,\n // onCompareChange needs the resolved range as its 2nd arg, so it is\n // called manually at the commit points rather than wired here.\n });\n // Draft compare toggle — the working state while the popover is open,\n // seeded from `committedCompare` on open. Binds to the Switch so an\n // apply-mode flip shows immediately without committing.\n const [draftCompare, setDraftCompare] = useState<boolean>(\n committedCompare ?? false,\n );\n const [compareMode, setCompareMode] =\n useState<DateRangeCompareMode>(defaultCompareMode);\n\n const triggerRef = useRef<HTMLButtonElement>(null);\n // Set true immediately before a commit-and-close so the close handler does\n // not also fire the `apply`-mode cancel path.\n const committingRef = useRef(false);\n\n const titleId = useId();\n const startFieldId = useId();\n const endFieldId = useId();\n const gridLabelId = useId();\n const errorId = useId();\n // The compare Switch gets its OWN id so — inside a FormField — it does not\n // inherit the field's id (which would clash with / steal the trigger label).\n const compareId = useId();\n\n const emit = useCallback(\n (range: DateRangeValue) => {\n setDateRangeValue(range);\n },\n [setDateRangeValue],\n );\n\n const disabledMatcher = buildDisabledMatcher(minDate, maxDate);\n\n // `buildDisabledMatcher` returns an rdp Matcher (for the grid), not a\n // predicate — so validate a TYPED endpoint against min/max directly.\n const isDateBlocked = (d: Date): boolean =>\n (minDate ? isBefore(d, minDate) : false) ||\n (maxDate ? isAfter(d, maxDate) : false);\n\n // Seed the draft from the committed value whenever the popover opens —\n // works for both the Radix trigger and the imperative handle / agent\n // `open()`, and resets any stale parse-error state.\n useEffect(() => {\n if (open) {\n setDraft(currentValue);\n // Re-seed the draft compare toggle from the committed (applied) value so\n // a reopened picker reflects the applied compare state — the fix for the\n // toggle silently resetting to OFF on reopen / remount.\n setDraftCompare(committedCompare ?? false);\n setStartInvalid(false);\n setEndInvalid(false);\n // Clear the commit guard on EVERY open transition (trigger, imperative\n // handle, agent) so a later Escape / outside-dismiss still runs the\n // apply-mode cancel path even if the last close was a commit.\n committingRef.current = false;\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- seed only on the open transition; must not re-seed as currentValue changes mid-edit\n }, [open]);\n\n // Keep the visible field text in sync with the draft (e.g. after a calendar\n // click or a preset), unless the user is mid-typing an invalid value.\n useEffect(() => {\n if (!startInvalid) {\n setStartText(\n draft.from ? fnsFormat(draft.from, dateFormat, { locale }) : '',\n );\n }\n if (!endInvalid) {\n setEndText(draft.to ? fnsFormat(draft.to, dateFormat, { locale }) : '');\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- text follows the draft; invalid flags gate the overwrite intentionally\n }, [draft.from, draft.to, dateFormat, locale]);\n\n // Default presets\n const defaultPresets: DateRangePreset[] = [\n {\n label: t('inputs.dateRangePicker.presets.today', 'Today'),\n range: () => {\n const today = new Date();\n return { from: today, to: today };\n },\n },\n {\n label: t('inputs.dateRangePicker.presets.yesterday', 'Yesterday'),\n range: () => {\n const yesterday = subDays(new Date(), 1);\n return { from: yesterday, to: yesterday };\n },\n },\n {\n label: t('inputs.dateRangePicker.presets.last7', 'Last 7 days'),\n range: () => ({\n from: subDays(new Date(), 6),\n to: new Date(),\n }),\n },\n {\n label: t('inputs.dateRangePicker.presets.thisWeek', 'This week'),\n range: () => ({\n // `weekStartsOn` (when set) overrides the locale's first day; when\n // undefined date-fns falls back to the locale.\n from: startOfWeek(new Date(), { locale, weekStartsOn }),\n to: endOfWeek(new Date(), { locale, weekStartsOn }),\n }),\n },\n {\n label: t('inputs.dateRangePicker.presets.lastWeek', 'Last week'),\n range: () => {\n const lastWeekDay = subWeeks(new Date(), 1);\n return {\n from: startOfWeek(lastWeekDay, { locale, weekStartsOn }),\n to: endOfWeek(lastWeekDay, { locale, weekStartsOn }),\n };\n },\n },\n {\n label: t('inputs.dateRangePicker.presets.thisMonth', 'This month'),\n range: () => ({\n from: startOfMonth(new Date()),\n to: endOfMonth(new Date()),\n }),\n },\n {\n label: t('inputs.dateRangePicker.presets.last30', 'Last 30 days'),\n range: () => ({\n from: subDays(new Date(), 29),\n to: new Date(),\n }),\n },\n ];\n\n const effectivePresets = presetsProp\n ? mergePresets\n ? [...defaultPresets, ...presetsProp]\n : presetsProp\n : defaultPresets;\n\n // Preset groups (Range Cockpit grouped rail). If no preset declares a\n // `group`, this collapses to a single unnamed group == the flat list.\n const presetGroups = (() => {\n const order: string[] = [];\n const byGroup = new Map<string, DateRangePreset[]>();\n for (const preset of effectivePresets) {\n const key = preset.group ?? '';\n if (!byGroup.has(key)) {\n byGroup.set(key, []);\n order.push(key);\n }\n byGroup.get(key)!.push(preset);\n }\n return order.map((key) => ({ group: key, presets: byGroup.get(key)! }));\n })();\n const hasPresetGroups = presetGroups.some((g) => g.group !== '');\n\n // Derive the comparison period from a base range. `mode` defaults to the\n // current state, but callers that are ALSO changing the mode pass the new\n // value explicitly (React state updates are async, so the closure would\n // otherwise still see the old mode).\n const computeCompareRange = (\n base: DateRangeValue,\n mode: DateRangeCompareMode = compareMode,\n ): DateRangeValue | undefined => {\n if (!base.from || !base.to) return undefined;\n if (mode === 'lastYear') {\n // Shift back one year. (For a selected range longer than a year the\n // comparison would overlap the primary band; that is an unusual\n // period-over-period selection and is left as-is.)\n return { from: subYears(base.from, 1), to: subYears(base.to, 1) };\n }\n // 'previous': same length, immediately before the start.\n const length = differenceInCalendarDays(base.to, base.from);\n const to = subDays(base.from, 1);\n return { from: subDays(to, length), to };\n };\n\n // Draft-scoped: working compare state while the popover is open — drives\n // the calendar band, preview chip, mode radios, and the Apply meta.\n const draftCompareActive = compare && draftCompare;\n // Committed-scoped: applied compare state — drives the imperative handle's\n // getCompareRange (read from the committed `currentValue`).\n const committedCompareActive = compare && (committedCompare ?? false);\n const draftCompareRange = draftCompareActive\n ? computeCompareRange(draft)\n : undefined;\n const compareModifiers:\n | Record<string, Date | { from: Date; to: Date }>\n | undefined = (() => {\n if (!draftCompareRange?.from || !draftCompareRange.to) return undefined;\n const from = draftCompareRange.from;\n const to = draftCompareRange.to;\n const mods: Record<string, Date | { from: Date; to: Date }> = {\n compareStart: from,\n compareEnd: to,\n };\n // Only add the middle band for spans of 3+ days. For a 1-2 day span the\n // start/end caps fully describe it, and an inverted middle range\n // ({from: from+1, to: to-1}) would be SWAPPED by rdp and paint cells\n // outside the comparison period (including the selected day).\n if (differenceInCalendarDays(to, from) >= 2) {\n mods.compareMiddle = { from: addDays(from, 1), to: subDays(to, 1) };\n }\n return mods;\n })();\n\n const applyMeta = (\n base: DateRangeValue,\n ): DateRangeApplyMeta | undefined => {\n const meta: DateRangeApplyMeta = {};\n if (granularityEnabled) meta.granularity = resolvedGranularity;\n if (draftCompareActive) meta.compareRange = computeCompareRange(base);\n return meta.granularity || meta.compareRange ? meta : undefined;\n };\n\n const handleCompareModeChange = (v: string) => {\n const mode = v as DateRangeCompareMode;\n setCompareMode(mode);\n // Report the recomputed comparison range for the newly-selected mode.\n // In apply mode the mode change rides Apply (applyMeta re-reads the\n // updated `compareMode`), so only auto mode reports it immediately.\n if (draftCompareActive && commitMode === 'auto') {\n onCompareChange?.(true, computeCompareRange(draft, mode));\n }\n };\n\n const handleCompareToggle = (next: boolean) => {\n // Always update the working draft so the Switch reflects the flip.\n setDraftCompare(next);\n // `auto` commits immediately (like a completed range); `apply` keeps it\n // a draft until Apply, so nothing commits early.\n if (commitMode === 'auto') {\n setCommittedCompare(next);\n onCompareChange?.(next, next ? computeCompareRange(draft) : undefined);\n }\n };\n\n /**\n * Update the draft. In `auto` mode, commit the instant the range is\n * complete (both ends) — and NEVER on a start-only partial, which removes\n * the old half-open `{from, to:undefined}` emit.\n */\n const updateDraft = useCallback(\n (next: DateRangeValue) => {\n setDraft(next);\n if (commitMode === 'auto' && next.from && next.to) {\n emit(next);\n }\n },\n [commitMode, emit],\n );\n\n const handleRangeSelect = (range: DateRange | undefined) => {\n // Picking on the calendar clears any stale typed-field parse error.\n setStartInvalid(false);\n setEndInvalid(false);\n updateDraft({ from: range?.from, to: range?.to });\n };\n\n const handlePresetClick = (preset: DateRangePreset) => {\n const range = preset.range();\n setStartInvalid(false);\n setEndInvalid(false);\n setDraft(range);\n setMonth(range.from);\n if (commitMode === 'auto') {\n emit(range);\n onApply?.(range, applyMeta(range));\n committingRef.current = true;\n setOpen(false);\n }\n };\n\n /** Parse + apply one typed endpoint, keeping `from <= to`. */\n const commitField = (which: 'from' | 'to', text: string) => {\n const setInvalid = which === 'from' ? setStartInvalid : setEndInvalid;\n if (text.trim() === '') {\n setInvalid(false);\n updateDraft(\n which === 'from'\n ? { from: undefined, to: draft.to }\n : { from: draft.from, to: undefined },\n );\n return;\n }\n const parsed = fnsParse(text, dateFormat, new Date(), { locale });\n if (!isValid(parsed) || isDateBlocked(parsed)) {\n setInvalid(true);\n return;\n }\n setInvalid(false);\n let next: DateRangeValue;\n if (which === 'from') {\n next =\n draft.to && isAfter(parsed, draft.to)\n ? { from: parsed, to: undefined }\n : { from: parsed, to: draft.to };\n } else {\n next =\n draft.from && isAfter(draft.from, parsed)\n ? { from: parsed, to: undefined }\n : { from: draft.from, to: parsed };\n }\n setMonth(parsed);\n updateDraft(next);\n };\n\n const handleFieldKeyDown =\n (which: 'from' | 'to') => (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n commitField(which, which === 'from' ? startText : endText);\n }\n };\n\n const handleClear = () => {\n setDraft({});\n setStartInvalid(false);\n setEndInvalid(false);\n if (commitMode === 'auto') {\n emit({});\n }\n };\n\n // The draft can be applied when it is a COMPLETE range or fully EMPTY\n // (so Clear -> Apply can commit a cleared range) — but not a half-picked\n // `{ from, to:undefined }` partial.\n const draftComplete = Boolean(draft.from && draft.to);\n const draftEmpty = !draft.from && !draft.to;\n // Block the commit while a typed field is showing a parse error, so Apply\n // can't silently save the last-valid range while the user sees an error.\n const canApply =\n (draftComplete || draftEmpty) && !startInvalid && !endInvalid;\n\n const handleApply = () => {\n if (canApply) {\n emit(draft);\n // Commit the draft compare toggle (apply-mode defers it here) and\n // report it once, with its resolved range, alongside the range Apply.\n if (compare && draftCompare !== (committedCompare ?? false)) {\n setCommittedCompare(draftCompare);\n onCompareChange?.(\n draftCompare,\n draftCompare ? computeCompareRange(draft) : undefined,\n );\n }\n onApply?.(draft, applyMeta(draft));\n }\n committingRef.current = true;\n setOpen(false);\n };\n\n const handleDone = () => {\n committingRef.current = true;\n setOpen(false);\n };\n\n // Programmatic `setOpen(false)` does NOT call Radix's `onOpenChange`, so the\n // Cancel button reverts explicitly (only Escape / outside-dismiss flows\n // through `handleOpenChange`).\n const handleCancel = () => {\n if (commitMode === 'apply') {\n onCancel?.();\n }\n setDraft(currentValue);\n setDraftCompare(committedCompare ?? false);\n setOpen(false);\n };\n\n const handleOpenChange = (next: boolean) => {\n if (next) {\n // Opening — the draft is seeded by the `open` effect below (which also\n // covers opening via the imperative handle / agent, not just Radix).\n committingRef.current = false;\n setOpen(true);\n return;\n }\n // Closing. A dismiss (Escape / outside-click) that is NOT one of our\n // commit buttons reverts the draft in `apply` mode.\n if (commitMode === 'apply' && !committingRef.current) {\n onCancel?.();\n }\n setDraft(currentValue);\n setDraftCompare(committedCompare ?? false);\n committingRef.current = false;\n setOpen(false);\n };\n\n // A preset is \"active\" when the committed range matches the range it would\n // produce (same calendar day at both ends) — so the highlighted band and\n // its shortcut visibly agree, and a custom range simply matches nothing.\n const isPresetActive = (preset: DateRangePreset): boolean => {\n if (!currentValue.from || !currentValue.to) return false;\n const range = preset.range();\n return (\n isSameDay(range.from, currentValue.from) &&\n isSameDay(range.to, currentValue.to)\n );\n };\n\n // Dedup coincident presets: when several presets resolve to the SAME\n // committed range (e.g. \"This month\" and \"This quarter\" both start on the\n // 1st in a quarter's first month), only the FIRST in consumer order is\n // marked active — one predictable highlight. This is the same first-match\n // rule `getActivePreset()` already reports, so the visual state and the\n // imperative handle never diverge. `effectivePresets` is the flat consumer\n // array; `presetGroups` regroups the SAME object references for display, so\n // an object-identity compare in the render loop is sound within a render.\n const activePreset = effectivePresets.find(isPresetActive);\n\n // Trigger label reflects the COMMITTED value (locale-aware short format).\n const displayText = (() => {\n if (currentValue.from && currentValue.to) {\n const fromStr = fnsFormat(currentValue.from, dateFormat, { locale });\n const toStr = fnsFormat(currentValue.to, dateFormat, { locale });\n return `${fromStr} – ${toStr}`;\n }\n if (currentValue.from) {\n return fnsFormat(currentValue.from, dateFormat, { locale });\n }\n return '';\n })();\n\n // Which endpoint the next calendar click will set — drives the field ring\n // and the guidance line.\n const activeEnd: 'start' | 'end' | null = !draft.from\n ? 'start'\n : !draft.to\n ? 'end'\n : null;\n\n // Footer live-region text: selection guidance until complete, then the full\n // localised summary (dates + day count), never a bare number.\n const summaryText = (() => {\n if (!draft.from) {\n return t('inputs.dateRangePicker.selectStart', 'Select start date');\n }\n if (!draft.to) {\n return t('inputs.dateRangePicker.selectEnd', 'Select end date');\n }\n // Compose the summary with a PLURALISED day count (via the `days_*`\n // plural keys) so the live region reads \"… · 1 day\" / \"… · 15 days\"\n // grammatically, rather than the \"{{count}} day(s)\" literal.\n const days = differenceInCalendarDays(draft.to, draft.from) + 1;\n const fromStr = fnsFormat(draft.from, dateFormat, { locale });\n const toStr = fnsFormat(draft.to, dateFormat, { locale });\n const daysLabel = t('inputs.dateRangePicker.days', { count: days });\n return `${fromStr} – ${toStr} · ${daysLabel}`;\n })();\n\n // A live pointer to the render-scoped values the imperative handle reads,\n // so the handle object stays STABLE (agent registers once) while always\n // reflecting current state.\n const liveRef = useRef({\n currentValue,\n open,\n getGranularity: (): DateRangeGranularity | undefined =>\n granularityEnabled ? resolvedGranularity : undefined,\n setGranularity: (g: DateRangeGranularity): void =>\n setGranularity(clampGranularity(g)),\n getCompareRange: (): DateRangeValue | undefined =>\n committedCompareActive ? computeCompareRange(currentValue) : undefined,\n // Imperative/agent path commits directly (like setRange/clear), so\n // toggle_compare reflects in getCompareRange immediately — not a draft.\n setCompare: (enabled: boolean): void => {\n setDraftCompare(enabled);\n setCommittedCompare(enabled);\n onCompareChange?.(\n enabled,\n enabled ? computeCompareRange(currentValue) : undefined,\n );\n },\n getActivePreset: (): string | undefined => activePreset?.label,\n getActiveEnd: (): 'start' | 'end' | null => activeEnd,\n apply: () => handleApply(),\n cancel: () => handleCancel(),\n });\n liveRef.current = {\n currentValue,\n open,\n getGranularity: () =>\n granularityEnabled ? resolvedGranularity : undefined,\n setGranularity: (g: DateRangeGranularity) =>\n setGranularity(clampGranularity(g)),\n getCompareRange: () =>\n committedCompareActive ? computeCompareRange(currentValue) : undefined,\n setCompare: (enabled: boolean) => {\n setDraftCompare(enabled);\n setCommittedCompare(enabled);\n onCompareChange?.(\n enabled,\n enabled ? computeCompareRange(currentValue) : undefined,\n );\n },\n getActivePreset: () => activePreset?.label,\n getActiveEnd: () => activeEnd,\n apply: () => handleApply(),\n cancel: () => handleCancel(),\n };\n\n const handle = useMemo<DateRangePickerHandle>(\n () => ({\n getRange: () => liveRef.current.currentValue,\n setRange: (range) => emit(range),\n clear: () => emit({}),\n isOpen: () => liveRef.current.open,\n open: () => setOpen(true),\n close: () => setOpen(false),\n getGranularity: () => liveRef.current.getGranularity(),\n setGranularity: (g) => liveRef.current.setGranularity(g),\n getCompareRange: () => liveRef.current.getCompareRange(),\n setCompare: (enabled) => liveRef.current.setCompare(enabled),\n getActivePreset: () => liveRef.current.getActivePreset(),\n getActiveEnd: () => liveRef.current.getActiveEnd(),\n apply: () => liveRef.current.apply(),\n cancel: () => liveRef.current.cancel(),\n }),\n [emit],\n );\n\n useImperativeHandle(ref, () => handle, [handle]);\n useAgentRegistration(dateRangePickerAgent, handle, effectiveId);\n\n const fieldState = (which: 'from' | 'to') => {\n if (which === 'from') {\n return startInvalid\n ? 'error'\n : activeEnd === 'start'\n ? 'active'\n : 'default';\n }\n return endInvalid ? 'error' : activeEnd === 'end' ? 'active' : 'default';\n };\n\n return (\n <Popover.Root open={open} onOpenChange={handleOpenChange}>\n <div\n className={className}\n data-component=\"date-range-picker\"\n data-component-id={effectiveId}\n >\n <Popover.Trigger asChild disabled={effectiveDisabled}>\n {/* eslint-disable-next-line jsx-a11y/role-supports-aria-props -- FormField propagates invalid state to its trigger; users rely on this for validation announcements */}\n <button\n ref={triggerRef}\n id={effectiveId}\n type=\"button\"\n disabled={effectiveDisabled}\n aria-describedby={ctx.describedBy || undefined}\n aria-invalid={ctx.invalid || undefined}\n // `aria-required` is not valid on `<button>` per ARIA 1.2 — the\n // FormField label's visible asterisk + `aria-describedby` link\n // communicate the required state instead.\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n className={triggerVariants({ size, tone: effectiveTone, chrome })}\n >\n <span\n className={[\n // Truncate so a long value/placeholder ellipsises instead of\n // running under the absolutely-positioned calendar icon (the\n // trigger reserves the icon's space via `pe-10`). `min-w-0`\n // lets the flex child shrink below its content width so\n // `truncate` engages.\n 'ds:min-w-0 ds:flex-1 ds:truncate ds:text-start',\n displayText\n ? 'ds:text-foreground'\n : 'ds:text-muted-foreground',\n ].join(' ')}\n >\n {displayText || placeholderText}\n </span>\n <span\n aria-hidden=\"true\"\n className={calendarIconVariants({ size })}\n >\n <CalendarDays />\n </span>\n </button>\n </Popover.Trigger>\n\n <Popover.Portal>\n <Popover.Content\n sideOffset={4}\n align=\"start\"\n role=\"dialog\"\n // Name the dialog with a direct `aria-label` rather than\n // `aria-labelledby` to the header title — the cross-reference did\n // not resolve reliably on the Radix-portaled content in a real\n // browser (axe `aria-dialog-name`), whereas a literal label is\n // read directly. The visible `<h2>` title still repeats it.\n aria-label={t(\n 'inputs.dateRangePicker.title',\n 'Select date range',\n )}\n /* Portaled outside [data-component] scope — needs its own\n marker for the kit's font/scrollbar/cascade baselines\n (same idiom as `select-content` / `date-picker-content`). */\n data-component=\"date-range-picker-content\"\n className={popoverContentVariants()}\n >\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n {/* Header — title + editable Start / End endpoints */}\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n <div className=\"ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <h2\n id={titleId}\n className=\"type-body-sm ds:font-semibold ds:text-foreground\"\n >\n {t('inputs.dateRangePicker.title', 'Select date range')}\n </h2>\n {granularityEnabled ? (\n <RadioGroup.Root\n value={resolvedGranularity}\n onValueChange={(v) =>\n setGranularity(\n clampGranularity(v as DateRangeGranularity),\n )\n }\n aria-label={t(\n 'inputs.dateRangePicker.granularityLabel',\n 'Granularity',\n )}\n className={granularityGroupVariants()}\n >\n {visibleGranularityOptions.map((o) => (\n <RadioGroup.Item\n key={o.value}\n value={o.value}\n className={granularityItemVariants()}\n >\n {t(\n `inputs.dateRangePicker.granularity.${o.value}`,\n o.fallback,\n )}\n </RadioGroup.Item>\n ))}\n </RadioGroup.Root>\n ) : null}\n </div>\n <div className=\"ds:flex ds:items-end ds:gap-[var(--spacing-xs)]\">\n <div className=\"ds:flex ds:min-w-0 ds:flex-1 ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <label\n htmlFor={startFieldId}\n className=\"ds:text-[length:var(--font-size-xs)] ds:text-muted-foreground\"\n >\n {t('inputs.dateRangePicker.startLabel', 'Start date')}\n </label>\n <input\n id={startFieldId}\n type=\"text\"\n inputMode=\"numeric\"\n value={startText}\n placeholder={dateFormat.toUpperCase()}\n aria-invalid={startInvalid || undefined}\n aria-describedby={startInvalid ? errorId : undefined}\n onChange={(e: ChangeEvent<HTMLInputElement>) =>\n setStartText(e.target.value)\n }\n onBlur={(e) => commitField('from', e.target.value)}\n onKeyDown={handleFieldKeyDown('from')}\n className={rangeFieldVariants({\n state: fieldState('from'),\n })}\n />\n </div>\n <span\n aria-hidden=\"true\"\n className=\"ds:pb-[var(--spacing-xs)] ds:text-muted-foreground\"\n >\n {'–'}\n </span>\n <div className=\"ds:flex ds:min-w-0 ds:flex-1 ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <label\n htmlFor={endFieldId}\n className=\"ds:text-[length:var(--font-size-xs)] ds:text-muted-foreground\"\n >\n {t('inputs.dateRangePicker.endLabel', 'End date')}\n </label>\n <input\n id={endFieldId}\n type=\"text\"\n inputMode=\"numeric\"\n value={endText}\n placeholder={dateFormat.toUpperCase()}\n aria-invalid={endInvalid || undefined}\n aria-describedby={endInvalid ? errorId : undefined}\n onChange={(e: ChangeEvent<HTMLInputElement>) =>\n setEndText(e.target.value)\n }\n onBlur={(e) => commitField('to', e.target.value)}\n onKeyDown={handleFieldKeyDown('to')}\n className={rangeFieldVariants({\n state: fieldState('to'),\n })}\n />\n </div>\n </div>\n {startInvalid || endInvalid ? (\n <p\n id={errorId}\n role=\"alert\"\n className=\"ds:text-[length:var(--font-size-xs)] ds:text-destructive\"\n >\n {t(\n 'inputs.dateRangePicker.invalidDate',\n 'Enter a date as {{format}}',\n {\n format: dateFormat.toUpperCase(),\n },\n )}\n </p>\n ) : null}\n </div>\n\n {/* Body — presets + dual calendar */}\n <div className=\"ds:flex ds:gap-[var(--spacing-md)]\">\n {effectivePresets.length > 0 ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:min-w-[8rem] ds:border-e ds:border-border ds:pe-[var(--spacing-md)]\">\n {presetGroups.map((grp, gi) => {\n const headingId = `${titleId}-pg${gi}`;\n const buttons = grp.presets.map((preset) => {\n // Identity compare against the single deduped active\n // preset — coincident presets collapse to one highlight.\n const active = preset === activePreset;\n return (\n <button\n key={preset.label}\n type=\"button\"\n aria-pressed={active}\n onClick={() => handlePresetClick(preset)}\n className={presetButtonVariants({ active })}\n >\n <span className=\"ds:min-w-0 ds:truncate\">\n {preset.label}\n </span>\n {active ? (\n <Check\n aria-hidden=\"true\"\n className=\"ds:size-4 ds:shrink-0 ds:text-primary\"\n />\n ) : null}\n </button>\n );\n });\n // A named group is a labelled `role=\"group\"` so the\n // visual grouping is conveyed to assistive tech.\n if (hasPresetGroups && grp.group) {\n return (\n <div\n key={grp.group}\n role=\"group\"\n aria-labelledby={headingId}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\"\n >\n <div\n id={headingId}\n className={presetGroupHeadingVariants()}\n >\n {grp.group}\n </div>\n {buttons}\n </div>\n );\n }\n return (\n <Fragment key={grp.group || '_ungrouped'}>\n {buttons}\n </Fragment>\n );\n })}\n </div>\n ) : null}\n\n <div role=\"region\" aria-labelledby={gridLabelId}>\n <span id={gridLabelId} className=\"ds:sr-only\">\n {t('inputs.dateRangePicker.gridLabel', 'Calendar')}\n </span>\n <DayPicker\n mode=\"range\"\n selected={\n draft.from\n ? { from: draft.from, to: draft.to }\n : undefined\n }\n onSelect={handleRangeSelect}\n month={month}\n onMonthChange={setMonth}\n numberOfMonths={numberOfMonths}\n weekStartsOn={weekStartsOn}\n disabled={disabledMatcher}\n locale={locale}\n showOutsideDays\n fixedWeeks\n pagedNavigation\n classNames={calendarClassNames}\n modifiers={compareModifiers}\n modifiersClassNames={calendarCompareModifierClassNames}\n components={{ Chevron: CalendarChevron }}\n />\n </div>\n </div>\n\n {/* Compare-to-previous-period row (Range Cockpit) */}\n {compare ? (\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-sm)] ds:border-t ds:border-border ds:pt-[var(--spacing-sm)]\">\n <Switch\n id={compareId}\n label={t(\n 'inputs.dateRangePicker.compareLabel',\n 'Compare to previous period',\n )}\n size=\"sm\"\n checked={draftCompare}\n onCheckedChange={handleCompareToggle}\n />\n {draftCompareActive && draftCompareRange ? (\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)] ds:text-[length:var(--font-size-xs)] ds:text-muted-foreground\">\n <span\n aria-hidden=\"true\"\n className=\"ds:inline-block ds:size-3 ds:rounded-[calc(var(--radius-sm)-2px)] ds:bg-[var(--calendar-compare-fill)] ds:border-b-2 ds:border-dashed ds:border-b-[var(--calendar-compare-edge)]\"\n />\n {`${fnsFormat(draftCompareRange.from as Date, dateFormat, { locale })} – ${fnsFormat(draftCompareRange.to as Date, dateFormat, { locale })}`}\n </span>\n ) : null}\n {draftCompare ? (\n <RadioGroup.Root\n value={compareMode}\n onValueChange={handleCompareModeChange}\n aria-label={t(\n 'inputs.dateRangePicker.compareModeLabel',\n 'Comparison period',\n )}\n className={`${granularityGroupVariants()} ds:ms-auto`}\n >\n {COMPARE_MODE_OPTIONS.map((o) => (\n <RadioGroup.Item\n key={o.value}\n value={o.value}\n className={granularityItemVariants()}\n >\n {t(\n `inputs.dateRangePicker.compareMode.${o.value}`,\n o.fallback,\n )}\n </RadioGroup.Item>\n ))}\n </RadioGroup.Root>\n ) : null}\n </div>\n ) : null}\n\n {/* Footer — persistent live summary + actions */}\n <div className=\"ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-md)] ds:border-t ds:border-border ds:pt-[var(--spacing-sm)]\">\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"type-body-sm ds:min-w-0 ds:truncate ds:text-muted-foreground\"\n >\n {summaryText}\n </div>\n <div className=\"ds:flex ds:shrink-0 ds:gap-[var(--spacing-xs)]\">\n {showClear ? (\n <button\n type=\"button\"\n onClick={handleClear}\n className={rangeActionVariants({ intent: 'ghost' })}\n >\n {t('inputs.dateRangePicker.clear', 'Clear')}\n </button>\n ) : null}\n {commitMode === 'apply' ? (\n <>\n <button\n type=\"button\"\n onClick={handleCancel}\n className={rangeActionVariants({ intent: 'ghost' })}\n >\n {t('inputs.dateRangePicker.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n disabled={!canApply}\n onClick={handleApply}\n className={rangeActionVariants({ intent: 'primary' })}\n >\n {t('inputs.dateRangePicker.apply', 'Apply')}\n </button>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={handleDone}\n className={rangeActionVariants({ intent: 'primary' })}\n >\n {t('inputs.dateRangePicker.done', 'Done')}\n </button>\n )}\n </div>\n </div>\n </div>\n </Popover.Content>\n </Popover.Portal>\n </div>\n </Popover.Root>\n );\n },\n);\n\nDateRangePicker.displayName = 'DateRangePicker';\n"],"names":["subWeeks","date","amount","options","addWeeks","subYears","addYears","dateRangePickerAgent","handle","range","_a","_b","compare","args","GRANULARITY_OPTIONS","COMPARE_MODE_OPTIONS","DateRangePicker","forwardRef","value","defaultValue","onChange","commitMode","onApply","onCancel","showClear","numberOfMonths","granularityProp","defaultGranularity","onGranularityChange","allowedGranularities","compareEnabled","defaultCompare","defaultCompareMode","onCompareChange","weekStartsOn","minDate","maxDate","presetsProp","mergePresets","size","chrome","disabled","placeholder","className","id","ref","t","i18n","useTranslation","ctx","useFormField","effectiveId","effectiveDisabled","effectiveTone","locale","useDateFnsLocale","dateFormat","getShortDateFormat","placeholderText","currentValueRaw","setDateRangeValue","useControllableState","currentValue","open","setOpen","useState","draft","setDraft","month","setMonth","startText","setStartText","endText","setEndText","startInvalid","setStartInvalid","endInvalid","setEndInvalid","granularityEnabled","allowedGranularitySet","visibleGranularityOptions","o","clampGranularity","g","granularity","setGranularity","resolvedGranularity","committedCompare","setCommittedCompare","draftCompare","setDraftCompare","compareMode","setCompareMode","triggerRef","useRef","committingRef","titleId","useId","startFieldId","endFieldId","gridLabelId","errorId","compareId","emit","useCallback","disabledMatcher","buildDisabledMatcher","isDateBlocked","d","isBefore","isAfter","useEffect","fnsFormat","defaultPresets","today","yesterday","subDays","startOfWeek","endOfWeek","lastWeekDay","startOfMonth","endOfMonth","effectivePresets","presetGroups","order","byGroup","preset","key","hasPresetGroups","computeCompareRange","base","mode","length","differenceInCalendarDays","to","draftCompareActive","committedCompareActive","draftCompareRange","compareModifiers","from","mods","addDays","applyMeta","meta","handleCompareModeChange","v","handleCompareToggle","next","updateDraft","handleRangeSelect","handlePresetClick","commitField","which","text","setInvalid","parsed","fnsParse","isValid","handleFieldKeyDown","e","handleClear","draftComplete","draftEmpty","canApply","handleApply","handleDone","handleCancel","handleOpenChange","isPresetActive","isSameDay","activePreset","displayText","fromStr","toStr","activeEnd","summaryText","days","daysLabel","liveRef","enabled","useMemo","useImperativeHandle","useAgentRegistration","fieldState","Popover","jsxs","jsx","triggerVariants","calendarIconVariants","CalendarDays","popoverContentVariants","RadioGroup","granularityGroupVariants","granularityItemVariants","rangeFieldVariants","grp","gi","headingId","buttons","active","presetButtonVariants","Check","presetGroupHeadingVariants","Fragment","DayPicker","calendarClassNames","calendarCompareModifierClassNames","CalendarChevron","Switch","rangeActionVariants"],"mappings":"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","x_google_ignoreList":[0,1]}