@alfadocs/ui-kit-debug 1.37.0 → 1.38.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 (101) hide show
  1. package/dist/_chunks/{agenda-tray-DYGCTIWB.js → agenda-tray-CG6jUjKc.js} +2 -2
  2. package/dist/_chunks/{agenda-tray-DYGCTIWB.js.map → agenda-tray-CG6jUjKc.js.map} +1 -1
  3. package/dist/_chunks/{anamnesis-summary-card-GYHa7VGp.js → anamnesis-summary-card-CclBbiXg.js} +2 -2
  4. package/dist/_chunks/{anamnesis-summary-card-GYHa7VGp.js.map → anamnesis-summary-card-CclBbiXg.js.map} +1 -1
  5. package/dist/_chunks/{appointment-timeline-ByfovxqR.js → appointment-timeline-D-rNwKpS.js} +2 -2
  6. package/dist/_chunks/{appointment-timeline-ByfovxqR.js.map → appointment-timeline-D-rNwKpS.js.map} +1 -1
  7. package/dist/_chunks/{appointment-tray-D6UyJHej.js → appointment-tray-CK2CULg_.js} +2 -2
  8. package/dist/_chunks/{appointment-tray-D6UyJHej.js.map → appointment-tray-CK2CULg_.js.map} +1 -1
  9. package/dist/_chunks/{balance-badge-cell-B0Z8Dwn8.js → balance-badge-cell-DHs5p0bA.js} +2 -2
  10. package/dist/_chunks/{balance-badge-cell-B0Z8Dwn8.js.map → balance-badge-cell-DHs5p0bA.js.map} +1 -1
  11. package/dist/_chunks/{booking-Dp-9qR6F.js → booking-x3J7sS2G.js} +3 -3
  12. package/dist/_chunks/{booking-Dp-9qR6F.js.map → booking-x3J7sS2G.js.map} +1 -1
  13. package/dist/_chunks/{calendar-workspace-BqKBj26r.js → calendar-workspace-Cc1LRVWY.js} +1665 -1480
  14. package/dist/_chunks/calendar-workspace-Cc1LRVWY.js.map +1 -0
  15. package/dist/_chunks/{data-table-BYGK-gUJ.js → data-table-Dy1lyumT.js} +2 -2
  16. package/dist/_chunks/{data-table-BYGK-gUJ.js.map → data-table-Dy1lyumT.js.map} +1 -1
  17. package/dist/_chunks/editable-description-7ptNpJEg.js +142 -0
  18. package/dist/_chunks/editable-description-7ptNpJEg.js.map +1 -0
  19. package/dist/_chunks/{file-manager-DvkFgj7d.js → file-manager-UlcYQv8n.js} +3 -3
  20. package/dist/_chunks/{file-manager-DvkFgj7d.js.map → file-manager-UlcYQv8n.js.map} +1 -1
  21. package/dist/_chunks/{message-tray-BcLyaO-G.js → message-tray-CXjVsKqp.js} +2 -2
  22. package/dist/_chunks/{message-tray-BcLyaO-G.js.map → message-tray-CXjVsKqp.js.map} +1 -1
  23. package/dist/_chunks/{note-timeline-CXfSV3yX.js → note-timeline-D2oPuQP6.js} +134 -73
  24. package/dist/_chunks/note-timeline-D2oPuQP6.js.map +1 -0
  25. package/dist/_chunks/notes-panel-CkbfDDzj.js +614 -0
  26. package/dist/_chunks/notes-panel-CkbfDDzj.js.map +1 -0
  27. package/dist/_chunks/{notification-tray-CJ8jOe-U.js → notification-tray-_NoChRKi.js} +2 -2
  28. package/dist/_chunks/{notification-tray-CJ8jOe-U.js.map → notification-tray-_NoChRKi.js.map} +1 -1
  29. package/dist/_chunks/{patient-summary-card-BzmJH9a-.js → patient-summary-card-B131Q7Bp.js} +2 -2
  30. package/dist/_chunks/{patient-summary-card-BzmJH9a-.js.map → patient-summary-card-B131Q7Bp.js.map} +1 -1
  31. package/dist/_chunks/{practice-results-DOcORTkP.js → practice-results-CBNRJNnY.js} +2 -2
  32. package/dist/_chunks/{practice-results-DOcORTkP.js.map → practice-results-CBNRJNnY.js.map} +1 -1
  33. package/dist/_chunks/{product-hub-panel-C0TO69P7.js → product-hub-panel-C3bMjscm.js} +2 -2
  34. package/dist/_chunks/{product-hub-panel-C0TO69P7.js.map → product-hub-panel-C3bMjscm.js.map} +1 -1
  35. package/dist/_chunks/{reviews-panel-BaHZb_ai.js → reviews-panel-Cy969x7q.js} +2 -2
  36. package/dist/_chunks/{reviews-panel-BaHZb_ai.js.map → reviews-panel-Cy969x7q.js.map} +1 -1
  37. package/dist/_chunks/{signature-request-Dej2g86d.js → signature-request-b-86AGlT.js} +2 -2
  38. package/dist/_chunks/{signature-request-Dej2g86d.js.map → signature-request-b-86AGlT.js.map} +1 -1
  39. package/dist/_chunks/{skeleton-CZbwyJAA.js → skeleton-B1KTdJ4N.js} +46 -33
  40. package/dist/_chunks/skeleton-B1KTdJ4N.js.map +1 -0
  41. package/dist/_chunks/{slot-grid-8Ko6thzz.js → slot-grid-Bk3GZL1p.js} +2 -2
  42. package/dist/_chunks/{slot-grid-8Ko6thzz.js.map → slot-grid-Bk3GZL1p.js.map} +1 -1
  43. package/dist/_chunks/{task-tray-CQ8UAR2j.js → task-tray-5co9Jr2L.js} +2 -2
  44. package/dist/_chunks/{task-tray-CQ8UAR2j.js.map → task-tray-5co9Jr2L.js.map} +1 -1
  45. package/dist/_chunks/{tasks-panel-CBGMtQ_f.js → tasks-panel-BpH-x7U3.js} +2 -2
  46. package/dist/_chunks/{tasks-panel-CBGMtQ_f.js.map → tasks-panel-BpH-x7U3.js.map} +1 -1
  47. package/dist/_chunks/treatment-receipt-dUm99mdn.js +160 -0
  48. package/dist/_chunks/treatment-receipt-dUm99mdn.js.map +1 -0
  49. package/dist/agent-catalog.json +1 -1
  50. package/dist/components/agenda-tray/index.js +1 -1
  51. package/dist/components/anamnesis-summary-card/index.js +1 -1
  52. package/dist/components/appointment-card/index.js +1 -1
  53. package/dist/components/appointment-timeline/index.js +1 -1
  54. package/dist/components/booking/index.js +1 -1
  55. package/dist/components/data-table/index.js +1 -1
  56. package/dist/components/editable-description/editable-description.d.ts +6 -0
  57. package/dist/components/editable-description/editable-description.d.ts.map +1 -1
  58. package/dist/components/editable-description/index.js +1 -1
  59. package/dist/components/file-manager/index.js +1 -1
  60. package/dist/components/message-tray/index.js +1 -1
  61. package/dist/components/note-timeline/index.js +1 -1
  62. package/dist/components/note-timeline/note-timeline.d.ts +8 -0
  63. package/dist/components/note-timeline/note-timeline.d.ts.map +1 -1
  64. package/dist/components/notes-panel/index.js +1 -1
  65. package/dist/components/notes-panel/notes-panel.d.ts.map +1 -1
  66. package/dist/components/notification-tray/index.js +1 -1
  67. package/dist/components/patient-summary-card/index.js +1 -1
  68. package/dist/components/patient-table/index.js +1 -1
  69. package/dist/components/practice-results/index.js +1 -1
  70. package/dist/components/product-hub-tray/index.js +1 -1
  71. package/dist/components/reviews-panel/index.js +1 -1
  72. package/dist/components/skeleton/index.d.ts +1 -1
  73. package/dist/components/skeleton/index.d.ts.map +1 -1
  74. package/dist/components/skeleton/index.js +3 -2
  75. package/dist/components/skeleton/skeleton.d.ts +12 -0
  76. package/dist/components/skeleton/skeleton.d.ts.map +1 -1
  77. package/dist/components/slot-grid/index.js +1 -1
  78. package/dist/components/task-tray/index.js +1 -1
  79. package/dist/components/tasks-panel/index.js +1 -1
  80. package/dist/components/treatment-receipt/index.js +1 -1
  81. package/dist/components/treatment-receipt/treatment-receipt.d.ts +8 -0
  82. package/dist/components/treatment-receipt/treatment-receipt.d.ts.map +1 -1
  83. package/dist/index.js +755 -754
  84. package/dist/patterns/calendar/calendar-appointment-details.d.ts +1 -1
  85. package/dist/patterns/calendar/calendar-appointment-details.d.ts.map +1 -1
  86. package/dist/patterns/calendar/calendar-editor-hydration.d.ts +0 -2
  87. package/dist/patterns/calendar/calendar-editor-hydration.d.ts.map +1 -1
  88. package/dist/patterns/calendar/calendar.sheets.d.ts.map +1 -1
  89. package/dist/patterns/calendar/index.js +1 -1
  90. package/dist/patterns/signature-request/index.js +1 -1
  91. package/dist/tokens.css +1 -1
  92. package/package.json +1 -1
  93. package/dist/_chunks/calendar-workspace-BqKBj26r.js.map +0 -1
  94. package/dist/_chunks/editable-description-BOfAeY3T.js +0 -112
  95. package/dist/_chunks/editable-description-BOfAeY3T.js.map +0 -1
  96. package/dist/_chunks/note-timeline-CXfSV3yX.js.map +0 -1
  97. package/dist/_chunks/notes-panel-4y5JioWN.js +0 -556
  98. package/dist/_chunks/notes-panel-4y5JioWN.js.map +0 -1
  99. package/dist/_chunks/skeleton-CZbwyJAA.js.map +0 -1
  100. package/dist/_chunks/treatment-receipt-D4kOLXOk.js +0 -94
  101. package/dist/_chunks/treatment-receipt-D4kOLXOk.js.map +0 -1
@@ -3,7 +3,7 @@ import { forwardRef as P, useRef as T, useImperativeHandle as q, useMemo as B }
3
3
  import { c as H } from "./index-D2ZczOXr.js";
4
4
  import { useTranslation as K } from "react-i18next";
5
5
  import { I as M } from "./icon-button-8xqRnkdG.js";
6
- import { S as A } from "./skeleton-CZbwyJAA.js";
6
+ import { S as A } from "./skeleton-B1KTdJ4N.js";
7
7
  import { E as D } from "./empty-state-X0sVPfVo.js";
8
8
  import { A as V } from "./agenda-card-DyQtHb9z.js";
9
9
  import { u as F } from "./registry-DvAUVLHh.js";
@@ -212,4 +212,4 @@ export {
212
212
  U as A,
213
213
  L as a
214
214
  };
215
- //# sourceMappingURL=agenda-tray-DYGCTIWB.js.map
215
+ //# sourceMappingURL=agenda-tray-CG6jUjKc.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agenda-tray-DYGCTIWB.js","sources":["../../src/components/agenda-tray/agenda-tray.agent.ts","../../src/components/agenda-tray/agenda-tray.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — AgendaTray. */\n/* */\n/* Tray-level surface: read appointment ids + lifecycle status (never */\n/* patient names / treatments), and route an activation through the */\n/* curated handle. AgendaTray is in-page (no open/close). */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { AgendaTrayHandle } from './agenda-tray';\n\nexport const agendaTrayAgent: AgentAdapter<AgendaTrayHandle> = {\n id: 'agenda-tray',\n // `pick` (NON-persisting): select_item activates / opens an appointment by\n // firing onOpenAppointment — it does not store a \"selected\" value on the tray\n // (there is no selection state slot), so this is a pick, not select_single.\n capabilities: ['pick'],\n state: {\n items: {\n type: 'Array<{ id: string; status: string }>',\n descriptionKey: 'ui.agent.agendaTray.state.items',\n description:\n 'Currently-displayed appointments. Ids + status only — never patient identity.',\n read: (handle) => handle.getItems(),\n },\n },\n actions: {\n select_item: {\n // Activates (opens) an appointment — pure navigation, no data mutation.\n safety: 'read',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: { type: 'string' } },\n required: ['id'],\n },\n descriptionKey: 'ui.agent.agendaTray.actions.selectItem',\n description: 'Activate the appointment with the given id.',\n examples: [\n { args: { id: 'appt-204' }, note: 'Open appointment appt-204.' },\n ],\n invoke: (handle, args: { id: string }) => {\n handle.selectItem(args.id);\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'agenda-tray',\n description: 'Marks the AgendaTray wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description:\n 'Sourced from the id prop. Required to address a specific tray from the agent.',\n },\n item: {\n attr: 'data-appointment-id',\n description:\n 'Stable appointment id emitted on each rendered AgendaCard inside the tray.',\n },\n },\n};\n","import {\n forwardRef,\n useImperativeHandle,\n useMemo,\n useRef,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { CalendarDays, Plus } from 'lucide-react';\nimport { IconButton } from '../button/icon-button';\nimport { Skeleton } from '../skeleton/skeleton';\nimport { EmptyState } from '../empty-state/empty-state';\nimport { AgendaCard, type AgendaItem } from '../agenda-card';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { agendaTrayAgent } from './agenda-tray.agent';\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst agendaTrayVariants = cva(\n [\n 'ds:flex ds:flex-col',\n 'ds:rounded-[var(--radius-md)] ds:shadow-[var(--shadow-card)]',\n 'ds:border ds:border-[color:var(--card-border)] ds:[.theme-accessible_&]:border-2',\n 'ds:bg-[color:var(--card)] ds:text-[color:var(--card-foreground)]',\n 'ds:overflow-hidden',\n ].join(' '),\n {\n variants: {\n size: {\n sm: '',\n md: '',\n },\n },\n defaultVariants: {\n size: 'sm',\n },\n },\n);\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\n/**\n * Curated imperative handle for AgendaTray. Exposed as the forwardRef\n * target so a future agent / MCP UI bridge can drive the tray without\n * touching the DOM. See `agenda-tray.agent.ts`.\n */\nexport interface AgendaTrayHandle {\n getItems: () => Array<{ id: string; status: string }>;\n selectItem: (id: string) => void;\n}\n\nexport interface AgendaTrayProps\n extends\n Omit<HTMLAttributes<HTMLElement>, 'onClick' | 'id'>,\n VariantProps<typeof agendaTrayVariants> {\n /**\n * Stable instance id. Surfaced on the root as `data-component-id` so\n * an agent / MCP UI bridge can address this specific tray.\n */\n id?: string;\n items: AgendaItem[];\n /** Section title — rendered in the header. Localised by the consumer. */\n title?: string;\n /** Optional small caption (e.g. localised date) shown next to the title. */\n caption?: string;\n size?: 'sm' | 'md';\n onOpenAppointment?: (item: AgendaItem) => void;\n onAddAppointment?: () => void;\n /** Localised label for the \"Add\" affordance. */\n addLabel?: string;\n loading?: boolean;\n emptyTitle?: string;\n emptyDescription?: string;\n /** Optional CTA wired into the empty state (EmptyState primaryAction). */\n emptyAction?: { label: string; onClick: () => void };\n /**\n * Pinned footer link (e.g. \"All upcoming appointments\"). Localised by\n * the consumer; rendered after the list, before the section closes.\n */\n viewAllLabel?: string;\n /** Fires when the footer link is activated. Required to render it. */\n onViewAll?: () => void;\n /**\n * Per-row trailing action toolbar, forwarded to each AgendaCard's\n * `actions` slot.\n */\n renderItemActions?: (item: AgendaItem) => ReactNode;\n /**\n * Constrain the list to the tray's own height and scroll inside it —\n * pair with a height/max-height on `className`. The scroll region is\n * keyboard-focusable so it stays operable without a pointer.\n */\n scrollable?: boolean;\n}\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport const AgendaTray = forwardRef<HTMLElement, AgendaTrayProps>(\n (\n {\n id,\n items,\n title,\n caption,\n size = 'sm',\n onOpenAppointment,\n onAddAppointment,\n addLabel,\n loading = false,\n emptyTitle,\n emptyDescription,\n emptyAction,\n viewAllLabel,\n onViewAll,\n renderItemActions,\n scrollable = false,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const heading = title ?? t('agendaTray.title', \"Today's appointments\");\n const resolvedAddLabel = addLabel ?? t('agendaTray.add', 'Add appointment');\n const isEmpty = !loading && items.length === 0;\n\n let body: ReactNode;\n if (loading) {\n body = (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-[var(--spacing-sm)]\">\n {[0, 1, 2].map((i) => (\n <Skeleton key={i} variant=\"rectangular\" height=\"2.5rem\" />\n ))}\n </div>\n );\n } else if (isEmpty) {\n body = (\n <div className=\"ds:p-[var(--spacing-md)]\">\n <EmptyState\n variant=\"first-use\"\n size=\"sm\"\n title={emptyTitle ?? t('agendaTray.empty.title', 'No appointments')}\n description={\n emptyDescription ??\n t(\n 'agendaTray.empty.description',\n 'New appointments will appear here.',\n )\n }\n primaryAction={emptyAction}\n />\n </div>\n );\n } else {\n body = (\n <ul\n className={[\n // flex-1 unconditionally so a footer pins to the tray's\n // bottom edge in equal-height grid rows.\n 'ds:flex ds:list-none ds:flex-col ds:flex-1 ds:min-h-0',\n // Keyboard-focusable when it scrolls (axe\n // scrollable-region-focusable) — pair with a height on the\n // tray's className.\n scrollable ? 'ds:overflow-y-auto' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- scrollable list must be keyboard focusable (axe scrollable-region-focusable)\n tabIndex={scrollable ? 0 : undefined}\n aria-label={scrollable ? heading : undefined}\n >\n {items.map((item) => (\n <AgendaCard\n key={item.id}\n item={item}\n size={size}\n onActivate={onOpenAppointment}\n actions={renderItemActions?.(item)}\n data-appointment-id={item.id}\n />\n ))}\n </ul>\n );\n }\n\n /* Curated imperative handle for agent integration. See\n * agenda-tray.agent.ts. */\n const itemsRef = useRef<AgendaItem[]>(items);\n itemsRef.current = items;\n\n const innerRef = useRef<HTMLElement>(null);\n useImperativeHandle(ref, () => innerRef.current as HTMLElement, []);\n\n const handle = useMemo<AgendaTrayHandle>(\n () => ({\n getItems: () =>\n itemsRef.current.map((i) => ({\n id: i.id,\n status: i.status ?? 'confirmed',\n })),\n selectItem: (targetId: string) => {\n const found = itemsRef.current.find((i) => i.id === targetId);\n if (found) onOpenAppointment?.(found);\n },\n }),\n [onOpenAppointment],\n );\n\n useAgentRegistration(agendaTrayAgent, handle, id);\n\n return (\n <section\n ref={innerRef}\n aria-label={heading}\n data-component=\"agenda-tray\"\n data-component-id={id}\n className={agendaTrayVariants({ size, className })}\n {...rest}\n >\n <header className=\"ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)] ds:p-[var(--spacing-sm)]\">\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)] ds:min-w-0\">\n <h3 className=\"ds:m-0 ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)] type-title-card\">\n <CalendarDays aria-hidden=\"true\" className=\"ds:size-4\" />\n {heading}\n </h3>\n {caption ? (\n <span className=\"type-eyebrow ds:text-[color:var(--muted-foreground)]\">\n {caption}\n </span>\n ) : null}\n </div>\n {onAddAppointment ? (\n <IconButton\n icon={<Plus />}\n intent=\"outline\"\n size=\"sm\"\n aria-label={resolvedAddLabel}\n onClick={onAddAppointment}\n />\n ) : null}\n </header>\n {body}\n {viewAllLabel && onViewAll && !isEmpty && !loading ? (\n <footer className=\"ds:[border-block-start:var(--border-width-sm)_solid_var(--card-border)] ds:p-[var(--spacing-sm)]\">\n <button\n type=\"button\"\n onClick={onViewAll}\n className={[\n 'type-label ds:text-[color:var(--link)] ds:underline ds:underline-offset-2 ds:hover:no-underline',\n 'ds:rounded-[var(--radius-sm)] ds:min-h-[var(--min-target-size)] ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n ].join(' ')}\n >\n {viewAllLabel}\n </button>\n </footer>\n ) : null}\n </section>\n );\n },\n);\n\nAgendaTray.displayName = 'AgendaTray';\n"],"names":["agendaTrayAgent","handle","args","agendaTrayVariants","cva","AgendaTray","forwardRef","id","items","title","caption","size","onOpenAppointment","onAddAppointment","addLabel","loading","emptyTitle","emptyDescription","emptyAction","viewAllLabel","onViewAll","renderItemActions","scrollable","className","rest","ref","t","useTranslation","heading","resolvedAddLabel","isEmpty","body","jsx","i","Skeleton","EmptyState","item","AgendaCard","itemsRef","useRef","innerRef","useImperativeHandle","useMemo","targetId","found","useAgentRegistration","jsxs","CalendarDays","IconButton","Plus"],"mappings":"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"}
1
+ {"version":3,"file":"agenda-tray-CG6jUjKc.js","sources":["../../src/components/agenda-tray/agenda-tray.agent.ts","../../src/components/agenda-tray/agenda-tray.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — AgendaTray. */\n/* */\n/* Tray-level surface: read appointment ids + lifecycle status (never */\n/* patient names / treatments), and route an activation through the */\n/* curated handle. AgendaTray is in-page (no open/close). */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { AgendaTrayHandle } from './agenda-tray';\n\nexport const agendaTrayAgent: AgentAdapter<AgendaTrayHandle> = {\n id: 'agenda-tray',\n // `pick` (NON-persisting): select_item activates / opens an appointment by\n // firing onOpenAppointment — it does not store a \"selected\" value on the tray\n // (there is no selection state slot), so this is a pick, not select_single.\n capabilities: ['pick'],\n state: {\n items: {\n type: 'Array<{ id: string; status: string }>',\n descriptionKey: 'ui.agent.agendaTray.state.items',\n description:\n 'Currently-displayed appointments. Ids + status only — never patient identity.',\n read: (handle) => handle.getItems(),\n },\n },\n actions: {\n select_item: {\n // Activates (opens) an appointment — pure navigation, no data mutation.\n safety: 'read',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: { type: 'string' } },\n required: ['id'],\n },\n descriptionKey: 'ui.agent.agendaTray.actions.selectItem',\n description: 'Activate the appointment with the given id.',\n examples: [\n { args: { id: 'appt-204' }, note: 'Open appointment appt-204.' },\n ],\n invoke: (handle, args: { id: string }) => {\n handle.selectItem(args.id);\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'agenda-tray',\n description: 'Marks the AgendaTray wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description:\n 'Sourced from the id prop. Required to address a specific tray from the agent.',\n },\n item: {\n attr: 'data-appointment-id',\n description:\n 'Stable appointment id emitted on each rendered AgendaCard inside the tray.',\n },\n },\n};\n","import {\n forwardRef,\n useImperativeHandle,\n useMemo,\n useRef,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { CalendarDays, Plus } from 'lucide-react';\nimport { IconButton } from '../button/icon-button';\nimport { Skeleton } from '../skeleton/skeleton';\nimport { EmptyState } from '../empty-state/empty-state';\nimport { AgendaCard, type AgendaItem } from '../agenda-card';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { agendaTrayAgent } from './agenda-tray.agent';\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst agendaTrayVariants = cva(\n [\n 'ds:flex ds:flex-col',\n 'ds:rounded-[var(--radius-md)] ds:shadow-[var(--shadow-card)]',\n 'ds:border ds:border-[color:var(--card-border)] ds:[.theme-accessible_&]:border-2',\n 'ds:bg-[color:var(--card)] ds:text-[color:var(--card-foreground)]',\n 'ds:overflow-hidden',\n ].join(' '),\n {\n variants: {\n size: {\n sm: '',\n md: '',\n },\n },\n defaultVariants: {\n size: 'sm',\n },\n },\n);\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\n/**\n * Curated imperative handle for AgendaTray. Exposed as the forwardRef\n * target so a future agent / MCP UI bridge can drive the tray without\n * touching the DOM. See `agenda-tray.agent.ts`.\n */\nexport interface AgendaTrayHandle {\n getItems: () => Array<{ id: string; status: string }>;\n selectItem: (id: string) => void;\n}\n\nexport interface AgendaTrayProps\n extends\n Omit<HTMLAttributes<HTMLElement>, 'onClick' | 'id'>,\n VariantProps<typeof agendaTrayVariants> {\n /**\n * Stable instance id. Surfaced on the root as `data-component-id` so\n * an agent / MCP UI bridge can address this specific tray.\n */\n id?: string;\n items: AgendaItem[];\n /** Section title — rendered in the header. Localised by the consumer. */\n title?: string;\n /** Optional small caption (e.g. localised date) shown next to the title. */\n caption?: string;\n size?: 'sm' | 'md';\n onOpenAppointment?: (item: AgendaItem) => void;\n onAddAppointment?: () => void;\n /** Localised label for the \"Add\" affordance. */\n addLabel?: string;\n loading?: boolean;\n emptyTitle?: string;\n emptyDescription?: string;\n /** Optional CTA wired into the empty state (EmptyState primaryAction). */\n emptyAction?: { label: string; onClick: () => void };\n /**\n * Pinned footer link (e.g. \"All upcoming appointments\"). Localised by\n * the consumer; rendered after the list, before the section closes.\n */\n viewAllLabel?: string;\n /** Fires when the footer link is activated. Required to render it. */\n onViewAll?: () => void;\n /**\n * Per-row trailing action toolbar, forwarded to each AgendaCard's\n * `actions` slot.\n */\n renderItemActions?: (item: AgendaItem) => ReactNode;\n /**\n * Constrain the list to the tray's own height and scroll inside it —\n * pair with a height/max-height on `className`. The scroll region is\n * keyboard-focusable so it stays operable without a pointer.\n */\n scrollable?: boolean;\n}\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport const AgendaTray = forwardRef<HTMLElement, AgendaTrayProps>(\n (\n {\n id,\n items,\n title,\n caption,\n size = 'sm',\n onOpenAppointment,\n onAddAppointment,\n addLabel,\n loading = false,\n emptyTitle,\n emptyDescription,\n emptyAction,\n viewAllLabel,\n onViewAll,\n renderItemActions,\n scrollable = false,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const heading = title ?? t('agendaTray.title', \"Today's appointments\");\n const resolvedAddLabel = addLabel ?? t('agendaTray.add', 'Add appointment');\n const isEmpty = !loading && items.length === 0;\n\n let body: ReactNode;\n if (loading) {\n body = (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-[var(--spacing-sm)]\">\n {[0, 1, 2].map((i) => (\n <Skeleton key={i} variant=\"rectangular\" height=\"2.5rem\" />\n ))}\n </div>\n );\n } else if (isEmpty) {\n body = (\n <div className=\"ds:p-[var(--spacing-md)]\">\n <EmptyState\n variant=\"first-use\"\n size=\"sm\"\n title={emptyTitle ?? t('agendaTray.empty.title', 'No appointments')}\n description={\n emptyDescription ??\n t(\n 'agendaTray.empty.description',\n 'New appointments will appear here.',\n )\n }\n primaryAction={emptyAction}\n />\n </div>\n );\n } else {\n body = (\n <ul\n className={[\n // flex-1 unconditionally so a footer pins to the tray's\n // bottom edge in equal-height grid rows.\n 'ds:flex ds:list-none ds:flex-col ds:flex-1 ds:min-h-0',\n // Keyboard-focusable when it scrolls (axe\n // scrollable-region-focusable) — pair with a height on the\n // tray's className.\n scrollable ? 'ds:overflow-y-auto' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- scrollable list must be keyboard focusable (axe scrollable-region-focusable)\n tabIndex={scrollable ? 0 : undefined}\n aria-label={scrollable ? heading : undefined}\n >\n {items.map((item) => (\n <AgendaCard\n key={item.id}\n item={item}\n size={size}\n onActivate={onOpenAppointment}\n actions={renderItemActions?.(item)}\n data-appointment-id={item.id}\n />\n ))}\n </ul>\n );\n }\n\n /* Curated imperative handle for agent integration. See\n * agenda-tray.agent.ts. */\n const itemsRef = useRef<AgendaItem[]>(items);\n itemsRef.current = items;\n\n const innerRef = useRef<HTMLElement>(null);\n useImperativeHandle(ref, () => innerRef.current as HTMLElement, []);\n\n const handle = useMemo<AgendaTrayHandle>(\n () => ({\n getItems: () =>\n itemsRef.current.map((i) => ({\n id: i.id,\n status: i.status ?? 'confirmed',\n })),\n selectItem: (targetId: string) => {\n const found = itemsRef.current.find((i) => i.id === targetId);\n if (found) onOpenAppointment?.(found);\n },\n }),\n [onOpenAppointment],\n );\n\n useAgentRegistration(agendaTrayAgent, handle, id);\n\n return (\n <section\n ref={innerRef}\n aria-label={heading}\n data-component=\"agenda-tray\"\n data-component-id={id}\n className={agendaTrayVariants({ size, className })}\n {...rest}\n >\n <header className=\"ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)] ds:p-[var(--spacing-sm)]\">\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)] ds:min-w-0\">\n <h3 className=\"ds:m-0 ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)] type-title-card\">\n <CalendarDays aria-hidden=\"true\" className=\"ds:size-4\" />\n {heading}\n </h3>\n {caption ? (\n <span className=\"type-eyebrow ds:text-[color:var(--muted-foreground)]\">\n {caption}\n </span>\n ) : null}\n </div>\n {onAddAppointment ? (\n <IconButton\n icon={<Plus />}\n intent=\"outline\"\n size=\"sm\"\n aria-label={resolvedAddLabel}\n onClick={onAddAppointment}\n />\n ) : null}\n </header>\n {body}\n {viewAllLabel && onViewAll && !isEmpty && !loading ? (\n <footer className=\"ds:[border-block-start:var(--border-width-sm)_solid_var(--card-border)] ds:p-[var(--spacing-sm)]\">\n <button\n type=\"button\"\n onClick={onViewAll}\n className={[\n 'type-label ds:text-[color:var(--link)] ds:underline ds:underline-offset-2 ds:hover:no-underline',\n 'ds:rounded-[var(--radius-sm)] ds:min-h-[var(--min-target-size)] ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n ].join(' ')}\n >\n {viewAllLabel}\n </button>\n </footer>\n ) : null}\n </section>\n );\n },\n);\n\nAgendaTray.displayName = 'AgendaTray';\n"],"names":["agendaTrayAgent","handle","args","agendaTrayVariants","cva","AgendaTray","forwardRef","id","items","title","caption","size","onOpenAppointment","onAddAppointment","addLabel","loading","emptyTitle","emptyDescription","emptyAction","viewAllLabel","onViewAll","renderItemActions","scrollable","className","rest","ref","t","useTranslation","heading","resolvedAddLabel","isEmpty","body","jsx","i","Skeleton","EmptyState","item","AgendaCard","itemsRef","useRef","innerRef","useImperativeHandle","useMemo","targetId","found","useAgentRegistration","jsxs","CalendarDays","IconButton","Plus"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { c as h } from "./index-D2ZczOXr.js";
4
4
  import { useTranslation as ie } from "react-i18next";
5
5
  import { B as H } from "./badge-BudgzTe_.js";
6
6
  import { C as oe } from "./card-DPmk26CL.js";
7
- import { S as k } from "./skeleton-CZbwyJAA.js";
7
+ import { S as k } from "./skeleton-B1KTdJ4N.js";
8
8
  import { a as le, T as ce } from "./tooltip-Cp-PLePW.js";
9
9
  import { u as me } from "./registry-DvAUVLHh.js";
10
10
  import { H as P } from "./heart-pulse-BCppJyN2.js";
@@ -365,4 +365,4 @@ export {
365
365
  we as A,
366
366
  ue as a
367
367
  };
368
- //# sourceMappingURL=anamnesis-summary-card-GYHa7VGp.js.map
368
+ //# sourceMappingURL=anamnesis-summary-card-CclBbiXg.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"anamnesis-summary-card-GYHa7VGp.js","sources":["../../src/components/anamnesis-summary-card/anamnesis-summary-card.agent.ts","../../src/components/anamnesis-summary-card/anamnesis-summary-card.tsx"],"sourcesContent":["import type { AgentAdapter } from '../../agent/types';\nimport type { AnamnesisSummaryCardHandle } from './anamnesis-summary-card';\n\n/* -------------------------------------------------------------------- */\n/* Agent adapter — AnamnesisSummaryCard */\n/* */\n/* Read-only adapter. Surfaces a STRUCTURAL snapshot of the anamnesis */\n/* summary — section / answer / highlighted / unanswered counts and the */\n/* consumer id only. Never a question label or an answer value, so an */\n/* agent can answer \"does this patient have highlighted answers?\" without */\n/* re-parsing the DOM and without leaking PHI (PRS §Security). */\n/* -------------------------------------------------------------------- */\n\nexport const anamnesisSummaryCardAgent: AgentAdapter<AnamnesisSummaryCardHandle> =\n {\n id: 'anamnesis-summary-card',\n // The card is read-only: the only handle method is a getSummary\n // projection, so there is no capability to declare.\n capabilities: [],\n state: {\n summary: {\n type: '{ id?: string; sectionCount: number; answerCount: number; highlightedCount: number; unansweredCount: number }',\n descriptionKey: 'ui.agent.anamnesisSummaryCard.state.summary',\n description:\n 'Structural summary of the anamnesis card — section, answer, highlighted and unanswered counts plus the consumer id. Never question labels or answer values.',\n read: (handle) => handle.getSummary(),\n },\n },\n actions: {},\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'anamnesis-summary-card',\n description: 'Marks the AnamnesisSummaryCard root region.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n item: {\n attr: 'data-answer-id',\n description:\n 'One per answer row (KeyValuePair); the value is the intake field key. Highlighted rows also carry data-highlighted.',\n },\n },\n };\n","import {\n forwardRef,\n useId,\n useImperativeHandle,\n useMemo,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { HeartPulse } from 'lucide-react';\nimport { Badge } from '../badge/badge';\nimport { Card } from '../card/card';\nimport { Skeleton } from '../skeleton/skeleton';\nimport { Tooltip, TooltipProvider } from '../tooltip/tooltip';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { anamnesisSummaryCardAgent } from './anamnesis-summary-card.agent';\n\n/* -------------------------------------------------------------------- */\n/* AnamnesisSummaryCard */\n/* */\n/* A read-only summary of a patient's anamnesis answers: every answered */\n/* question as a label/value row, grouped by section, two columns when */\n/* the card is wide. Practice-flagged (\"highlighted\") answers carry an */\n/* accent rail plus a HeartPulse chip whose tooltip says WHEN the answer */\n/* was recorded (and from which module when the card combines several). */\n/* */\n/* Templated on PatientSummaryCard: the consumer passes rows, the kit */\n/* owns layout, icons, i18n, and a11y. Used by the platform both for the */\n/* Overview tab (one card combining each module's latest interview) and */\n/* under the archive table (one card per interview). */\n/* -------------------------------------------------------------------- */\n\n/* -------------------------------------------------------------------- */\n/* CVA */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva('ds:w-full ds:text-[color:var(--foreground)]', {\n variants: {\n // Same `surface` vocabulary as PatientSummaryCard / ContactProfileCard,\n // plus `bare` for a host that already frames the card — an expanded\n // table row, where a card inside a row inside a table was three layers.\n surface: {\n flat: '',\n elevated: '',\n bare: '',\n },\n density: {\n comfortable: '',\n compact: '',\n },\n },\n defaultVariants: {\n surface: 'elevated',\n density: 'comfortable',\n },\n});\n\n/** Stack of sections inside the body — the density tunes the section gap. */\nconst sectionsVariants = cva('ds:flex ds:flex-col', {\n variants: {\n density: {\n comfortable: 'ds:gap-[var(--spacing-lg)]',\n compact: 'ds:gap-[var(--spacing-md)]',\n },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\n/** Card padding per density — overrides Card.Header / Body / Footer defaults. */\nconst headerPaddingVariants = cva('', {\n variants: {\n density: {\n comfortable: '',\n compact: 'ds:p-[var(--spacing-sm)] ds:pb-0',\n },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\nconst bodyPaddingVariants = cva('', {\n variants: {\n density: {\n comfortable: '',\n compact: 'ds:p-[var(--spacing-sm)]',\n },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\nconst footerPaddingVariants = cva('', {\n variants: {\n density: {\n comfortable: '',\n compact: 'ds:p-[var(--spacing-sm)] ds:pt-0',\n },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\n/* -------------------------------------------------------------------- */\n/* Public types */\n/* -------------------------------------------------------------------- */\n\nexport interface AnamnesisSummaryAnswer {\n /** Stable key (the intake field key). */\n id: string;\n /** Question label, already translated by the consumer. */\n label: string;\n /** Formatted answer. Strings preferred; ReactNode allowed. */\n value: ReactNode;\n /** Unit symbol rendered muted after the value (e.g. \"kg\"). */\n unit?: string;\n /** The practice's highlight rule fired for this answer. */\n highlighted?: boolean;\n /**\n * When the answer was recorded — `YYYY-MM-DD`, `YYYY-MM-DDTHH:mm` (LOCAL\n * wall time, no zone) or full ISO-8601. A bare date is never read as UTC.\n */\n reportedAtIso?: string;\n /** Where the answer came from when the card combines modules (module name). */\n sourceLabel?: string;\n}\n\nexport interface AnamnesisSummarySection {\n id: string;\n heading: string;\n answers: AnamnesisSummaryAnswer[];\n}\n\nexport interface AnamnesisSummaryCardHandle {\n /** Structural snapshot only — counts, never labels or values. */\n getSummary: () => {\n id?: string;\n sectionCount: number;\n answerCount: number;\n highlightedCount: number;\n unansweredCount: number;\n };\n}\n\nexport interface AnamnesisSummaryCardProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title'\n> {\n sections: AnamnesisSummarySection[];\n /** Card title (e.g. the module name). Falls back to the \"Anamnesis summary\" region label. */\n title?: string;\n /** Muted line under the title (e.g. the interview date-time, already formatted). */\n subtitle?: string;\n /** Questions the consumer omitted because they were unanswered — one muted footer line when > 0. */\n unansweredCount?: number;\n /** BCP-47 tag for the tooltip date; defaults to the active i18next language. */\n locale?: string;\n /** Highlighted answers sort to the top of their section. Default `true`. */\n highlightedFirst?: boolean;\n /**\n * Open the card with a \"Highlighted answers\" group collecting every\n * flagged answer, each with where and when it was recorded, before the\n * sections. Default `true`; absent when nothing is flagged.\n */\n highlightedGroup?: boolean;\n /**\n * Render ONLY the highlighted-answers group — no sections, no unanswered\n * footer — for a card whose sole job is the flagged answers. Empty copy\n * when nothing is flagged. Default `false`.\n */\n highlightedOnly?: boolean;\n /** `'elevated'` (default) — Card with shadow; `'flat'` — outlined Card. */\n surface?: 'flat' | 'elevated' | 'bare';\n /** `'comfortable'` (default) or `'compact'` (tighter row gap + paddings). */\n density?: 'comfortable' | 'compact';\n /** Render the skeleton loading placeholder. */\n loading?: boolean;\n /** Rendered at the inline-end of the heading row (e.g. an action menu). */\n actionSlot?: ReactNode;\n /** Agent-readiness consumer id, surfaced as `data-component-id`. */\n id?: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* Date helpers */\n/* -------------------------------------------------------------------- */\n\nconst DATE_ONLY_RE = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst LOCAL_DATETIME_RE =\n /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::(\\d{2}))?$/;\nconst CLOCK_TIME_RE = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}/;\n\n/**\n * Parse a stored stamp by fields when it carries no zone, so `2026-03-01`\n * stays 1 March in every viewer zone (`new Date('2026-03-01')` would read\n * it as UTC midnight and print 29 February west of Greenwich).\n */\nfunction parseReportedAt(iso: string): { date: Date; hasTime: boolean } | null {\n const day = DATE_ONLY_RE.exec(iso);\n if (day) {\n return {\n date: new Date(Number(day[1]), Number(day[2]) - 1, Number(day[3])),\n hasTime: false,\n };\n }\n const local = LOCAL_DATETIME_RE.exec(iso);\n if (local) {\n return {\n date: new Date(\n Number(local[1]),\n Number(local[2]) - 1,\n Number(local[3]),\n Number(local[4]),\n Number(local[5]),\n local[6] ? Number(local[6]) : 0,\n ),\n hasTime: true,\n };\n }\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) return null;\n return { date, hasTime: CLOCK_TIME_RE.test(iso) };\n}\n\nfunction formatReportedAt(iso: string, locale: string): string | null {\n const parsed = parseReportedAt(iso);\n if (!parsed) return null;\n const options: Intl.DateTimeFormatOptions = parsed.hasTime\n ? { dateStyle: 'medium', timeStyle: 'short' }\n : { dateStyle: 'medium' };\n try {\n return new Intl.DateTimeFormat(locale, options).format(parsed.date);\n } catch {\n // An unknown BCP-47 tag throws — fall back to the runtime default locale.\n return new Intl.DateTimeFormat(undefined, options).format(parsed.date);\n }\n}\n\n/* -------------------------------------------------------------------- */\n/* Styling fragments */\n/* -------------------------------------------------------------------- */\n\n/** Bare button carrying the chip — it exists only to give the tooltip a\n * keyboard-focusable trigger, so it inherits the chip's pill outline.\n * The 16px chip sits below `--min-target-size`, so the same `::before`\n * hit-area recipe as InfoTip `size=\"sm\"` expands the target: the full\n * 44/48px on touch, relaxing to the `--spacing-lg` 24px floor from `sm:` up. */\nconst CHIP_BUTTON_CLASS =\n 'ds:relative ds:inline-flex ds:appearance-none ds:border-0 ds:bg-transparent ds:p-0 ds:m-0 ds:cursor-default ds:rounded-[var(--radius-full)] ds:before:absolute ds:before:content-[\"\"] ds:before:inset-x-[calc((var(--min-target-size)-100%)/-2)] ds:before:inset-y-[calc((var(--min-target-size)-100%)/-2)] ds:sm:before:inset-x-[calc((var(--spacing-lg)-100%)/-2)] ds:sm:before:inset-y-[calc((var(--spacing-lg)-100%)/-2)] ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]';\n\n/** Every cell carries a start bar: a quiet edge in the border colour, the\n * accent when the answer is flagged. It gives a cell an edge without a box. */\nconst CELL_CLASS =\n 'ds:flex ds:min-w-0 ds:flex-col ds:gap-[var(--spacing-2xs)] ds:border-s-2 ds:ps-[var(--spacing-sm)]';\nconst CELL_BAR_CLASS = 'ds:border-[color:var(--border)]';\nconst CELL_BAR_ACCENT_CLASS = 'ds:border-[color:var(--accent)]';\n\nconst MUTED_LINE_CLASS =\n 'type-meta ds:m-0 ds:text-[color:var(--muted-foreground)]';\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\n/** Card's slot shape without its box — for `surface=\"bare\"`. */\nconst BareShell = Object.assign(\n ({ children }: { children?: ReactNode }) => <>{children}</>,\n {\n Header: ({\n children,\n className,\n }: {\n children?: ReactNode;\n className?: string;\n }) => <div className={className}>{children}</div>,\n Body: ({\n children,\n className,\n }: {\n children?: ReactNode;\n className?: string;\n }) => <div className={className}>{children}</div>,\n Footer: ({\n children,\n className,\n }: {\n children?: ReactNode;\n className?: string;\n }) => <div className={className}>{children}</div>,\n },\n);\n\nexport const AnamnesisSummaryCard = forwardRef<\n AnamnesisSummaryCardHandle,\n AnamnesisSummaryCardProps\n>(\n (\n {\n sections,\n title,\n subtitle,\n unansweredCount = 0,\n locale,\n highlightedFirst = true,\n highlightedGroup = true,\n highlightedOnly = false,\n surface = 'elevated',\n density = 'comfortable',\n loading = false,\n actionSlot,\n id,\n className,\n 'aria-label': ariaLabel,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const headingId = useId();\n const activeLocale = locale ?? i18n.language ?? 'en';\n\n // Drop empty sections and apply the highlighted-first order once per\n // input change; `sort` is stable so unhighlighted rows keep their order.\n const visibleSections = useMemo(\n () =>\n sections\n .filter((section) => section.answers.length > 0)\n .map((section) => ({\n ...section,\n answers: highlightedFirst\n ? [...section.answers].sort(\n (a, b) => Number(!!b.highlighted) - Number(!!a.highlighted),\n )\n : section.answers,\n })),\n [sections, highlightedFirst],\n );\n\n const answerCount = visibleSections.reduce(\n (sum, section) => sum + section.answers.length,\n 0,\n );\n const highlightedCount = visibleSections.reduce(\n (sum, section) =>\n sum + section.answers.filter((answer) => answer.highlighted).length,\n 0,\n );\n const sectionCount = visibleSections.length;\n // Every flagged answer in section order, for the opening group.\n const highlightedAnswers = visibleSections.flatMap((section) =>\n section.answers.filter((answer) => answer.highlighted),\n );\n // A flagged-answers-only card lists no sections at all.\n const listedSections = highlightedOnly ? [] : visibleSections;\n const recordedLine = (answer: AnamnesisSummaryAnswer): string | null => {\n const date = answer.reportedAtIso\n ? formatReportedAt(answer.reportedAtIso, activeLocale)\n : null;\n const parts = [answer.sourceLabel, date].filter((part): part is string =>\n Boolean(part),\n );\n return parts.length > 0 ? parts.join(' · ') : null;\n };\n\n // Imperative handle — structural snapshot only (counts + consumer id),\n // never a question label or an answer value.\n const handle = useMemo<AnamnesisSummaryCardHandle>(\n () => ({\n getSummary: () => ({\n id,\n sectionCount,\n answerCount,\n highlightedCount,\n unansweredCount,\n }),\n }),\n [id, sectionCount, answerCount, highlightedCount, unansweredCount],\n );\n useImperativeHandle(ref, () => handle, [handle]);\n useAgentRegistration(anamnesisSummaryCardAgent, handle, id);\n\n const regionLabel = t('anamnesisSummaryCard.regionLabel', {\n defaultValue: 'Anamnesis summary',\n });\n const resolvedTitle = title ?? regionLabel;\n const resolvedAriaLabel = ariaLabel ?? resolvedTitle;\n const highlightedText = t('anamnesisSummaryCard.highlighted', {\n defaultValue: 'Highlighted',\n });\n\n const titleClass =\n density === 'comfortable' ? 'type-title-card' : 'type-title-item';\n const rowGap = density === 'comfortable' ? 'md' : 'sm';\n\n /* ---------------------------------------------------------------- */\n /* Highlight chip */\n /* ---------------------------------------------------------------- */\n\n const renderChip = (answer: AnamnesisSummaryAnswer) => {\n const date = answer.reportedAtIso\n ? formatReportedAt(answer.reportedAtIso, activeLocale)\n : null;\n const tooltipLabel =\n date && answer.sourceLabel\n ? t('anamnesisSummaryCard.reportedOnFrom', {\n date,\n source: answer.sourceLabel,\n defaultValue: 'Highlighted on {{date}} · {{source}}',\n })\n : date\n ? t('anamnesisSummaryCard.reportedOn', {\n date,\n defaultValue: 'Highlighted on {{date}}',\n })\n : highlightedText;\n return (\n <Tooltip label={tooltipLabel} side=\"top\">\n <button\n type=\"button\"\n className={CHIP_BUTTON_CLASS}\n data-highlighted-chip=\"\"\n >\n {/* Icon only: the heart is the mark, the tooltip and the\n accessible name say why. */}\n <Badge\n variant=\"accentSoft\"\n size=\"sm\"\n aria-label={highlightedText}\n leading={<HeartPulse aria-hidden=\"true\" />}\n />\n </button>\n </Tooltip>\n );\n };\n\n /* ---------------------------------------------------------------- */\n /* Header */\n /* ---------------------------------------------------------------- */\n\n const header = (\n <div className=\"ds:flex ds:flex-wrap ds:items-start ds:gap-[var(--spacing-sm)]\">\n <div className=\"ds:flex ds:min-w-0 ds:flex-1 ds:flex-col ds:gap-[var(--spacing-2xs)]\">\n <h3\n id={headingId}\n className={`ds:m-0 ${titleClass} ds:text-[color:var(--foreground)]`}\n >\n {resolvedTitle}\n </h3>\n {subtitle ? <p className={MUTED_LINE_CLASS}>{subtitle}</p> : null}\n </div>\n {!loading && highlightedCount > 0 ? (\n <Badge\n variant=\"accentSoft\"\n size=\"sm\"\n leading={<HeartPulse aria-hidden=\"true\" />}\n className=\"ds:shrink-0\"\n >\n {t('anamnesisSummaryCard.highlightedCount', {\n count: highlightedCount,\n defaultValue: '{{count}} highlighted',\n })}\n </Badge>\n ) : null}\n {actionSlot ? (\n <div className=\"ds:ms-auto ds:flex ds:shrink-0 ds:items-center ds:gap-[var(--spacing-xs)]\">\n {actionSlot}\n </div>\n ) : null}\n </div>\n );\n\n /* ---------------------------------------------------------------- */\n /* Body */\n /* ---------------------------------------------------------------- */\n\n const skeletonBody = (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n <Skeleton variant=\"text\" width=\"40%\" />\n <Skeleton variant=\"text\" lines={4} />\n </div>\n );\n\n const emptyBody = (\n <p className={MUTED_LINE_CLASS}>\n {t('anamnesisSummaryCard.empty', {\n defaultValue: 'No answers recorded yet',\n })}\n </p>\n );\n\n /* ---------------------------------------------------------------- */\n /* Q&A cells */\n /* ---------------------------------------------------------------- */\n\n // Question above answer, one accent glyph each, two cells across when\n // the card is wide: a reader scans questions the way they were asked,\n // and a label|value pair doubled up made four ragged columns.\n const gridClass = [\n 'ds:m-0 ds:grid ds:grid-cols-1 ds:@[672px]/afields:grid-cols-2',\n 'ds:gap-x-[var(--spacing-lg)]',\n rowGap === 'md'\n ? 'ds:gap-y-[var(--spacing-md)]'\n : 'ds:gap-y-[var(--spacing-sm)]',\n ].join(' ');\n\n const renderAnswer = (\n answer: AnamnesisSummaryAnswer,\n opts: {\n recorded: boolean;\n chip: boolean;\n accentBar: boolean;\n keyPrefix: string;\n },\n ) => {\n const flagged = opts.chip && answer.highlighted;\n const recorded = opts.recorded ? recordedLine(answer) : null;\n return (\n <div\n key={`${opts.keyPrefix}${answer.id}`}\n data-answer-id={answer.id}\n data-highlighted={answer.highlighted ? '' : undefined}\n className={[\n CELL_CLASS,\n opts.accentBar || flagged ? CELL_BAR_ACCENT_CLASS : CELL_BAR_CLASS,\n ].join(' ')}\n >\n {/* The question leads: heavier and larger than its answer, the\n flag's heart ahead of it — a mark beside the question, never a\n box around the answer. */}\n <dt className=\"type-title-item ds:m-0 ds:flex ds:items-start ds:gap-[var(--spacing-xs)] ds:font-semibold ds:text-[color:var(--foreground)]\">\n {flagged ? renderChip(answer) : null}\n <span>{answer.label}</span>\n </dt>\n <dd className=\"type-body-sm ds:m-0 ds:flex ds:min-w-0 ds:flex-col ds:gap-[var(--spacing-2xs)] ds:text-[color:var(--foreground)]\">\n <span className=\"ds:inline-flex ds:flex-wrap ds:items-center ds:gap-x-[var(--spacing-xs)] ds:gap-y-[var(--spacing-2xs)]\">\n <span>{answer.value}</span>\n {answer.unit ? (\n <span className=\"ds:text-[color:var(--muted-foreground)]\">\n {answer.unit}\n </span>\n ) : null}\n </span>\n {recorded ? (\n <span className={MUTED_LINE_CLASS}>{recorded}</span>\n ) : null}\n </dd>\n </div>\n );\n };\n\n const sectionsBody = (\n <div\n role=\"group\"\n aria-label={t('anamnesisSummaryCard.sectionsLabel', {\n defaultValue: 'Anamnesis sections',\n })}\n className={sectionsVariants({ density })}\n >\n {/* Plain <div>, deliberately not a named <section>: a named\n <section> is a `region` landmark, and one per anamnesis section\n per card would drown the page-level landmarks. The <h4> already\n gives heading navigation into each section. */}\n {(highlightedGroup || highlightedOnly) &&\n highlightedAnswers.length > 0 ? (\n <div\n data-highlighted-group=\"\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n {/* The card's own title already says it when this is all it shows. */}\n {highlightedOnly ? null : (\n <h4 className=\"type-label ds:m-0 ds:font-semibold ds:text-[color:var(--foreground)]\">\n {t('anamnesisSummaryCard.highlightedGroup', {\n defaultValue: 'Highlighted answers',\n })}\n </h4>\n )}\n {/* Container-queried so the cells flow into two columns when the\n card is wide enough and stay single-column in a narrow rail —\n the same 672px PatientSummaryCard breaks at (px, not rem:\n container-query rem follows the host root font-size). */}\n <div className=\"ds:@container/afields\">\n <dl className={gridClass}>\n {highlightedAnswers.map((answer) =>\n renderAnswer(answer, {\n recorded: true,\n chip: false,\n accentBar: true,\n keyPrefix: 'highlighted-',\n }),\n )}\n </dl>\n </div>\n </div>\n ) : null}\n {listedSections.map((section) => (\n <div\n key={section.id}\n data-section-id={section.id}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n <h4 className=\"type-label ds:m-0 ds:font-semibold ds:text-[color:var(--foreground)]\">\n {section.heading}\n </h4>\n <div className=\"ds:@container/afields\">\n <dl className={gridClass}>\n {section.answers.map((answer) =>\n renderAnswer(answer, {\n recorded: false,\n chip: true,\n accentBar: false,\n keyPrefix: '',\n }),\n )}\n </dl>\n </div>\n </div>\n ))}\n </div>\n );\n\n const body = loading\n ? skeletonBody\n : visibleSections.length === 0 ||\n (highlightedOnly && highlightedAnswers.length === 0)\n ? emptyBody\n : sectionsBody;\n\n // `bare` drops the Card chrome and keeps its slots, so a host that\n // already frames the content (an expanded table row) gets one layer.\n const Shell = surface === 'bare' ? BareShell : Card;\n\n return (\n <div\n role=\"region\"\n aria-label={resolvedAriaLabel}\n aria-busy={loading || undefined}\n data-component=\"anamnesis-summary-card\"\n data-component-id={id}\n data-density={density}\n data-highlighted-only={highlightedOnly ? '' : undefined}\n id={id}\n className={rootVariants({ surface, density, className })}\n {...rest}\n >\n <Shell variant={surface === 'flat' ? 'outlined' : 'elevated'}>\n <Shell.Header className={headerPaddingVariants({ density })}>\n {header}\n </Shell.Header>\n {/* Own TooltipProvider so a host without one still gets the chip\n tooltips; Radix providers compose, so a host-level one also works. */}\n <TooltipProvider>\n <Shell.Body className={bodyPaddingVariants({ density })}>\n {body}\n </Shell.Body>\n </TooltipProvider>\n {!loading && !highlightedOnly && unansweredCount > 0 ? (\n <Shell.Footer className={footerPaddingVariants({ density })}>\n <p className={MUTED_LINE_CLASS}>\n {t('anamnesisSummaryCard.unanswered', {\n count: unansweredCount,\n defaultValue: '{{count}} questions not answered',\n })}\n </p>\n </Shell.Footer>\n ) : null}\n </Shell>\n </div>\n );\n },\n);\n\nAnamnesisSummaryCard.displayName = 'AnamnesisSummaryCard';\n\n/** Variant unions re-exported for downstream typing. */\nexport type AnamnesisSummaryCardSurface = NonNullable<\n VariantProps<typeof rootVariants>['surface']\n>;\nexport type AnamnesisSummaryCardDensity = NonNullable<\n VariantProps<typeof rootVariants>['density']\n>;\n"],"names":["anamnesisSummaryCardAgent","handle","rootVariants","cva","sectionsVariants","headerPaddingVariants","bodyPaddingVariants","footerPaddingVariants","DATE_ONLY_RE","LOCAL_DATETIME_RE","CLOCK_TIME_RE","parseReportedAt","iso","day","local","date","formatReportedAt","locale","parsed","options","CHIP_BUTTON_CLASS","CELL_CLASS","CELL_BAR_CLASS","CELL_BAR_ACCENT_CLASS","MUTED_LINE_CLASS","BareShell","children","className","jsx","AnamnesisSummaryCard","forwardRef","sections","title","subtitle","unansweredCount","highlightedFirst","highlightedGroup","highlightedOnly","surface","density","loading","actionSlot","id","ariaLabel","rest","ref","t","i18n","useTranslation","headingId","useId","activeLocale","visibleSections","useMemo","section","a","b","answerCount","sum","highlightedCount","answer","sectionCount","highlightedAnswers","listedSections","recordedLine","parts","part","useImperativeHandle","useAgentRegistration","regionLabel","resolvedTitle","resolvedAriaLabel","highlightedText","titleClass","rowGap","renderChip","tooltipLabel","Tooltip","Badge","HeartPulse","header","jsxs","skeletonBody","Skeleton","emptyBody","gridClass","renderAnswer","opts","flagged","recorded","sectionsBody","body","Shell","Card","TooltipProvider"],"mappings":"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"}
1
+ {"version":3,"file":"anamnesis-summary-card-CclBbiXg.js","sources":["../../src/components/anamnesis-summary-card/anamnesis-summary-card.agent.ts","../../src/components/anamnesis-summary-card/anamnesis-summary-card.tsx"],"sourcesContent":["import type { AgentAdapter } from '../../agent/types';\nimport type { AnamnesisSummaryCardHandle } from './anamnesis-summary-card';\n\n/* -------------------------------------------------------------------- */\n/* Agent adapter — AnamnesisSummaryCard */\n/* */\n/* Read-only adapter. Surfaces a STRUCTURAL snapshot of the anamnesis */\n/* summary — section / answer / highlighted / unanswered counts and the */\n/* consumer id only. Never a question label or an answer value, so an */\n/* agent can answer \"does this patient have highlighted answers?\" without */\n/* re-parsing the DOM and without leaking PHI (PRS §Security). */\n/* -------------------------------------------------------------------- */\n\nexport const anamnesisSummaryCardAgent: AgentAdapter<AnamnesisSummaryCardHandle> =\n {\n id: 'anamnesis-summary-card',\n // The card is read-only: the only handle method is a getSummary\n // projection, so there is no capability to declare.\n capabilities: [],\n state: {\n summary: {\n type: '{ id?: string; sectionCount: number; answerCount: number; highlightedCount: number; unansweredCount: number }',\n descriptionKey: 'ui.agent.anamnesisSummaryCard.state.summary',\n description:\n 'Structural summary of the anamnesis card — section, answer, highlighted and unanswered counts plus the consumer id. Never question labels or answer values.',\n read: (handle) => handle.getSummary(),\n },\n },\n actions: {},\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'anamnesis-summary-card',\n description: 'Marks the AnamnesisSummaryCard root region.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n item: {\n attr: 'data-answer-id',\n description:\n 'One per answer row (KeyValuePair); the value is the intake field key. Highlighted rows also carry data-highlighted.',\n },\n },\n };\n","import {\n forwardRef,\n useId,\n useImperativeHandle,\n useMemo,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { HeartPulse } from 'lucide-react';\nimport { Badge } from '../badge/badge';\nimport { Card } from '../card/card';\nimport { Skeleton } from '../skeleton/skeleton';\nimport { Tooltip, TooltipProvider } from '../tooltip/tooltip';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { anamnesisSummaryCardAgent } from './anamnesis-summary-card.agent';\n\n/* -------------------------------------------------------------------- */\n/* AnamnesisSummaryCard */\n/* */\n/* A read-only summary of a patient's anamnesis answers: every answered */\n/* question as a label/value row, grouped by section, two columns when */\n/* the card is wide. Practice-flagged (\"highlighted\") answers carry an */\n/* accent rail plus a HeartPulse chip whose tooltip says WHEN the answer */\n/* was recorded (and from which module when the card combines several). */\n/* */\n/* Templated on PatientSummaryCard: the consumer passes rows, the kit */\n/* owns layout, icons, i18n, and a11y. Used by the platform both for the */\n/* Overview tab (one card combining each module's latest interview) and */\n/* under the archive table (one card per interview). */\n/* -------------------------------------------------------------------- */\n\n/* -------------------------------------------------------------------- */\n/* CVA */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva('ds:w-full ds:text-[color:var(--foreground)]', {\n variants: {\n // Same `surface` vocabulary as PatientSummaryCard / ContactProfileCard,\n // plus `bare` for a host that already frames the card — an expanded\n // table row, where a card inside a row inside a table was three layers.\n surface: {\n flat: '',\n elevated: '',\n bare: '',\n },\n density: {\n comfortable: '',\n compact: '',\n },\n },\n defaultVariants: {\n surface: 'elevated',\n density: 'comfortable',\n },\n});\n\n/** Stack of sections inside the body — the density tunes the section gap. */\nconst sectionsVariants = cva('ds:flex ds:flex-col', {\n variants: {\n density: {\n comfortable: 'ds:gap-[var(--spacing-lg)]',\n compact: 'ds:gap-[var(--spacing-md)]',\n },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\n/** Card padding per density — overrides Card.Header / Body / Footer defaults. */\nconst headerPaddingVariants = cva('', {\n variants: {\n density: {\n comfortable: '',\n compact: 'ds:p-[var(--spacing-sm)] ds:pb-0',\n },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\nconst bodyPaddingVariants = cva('', {\n variants: {\n density: {\n comfortable: '',\n compact: 'ds:p-[var(--spacing-sm)]',\n },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\nconst footerPaddingVariants = cva('', {\n variants: {\n density: {\n comfortable: '',\n compact: 'ds:p-[var(--spacing-sm)] ds:pt-0',\n },\n },\n defaultVariants: { density: 'comfortable' },\n});\n\n/* -------------------------------------------------------------------- */\n/* Public types */\n/* -------------------------------------------------------------------- */\n\nexport interface AnamnesisSummaryAnswer {\n /** Stable key (the intake field key). */\n id: string;\n /** Question label, already translated by the consumer. */\n label: string;\n /** Formatted answer. Strings preferred; ReactNode allowed. */\n value: ReactNode;\n /** Unit symbol rendered muted after the value (e.g. \"kg\"). */\n unit?: string;\n /** The practice's highlight rule fired for this answer. */\n highlighted?: boolean;\n /**\n * When the answer was recorded — `YYYY-MM-DD`, `YYYY-MM-DDTHH:mm` (LOCAL\n * wall time, no zone) or full ISO-8601. A bare date is never read as UTC.\n */\n reportedAtIso?: string;\n /** Where the answer came from when the card combines modules (module name). */\n sourceLabel?: string;\n}\n\nexport interface AnamnesisSummarySection {\n id: string;\n heading: string;\n answers: AnamnesisSummaryAnswer[];\n}\n\nexport interface AnamnesisSummaryCardHandle {\n /** Structural snapshot only — counts, never labels or values. */\n getSummary: () => {\n id?: string;\n sectionCount: number;\n answerCount: number;\n highlightedCount: number;\n unansweredCount: number;\n };\n}\n\nexport interface AnamnesisSummaryCardProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title'\n> {\n sections: AnamnesisSummarySection[];\n /** Card title (e.g. the module name). Falls back to the \"Anamnesis summary\" region label. */\n title?: string;\n /** Muted line under the title (e.g. the interview date-time, already formatted). */\n subtitle?: string;\n /** Questions the consumer omitted because they were unanswered — one muted footer line when > 0. */\n unansweredCount?: number;\n /** BCP-47 tag for the tooltip date; defaults to the active i18next language. */\n locale?: string;\n /** Highlighted answers sort to the top of their section. Default `true`. */\n highlightedFirst?: boolean;\n /**\n * Open the card with a \"Highlighted answers\" group collecting every\n * flagged answer, each with where and when it was recorded, before the\n * sections. Default `true`; absent when nothing is flagged.\n */\n highlightedGroup?: boolean;\n /**\n * Render ONLY the highlighted-answers group — no sections, no unanswered\n * footer — for a card whose sole job is the flagged answers. Empty copy\n * when nothing is flagged. Default `false`.\n */\n highlightedOnly?: boolean;\n /** `'elevated'` (default) — Card with shadow; `'flat'` — outlined Card. */\n surface?: 'flat' | 'elevated' | 'bare';\n /** `'comfortable'` (default) or `'compact'` (tighter row gap + paddings). */\n density?: 'comfortable' | 'compact';\n /** Render the skeleton loading placeholder. */\n loading?: boolean;\n /** Rendered at the inline-end of the heading row (e.g. an action menu). */\n actionSlot?: ReactNode;\n /** Agent-readiness consumer id, surfaced as `data-component-id`. */\n id?: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* Date helpers */\n/* -------------------------------------------------------------------- */\n\nconst DATE_ONLY_RE = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst LOCAL_DATETIME_RE =\n /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::(\\d{2}))?$/;\nconst CLOCK_TIME_RE = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}/;\n\n/**\n * Parse a stored stamp by fields when it carries no zone, so `2026-03-01`\n * stays 1 March in every viewer zone (`new Date('2026-03-01')` would read\n * it as UTC midnight and print 29 February west of Greenwich).\n */\nfunction parseReportedAt(iso: string): { date: Date; hasTime: boolean } | null {\n const day = DATE_ONLY_RE.exec(iso);\n if (day) {\n return {\n date: new Date(Number(day[1]), Number(day[2]) - 1, Number(day[3])),\n hasTime: false,\n };\n }\n const local = LOCAL_DATETIME_RE.exec(iso);\n if (local) {\n return {\n date: new Date(\n Number(local[1]),\n Number(local[2]) - 1,\n Number(local[3]),\n Number(local[4]),\n Number(local[5]),\n local[6] ? Number(local[6]) : 0,\n ),\n hasTime: true,\n };\n }\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) return null;\n return { date, hasTime: CLOCK_TIME_RE.test(iso) };\n}\n\nfunction formatReportedAt(iso: string, locale: string): string | null {\n const parsed = parseReportedAt(iso);\n if (!parsed) return null;\n const options: Intl.DateTimeFormatOptions = parsed.hasTime\n ? { dateStyle: 'medium', timeStyle: 'short' }\n : { dateStyle: 'medium' };\n try {\n return new Intl.DateTimeFormat(locale, options).format(parsed.date);\n } catch {\n // An unknown BCP-47 tag throws — fall back to the runtime default locale.\n return new Intl.DateTimeFormat(undefined, options).format(parsed.date);\n }\n}\n\n/* -------------------------------------------------------------------- */\n/* Styling fragments */\n/* -------------------------------------------------------------------- */\n\n/** Bare button carrying the chip — it exists only to give the tooltip a\n * keyboard-focusable trigger, so it inherits the chip's pill outline.\n * The 16px chip sits below `--min-target-size`, so the same `::before`\n * hit-area recipe as InfoTip `size=\"sm\"` expands the target: the full\n * 44/48px on touch, relaxing to the `--spacing-lg` 24px floor from `sm:` up. */\nconst CHIP_BUTTON_CLASS =\n 'ds:relative ds:inline-flex ds:appearance-none ds:border-0 ds:bg-transparent ds:p-0 ds:m-0 ds:cursor-default ds:rounded-[var(--radius-full)] ds:before:absolute ds:before:content-[\"\"] ds:before:inset-x-[calc((var(--min-target-size)-100%)/-2)] ds:before:inset-y-[calc((var(--min-target-size)-100%)/-2)] ds:sm:before:inset-x-[calc((var(--spacing-lg)-100%)/-2)] ds:sm:before:inset-y-[calc((var(--spacing-lg)-100%)/-2)] ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]';\n\n/** Every cell carries a start bar: a quiet edge in the border colour, the\n * accent when the answer is flagged. It gives a cell an edge without a box. */\nconst CELL_CLASS =\n 'ds:flex ds:min-w-0 ds:flex-col ds:gap-[var(--spacing-2xs)] ds:border-s-2 ds:ps-[var(--spacing-sm)]';\nconst CELL_BAR_CLASS = 'ds:border-[color:var(--border)]';\nconst CELL_BAR_ACCENT_CLASS = 'ds:border-[color:var(--accent)]';\n\nconst MUTED_LINE_CLASS =\n 'type-meta ds:m-0 ds:text-[color:var(--muted-foreground)]';\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\n/** Card's slot shape without its box — for `surface=\"bare\"`. */\nconst BareShell = Object.assign(\n ({ children }: { children?: ReactNode }) => <>{children}</>,\n {\n Header: ({\n children,\n className,\n }: {\n children?: ReactNode;\n className?: string;\n }) => <div className={className}>{children}</div>,\n Body: ({\n children,\n className,\n }: {\n children?: ReactNode;\n className?: string;\n }) => <div className={className}>{children}</div>,\n Footer: ({\n children,\n className,\n }: {\n children?: ReactNode;\n className?: string;\n }) => <div className={className}>{children}</div>,\n },\n);\n\nexport const AnamnesisSummaryCard = forwardRef<\n AnamnesisSummaryCardHandle,\n AnamnesisSummaryCardProps\n>(\n (\n {\n sections,\n title,\n subtitle,\n unansweredCount = 0,\n locale,\n highlightedFirst = true,\n highlightedGroup = true,\n highlightedOnly = false,\n surface = 'elevated',\n density = 'comfortable',\n loading = false,\n actionSlot,\n id,\n className,\n 'aria-label': ariaLabel,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const headingId = useId();\n const activeLocale = locale ?? i18n.language ?? 'en';\n\n // Drop empty sections and apply the highlighted-first order once per\n // input change; `sort` is stable so unhighlighted rows keep their order.\n const visibleSections = useMemo(\n () =>\n sections\n .filter((section) => section.answers.length > 0)\n .map((section) => ({\n ...section,\n answers: highlightedFirst\n ? [...section.answers].sort(\n (a, b) => Number(!!b.highlighted) - Number(!!a.highlighted),\n )\n : section.answers,\n })),\n [sections, highlightedFirst],\n );\n\n const answerCount = visibleSections.reduce(\n (sum, section) => sum + section.answers.length,\n 0,\n );\n const highlightedCount = visibleSections.reduce(\n (sum, section) =>\n sum + section.answers.filter((answer) => answer.highlighted).length,\n 0,\n );\n const sectionCount = visibleSections.length;\n // Every flagged answer in section order, for the opening group.\n const highlightedAnswers = visibleSections.flatMap((section) =>\n section.answers.filter((answer) => answer.highlighted),\n );\n // A flagged-answers-only card lists no sections at all.\n const listedSections = highlightedOnly ? [] : visibleSections;\n const recordedLine = (answer: AnamnesisSummaryAnswer): string | null => {\n const date = answer.reportedAtIso\n ? formatReportedAt(answer.reportedAtIso, activeLocale)\n : null;\n const parts = [answer.sourceLabel, date].filter((part): part is string =>\n Boolean(part),\n );\n return parts.length > 0 ? parts.join(' · ') : null;\n };\n\n // Imperative handle — structural snapshot only (counts + consumer id),\n // never a question label or an answer value.\n const handle = useMemo<AnamnesisSummaryCardHandle>(\n () => ({\n getSummary: () => ({\n id,\n sectionCount,\n answerCount,\n highlightedCount,\n unansweredCount,\n }),\n }),\n [id, sectionCount, answerCount, highlightedCount, unansweredCount],\n );\n useImperativeHandle(ref, () => handle, [handle]);\n useAgentRegistration(anamnesisSummaryCardAgent, handle, id);\n\n const regionLabel = t('anamnesisSummaryCard.regionLabel', {\n defaultValue: 'Anamnesis summary',\n });\n const resolvedTitle = title ?? regionLabel;\n const resolvedAriaLabel = ariaLabel ?? resolvedTitle;\n const highlightedText = t('anamnesisSummaryCard.highlighted', {\n defaultValue: 'Highlighted',\n });\n\n const titleClass =\n density === 'comfortable' ? 'type-title-card' : 'type-title-item';\n const rowGap = density === 'comfortable' ? 'md' : 'sm';\n\n /* ---------------------------------------------------------------- */\n /* Highlight chip */\n /* ---------------------------------------------------------------- */\n\n const renderChip = (answer: AnamnesisSummaryAnswer) => {\n const date = answer.reportedAtIso\n ? formatReportedAt(answer.reportedAtIso, activeLocale)\n : null;\n const tooltipLabel =\n date && answer.sourceLabel\n ? t('anamnesisSummaryCard.reportedOnFrom', {\n date,\n source: answer.sourceLabel,\n defaultValue: 'Highlighted on {{date}} · {{source}}',\n })\n : date\n ? t('anamnesisSummaryCard.reportedOn', {\n date,\n defaultValue: 'Highlighted on {{date}}',\n })\n : highlightedText;\n return (\n <Tooltip label={tooltipLabel} side=\"top\">\n <button\n type=\"button\"\n className={CHIP_BUTTON_CLASS}\n data-highlighted-chip=\"\"\n >\n {/* Icon only: the heart is the mark, the tooltip and the\n accessible name say why. */}\n <Badge\n variant=\"accentSoft\"\n size=\"sm\"\n aria-label={highlightedText}\n leading={<HeartPulse aria-hidden=\"true\" />}\n />\n </button>\n </Tooltip>\n );\n };\n\n /* ---------------------------------------------------------------- */\n /* Header */\n /* ---------------------------------------------------------------- */\n\n const header = (\n <div className=\"ds:flex ds:flex-wrap ds:items-start ds:gap-[var(--spacing-sm)]\">\n <div className=\"ds:flex ds:min-w-0 ds:flex-1 ds:flex-col ds:gap-[var(--spacing-2xs)]\">\n <h3\n id={headingId}\n className={`ds:m-0 ${titleClass} ds:text-[color:var(--foreground)]`}\n >\n {resolvedTitle}\n </h3>\n {subtitle ? <p className={MUTED_LINE_CLASS}>{subtitle}</p> : null}\n </div>\n {!loading && highlightedCount > 0 ? (\n <Badge\n variant=\"accentSoft\"\n size=\"sm\"\n leading={<HeartPulse aria-hidden=\"true\" />}\n className=\"ds:shrink-0\"\n >\n {t('anamnesisSummaryCard.highlightedCount', {\n count: highlightedCount,\n defaultValue: '{{count}} highlighted',\n })}\n </Badge>\n ) : null}\n {actionSlot ? (\n <div className=\"ds:ms-auto ds:flex ds:shrink-0 ds:items-center ds:gap-[var(--spacing-xs)]\">\n {actionSlot}\n </div>\n ) : null}\n </div>\n );\n\n /* ---------------------------------------------------------------- */\n /* Body */\n /* ---------------------------------------------------------------- */\n\n const skeletonBody = (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n <Skeleton variant=\"text\" width=\"40%\" />\n <Skeleton variant=\"text\" lines={4} />\n </div>\n );\n\n const emptyBody = (\n <p className={MUTED_LINE_CLASS}>\n {t('anamnesisSummaryCard.empty', {\n defaultValue: 'No answers recorded yet',\n })}\n </p>\n );\n\n /* ---------------------------------------------------------------- */\n /* Q&A cells */\n /* ---------------------------------------------------------------- */\n\n // Question above answer, one accent glyph each, two cells across when\n // the card is wide: a reader scans questions the way they were asked,\n // and a label|value pair doubled up made four ragged columns.\n const gridClass = [\n 'ds:m-0 ds:grid ds:grid-cols-1 ds:@[672px]/afields:grid-cols-2',\n 'ds:gap-x-[var(--spacing-lg)]',\n rowGap === 'md'\n ? 'ds:gap-y-[var(--spacing-md)]'\n : 'ds:gap-y-[var(--spacing-sm)]',\n ].join(' ');\n\n const renderAnswer = (\n answer: AnamnesisSummaryAnswer,\n opts: {\n recorded: boolean;\n chip: boolean;\n accentBar: boolean;\n keyPrefix: string;\n },\n ) => {\n const flagged = opts.chip && answer.highlighted;\n const recorded = opts.recorded ? recordedLine(answer) : null;\n return (\n <div\n key={`${opts.keyPrefix}${answer.id}`}\n data-answer-id={answer.id}\n data-highlighted={answer.highlighted ? '' : undefined}\n className={[\n CELL_CLASS,\n opts.accentBar || flagged ? CELL_BAR_ACCENT_CLASS : CELL_BAR_CLASS,\n ].join(' ')}\n >\n {/* The question leads: heavier and larger than its answer, the\n flag's heart ahead of it — a mark beside the question, never a\n box around the answer. */}\n <dt className=\"type-title-item ds:m-0 ds:flex ds:items-start ds:gap-[var(--spacing-xs)] ds:font-semibold ds:text-[color:var(--foreground)]\">\n {flagged ? renderChip(answer) : null}\n <span>{answer.label}</span>\n </dt>\n <dd className=\"type-body-sm ds:m-0 ds:flex ds:min-w-0 ds:flex-col ds:gap-[var(--spacing-2xs)] ds:text-[color:var(--foreground)]\">\n <span className=\"ds:inline-flex ds:flex-wrap ds:items-center ds:gap-x-[var(--spacing-xs)] ds:gap-y-[var(--spacing-2xs)]\">\n <span>{answer.value}</span>\n {answer.unit ? (\n <span className=\"ds:text-[color:var(--muted-foreground)]\">\n {answer.unit}\n </span>\n ) : null}\n </span>\n {recorded ? (\n <span className={MUTED_LINE_CLASS}>{recorded}</span>\n ) : null}\n </dd>\n </div>\n );\n };\n\n const sectionsBody = (\n <div\n role=\"group\"\n aria-label={t('anamnesisSummaryCard.sectionsLabel', {\n defaultValue: 'Anamnesis sections',\n })}\n className={sectionsVariants({ density })}\n >\n {/* Plain <div>, deliberately not a named <section>: a named\n <section> is a `region` landmark, and one per anamnesis section\n per card would drown the page-level landmarks. The <h4> already\n gives heading navigation into each section. */}\n {(highlightedGroup || highlightedOnly) &&\n highlightedAnswers.length > 0 ? (\n <div\n data-highlighted-group=\"\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n {/* The card's own title already says it when this is all it shows. */}\n {highlightedOnly ? null : (\n <h4 className=\"type-label ds:m-0 ds:font-semibold ds:text-[color:var(--foreground)]\">\n {t('anamnesisSummaryCard.highlightedGroup', {\n defaultValue: 'Highlighted answers',\n })}\n </h4>\n )}\n {/* Container-queried so the cells flow into two columns when the\n card is wide enough and stay single-column in a narrow rail —\n the same 672px PatientSummaryCard breaks at (px, not rem:\n container-query rem follows the host root font-size). */}\n <div className=\"ds:@container/afields\">\n <dl className={gridClass}>\n {highlightedAnswers.map((answer) =>\n renderAnswer(answer, {\n recorded: true,\n chip: false,\n accentBar: true,\n keyPrefix: 'highlighted-',\n }),\n )}\n </dl>\n </div>\n </div>\n ) : null}\n {listedSections.map((section) => (\n <div\n key={section.id}\n data-section-id={section.id}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n <h4 className=\"type-label ds:m-0 ds:font-semibold ds:text-[color:var(--foreground)]\">\n {section.heading}\n </h4>\n <div className=\"ds:@container/afields\">\n <dl className={gridClass}>\n {section.answers.map((answer) =>\n renderAnswer(answer, {\n recorded: false,\n chip: true,\n accentBar: false,\n keyPrefix: '',\n }),\n )}\n </dl>\n </div>\n </div>\n ))}\n </div>\n );\n\n const body = loading\n ? skeletonBody\n : visibleSections.length === 0 ||\n (highlightedOnly && highlightedAnswers.length === 0)\n ? emptyBody\n : sectionsBody;\n\n // `bare` drops the Card chrome and keeps its slots, so a host that\n // already frames the content (an expanded table row) gets one layer.\n const Shell = surface === 'bare' ? BareShell : Card;\n\n return (\n <div\n role=\"region\"\n aria-label={resolvedAriaLabel}\n aria-busy={loading || undefined}\n data-component=\"anamnesis-summary-card\"\n data-component-id={id}\n data-density={density}\n data-highlighted-only={highlightedOnly ? '' : undefined}\n id={id}\n className={rootVariants({ surface, density, className })}\n {...rest}\n >\n <Shell variant={surface === 'flat' ? 'outlined' : 'elevated'}>\n <Shell.Header className={headerPaddingVariants({ density })}>\n {header}\n </Shell.Header>\n {/* Own TooltipProvider so a host without one still gets the chip\n tooltips; Radix providers compose, so a host-level one also works. */}\n <TooltipProvider>\n <Shell.Body className={bodyPaddingVariants({ density })}>\n {body}\n </Shell.Body>\n </TooltipProvider>\n {!loading && !highlightedOnly && unansweredCount > 0 ? (\n <Shell.Footer className={footerPaddingVariants({ density })}>\n <p className={MUTED_LINE_CLASS}>\n {t('anamnesisSummaryCard.unanswered', {\n count: unansweredCount,\n defaultValue: '{{count}} questions not answered',\n })}\n </p>\n </Shell.Footer>\n ) : null}\n </Shell>\n </div>\n );\n },\n);\n\nAnamnesisSummaryCard.displayName = 'AnamnesisSummaryCard';\n\n/** Variant unions re-exported for downstream typing. */\nexport type AnamnesisSummaryCardSurface = NonNullable<\n VariantProps<typeof rootVariants>['surface']\n>;\nexport type AnamnesisSummaryCardDensity = NonNullable<\n VariantProps<typeof rootVariants>['density']\n>;\n"],"names":["anamnesisSummaryCardAgent","handle","rootVariants","cva","sectionsVariants","headerPaddingVariants","bodyPaddingVariants","footerPaddingVariants","DATE_ONLY_RE","LOCAL_DATETIME_RE","CLOCK_TIME_RE","parseReportedAt","iso","day","local","date","formatReportedAt","locale","parsed","options","CHIP_BUTTON_CLASS","CELL_CLASS","CELL_BAR_CLASS","CELL_BAR_ACCENT_CLASS","MUTED_LINE_CLASS","BareShell","children","className","jsx","AnamnesisSummaryCard","forwardRef","sections","title","subtitle","unansweredCount","highlightedFirst","highlightedGroup","highlightedOnly","surface","density","loading","actionSlot","id","ariaLabel","rest","ref","t","i18n","useTranslation","headingId","useId","activeLocale","visibleSections","useMemo","section","a","b","answerCount","sum","highlightedCount","answer","sectionCount","highlightedAnswers","listedSections","recordedLine","parts","part","useImperativeHandle","useAgentRegistration","regionLabel","resolvedTitle","resolvedAriaLabel","highlightedText","titleClass","rowGap","renderChip","tooltipLabel","Tooltip","Badge","HeartPulse","header","jsxs","skeletonBody","Skeleton","emptyBody","gridClass","renderAnswer","opts","flagged","recorded","sectionsBody","body","Shell","Card","TooltipProvider"],"mappings":"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"}
@@ -7,7 +7,7 @@ import { B as P } from "./button-nJaPh_HC.js";
7
7
  import { B as ze } from "./button-group-QmN1VdUW.js";
8
8
  import { D as Ae } from "./date-picker-6tFnKFty.js";
9
9
  import { P as Be } from "./panel-header-D_mTdhUp.js";
10
- import { S as Ke } from "./skeleton-CZbwyJAA.js";
10
+ import { S as Ke } from "./skeleton-B1KTdJ4N.js";
11
11
  import { E as Ee } from "./empty-state-X0sVPfVo.js";
12
12
  import { u as ae } from "./use-isomorphic-layout-effect-BGfaCOP1.js";
13
13
  import { u as Ye } from "./use-prefers-reduced-motion-BMwIQRjB.js";
@@ -446,4 +446,4 @@ export {
446
446
  Ue as A,
447
447
  Ve as a
448
448
  };
449
- //# sourceMappingURL=appointment-timeline-ByfovxqR.js.map
449
+ //# sourceMappingURL=appointment-timeline-D-rNwKpS.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"appointment-timeline-ByfovxqR.js","sources":["../../src/components/appointment-timeline/appointment-timeline.agent.ts","../../src/components/appointment-timeline/appointment-timeline.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — AppointmentTimeline. */\n/* */\n/* Timeline surface: read appointment ids + lifecycle state (never */\n/* patient names / treatments), activate a row through the curated handle, */\n/* and re-anchor the scroll on today. In-page (no open/close). */\n/* `showPatient` is presentation only — it changes which name each row */\n/* leads with, never what the adapter reads or exposes. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { AppointmentTimelineHandle } from './appointment-timeline';\n\nexport const appointmentTimelineAgent: AgentAdapter<AppointmentTimelineHandle> =\n {\n id: 'appointment-timeline',\n // 'pick' backs select_item (non-persisting activation through\n // onOpenAppointment); 'scroll' backs jump_to_today, which re-anchors the\n // scroll viewport (mirrors practice-results' scroll_to_result).\n capabilities: ['scroll', 'pick'],\n state: {\n items: {\n type: 'Array<{ id: string; state: string }>',\n descriptionKey: 'ui.agent.appointmentTimeline.state.items',\n description:\n 'Currently-rendered appointments. Ids + lifecycle state only — never patient identity.',\n read: (handle) => handle.getItems(),\n },\n },\n actions: {\n select_item: {\n safety: 'read',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: { type: 'string' } },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.appointmentTimeline.actions.selectItem',\n description: 'Activate the appointment with the given id.',\n invoke: (handle, args: { id: string }) => {\n handle.selectItem(args.id);\n },\n },\n jump_to_today: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.appointmentTimeline.actions.jumpToToday',\n description: \"Re-anchor the timeline scroll on today's appointments.\",\n invoke: (handle) => {\n handle.jumpToToday();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'appointment-timeline',\n description: 'Marks the AppointmentTimeline wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description:\n 'Sourced from the id prop. Required to address a specific timeline from the agent.',\n },\n item: {\n attr: 'data-appointment-id',\n description:\n 'Stable appointment id emitted on each rendered AppointmentCard inside the timeline.',\n },\n },\n };\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { CalendarDays } from 'lucide-react';\nimport { AppointmentCard, type AppointmentItem } from '../appointment-card';\nimport { Button } from '../button';\nimport { ButtonGroup } from '../button-group';\nimport { DatePicker } from '../date-picker';\nimport { PanelHeader } from '../panel-header';\nimport { Skeleton } from '../skeleton/skeleton';\nimport { EmptyState } from '../empty-state/empty-state';\nimport { useIsomorphicLayoutEffect } from '../../hooks/use-isomorphic-layout-effect';\nimport { usePrefersReducedMotion } from '../../hooks/use-prefers-reduced-motion';\nimport { REFLOW_ROW_CLASS, useListReflow } from '../../hooks/use-list-reflow';\nimport { useExitTransition } from '../../hooks/use-exit-transition';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { appointmentTimelineAgent } from './appointment-timeline.agent';\n\n/* -------------------------------------------------------------------- */\n/* AppointmentTimeline */\n/* */\n/* A vertical, day-grouped appointment timeline for the Control Room */\n/* rail. Unlike AppointmentTray (a bordered card of a flat list), this */\n/* fills the rail edge-to-edge: appointments are grouped under day */\n/* headers, the scroll is anchored on TODAY, and it scrolls infinitely */\n/* into the past and future — the consumer feeds more via onLoadEarlier */\n/* / onLoadLater as either edge is reached (scroll position is preserved */\n/* when earlier items prepend). Date-filter presets + a jump-to-date */\n/* picker re-anchor the scroll. Top + bottom transparent gradient fades */\n/* (a block-axis mask) signal more content beyond each edge. */\n/* */\n/* Each row reuses AppointmentCard (care-plan + chair chips included). */\n/* All visible text flows through useTranslation('ui') against the */\n/* `appointmentTimeline.*` namespace; no PHI reaches the agent adapter. */\n/* -------------------------------------------------------------------- */\n\n/* How close (px) to an edge before the matching load callback fires. */\nconst EDGE_LOAD_THRESHOLD = 96;\n\n/* -------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------- */\n\n/** Curated imperative handle — identifiers + lifecycle state only. */\nexport interface AppointmentTimelineHandle {\n getItems: () => Array<{ id: string; state: string }>;\n selectItem: (id: string) => void;\n jumpToToday: () => void;\n}\n\nexport interface AppointmentTimelineProps extends Omit<\n HTMLAttributes<HTMLElement>,\n 'onClick' | 'id' | 'title'\n> {\n /** Stable instance id, surfaced as `data-component-id`. */\n id?: string;\n /** Appointments to render — any order; grouped + sorted internally. */\n items: AppointmentItem[];\n /**\n * ISO-8601 \"now\" anchor — the timeline scrolls to this day on mount and the\n * \"Today\" preset returns to it. Defaults to the current instant. Stories pass\n * a fixed value for deterministic snapshots.\n */\n nowIso?: string;\n /** Panel heading. Defaults to the localised \"Appointments\". */\n title?: string;\n /** BCP-47 locale override for day headers + each AppointmentCard. */\n locale?: string;\n /** ISO-4217 currency code passed to each AppointmentCard. */\n currency?: string;\n /** Visual density passed to each AppointmentCard. */\n size?: 'sm' | 'md';\n /**\n * Lead each row with the PATIENT (avatar + name as the row title) rather\n * than the operator. Default `false`: the timeline was built for a\n * patient-scoped rail, where repeating the name on every row is noise. A\n * clinic-wide agenda — \"who is coming in next\" across the practice —\n * passes `true`, and rows with no patient read as \"No patient\".\n */\n showPatient?: boolean;\n /** Fires when a row is activated. */\n onOpenAppointment?: (item: AppointmentItem) => void;\n /**\n * The id of the currently selected appointment. The matching card\n * renders with `selected` (info-tinted accent + `aria-current`) so the\n * eye keeps its place in the list after a click.\n */\n selectedId?: string;\n\n /* ----- Infinite scroll ----- */\n /** Load earlier (past) appointments — fired when the top edge is reached. */\n onLoadEarlier?: () => void;\n /** Load later (future) appointments — fired when the bottom edge is reached. */\n onLoadLater?: () => void;\n /** Whether more past appointments exist. Defaults to `true` when `onLoadEarlier` is set. */\n hasMoreEarlier?: boolean;\n /** Whether more future appointments exist. Defaults to `true` when `onLoadLater` is set. */\n hasMoreLater?: boolean;\n /** Show a loading row at the top edge. */\n loadingEarlier?: boolean;\n /** Show a loading row at the bottom edge. */\n loadingLater?: boolean;\n\n /* ----- Filters ----- */\n /** Show the date-filter row (presets + jump-to-date). Default `true`. */\n showFilters?: boolean;\n /**\n * Fired when a jump-to-date / preset targets a day outside the loaded range,\n * so the consumer can load it. The timeline scrolls to the nearest rendered\n * day until then.\n */\n onJumpToDate?: (dateIso: string) => void;\n\n /* ----- Empty ----- */\n emptyTitle?: string;\n emptyDescription?: string;\n\n /** Accessible name for the panel region (defaults to `title`). */\n 'aria-label'?: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* CVA */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n [\n 'ds:flex ds:flex-col ds:h-full ds:min-h-0',\n 'ds:bg-[color:var(--card)] ds:text-[color:var(--card-foreground)]',\n ].join(' '),\n { variants: { size: { sm: '', md: '' } }, defaultVariants: { size: 'sm' } },\n);\n\n/* Scroll region with a block-axis fade mask. The CSS custom properties flip to\n `transparent` at whichever edge has more content (data-fade-top /\n data-fade-bottom), mirroring the TabBar edge-mask technique on the Y axis.\n Disabled under reduced-motion, the accessible theme, and forced-colors. */\nconst scrollRegionClasses = [\n 'ds:flex-1 ds:min-h-0 ds:overflow-y-auto',\n 'ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]',\n 'ds:px-[var(--spacing-sm)] ds:py-[var(--spacing-xs)]',\n 'ds:[--atl-mask-top:black] ds:[--atl-mask-bottom:black]',\n 'ds:data-[fade-top=true]:[--atl-mask-top:transparent]',\n 'ds:data-[fade-bottom=true]:[--atl-mask-bottom:transparent]',\n 'ds:[mask-image:linear-gradient(to_bottom,var(--atl-mask-top)_0,black_var(--atl-fade-distance,var(--spacing-lg)),black_calc(100%-var(--atl-fade-distance,var(--spacing-lg))),var(--atl-mask-bottom)_100%)]',\n 'ds:motion-reduce:[mask-image:none]',\n 'ds:[.theme-accessible_&]:[mask-image:none]',\n 'ds:forced-colors:[mask-image:none]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:[outline-offset:-2px]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n].join(' ');\n\n/* -------------------------------------------------------------------- */\n/* Day grouping + header labels */\n/* -------------------------------------------------------------------- */\n\ninterface DayGroup {\n key: string;\n date: Date;\n items: AppointmentItem[];\n}\n\n/** Local calendar day key (`YYYY-MM-DD`) — sorts chronologically as a string. */\nfunction localDayKey(d: Date): string {\n const m = `${d.getMonth() + 1}`.padStart(2, '0');\n const day = `${d.getDate()}`.padStart(2, '0');\n return `${d.getFullYear()}-${m}-${day}`;\n}\n\nfunction groupByDay(items: AppointmentItem[]): DayGroup[] {\n const sorted = [...items].sort(\n (a, b) => +new Date(a.startIso) - +new Date(b.startIso),\n );\n const map = new Map<string, DayGroup>();\n for (const item of sorted) {\n const d = new Date(item.startIso);\n const key = localDayKey(d);\n let group = map.get(key);\n if (!group) {\n group = {\n key,\n date: new Date(d.getFullYear(), d.getMonth(), d.getDate()),\n items: [],\n };\n map.set(key, group);\n }\n group.items.push(item);\n }\n return [...map.values()];\n}\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\nexport const AppointmentTimeline = forwardRef<\n HTMLElement,\n AppointmentTimelineProps\n>(\n (\n {\n id,\n items,\n nowIso,\n title,\n locale,\n currency,\n size = 'sm',\n showPatient = false,\n onOpenAppointment,\n selectedId,\n onLoadEarlier,\n onLoadLater,\n hasMoreEarlier,\n hasMoreLater,\n loadingEarlier = false,\n loadingLater = false,\n showFilters = true,\n onJumpToDate,\n emptyTitle,\n emptyDescription,\n className,\n 'aria-label': ariaLabel,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const jumpId = useId();\n const prefersReducedMotion = usePrefersReducedMotion();\n const resolvedLocale = locale ?? i18n.language;\n\n const heading = title ?? t('appointmentTimeline.title');\n const now = useMemo(\n () => (nowIso ? new Date(nowIso) : new Date()),\n [nowIso],\n );\n\n const groups = useMemo(() => groupByDay(items), [items]);\n\n /* Reflow + exit transitions — the same pair notes and tasks use. When\n an appointment is added or removed, surviving cards slide to their\n new positions instead of snapping; a removed card fades out in place\n before the host drops it, so the removal reads as a removal. */\n const reduceMotion = prefersReducedMotion || undefined;\n const flatItemIds = useMemo(\n () => groups.flatMap((g) => g.items.map((i) => i.id)).join(),\n [groups],\n );\n const { containerRef: reflowRef } = useListReflow<HTMLOListElement>({\n key: flatItemIds,\n reduceMotion,\n });\n const exitTransition = useExitTransition({ reduceMotion });\n\n /* Track removed items so they can fade out in place. The previous\n items array is diffed against the current one; removed ids are held\n in `leavingItems` and rendered after the live groups, faded, until\n the exit transition elapses and drops them. */\n const prevItemsRef = useRef(items);\n const [leavingItems, setLeavingItems] = useState<AppointmentItem[]>([]);\n useEffect(() => {\n const prev = prevItemsRef.current;\n if (prev === items) return;\n const currentIds = new Set(items.map((i) => i.id));\n const removed = prev.filter((i) => !currentIds.has(i.id));\n if (removed.length > 0) {\n setLeavingItems((prev) => [...prev, ...removed]);\n removed.forEach((item) => {\n exitTransition.leave(item.id, () => {\n setLeavingItems((prev) => prev.filter((i) => i.id !== item.id));\n });\n });\n }\n prevItemsRef.current = items;\n }, [items, exitTransition]);\n\n const moreEarlier = hasMoreEarlier ?? !!onLoadEarlier;\n const moreLater = hasMoreLater ?? !!onLoadLater;\n\n /* The day to anchor on: today if present, else the nearest upcoming day,\n else the last day we have. */\n const anchorKey = useMemo(() => {\n if (groups.length === 0) return undefined;\n const todayKey = localDayKey(now);\n const keys = groups.map((g) => g.key);\n if (keys.includes(todayKey)) return todayKey;\n return keys.find((k) => k >= todayKey) ?? keys[keys.length - 1];\n }, [groups, now]);\n\n const scrollRef = useRef<HTMLDivElement>(null);\n const dayElRefs = useRef<Map<string, HTMLElement>>(new Map());\n const hasAnchored = useRef(false);\n const prevFirstKey = useRef<string | undefined>(undefined);\n const prevScrollHeight = useRef(0);\n\n const [fadeTop, setFadeTop] = useState(false);\n const [fadeBottom, setFadeBottom] = useState(false);\n\n const updateEdges = useCallback(() => {\n const region = scrollRef.current;\n if (!region) return;\n const { scrollTop, scrollHeight, clientHeight } = region;\n setFadeTop(scrollTop > 1);\n setFadeBottom(scrollTop + clientHeight < scrollHeight - 1);\n }, []);\n\n /* Scroll a day into view at the top of the region. Falls back to the\n nearest rendered day on/after the target (presets may land on an empty\n day). Uses getBoundingClientRect so it is offsetParent-agnostic. */\n const scrollToKey = useCallback(\n (targetKey: string, smooth: boolean) => {\n const region = scrollRef.current;\n if (!region) return;\n let el = dayElRefs.current.get(targetKey);\n if (!el) {\n const keys = [...dayElRefs.current.keys()].sort();\n const next =\n keys.find((k) => k >= targetKey) ?? keys[keys.length - 1];\n el = next ? dayElRefs.current.get(next) : undefined;\n }\n if (!el) return;\n const delta =\n el.getBoundingClientRect().top - region.getBoundingClientRect().top;\n region.scrollTo({\n top: region.scrollTop + delta,\n behavior: smooth && !prefersReducedMotion ? 'smooth' : 'auto',\n });\n },\n [prefersReducedMotion],\n );\n\n /* Anchor on mount; preserve the viewport when earlier days prepend. */\n useIsomorphicLayoutEffect(() => {\n const region = scrollRef.current;\n if (!region) return;\n const firstKey = groups[0]?.key;\n if (!hasAnchored.current) {\n if (groups.length > 0 && anchorKey) {\n scrollToKey(anchorKey, false);\n hasAnchored.current = true;\n }\n } else if (\n prevFirstKey.current &&\n firstKey &&\n firstKey < prevFirstKey.current\n ) {\n const grew = region.scrollHeight - prevScrollHeight.current;\n if (grew > 0) region.scrollTop += grew;\n }\n prevFirstKey.current = firstKey;\n prevScrollHeight.current = region.scrollHeight;\n updateEdges();\n }, [groups, anchorKey, scrollToKey, updateEdges]);\n\n /* Recompute the fades when the region resizes. */\n useIsomorphicLayoutEffect(() => {\n const region = scrollRef.current;\n if (!region || typeof ResizeObserver === 'undefined') return undefined;\n const observer = new ResizeObserver(updateEdges);\n observer.observe(region);\n return () => observer.disconnect();\n }, [updateEdges]);\n\n const handleScroll = useCallback(() => {\n updateEdges();\n const region = scrollRef.current;\n if (!region) return;\n const { scrollTop, scrollHeight, clientHeight } = region;\n if (\n onLoadEarlier &&\n moreEarlier &&\n !loadingEarlier &&\n scrollTop <= EDGE_LOAD_THRESHOLD\n ) {\n onLoadEarlier();\n }\n if (\n onLoadLater &&\n moreLater &&\n !loadingLater &&\n scrollHeight - scrollTop - clientHeight <= EDGE_LOAD_THRESHOLD\n ) {\n onLoadLater();\n }\n }, [\n updateEdges,\n onLoadEarlier,\n onLoadLater,\n moreEarlier,\n moreLater,\n loadingEarlier,\n loadingLater,\n ]);\n\n /* ----- Filter actions ----- */\n const jumpTo = useCallback(\n (date: Date) => {\n const key = localDayKey(date);\n const known = dayElRefs.current.has(key);\n scrollToKey(key, true);\n if (!known) onJumpToDate?.(date.toISOString());\n },\n [scrollToKey, onJumpToDate],\n );\n\n const handleToday = useCallback(() => jumpTo(now), [jumpTo, now]);\n const handleThisWeek = useCallback(() => {\n const start = new Date(now);\n const dow = (start.getDay() + 6) % 7; // Monday-based week start\n start.setDate(start.getDate() - dow);\n jumpTo(start);\n }, [jumpTo, now]);\n const handleThisMonth = useCallback(() => {\n jumpTo(new Date(now.getFullYear(), now.getMonth(), 1));\n }, [jumpTo, now]);\n\n /* ----- Agent handle ----- */\n const itemsRef = useRef<AppointmentItem[]>(items);\n itemsRef.current = items;\n const innerRef = useRef<HTMLElement>(null);\n useImperativeHandle(ref, () => innerRef.current as HTMLElement, []);\n\n const handle = useMemo<AppointmentTimelineHandle>(\n () => ({\n getItems: () =>\n itemsRef.current.map((i) => ({ id: i.id, state: i.state })),\n selectItem: (targetId: string) => {\n const found = itemsRef.current.find((i) => i.id === targetId);\n if (found) onOpenAppointment?.(found);\n },\n jumpToToday: () => scrollToKey(localDayKey(now), true),\n }),\n [onOpenAppointment, scrollToKey, now],\n );\n useAgentRegistration(appointmentTimelineAgent, handle, id);\n\n const isEmpty = groups.length === 0 && !loadingEarlier && !loadingLater;\n\n const loadingRow = (key: string) => (\n <div\n key={key}\n aria-hidden=\"true\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)] ds:py-[var(--spacing-xs)]\"\n >\n <Skeleton variant=\"rectangular\" height=\"3.5rem\" />\n </div>\n );\n\n return (\n <section\n ref={innerRef}\n data-component=\"appointment-timeline\"\n data-component-id={id}\n aria-label={ariaLabel ?? heading}\n className={rootVariants({ size, className })}\n {...rest}\n >\n {/* Shared rail header: title + (optional) filter row + divider. This\n panel owns its own scroll/fill, so the header stays a fixed band\n (sticky={false}). The rail injects the favourite/close cluster via\n PanelHeaderActionsProvider; standalone it renders none. */}\n <PanelHeader\n icon={<CalendarDays />}\n title={heading}\n filters={\n showFilters ? (\n <>\n <ButtonGroup label={t('appointmentTimeline.filtersLabel')}>\n <Button intent=\"outline\" size=\"sm\" onClick={handleToday}>\n {t('appointmentTimeline.today')}\n </Button>\n <Button intent=\"outline\" size=\"sm\" onClick={handleThisWeek}>\n {t('appointmentTimeline.thisWeek')}\n </Button>\n <Button intent=\"outline\" size=\"sm\" onClick={handleThisMonth}>\n {t('appointmentTimeline.thisMonth')}\n </Button>\n </ButtonGroup>\n <label htmlFor={jumpId} className=\"ds:sr-only\">\n {t('appointmentTimeline.jumpToDate')}\n </label>\n <DatePicker\n id={jumpId}\n size=\"sm\"\n placeholder={t('appointmentTimeline.jumpToDate')}\n onChange={(date) => {\n if (date) jumpTo(date);\n }}\n />\n </>\n ) : undefined\n }\n />\n\n {isEmpty ? (\n <div className=\"ds:flex-1 ds:p-[var(--spacing-md)]\">\n <EmptyState\n variant=\"first-use\"\n size=\"sm\"\n title={emptyTitle ?? t('appointmentTimeline.empty.title')}\n description={\n emptyDescription ?? t('appointmentTimeline.empty.description')\n }\n />\n </div>\n ) : (\n <div\n ref={scrollRef}\n // The scroll region must be keyboard-focusable so arrow / Home /\n // End can drive it (axe scrollable-region-focusable); jsx-a11y's\n // generic rule doesn't know this element scrolls.\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n tabIndex={0}\n onScroll={handleScroll}\n data-fade-top={fadeTop ? 'true' : undefined}\n data-fade-bottom={fadeBottom ? 'true' : undefined}\n className={scrollRegionClasses}\n >\n {loadingEarlier ? loadingRow('loading-earlier') : null}\n {/* eslint-disable-next-line jsx-a11y/no-redundant-roles -- role=\"list\" is NOT redundant: WebKit drops implicit list semantics when list-style is none */}\n <ol\n ref={reflowRef}\n role=\"list\"\n className=\"ds:m-0 ds:flex ds:list-none ds:flex-col ds:gap-[var(--spacing-md)] ds:p-0\"\n >\n {groups.map((group) => (\n <li\n key={group.key}\n ref={(node) => {\n if (node) dayElRefs.current.set(group.key, node);\n else dayElRefs.current.delete(group.key);\n }}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n >\n <DayHeader\n date={group.date}\n now={now}\n locale={resolvedLocale}\n todayLabel={t('appointmentTimeline.today')}\n tomorrowLabel={t('appointmentTimeline.tomorrow')}\n yesterdayLabel={t('appointmentTimeline.yesterday')}\n />\n {/* eslint-disable-next-line jsx-a11y/no-redundant-roles -- role=\"list\" is NOT redundant: WebKit drops implicit list semantics when list-style is none */}\n <ul\n role=\"list\"\n className=\"ds:m-0 ds:flex ds:list-none ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-0\"\n >\n {group.items.map((item) => (\n <AppointmentCard\n key={item.id}\n item={item}\n size={size}\n surface=\"elevated\"\n showPatient={showPatient}\n locale={resolvedLocale}\n currency={currency}\n onActivate={onOpenAppointment}\n selected={item.id === selectedId}\n data-appointment-id={item.id}\n data-reflow-id={item.id}\n className={REFLOW_ROW_CLASS}\n />\n ))}\n </ul>\n </li>\n ))}\n {/* Leaving cards: removed from state but held for the exit\n fade. Rendered without `data-reflow-id` so the reflow hook\n ignores them; faded via `data-leaving`. */}\n {leavingItems.length > 0\n ? leavingItems.map((item) => (\n <AppointmentCard\n key={`leaving-${item.id}`}\n item={item}\n size={size}\n surface=\"elevated\"\n showPatient={showPatient}\n locale={resolvedLocale}\n currency={currency}\n aria-hidden=\"true\"\n data-leaving\n className={[\n 'ds:opacity-0',\n 'ds:transition-opacity',\n 'ds:duration-[var(--animation-duration)]',\n 'ds:ease-[var(--ease-out)]',\n 'ds:motion-reduce:transition-none',\n ].join(' ')}\n />\n ))\n : null}\n </ol>\n {loadingLater ? loadingRow('loading-later') : null}\n </div>\n )}\n </section>\n );\n },\n);\n\nAppointmentTimeline.displayName = 'AppointmentTimeline';\n\n/* -------------------------------------------------------------------- */\n/* Day header — sticky label per day group */\n/* -------------------------------------------------------------------- */\n\ninterface DayHeaderProps {\n date: Date;\n now: Date;\n locale: string | undefined;\n todayLabel: string;\n tomorrowLabel: string;\n yesterdayLabel: string;\n}\n\nfunction DayHeader({\n date,\n now,\n locale,\n todayLabel,\n tomorrowLabel,\n yesterdayLabel,\n}: DayHeaderProps): ReactNode {\n const key = localDayKey(date);\n const todayKey = localDayKey(now);\n const tomorrow = new Date(now);\n tomorrow.setDate(now.getDate() + 1);\n const yesterday = new Date(now);\n yesterday.setDate(now.getDate() - 1);\n\n const isToday = key === todayKey;\n\n let label: string;\n if (isToday) label = todayLabel;\n else if (key === localDayKey(tomorrow)) label = tomorrowLabel;\n else if (key === localDayKey(yesterday)) label = yesterdayLabel;\n else {\n const sameYear = date.getFullYear() === now.getFullYear();\n label = new Intl.DateTimeFormat(locale, {\n weekday: 'short',\n day: 'numeric',\n month: 'short',\n ...(sameYear ? {} : { year: 'numeric' }),\n }).format(date);\n }\n\n // Today's group is marked with the brand colour + a leading dot so the\n // current day reads at a glance. `--primary` clears AA on the card in every\n // theme (violet-500 light, magenta on dark); the dot is decorative.\n return (\n <div\n data-today={isToday ? 'true' : undefined}\n className={[\n 'ds:sticky ds:top-0 ds:z-[1] ds:bg-[color:var(--card)] ds:py-[var(--spacing-2xs)]',\n 'ds:flex ds:items-center ds:gap-[var(--spacing-2xs)] type-eyebrow',\n isToday\n ? 'ds:text-[color:var(--primary)] ds:font-semibold'\n : 'ds:text-[color:var(--muted-foreground)]',\n ].join(' ')}\n >\n {isToday ? (\n <span\n aria-hidden=\"true\"\n className=\"ds:size-1.5 ds:shrink-0 ds:rounded-[var(--radius-full)] ds:bg-[color:var(--primary)]\"\n />\n ) : null}\n {label}\n </div>\n );\n}\n\n/** Re-export of the size variant union for downstream typing. */\nexport type AppointmentTimelineSize = NonNullable<\n VariantProps<typeof rootVariants>['size']\n>;\n"],"names":["appointmentTimelineAgent","handle","args","EDGE_LOAD_THRESHOLD","rootVariants","cva","scrollRegionClasses","localDayKey","d","m","day","groupByDay","items","sorted","a","b","map","item","key","group","AppointmentTimeline","forwardRef","id","nowIso","title","locale","currency","size","showPatient","onOpenAppointment","selectedId","onLoadEarlier","onLoadLater","hasMoreEarlier","hasMoreLater","loadingEarlier","loadingLater","showFilters","onJumpToDate","emptyTitle","emptyDescription","className","ariaLabel","rest","ref","t","i18n","useTranslation","jumpId","useId","prefersReducedMotion","usePrefersReducedMotion","resolvedLocale","heading","now","useMemo","groups","reduceMotion","flatItemIds","g","i","reflowRef","useListReflow","exitTransition","useExitTransition","prevItemsRef","useRef","leavingItems","setLeavingItems","useState","useEffect","prev","currentIds","removed","moreEarlier","moreLater","anchorKey","todayKey","keys","k","scrollRef","dayElRefs","hasAnchored","prevFirstKey","prevScrollHeight","fadeTop","setFadeTop","fadeBottom","setFadeBottom","updateEdges","useCallback","region","scrollTop","scrollHeight","clientHeight","scrollToKey","targetKey","smooth","el","next","delta","useIsomorphicLayoutEffect","firstKey","_a","grew","observer","handleScroll","jumpTo","date","known","handleToday","handleThisWeek","start","dow","handleThisMonth","itemsRef","innerRef","useImperativeHandle","targetId","found","useAgentRegistration","isEmpty","loadingRow","jsx","Skeleton","jsxs","PanelHeader","CalendarDays","Fragment","ButtonGroup","Button","DatePicker","EmptyState","node","DayHeader","AppointmentCard","REFLOW_ROW_CLASS","todayLabel","tomorrowLabel","yesterdayLabel","tomorrow","yesterday","isToday","label","sameYear"],"mappings":"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"}
1
+ {"version":3,"file":"appointment-timeline-D-rNwKpS.js","sources":["../../src/components/appointment-timeline/appointment-timeline.agent.ts","../../src/components/appointment-timeline/appointment-timeline.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — AppointmentTimeline. */\n/* */\n/* Timeline surface: read appointment ids + lifecycle state (never */\n/* patient names / treatments), activate a row through the curated handle, */\n/* and re-anchor the scroll on today. In-page (no open/close). */\n/* `showPatient` is presentation only — it changes which name each row */\n/* leads with, never what the adapter reads or exposes. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { AppointmentTimelineHandle } from './appointment-timeline';\n\nexport const appointmentTimelineAgent: AgentAdapter<AppointmentTimelineHandle> =\n {\n id: 'appointment-timeline',\n // 'pick' backs select_item (non-persisting activation through\n // onOpenAppointment); 'scroll' backs jump_to_today, which re-anchors the\n // scroll viewport (mirrors practice-results' scroll_to_result).\n capabilities: ['scroll', 'pick'],\n state: {\n items: {\n type: 'Array<{ id: string; state: string }>',\n descriptionKey: 'ui.agent.appointmentTimeline.state.items',\n description:\n 'Currently-rendered appointments. Ids + lifecycle state only — never patient identity.',\n read: (handle) => handle.getItems(),\n },\n },\n actions: {\n select_item: {\n safety: 'read',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: { type: 'string' } },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.appointmentTimeline.actions.selectItem',\n description: 'Activate the appointment with the given id.',\n invoke: (handle, args: { id: string }) => {\n handle.selectItem(args.id);\n },\n },\n jump_to_today: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.appointmentTimeline.actions.jumpToToday',\n description: \"Re-anchor the timeline scroll on today's appointments.\",\n invoke: (handle) => {\n handle.jumpToToday();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'appointment-timeline',\n description: 'Marks the AppointmentTimeline wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description:\n 'Sourced from the id prop. Required to address a specific timeline from the agent.',\n },\n item: {\n attr: 'data-appointment-id',\n description:\n 'Stable appointment id emitted on each rendered AppointmentCard inside the timeline.',\n },\n },\n };\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { CalendarDays } from 'lucide-react';\nimport { AppointmentCard, type AppointmentItem } from '../appointment-card';\nimport { Button } from '../button';\nimport { ButtonGroup } from '../button-group';\nimport { DatePicker } from '../date-picker';\nimport { PanelHeader } from '../panel-header';\nimport { Skeleton } from '../skeleton/skeleton';\nimport { EmptyState } from '../empty-state/empty-state';\nimport { useIsomorphicLayoutEffect } from '../../hooks/use-isomorphic-layout-effect';\nimport { usePrefersReducedMotion } from '../../hooks/use-prefers-reduced-motion';\nimport { REFLOW_ROW_CLASS, useListReflow } from '../../hooks/use-list-reflow';\nimport { useExitTransition } from '../../hooks/use-exit-transition';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { appointmentTimelineAgent } from './appointment-timeline.agent';\n\n/* -------------------------------------------------------------------- */\n/* AppointmentTimeline */\n/* */\n/* A vertical, day-grouped appointment timeline for the Control Room */\n/* rail. Unlike AppointmentTray (a bordered card of a flat list), this */\n/* fills the rail edge-to-edge: appointments are grouped under day */\n/* headers, the scroll is anchored on TODAY, and it scrolls infinitely */\n/* into the past and future — the consumer feeds more via onLoadEarlier */\n/* / onLoadLater as either edge is reached (scroll position is preserved */\n/* when earlier items prepend). Date-filter presets + a jump-to-date */\n/* picker re-anchor the scroll. Top + bottom transparent gradient fades */\n/* (a block-axis mask) signal more content beyond each edge. */\n/* */\n/* Each row reuses AppointmentCard (care-plan + chair chips included). */\n/* All visible text flows through useTranslation('ui') against the */\n/* `appointmentTimeline.*` namespace; no PHI reaches the agent adapter. */\n/* -------------------------------------------------------------------- */\n\n/* How close (px) to an edge before the matching load callback fires. */\nconst EDGE_LOAD_THRESHOLD = 96;\n\n/* -------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------- */\n\n/** Curated imperative handle — identifiers + lifecycle state only. */\nexport interface AppointmentTimelineHandle {\n getItems: () => Array<{ id: string; state: string }>;\n selectItem: (id: string) => void;\n jumpToToday: () => void;\n}\n\nexport interface AppointmentTimelineProps extends Omit<\n HTMLAttributes<HTMLElement>,\n 'onClick' | 'id' | 'title'\n> {\n /** Stable instance id, surfaced as `data-component-id`. */\n id?: string;\n /** Appointments to render — any order; grouped + sorted internally. */\n items: AppointmentItem[];\n /**\n * ISO-8601 \"now\" anchor — the timeline scrolls to this day on mount and the\n * \"Today\" preset returns to it. Defaults to the current instant. Stories pass\n * a fixed value for deterministic snapshots.\n */\n nowIso?: string;\n /** Panel heading. Defaults to the localised \"Appointments\". */\n title?: string;\n /** BCP-47 locale override for day headers + each AppointmentCard. */\n locale?: string;\n /** ISO-4217 currency code passed to each AppointmentCard. */\n currency?: string;\n /** Visual density passed to each AppointmentCard. */\n size?: 'sm' | 'md';\n /**\n * Lead each row with the PATIENT (avatar + name as the row title) rather\n * than the operator. Default `false`: the timeline was built for a\n * patient-scoped rail, where repeating the name on every row is noise. A\n * clinic-wide agenda — \"who is coming in next\" across the practice —\n * passes `true`, and rows with no patient read as \"No patient\".\n */\n showPatient?: boolean;\n /** Fires when a row is activated. */\n onOpenAppointment?: (item: AppointmentItem) => void;\n /**\n * The id of the currently selected appointment. The matching card\n * renders with `selected` (info-tinted accent + `aria-current`) so the\n * eye keeps its place in the list after a click.\n */\n selectedId?: string;\n\n /* ----- Infinite scroll ----- */\n /** Load earlier (past) appointments — fired when the top edge is reached. */\n onLoadEarlier?: () => void;\n /** Load later (future) appointments — fired when the bottom edge is reached. */\n onLoadLater?: () => void;\n /** Whether more past appointments exist. Defaults to `true` when `onLoadEarlier` is set. */\n hasMoreEarlier?: boolean;\n /** Whether more future appointments exist. Defaults to `true` when `onLoadLater` is set. */\n hasMoreLater?: boolean;\n /** Show a loading row at the top edge. */\n loadingEarlier?: boolean;\n /** Show a loading row at the bottom edge. */\n loadingLater?: boolean;\n\n /* ----- Filters ----- */\n /** Show the date-filter row (presets + jump-to-date). Default `true`. */\n showFilters?: boolean;\n /**\n * Fired when a jump-to-date / preset targets a day outside the loaded range,\n * so the consumer can load it. The timeline scrolls to the nearest rendered\n * day until then.\n */\n onJumpToDate?: (dateIso: string) => void;\n\n /* ----- Empty ----- */\n emptyTitle?: string;\n emptyDescription?: string;\n\n /** Accessible name for the panel region (defaults to `title`). */\n 'aria-label'?: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* CVA */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n [\n 'ds:flex ds:flex-col ds:h-full ds:min-h-0',\n 'ds:bg-[color:var(--card)] ds:text-[color:var(--card-foreground)]',\n ].join(' '),\n { variants: { size: { sm: '', md: '' } }, defaultVariants: { size: 'sm' } },\n);\n\n/* Scroll region with a block-axis fade mask. The CSS custom properties flip to\n `transparent` at whichever edge has more content (data-fade-top /\n data-fade-bottom), mirroring the TabBar edge-mask technique on the Y axis.\n Disabled under reduced-motion, the accessible theme, and forced-colors. */\nconst scrollRegionClasses = [\n 'ds:flex-1 ds:min-h-0 ds:overflow-y-auto',\n 'ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]',\n 'ds:px-[var(--spacing-sm)] ds:py-[var(--spacing-xs)]',\n 'ds:[--atl-mask-top:black] ds:[--atl-mask-bottom:black]',\n 'ds:data-[fade-top=true]:[--atl-mask-top:transparent]',\n 'ds:data-[fade-bottom=true]:[--atl-mask-bottom:transparent]',\n 'ds:[mask-image:linear-gradient(to_bottom,var(--atl-mask-top)_0,black_var(--atl-fade-distance,var(--spacing-lg)),black_calc(100%-var(--atl-fade-distance,var(--spacing-lg))),var(--atl-mask-bottom)_100%)]',\n 'ds:motion-reduce:[mask-image:none]',\n 'ds:[.theme-accessible_&]:[mask-image:none]',\n 'ds:forced-colors:[mask-image:none]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:[outline-offset:-2px]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n].join(' ');\n\n/* -------------------------------------------------------------------- */\n/* Day grouping + header labels */\n/* -------------------------------------------------------------------- */\n\ninterface DayGroup {\n key: string;\n date: Date;\n items: AppointmentItem[];\n}\n\n/** Local calendar day key (`YYYY-MM-DD`) — sorts chronologically as a string. */\nfunction localDayKey(d: Date): string {\n const m = `${d.getMonth() + 1}`.padStart(2, '0');\n const day = `${d.getDate()}`.padStart(2, '0');\n return `${d.getFullYear()}-${m}-${day}`;\n}\n\nfunction groupByDay(items: AppointmentItem[]): DayGroup[] {\n const sorted = [...items].sort(\n (a, b) => +new Date(a.startIso) - +new Date(b.startIso),\n );\n const map = new Map<string, DayGroup>();\n for (const item of sorted) {\n const d = new Date(item.startIso);\n const key = localDayKey(d);\n let group = map.get(key);\n if (!group) {\n group = {\n key,\n date: new Date(d.getFullYear(), d.getMonth(), d.getDate()),\n items: [],\n };\n map.set(key, group);\n }\n group.items.push(item);\n }\n return [...map.values()];\n}\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\nexport const AppointmentTimeline = forwardRef<\n HTMLElement,\n AppointmentTimelineProps\n>(\n (\n {\n id,\n items,\n nowIso,\n title,\n locale,\n currency,\n size = 'sm',\n showPatient = false,\n onOpenAppointment,\n selectedId,\n onLoadEarlier,\n onLoadLater,\n hasMoreEarlier,\n hasMoreLater,\n loadingEarlier = false,\n loadingLater = false,\n showFilters = true,\n onJumpToDate,\n emptyTitle,\n emptyDescription,\n className,\n 'aria-label': ariaLabel,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const jumpId = useId();\n const prefersReducedMotion = usePrefersReducedMotion();\n const resolvedLocale = locale ?? i18n.language;\n\n const heading = title ?? t('appointmentTimeline.title');\n const now = useMemo(\n () => (nowIso ? new Date(nowIso) : new Date()),\n [nowIso],\n );\n\n const groups = useMemo(() => groupByDay(items), [items]);\n\n /* Reflow + exit transitions — the same pair notes and tasks use. When\n an appointment is added or removed, surviving cards slide to their\n new positions instead of snapping; a removed card fades out in place\n before the host drops it, so the removal reads as a removal. */\n const reduceMotion = prefersReducedMotion || undefined;\n const flatItemIds = useMemo(\n () => groups.flatMap((g) => g.items.map((i) => i.id)).join(),\n [groups],\n );\n const { containerRef: reflowRef } = useListReflow<HTMLOListElement>({\n key: flatItemIds,\n reduceMotion,\n });\n const exitTransition = useExitTransition({ reduceMotion });\n\n /* Track removed items so they can fade out in place. The previous\n items array is diffed against the current one; removed ids are held\n in `leavingItems` and rendered after the live groups, faded, until\n the exit transition elapses and drops them. */\n const prevItemsRef = useRef(items);\n const [leavingItems, setLeavingItems] = useState<AppointmentItem[]>([]);\n useEffect(() => {\n const prev = prevItemsRef.current;\n if (prev === items) return;\n const currentIds = new Set(items.map((i) => i.id));\n const removed = prev.filter((i) => !currentIds.has(i.id));\n if (removed.length > 0) {\n setLeavingItems((prev) => [...prev, ...removed]);\n removed.forEach((item) => {\n exitTransition.leave(item.id, () => {\n setLeavingItems((prev) => prev.filter((i) => i.id !== item.id));\n });\n });\n }\n prevItemsRef.current = items;\n }, [items, exitTransition]);\n\n const moreEarlier = hasMoreEarlier ?? !!onLoadEarlier;\n const moreLater = hasMoreLater ?? !!onLoadLater;\n\n /* The day to anchor on: today if present, else the nearest upcoming day,\n else the last day we have. */\n const anchorKey = useMemo(() => {\n if (groups.length === 0) return undefined;\n const todayKey = localDayKey(now);\n const keys = groups.map((g) => g.key);\n if (keys.includes(todayKey)) return todayKey;\n return keys.find((k) => k >= todayKey) ?? keys[keys.length - 1];\n }, [groups, now]);\n\n const scrollRef = useRef<HTMLDivElement>(null);\n const dayElRefs = useRef<Map<string, HTMLElement>>(new Map());\n const hasAnchored = useRef(false);\n const prevFirstKey = useRef<string | undefined>(undefined);\n const prevScrollHeight = useRef(0);\n\n const [fadeTop, setFadeTop] = useState(false);\n const [fadeBottom, setFadeBottom] = useState(false);\n\n const updateEdges = useCallback(() => {\n const region = scrollRef.current;\n if (!region) return;\n const { scrollTop, scrollHeight, clientHeight } = region;\n setFadeTop(scrollTop > 1);\n setFadeBottom(scrollTop + clientHeight < scrollHeight - 1);\n }, []);\n\n /* Scroll a day into view at the top of the region. Falls back to the\n nearest rendered day on/after the target (presets may land on an empty\n day). Uses getBoundingClientRect so it is offsetParent-agnostic. */\n const scrollToKey = useCallback(\n (targetKey: string, smooth: boolean) => {\n const region = scrollRef.current;\n if (!region) return;\n let el = dayElRefs.current.get(targetKey);\n if (!el) {\n const keys = [...dayElRefs.current.keys()].sort();\n const next =\n keys.find((k) => k >= targetKey) ?? keys[keys.length - 1];\n el = next ? dayElRefs.current.get(next) : undefined;\n }\n if (!el) return;\n const delta =\n el.getBoundingClientRect().top - region.getBoundingClientRect().top;\n region.scrollTo({\n top: region.scrollTop + delta,\n behavior: smooth && !prefersReducedMotion ? 'smooth' : 'auto',\n });\n },\n [prefersReducedMotion],\n );\n\n /* Anchor on mount; preserve the viewport when earlier days prepend. */\n useIsomorphicLayoutEffect(() => {\n const region = scrollRef.current;\n if (!region) return;\n const firstKey = groups[0]?.key;\n if (!hasAnchored.current) {\n if (groups.length > 0 && anchorKey) {\n scrollToKey(anchorKey, false);\n hasAnchored.current = true;\n }\n } else if (\n prevFirstKey.current &&\n firstKey &&\n firstKey < prevFirstKey.current\n ) {\n const grew = region.scrollHeight - prevScrollHeight.current;\n if (grew > 0) region.scrollTop += grew;\n }\n prevFirstKey.current = firstKey;\n prevScrollHeight.current = region.scrollHeight;\n updateEdges();\n }, [groups, anchorKey, scrollToKey, updateEdges]);\n\n /* Recompute the fades when the region resizes. */\n useIsomorphicLayoutEffect(() => {\n const region = scrollRef.current;\n if (!region || typeof ResizeObserver === 'undefined') return undefined;\n const observer = new ResizeObserver(updateEdges);\n observer.observe(region);\n return () => observer.disconnect();\n }, [updateEdges]);\n\n const handleScroll = useCallback(() => {\n updateEdges();\n const region = scrollRef.current;\n if (!region) return;\n const { scrollTop, scrollHeight, clientHeight } = region;\n if (\n onLoadEarlier &&\n moreEarlier &&\n !loadingEarlier &&\n scrollTop <= EDGE_LOAD_THRESHOLD\n ) {\n onLoadEarlier();\n }\n if (\n onLoadLater &&\n moreLater &&\n !loadingLater &&\n scrollHeight - scrollTop - clientHeight <= EDGE_LOAD_THRESHOLD\n ) {\n onLoadLater();\n }\n }, [\n updateEdges,\n onLoadEarlier,\n onLoadLater,\n moreEarlier,\n moreLater,\n loadingEarlier,\n loadingLater,\n ]);\n\n /* ----- Filter actions ----- */\n const jumpTo = useCallback(\n (date: Date) => {\n const key = localDayKey(date);\n const known = dayElRefs.current.has(key);\n scrollToKey(key, true);\n if (!known) onJumpToDate?.(date.toISOString());\n },\n [scrollToKey, onJumpToDate],\n );\n\n const handleToday = useCallback(() => jumpTo(now), [jumpTo, now]);\n const handleThisWeek = useCallback(() => {\n const start = new Date(now);\n const dow = (start.getDay() + 6) % 7; // Monday-based week start\n start.setDate(start.getDate() - dow);\n jumpTo(start);\n }, [jumpTo, now]);\n const handleThisMonth = useCallback(() => {\n jumpTo(new Date(now.getFullYear(), now.getMonth(), 1));\n }, [jumpTo, now]);\n\n /* ----- Agent handle ----- */\n const itemsRef = useRef<AppointmentItem[]>(items);\n itemsRef.current = items;\n const innerRef = useRef<HTMLElement>(null);\n useImperativeHandle(ref, () => innerRef.current as HTMLElement, []);\n\n const handle = useMemo<AppointmentTimelineHandle>(\n () => ({\n getItems: () =>\n itemsRef.current.map((i) => ({ id: i.id, state: i.state })),\n selectItem: (targetId: string) => {\n const found = itemsRef.current.find((i) => i.id === targetId);\n if (found) onOpenAppointment?.(found);\n },\n jumpToToday: () => scrollToKey(localDayKey(now), true),\n }),\n [onOpenAppointment, scrollToKey, now],\n );\n useAgentRegistration(appointmentTimelineAgent, handle, id);\n\n const isEmpty = groups.length === 0 && !loadingEarlier && !loadingLater;\n\n const loadingRow = (key: string) => (\n <div\n key={key}\n aria-hidden=\"true\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)] ds:py-[var(--spacing-xs)]\"\n >\n <Skeleton variant=\"rectangular\" height=\"3.5rem\" />\n </div>\n );\n\n return (\n <section\n ref={innerRef}\n data-component=\"appointment-timeline\"\n data-component-id={id}\n aria-label={ariaLabel ?? heading}\n className={rootVariants({ size, className })}\n {...rest}\n >\n {/* Shared rail header: title + (optional) filter row + divider. This\n panel owns its own scroll/fill, so the header stays a fixed band\n (sticky={false}). The rail injects the favourite/close cluster via\n PanelHeaderActionsProvider; standalone it renders none. */}\n <PanelHeader\n icon={<CalendarDays />}\n title={heading}\n filters={\n showFilters ? (\n <>\n <ButtonGroup label={t('appointmentTimeline.filtersLabel')}>\n <Button intent=\"outline\" size=\"sm\" onClick={handleToday}>\n {t('appointmentTimeline.today')}\n </Button>\n <Button intent=\"outline\" size=\"sm\" onClick={handleThisWeek}>\n {t('appointmentTimeline.thisWeek')}\n </Button>\n <Button intent=\"outline\" size=\"sm\" onClick={handleThisMonth}>\n {t('appointmentTimeline.thisMonth')}\n </Button>\n </ButtonGroup>\n <label htmlFor={jumpId} className=\"ds:sr-only\">\n {t('appointmentTimeline.jumpToDate')}\n </label>\n <DatePicker\n id={jumpId}\n size=\"sm\"\n placeholder={t('appointmentTimeline.jumpToDate')}\n onChange={(date) => {\n if (date) jumpTo(date);\n }}\n />\n </>\n ) : undefined\n }\n />\n\n {isEmpty ? (\n <div className=\"ds:flex-1 ds:p-[var(--spacing-md)]\">\n <EmptyState\n variant=\"first-use\"\n size=\"sm\"\n title={emptyTitle ?? t('appointmentTimeline.empty.title')}\n description={\n emptyDescription ?? t('appointmentTimeline.empty.description')\n }\n />\n </div>\n ) : (\n <div\n ref={scrollRef}\n // The scroll region must be keyboard-focusable so arrow / Home /\n // End can drive it (axe scrollable-region-focusable); jsx-a11y's\n // generic rule doesn't know this element scrolls.\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n tabIndex={0}\n onScroll={handleScroll}\n data-fade-top={fadeTop ? 'true' : undefined}\n data-fade-bottom={fadeBottom ? 'true' : undefined}\n className={scrollRegionClasses}\n >\n {loadingEarlier ? loadingRow('loading-earlier') : null}\n {/* eslint-disable-next-line jsx-a11y/no-redundant-roles -- role=\"list\" is NOT redundant: WebKit drops implicit list semantics when list-style is none */}\n <ol\n ref={reflowRef}\n role=\"list\"\n className=\"ds:m-0 ds:flex ds:list-none ds:flex-col ds:gap-[var(--spacing-md)] ds:p-0\"\n >\n {groups.map((group) => (\n <li\n key={group.key}\n ref={(node) => {\n if (node) dayElRefs.current.set(group.key, node);\n else dayElRefs.current.delete(group.key);\n }}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n >\n <DayHeader\n date={group.date}\n now={now}\n locale={resolvedLocale}\n todayLabel={t('appointmentTimeline.today')}\n tomorrowLabel={t('appointmentTimeline.tomorrow')}\n yesterdayLabel={t('appointmentTimeline.yesterday')}\n />\n {/* eslint-disable-next-line jsx-a11y/no-redundant-roles -- role=\"list\" is NOT redundant: WebKit drops implicit list semantics when list-style is none */}\n <ul\n role=\"list\"\n className=\"ds:m-0 ds:flex ds:list-none ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-0\"\n >\n {group.items.map((item) => (\n <AppointmentCard\n key={item.id}\n item={item}\n size={size}\n surface=\"elevated\"\n showPatient={showPatient}\n locale={resolvedLocale}\n currency={currency}\n onActivate={onOpenAppointment}\n selected={item.id === selectedId}\n data-appointment-id={item.id}\n data-reflow-id={item.id}\n className={REFLOW_ROW_CLASS}\n />\n ))}\n </ul>\n </li>\n ))}\n {/* Leaving cards: removed from state but held for the exit\n fade. Rendered without `data-reflow-id` so the reflow hook\n ignores them; faded via `data-leaving`. */}\n {leavingItems.length > 0\n ? leavingItems.map((item) => (\n <AppointmentCard\n key={`leaving-${item.id}`}\n item={item}\n size={size}\n surface=\"elevated\"\n showPatient={showPatient}\n locale={resolvedLocale}\n currency={currency}\n aria-hidden=\"true\"\n data-leaving\n className={[\n 'ds:opacity-0',\n 'ds:transition-opacity',\n 'ds:duration-[var(--animation-duration)]',\n 'ds:ease-[var(--ease-out)]',\n 'ds:motion-reduce:transition-none',\n ].join(' ')}\n />\n ))\n : null}\n </ol>\n {loadingLater ? loadingRow('loading-later') : null}\n </div>\n )}\n </section>\n );\n },\n);\n\nAppointmentTimeline.displayName = 'AppointmentTimeline';\n\n/* -------------------------------------------------------------------- */\n/* Day header — sticky label per day group */\n/* -------------------------------------------------------------------- */\n\ninterface DayHeaderProps {\n date: Date;\n now: Date;\n locale: string | undefined;\n todayLabel: string;\n tomorrowLabel: string;\n yesterdayLabel: string;\n}\n\nfunction DayHeader({\n date,\n now,\n locale,\n todayLabel,\n tomorrowLabel,\n yesterdayLabel,\n}: DayHeaderProps): ReactNode {\n const key = localDayKey(date);\n const todayKey = localDayKey(now);\n const tomorrow = new Date(now);\n tomorrow.setDate(now.getDate() + 1);\n const yesterday = new Date(now);\n yesterday.setDate(now.getDate() - 1);\n\n const isToday = key === todayKey;\n\n let label: string;\n if (isToday) label = todayLabel;\n else if (key === localDayKey(tomorrow)) label = tomorrowLabel;\n else if (key === localDayKey(yesterday)) label = yesterdayLabel;\n else {\n const sameYear = date.getFullYear() === now.getFullYear();\n label = new Intl.DateTimeFormat(locale, {\n weekday: 'short',\n day: 'numeric',\n month: 'short',\n ...(sameYear ? {} : { year: 'numeric' }),\n }).format(date);\n }\n\n // Today's group is marked with the brand colour + a leading dot so the\n // current day reads at a glance. `--primary` clears AA on the card in every\n // theme (violet-500 light, magenta on dark); the dot is decorative.\n return (\n <div\n data-today={isToday ? 'true' : undefined}\n className={[\n 'ds:sticky ds:top-0 ds:z-[1] ds:bg-[color:var(--card)] ds:py-[var(--spacing-2xs)]',\n 'ds:flex ds:items-center ds:gap-[var(--spacing-2xs)] type-eyebrow',\n isToday\n ? 'ds:text-[color:var(--primary)] ds:font-semibold'\n : 'ds:text-[color:var(--muted-foreground)]',\n ].join(' ')}\n >\n {isToday ? (\n <span\n aria-hidden=\"true\"\n className=\"ds:size-1.5 ds:shrink-0 ds:rounded-[var(--radius-full)] ds:bg-[color:var(--primary)]\"\n />\n ) : null}\n {label}\n </div>\n );\n}\n\n/** Re-export of the size variant union for downstream typing. */\nexport type AppointmentTimelineSize = NonNullable<\n VariantProps<typeof rootVariants>['size']\n>;\n"],"names":["appointmentTimelineAgent","handle","args","EDGE_LOAD_THRESHOLD","rootVariants","cva","scrollRegionClasses","localDayKey","d","m","day","groupByDay","items","sorted","a","b","map","item","key","group","AppointmentTimeline","forwardRef","id","nowIso","title","locale","currency","size","showPatient","onOpenAppointment","selectedId","onLoadEarlier","onLoadLater","hasMoreEarlier","hasMoreLater","loadingEarlier","loadingLater","showFilters","onJumpToDate","emptyTitle","emptyDescription","className","ariaLabel","rest","ref","t","i18n","useTranslation","jumpId","useId","prefersReducedMotion","usePrefersReducedMotion","resolvedLocale","heading","now","useMemo","groups","reduceMotion","flatItemIds","g","i","reflowRef","useListReflow","exitTransition","useExitTransition","prevItemsRef","useRef","leavingItems","setLeavingItems","useState","useEffect","prev","currentIds","removed","moreEarlier","moreLater","anchorKey","todayKey","keys","k","scrollRef","dayElRefs","hasAnchored","prevFirstKey","prevScrollHeight","fadeTop","setFadeTop","fadeBottom","setFadeBottom","updateEdges","useCallback","region","scrollTop","scrollHeight","clientHeight","scrollToKey","targetKey","smooth","el","next","delta","useIsomorphicLayoutEffect","firstKey","_a","grew","observer","handleScroll","jumpTo","date","known","handleToday","handleThisWeek","start","dow","handleThisMonth","itemsRef","innerRef","useImperativeHandle","targetId","found","useAgentRegistration","isEmpty","loadingRow","jsx","Skeleton","jsxs","PanelHeader","CalendarDays","Fragment","ButtonGroup","Button","DatePicker","EmptyState","node","DayHeader","AppointmentCard","REFLOW_ROW_CLASS","todayLabel","tomorrowLabel","yesterdayLabel","tomorrow","yesterday","isToday","label","sameYear"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { forwardRef as j, useRef as h, useImperativeHandle as z, useMemo as P }
3
3
  import { c as E } from "./index-D2ZczOXr.js";
4
4
  import { useTranslation as H } from "react-i18next";
5
5
  import { I as M } from "./icon-button-8xqRnkdG.js";
6
- import { S as q } from "./skeleton-CZbwyJAA.js";
6
+ import { S as q } from "./skeleton-B1KTdJ4N.js";
7
7
  import { E as K } from "./empty-state-X0sVPfVo.js";
8
8
  import { A as V } from "./appointment-card-WsDGBBut.js";
9
9
  import { u as _ } from "./registry-DvAUVLHh.js";
@@ -190,4 +190,4 @@ export {
190
190
  ae as a,
191
191
  L as b
192
192
  };
193
- //# sourceMappingURL=appointment-tray-D6UyJHej.js.map
193
+ //# sourceMappingURL=appointment-tray-CK2CULg_.js.map