@alfadocs/ui-kit-debug 1.11.0 → 1.12.4

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 (88) hide show
  1. package/dist/_chunks/{actions-cell-renderer-xfSYiTXQ.js → actions-cell-renderer-BCEjn3-f.js} +2 -2
  2. package/dist/_chunks/{actions-cell-renderer-xfSYiTXQ.js.map → actions-cell-renderer-BCEjn3-f.js.map} +1 -1
  3. package/dist/_chunks/{anamnesis-summary-rows-d44s_whc.js → anamnesis-summary-rows-BfhxWPO3.js} +1421 -1418
  4. package/dist/_chunks/anamnesis-summary-rows-BfhxWPO3.js.map +1 -0
  5. package/dist/_chunks/appointment-card-Dzr3GeVz.js.map +1 -1
  6. package/dist/_chunks/appointment-timeline-DCn0Jx55.js +403 -0
  7. package/dist/_chunks/appointment-timeline-DCn0Jx55.js.map +1 -0
  8. package/dist/_chunks/{balance-badge-cell-C_Qz76x7.js → balance-badge-cell-BrlikHfT.js} +317 -290
  9. package/dist/_chunks/balance-badge-cell-BrlikHfT.js.map +1 -0
  10. package/dist/_chunks/{chart-Bkr1Sh4o.js → chart-C6eHTxkO.js} +2 -2
  11. package/dist/_chunks/{chart-Bkr1Sh4o.js.map → chart-C6eHTxkO.js.map} +1 -1
  12. package/dist/_chunks/{control-room-rail-Cj2iX9h_.js → control-room-rail-iO_BCaq1.js} +2 -2
  13. package/dist/_chunks/{control-room-rail-Cj2iX9h_.js.map → control-room-rail-iO_BCaq1.js.map} +1 -1
  14. package/dist/_chunks/{editable-currency-cell-renderer-zLf_bXvL.js → editable-currency-cell-renderer-B7TVZ5ZJ.js} +2 -2
  15. package/dist/_chunks/{editable-currency-cell-renderer-zLf_bXvL.js.map → editable-currency-cell-renderer-B7TVZ5ZJ.js.map} +1 -1
  16. package/dist/_chunks/{face-scheme-BfDi4YkW.js → face-scheme-CZaKm6fG.js} +2 -2
  17. package/dist/_chunks/{face-scheme-BfDi4YkW.js.map → face-scheme-CZaKm6fG.js.map} +1 -1
  18. package/dist/_chunks/{file-manager-Df6hyUYg.js → file-manager-Moc7sUFj.js} +3 -3
  19. package/dist/_chunks/{file-manager-Df6hyUYg.js.map → file-manager-Moc7sUFj.js.map} +1 -1
  20. package/dist/_chunks/{header-settings-CVC_davE.js → header-settings-J-ZCAfir.js} +2 -2
  21. package/dist/_chunks/{header-settings-CVC_davE.js.map → header-settings-J-ZCAfir.js.map} +1 -1
  22. package/dist/_chunks/{icon-button-group-D6SVMteH.js → icon-button-group-VCCod3Hz.js} +154 -145
  23. package/dist/_chunks/icon-button-group-VCCod3Hz.js.map +1 -0
  24. package/dist/_chunks/{link-cell-renderer-Cp2MSlJl.js → link-cell-renderer-CenJx-_7.js} +197 -181
  25. package/dist/_chunks/link-cell-renderer-CenJx-_7.js.map +1 -0
  26. package/dist/_chunks/{marketplace-app-shell-Chfdia7m.js → marketplace-app-shell-DDQXQ8hX.js} +2 -2
  27. package/dist/_chunks/{marketplace-app-shell-Chfdia7m.js.map → marketplace-app-shell-DDQXQ8hX.js.map} +1 -1
  28. package/dist/_chunks/{notes-panel-e9cJWVRg.js → notes-panel-CymneXtl.js} +93 -91
  29. package/dist/_chunks/notes-panel-CymneXtl.js.map +1 -0
  30. package/dist/_chunks/{signature-request-CW30B77U.js → signature-request-DVDTKLjM.js} +2 -2
  31. package/dist/_chunks/{signature-request-CW30B77U.js.map → signature-request-DVDTKLjM.js.map} +1 -1
  32. package/dist/_chunks/{theme-toggle-DuMhQNFS.js → theme-toggle-uEwoKjl3.js} +2 -2
  33. package/dist/_chunks/{theme-toggle-DuMhQNFS.js.map → theme-toggle-uEwoKjl3.js.map} +1 -1
  34. package/dist/_chunks/{tool-rail-CHyUmulN.js → tool-rail-8Scslq3X.js} +2 -2
  35. package/dist/_chunks/tool-rail-8Scslq3X.js.map +1 -0
  36. package/dist/_chunks/{whatsapp-button-Ca9towA4.js → whatsapp-button-CuFIHJzG.js} +27 -19
  37. package/dist/_chunks/{whatsapp-button-Ca9towA4.js.map → whatsapp-button-CuFIHJzG.js.map} +1 -1
  38. package/dist/agent-catalog.json +2 -2
  39. package/dist/agent-i18n/en.json +1 -1
  40. package/dist/components/appointment-card/appointment-card.d.ts +1 -1
  41. package/dist/components/appointment-card/appointment-card.d.ts.map +1 -1
  42. package/dist/components/appointment-timeline/appointment-timeline.agent.d.ts.map +1 -1
  43. package/dist/components/appointment-timeline/appointment-timeline.d.ts +8 -0
  44. package/dist/components/appointment-timeline/appointment-timeline.d.ts.map +1 -1
  45. package/dist/components/appointment-timeline/index.js +1 -1
  46. package/dist/components/chart/index.js +1 -1
  47. package/dist/components/control-room-rail/index.js +1 -1
  48. package/dist/components/data-table/filters/date-range-filter.d.ts +22 -0
  49. package/dist/components/data-table/filters/date-range-filter.d.ts.map +1 -1
  50. package/dist/components/data-table/index.js +3 -3
  51. package/dist/components/face-scheme/index.js +1 -1
  52. package/dist/components/file-manager/index.js +1 -1
  53. package/dist/components/header-settings/index.js +1 -1
  54. package/dist/components/icon-button-group/icon-button-group.d.ts.map +1 -1
  55. package/dist/components/icon-button-group/index.js +1 -1
  56. package/dist/components/notes-panel/index.js +1 -1
  57. package/dist/components/notes-panel/notes-panel.agent.d.ts.map +1 -1
  58. package/dist/components/notes-panel/notes-panel.d.ts +6 -0
  59. package/dist/components/notes-panel/notes-panel.d.ts.map +1 -1
  60. package/dist/components/patient-table/columns.d.ts.map +1 -1
  61. package/dist/components/patient-table/index.js +1 -1
  62. package/dist/components/theme-toggle/index.js +1 -1
  63. package/dist/components/tool-rail/index.js +1 -1
  64. package/dist/components/tool-rail/tool-rail.d.ts +1 -1
  65. package/dist/components/tool-rail/tool-rail.d.ts.map +1 -1
  66. package/dist/components/whatsapp-button/index.js +1 -1
  67. package/dist/components/whatsapp-button/whatsapp-button.d.ts.map +1 -1
  68. package/dist/host-compat.css +7 -1
  69. package/dist/index.js +16 -16
  70. package/dist/patterns/anamnesis/anamnesis-catalogue.d.ts.map +1 -1
  71. package/dist/patterns/anamnesis/anamnesis-compile.d.ts.map +1 -1
  72. package/dist/patterns/anamnesis/anamnesis-config.d.ts +18 -7
  73. package/dist/patterns/anamnesis/anamnesis-config.d.ts.map +1 -1
  74. package/dist/patterns/anamnesis/anamnesis-types.d.ts +8 -0
  75. package/dist/patterns/anamnesis/anamnesis-types.d.ts.map +1 -1
  76. package/dist/patterns/anamnesis/index.js +1 -1
  77. package/dist/patterns/marketplace-app-shell/index.js +1 -1
  78. package/dist/patterns/signature-request/index.js +1 -1
  79. package/dist/tokens.css +1 -1
  80. package/package.json +1 -1
  81. package/dist/_chunks/anamnesis-summary-rows-d44s_whc.js.map +0 -1
  82. package/dist/_chunks/appointment-timeline-0mwtpErq.js +0 -388
  83. package/dist/_chunks/appointment-timeline-0mwtpErq.js.map +0 -1
  84. package/dist/_chunks/balance-badge-cell-C_Qz76x7.js.map +0 -1
  85. package/dist/_chunks/icon-button-group-D6SVMteH.js.map +0 -1
  86. package/dist/_chunks/link-cell-renderer-Cp2MSlJl.js.map +0 -1
  87. package/dist/_chunks/notes-panel-e9cJWVRg.js.map +0 -1
  88. package/dist/_chunks/tool-rail-CHyUmulN.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"appointment-card-Dzr3GeVz.js","sources":["../../node_modules/lucide-react/dist/esm/icons/clipboard-plus.mjs","../../src/components/appointment-card/appointment-card.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"rect\", { width: \"8\", height: \"4\", x: \"8\", y: \"2\", rx: \"1\", ry: \"1\", key: \"tgr4d6\" }],\n [\n \"path\",\n {\n d: \"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2\",\n key: \"116196\"\n }\n ],\n [\"path\", { d: \"M9 14h6\", key: \"159ibu\" }],\n [\"path\", { d: \"M12 17v-6\", key: \"1y8rbf\" }]\n];\nconst ClipboardPlus = createLucideIcon(\"clipboard-plus\", __iconNode);\n\nexport { __iconNode, ClipboardPlus as default };\n//# sourceMappingURL=clipboard-plus.mjs.map\n","import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport {\n Armchair,\n ChevronRight,\n ClipboardPlus,\n Globe,\n MapPin,\n Star,\n Video,\n} from 'lucide-react';\nimport { Avatar } from '../avatar/avatar';\nimport { Badge } from '../badge/badge';\nimport { Tag } from '../tag/tag';\nimport { Timestamp } from '../timestamp/timestamp';\nimport { TransactionChip } from '../transaction-chip/transaction-chip';\nimport {\n appointmentStateBadge,\n colorIdToToken,\n colorIdToAvatarToken,\n isSafeEntityUrl,\n} from '../_shared/entity-card';\nimport type { AppointmentState } from '../_shared/entity-card';\n\n/*\n * Closed colour-dot class map. `colorIdToToken` resolves the operator's\n * numeric colour id to one of a fixed set of palette tokens; the operator\n * dot needs a *static* Tailwind class for each so the compiler can emit it\n * (dynamic `ds:bg-[…]` strings are not seen by the JIT). The keys mirror the\n * exact token strings the shared helper returns.\n */\nconst OPERATOR_DOT_CLASS: Record<string, string> = {\n 'var(--color-violet-500)': 'ds:bg-[var(--color-violet-500)]',\n 'var(--color-purple-500)': 'ds:bg-[var(--color-purple-500)]',\n 'var(--color-magenta-500)': 'ds:bg-[var(--color-magenta-500)]',\n 'var(--color-blue-500)': 'ds:bg-[var(--color-blue-500)]',\n 'var(--color-green-500)': 'ds:bg-[var(--color-green-500)]',\n 'var(--color-red-500)': 'ds:bg-[var(--color-red-500)]',\n 'var(--color-yellow-500)': 'ds:bg-[var(--color-yellow-500)]',\n 'var(--color-indigo-500)': 'ds:bg-[var(--color-indigo-500)]',\n 'var(--color-orange-500)': 'ds:bg-[var(--color-orange-500)]',\n 'var(--color-fuchsia-500)': 'ds:bg-[var(--color-fuchsia-500)]',\n 'var(--color-grey-700)': 'ds:bg-[var(--color-grey-700)]',\n};\n\nfunction operatorDotClass(colorId: number | string | undefined): string {\n return (\n OPERATOR_DOT_CLASS[colorIdToToken(colorId)] ??\n 'ds:bg-[var(--color-grey-700)]'\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* AppointmentCard */\n/* */\n/* Single-row appointment surface — sibling of AgendaCard, but richer: */\n/* leading timestamp, operator + patient avatars with the operator */\n/* colour dot, a lifecycle-state Badge (announced by label, never colour */\n/* alone), treatment / tooth Tags, and an optional advance-deposit */\n/* TransactionChip. State→Badge-variant + colour-id→token + URL-safety */\n/* come from the shared `_shared/entity-card` helpers. */\n/* */\n/* All visible text flows through useTranslation('ui') against the */\n/* `appointmentCard.*` namespace (plus shared `financial.*` for money). */\n/* -------------------------------------------------------------------- */\n\n// Back-compat / one-stop import — the URL guard lives in the shared helper.\nexport { isSafeEntityUrl as isSafeAppointmentUrl } from '../_shared/is-safe-url';\n\n/* -------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------- */\n\n/** Appointment delivery channel. */\nexport type AppointmentType = 'inPractice' | 'telehealth';\n\n/** First-visit tri-state flag. */\nexport type AppointmentFirstVisit = 'yes' | 'no' | 'unknown';\n\n/** Operator (clinician) summary — name + optional palette colour id. */\nexport interface AppointmentOperator {\n /** Operator display name — drives the avatar initials + accessible name. */\n name: string;\n /** Numeric palette colour id, resolved to a kit token for the colour dot. */\n colorId?: number | string;\n}\n\n/** Patient summary — identity only, drives the avatar. */\nexport interface AppointmentPatient {\n /** Patient display name. */\n name: string;\n /** Optional avatar image URL. */\n avatarUrl?: string;\n}\n\n/** One treatment line within the appointment. */\nexport interface AppointmentTreatment {\n /** Localised care / treatment name. */\n careName: string;\n /** Optional tooth elements (e.g. FDI ids) the treatment targets. */\n elements?: string[];\n}\n\n/** Booking-reason metadata captured at online-booking time. */\nexport interface AppointmentBookingReason {\n /** Localised booking-reason description. */\n description: string;\n /** Expected duration in minutes. */\n durationMin?: number;\n /** Advance / deposit amount in cents (non-negative). */\n advanceAmountCents?: number;\n /** Localised specialty label. */\n specialty?: string;\n}\n\n/** Boolean appointment flags surfaced as chips / indicators. */\nexport interface AppointmentFlags {\n bookedOnline?: boolean;\n noPatientAssigned?: boolean;\n highlightedAnamnesis?: boolean;\n billable?: boolean;\n}\n\nexport interface AppointmentItem {\n /** Stable appointment identifier. */\n id: string;\n /** ISO-8601 start instant. */\n startIso: string;\n /** Duration in minutes. */\n durationMin: number;\n /** Lifecycle state — drives the Badge variant + label. */\n state: AppointmentState;\n /** Delivery channel — drives the leading type glyph. */\n type: AppointmentType;\n /** First-visit flag. */\n firstVisit?: AppointmentFirstVisit;\n /** Assigned operator. */\n operator?: AppointmentOperator;\n /** Chair / room label — surfaced as a chip. */\n chair?: string;\n /**\n * The care plan this appointment belongs to, when any — surfaced as a chip\n * (`Care plan {number}`). `url` is the deep link the consumer may use for\n * navigation; the chip itself is non-interactive so it never nests an anchor\n * inside the row's own stretched link.\n */\n carePlan?: { number: string; url?: string };\n /** Patient identity. */\n patient?: AppointmentPatient;\n /** Treatment lines. */\n treatments?: AppointmentTreatment[];\n /** Online booking-reason metadata. */\n bookingReason?: AppointmentBookingReason;\n /** Boolean flags. */\n flags?: AppointmentFlags;\n /** Optional deep link — when present (and safe) the row renders as an anchor. */\n url?: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* CVA — row */\n/* -------------------------------------------------------------------- */\n\nconst appointmentCardVariants = cva(\n [\n 'ds:relative ds:flex ds:items-start ds:gap-[var(--spacing-sm)]',\n 'ds:rounded-[var(--radius-md)]',\n 'ds:text-start ds:group',\n 'ds:bg-[var(--card)] ds:text-[color:var(--card-foreground)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:transition-none',\n 'ds:forced-colors:border ds:forced-colors:border-[CanvasText]',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:p-[var(--spacing-sm)]',\n md: 'ds:p-[var(--spacing-md)]',\n },\n interactive: {\n true: 'ds:hover:bg-[color:var(--muted)]/40 ds:cursor-pointer',\n false: '',\n },\n surface: {\n // `flat` (default) is the chrome-less row used inside a bordered\n // AppointmentTray. `elevated` gives each row its own card chrome\n // (border + shadow) for use directly on a rail, e.g. AppointmentTimeline.\n // Uses `--shadow-sm` (a row lift), not `--shadow-card`/`--shadow-lg`:\n // the rail's `overflow-y:auto` scroll region clips `overflow-x` at a\n // narrow gutter, so a wide 30px-blur shadow gets a hard-edged clip.\n flat: '',\n elevated:\n 'ds:border ds:border-[color:var(--card-border)] ds:[.theme-accessible_&]:border-2 ds:shadow-[var(--shadow-sm)]',\n },\n },\n defaultVariants: {\n size: 'sm',\n interactive: false,\n surface: 'flat',\n },\n },\n);\n\nconst stretchedLinkClass = [\n 'ds:focus-visible:outline-none',\n \"ds:after:content-[''] ds:after:absolute ds:after:inset-0 ds:after:rounded-[var(--radius-md)]\",\n 'ds:after:pointer-events-auto',\n 'ds:focus-visible:after:outline-[length:var(--focus-ring-width)]',\n 'ds:focus-visible:after:outline-solid',\n 'ds:focus-visible:after:outline-[color:var(--ring)]',\n 'ds:focus-visible:after:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:after:outline-[CanvasText]',\n].join(' ');\n\nconst TYPE_ICON: Record<AppointmentType, typeof MapPin> = {\n inPractice: MapPin,\n telehealth: Video,\n};\n\n/* -------------------------------------------------------------------- */\n/* Props */\n/* -------------------------------------------------------------------- */\n\nexport interface AppointmentCardProps\n extends\n Omit<HTMLAttributes<HTMLDivElement>, 'onClick' | 'role' | 'title'>,\n VariantProps<typeof appointmentCardVariants> {\n /** The appointment to render. */\n item: AppointmentItem;\n /** Visual density. */\n size?: 'sm' | 'md';\n /**\n * `'flat'` (default) — chrome-less row for use inside a bordered\n * `AppointmentTray`. `'elevated'` — each row carries its own card chrome\n * (border + shadow), for placing rows directly on a rail (AppointmentTimeline).\n */\n surface?: 'flat' | 'elevated';\n /** BCP-47 locale override for the timestamp + currency. */\n locale?: string;\n /** ISO-4217 currency code for the advance-deposit chip. Defaults to `EUR`. */\n currency?: string;\n /**\n * Show the patient identity (avatar + name as the row title). Default `true`.\n * Set `false` in single-patient surfaces (e.g. a patient's own\n * `AppointmentTimeline`) where repeating the name is redundant — the row then\n * leads with the operator as its title, falling back to a generic label.\n */\n showPatient?: boolean;\n /** Fires when the row is activated. */\n onActivate?: (item: AppointmentItem) => void;\n}\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\nexport const AppointmentCard = forwardRef<HTMLDivElement, AppointmentCardProps>(\n (\n {\n item,\n size = 'sm',\n surface = 'flat',\n locale,\n currency = 'EUR',\n showPatient = true,\n onActivate,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const interactive = !!onActivate;\n const hasSafeUrl = !!item.url && isSafeEntityUrl(item.url);\n\n const patientName = item.patient?.name ?? t('appointmentCard.noPatient');\n const operatorName = item.operator?.name;\n const stateLabel = t(`appointmentCard.state.${item.state}`);\n\n const avatarSize = size === 'md' ? 'md' : 'sm';\n const TypeIcon = TYPE_ICON[item.type];\n\n // In single-patient context the patient name is redundant, so the row leads\n // with the operator (the \"who am I seeing\") and falls back to a generic\n // label when no operator is set.\n const title = showPatient\n ? patientName\n : (operatorName ?? t('appointmentCard.appointment'));\n\n const ariaLabel = showPatient\n ? t('appointmentCard.itemLabel', {\n patient: patientName,\n state: stateLabel,\n })\n : t('appointmentCard.itemLabelGeneric', { title, state: stateLabel });\n\n const titleNode = (\n <span className=\"ds:min-w-0 ds:truncate type-title-item ds:text-[color:var(--foreground)]\">\n {title}\n </span>\n );\n\n let activator: ReactNode;\n if (interactive && hasSafeUrl) {\n activator = (\n <a\n href={item.url}\n aria-label={ariaLabel}\n className={stretchedLinkClass + ' ds:contents'}\n onClick={(e) => {\n if (e.defaultPrevented || e.metaKey || e.ctrlKey || e.shiftKey)\n return;\n onActivate?.(item);\n }}\n >\n {titleNode}\n </a>\n );\n } else if (interactive) {\n activator = (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n className={\n stretchedLinkClass +\n ' ds:contents ds:bg-transparent ds:border-0 ds:p-0 ds:text-start'\n }\n onClick={() => onActivate?.(item)}\n >\n {titleNode}\n </button>\n );\n } else {\n activator = titleNode;\n }\n\n // Treatment / tooth-element Tags. Each treatment becomes one Tag; the\n // tooth elements (when present) are appended to the visible label.\n const treatmentTags = (item.treatments ?? []).map((tx, index) => {\n const label =\n tx.elements && tx.elements.length > 0\n ? t('appointmentCard.treatmentWithElements', {\n care: tx.careName,\n elements: tx.elements.join(', '),\n })\n : tx.careName;\n return (\n <Tag\n key={`${tx.careName}-${index}`}\n label={label}\n variant=\"neutral\"\n fill=\"outline\"\n size=\"sm\"\n maxInlineSize=\"12rem\"\n />\n );\n });\n\n const advanceCents = item.bookingReason?.advanceAmountCents;\n const hasAdvance = typeof advanceCents === 'number' && advanceCents > 0;\n\n return (\n <div\n ref={ref}\n role=\"listitem\"\n data-component=\"appointment-card\"\n data-component-id={item.id}\n data-state={item.state}\n data-type={item.type}\n className={appointmentCardVariants({\n size,\n interactive,\n surface,\n className,\n })}\n {...rest}\n >\n {/* Leading rail: type glyph + start time + duration */}\n <span className=\"ds:flex ds:w-16 ds:shrink-0 ds:flex-col ds:items-start ds:gap-[var(--spacing-2xs)]\">\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)] ds:text-[color:var(--muted-foreground)]\">\n <TypeIcon aria-hidden=\"true\" className=\"ds:size-3.5 ds:shrink-0\" />\n <span className=\"ds:sr-only\">\n {t(`appointmentCard.type.${item.type}`)}\n </span>\n <Timestamp\n value={item.startIso}\n format=\"absolute\"\n shape=\"bare\"\n locale={locale}\n absoluteFormat={{ hour: 'numeric', minute: '2-digit' }}\n className=\"type-body-sm ds:font-medium ds:text-[color:var(--foreground)] ds:tabular-nums\"\n />\n </span>\n <span className=\"type-eyebrow ds:text-[color:var(--muted-foreground)]\">\n {t('appointmentCard.duration', { count: item.durationMin })}\n </span>\n </span>\n\n {/* Operator avatar with colour dot */}\n {operatorName ? (\n <span className=\"ds:relative ds:shrink-0\">\n <Avatar\n name={operatorName}\n color={colorIdToAvatarToken(item.operator?.colorId)}\n size={avatarSize}\n aria-hidden=\"true\"\n role=\"presentation\"\n aria-label={undefined}\n />\n <span\n aria-hidden=\"true\"\n className={\n 'ds:absolute ds:bottom-0 ds:end-0 ds:size-2.5 ds:rounded-[var(--radius-full)] ds:border ds:border-[color:var(--card)] ' +\n operatorDotClass(item.operator?.colorId)\n }\n />\n </span>\n ) : null}\n\n {/* Main content column */}\n <div className=\"ds:flex-1 ds:min-w-0 ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <div className=\"ds:flex ds:items-start ds:gap-[var(--spacing-sm)]\">\n <div className=\"ds:flex-1 ds:min-w-0 ds:flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n {showPatient && item.patient?.avatarUrl ? (\n <Avatar\n name={patientName}\n src={item.patient.avatarUrl}\n size={avatarSize}\n className=\"ds:shrink-0\"\n aria-hidden=\"true\"\n role=\"presentation\"\n aria-label={undefined}\n />\n ) : null}\n {activator}\n </div>\n <Badge\n variant={appointmentStateBadge(item.state)}\n size=\"sm\"\n withDot\n className=\"ds:shrink-0\"\n >\n {stateLabel}\n </Badge>\n </div>\n\n {/* Meta line: operator name · specialty · first visit\n (chair moved to a chip below). When the patient is hidden the\n operator is promoted to the title, so it drops out of the meta. */}\n {(showPatient && operatorName) ||\n item.bookingReason?.specialty ||\n item.firstVisit === 'yes' ? (\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-x-[var(--spacing-sm)] ds:gap-y-[var(--spacing-2xs)] type-eyebrow ds:text-[color:var(--muted-foreground)]\">\n {showPatient && operatorName ? <span>{operatorName}</span> : null}\n {item.bookingReason?.specialty ? (\n <span>{item.bookingReason.specialty}</span>\n ) : null}\n {item.firstVisit === 'yes' ? (\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]\">\n <Star aria-hidden=\"true\" className=\"ds:size-3 ds:shrink-0\" />\n {t('appointmentCard.firstVisit')}\n </span>\n ) : null}\n </div>\n ) : null}\n\n {/* Booking reason */}\n {item.bookingReason?.description ? (\n <span className=\"type-body-sm ds:text-[color:var(--foreground)] ds:truncate\">\n {item.bookingReason.description}\n </span>\n ) : null}\n\n {/* Treatment / tooth Tags */}\n {treatmentTags.length > 0 ? (\n <div className=\"ds:flex ds:flex-wrap ds:gap-[var(--spacing-2xs)]\">\n {treatmentTags}\n </div>\n ) : null}\n\n {/* Chips: care plan · chair · flags · advance-deposit */}\n {item.carePlan ||\n item.chair ||\n item.flags?.bookedOnline ||\n item.flags?.highlightedAnamnesis ||\n hasAdvance ? (\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-xs)]\">\n {item.carePlan ? (\n <Badge\n variant=\"accentSoft\"\n size=\"sm\"\n leading={<ClipboardPlus />}\n >\n {t('appointmentCard.carePlan', {\n number: item.carePlan.number,\n })}\n </Badge>\n ) : null}\n {item.chair ? (\n <Badge variant=\"neutral\" size=\"sm\" leading={<Armchair />}>\n {t('appointmentCard.chair', { chair: item.chair })}\n </Badge>\n ) : null}\n {item.flags?.bookedOnline ? (\n <Badge variant=\"info\" size=\"sm\" leading={<Globe />}>\n {t('appointmentCard.flag.bookedOnline')}\n </Badge>\n ) : null}\n {item.flags?.highlightedAnamnesis ? (\n <Badge variant=\"warning\" size=\"sm\" withDot>\n {t('appointmentCard.flag.highlightedAnamnesis')}\n </Badge>\n ) : null}\n {hasAdvance ? (\n <TransactionChip\n state=\"to-invoice\"\n amount={(advanceCents as number) / 100}\n currency={currency}\n locale={locale}\n size=\"sm\"\n showLabel\n />\n ) : null}\n </div>\n ) : null}\n </div>\n\n {interactive ? (\n <ChevronRight\n aria-hidden=\"true\"\n className=\"ds:size-4 ds:shrink-0 ds:self-center ds:text-[color:var(--muted-foreground)] ds:opacity-0 ds:transition-opacity ds:motion-reduce:transition-none ds:group-hover:opacity-100 ds:group-focus-within:opacity-100 ds:rtl:rotate-180\"\n />\n ) : null}\n </div>\n );\n },\n);\n\nAppointmentCard.displayName = 'AppointmentCard';\n"],"names":["__iconNode","ClipboardPlus","createLucideIcon","OPERATOR_DOT_CLASS","operatorDotClass","colorId","colorIdToToken","appointmentCardVariants","cva","stretchedLinkClass","TYPE_ICON","MapPin","Video","AppointmentCard","forwardRef","item","size","surface","locale","currency","showPatient","onActivate","className","rest","ref","t","useTranslation","interactive","hasSafeUrl","isSafeEntityUrl","patientName","_a","operatorName","_b","stateLabel","avatarSize","TypeIcon","title","ariaLabel","titleNode","jsx","activator","treatmentTags","tx","index","label","Tag","advanceCents","_c","hasAdvance","jsxs","Timestamp","Avatar","colorIdToAvatarToken","_d","_e","_f","Badge","appointmentStateBadge","_g","_h","Star","_i","_j","_k","Armchair","_l","Globe","_m","TransactionChip","ChevronRight"],"mappings":"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","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"appointment-card-Dzr3GeVz.js","sources":["../../node_modules/lucide-react/dist/esm/icons/clipboard-plus.mjs","../../src/components/appointment-card/appointment-card.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"rect\", { width: \"8\", height: \"4\", x: \"8\", y: \"2\", rx: \"1\", ry: \"1\", key: \"tgr4d6\" }],\n [\n \"path\",\n {\n d: \"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2\",\n key: \"116196\"\n }\n ],\n [\"path\", { d: \"M9 14h6\", key: \"159ibu\" }],\n [\"path\", { d: \"M12 17v-6\", key: \"1y8rbf\" }]\n];\nconst ClipboardPlus = createLucideIcon(\"clipboard-plus\", __iconNode);\n\nexport { __iconNode, ClipboardPlus as default };\n//# sourceMappingURL=clipboard-plus.mjs.map\n","import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport {\n Armchair,\n ChevronRight,\n ClipboardPlus,\n Globe,\n MapPin,\n Star,\n Video,\n} from 'lucide-react';\nimport { Avatar } from '../avatar/avatar';\nimport { Badge } from '../badge/badge';\nimport { Tag } from '../tag/tag';\nimport { Timestamp } from '../timestamp/timestamp';\nimport { TransactionChip } from '../transaction-chip/transaction-chip';\nimport {\n appointmentStateBadge,\n colorIdToToken,\n colorIdToAvatarToken,\n isSafeEntityUrl,\n} from '../_shared/entity-card';\nimport type { AppointmentState } from '../_shared/entity-card';\n\n/*\n * Closed colour-dot class map. `colorIdToToken` resolves the operator's\n * numeric colour id to one of a fixed set of palette tokens; the operator\n * dot needs a *static* Tailwind class for each so the compiler can emit it\n * (dynamic `ds:bg-[…]` strings are not seen by the JIT). The keys mirror the\n * exact token strings the shared helper returns.\n */\nconst OPERATOR_DOT_CLASS: Record<string, string> = {\n 'var(--color-violet-500)': 'ds:bg-[var(--color-violet-500)]',\n 'var(--color-purple-500)': 'ds:bg-[var(--color-purple-500)]',\n 'var(--color-magenta-500)': 'ds:bg-[var(--color-magenta-500)]',\n 'var(--color-blue-500)': 'ds:bg-[var(--color-blue-500)]',\n 'var(--color-green-500)': 'ds:bg-[var(--color-green-500)]',\n 'var(--color-red-500)': 'ds:bg-[var(--color-red-500)]',\n 'var(--color-yellow-500)': 'ds:bg-[var(--color-yellow-500)]',\n 'var(--color-indigo-500)': 'ds:bg-[var(--color-indigo-500)]',\n 'var(--color-orange-500)': 'ds:bg-[var(--color-orange-500)]',\n 'var(--color-fuchsia-500)': 'ds:bg-[var(--color-fuchsia-500)]',\n 'var(--color-grey-700)': 'ds:bg-[var(--color-grey-700)]',\n};\n\nfunction operatorDotClass(colorId: number | string | undefined): string {\n return (\n OPERATOR_DOT_CLASS[colorIdToToken(colorId)] ??\n 'ds:bg-[var(--color-grey-700)]'\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* AppointmentCard */\n/* */\n/* Single-row appointment surface — sibling of AgendaCard, but richer: */\n/* leading timestamp, operator + patient avatars with the operator */\n/* colour dot, a lifecycle-state Badge (announced by label, never colour */\n/* alone), treatment / tooth Tags, and an optional advance-deposit */\n/* TransactionChip. State→Badge-variant + colour-id→token + URL-safety */\n/* come from the shared `_shared/entity-card` helpers. */\n/* */\n/* All visible text flows through useTranslation('ui') against the */\n/* `appointmentCard.*` namespace (plus shared `financial.*` for money). */\n/* -------------------------------------------------------------------- */\n\n// Back-compat / one-stop import — the URL guard lives in the shared helper.\nexport { isSafeEntityUrl as isSafeAppointmentUrl } from '../_shared/is-safe-url';\n\n/* -------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------- */\n\n/** Appointment delivery channel. */\nexport type AppointmentType = 'inPractice' | 'telehealth';\n\n/** First-visit tri-state flag. */\nexport type AppointmentFirstVisit = 'yes' | 'no' | 'unknown';\n\n/** Operator (clinician) summary — name + optional palette colour id. */\nexport interface AppointmentOperator {\n /** Operator display name — drives the avatar initials + accessible name. */\n name: string;\n /** Numeric palette colour id, resolved to a kit token for the colour dot. */\n colorId?: number | string;\n}\n\n/** Patient summary — identity only, drives the avatar. */\nexport interface AppointmentPatient {\n /** Patient display name. */\n name: string;\n /** Optional avatar image URL. */\n avatarUrl?: string;\n}\n\n/** One treatment line within the appointment. */\nexport interface AppointmentTreatment {\n /** Localised care / treatment name. */\n careName: string;\n /** Optional tooth elements (e.g. FDI ids) the treatment targets. */\n elements?: string[];\n}\n\n/** Booking-reason metadata captured at online-booking time. */\nexport interface AppointmentBookingReason {\n /** Localised booking-reason description. */\n description: string;\n /** Expected duration in minutes. */\n durationMin?: number;\n /** Advance / deposit amount in cents (non-negative). */\n advanceAmountCents?: number;\n /** Localised specialty label. */\n specialty?: string;\n}\n\n/** Boolean appointment flags surfaced as chips / indicators. */\nexport interface AppointmentFlags {\n bookedOnline?: boolean;\n noPatientAssigned?: boolean;\n highlightedAnamnesis?: boolean;\n billable?: boolean;\n}\n\nexport interface AppointmentItem {\n /** Stable appointment identifier. */\n id: string;\n /** ISO-8601 start instant. */\n startIso: string;\n /** Duration in minutes. */\n durationMin: number;\n /** Lifecycle state — drives the Badge variant + label. */\n state: AppointmentState;\n /** Delivery channel — drives the leading type glyph. */\n type: AppointmentType;\n /** First-visit flag. */\n firstVisit?: AppointmentFirstVisit;\n /** Assigned operator. */\n operator?: AppointmentOperator;\n /** Chair / room label — surfaced as a chip. */\n chair?: string;\n /**\n * The care plan this appointment belongs to, when any — surfaced as a chip\n * (`Care plan {number}`). `url` is the deep link the consumer may use for\n * navigation; the chip itself is non-interactive so it never nests an anchor\n * inside the row's own stretched link.\n */\n carePlan?: { number: string; url?: string };\n /** Patient identity. */\n patient?: AppointmentPatient;\n /** Treatment lines. */\n treatments?: AppointmentTreatment[];\n /** Online booking-reason metadata. */\n bookingReason?: AppointmentBookingReason;\n /** Boolean flags. */\n flags?: AppointmentFlags;\n /** Optional deep link — when present (and safe) the row renders as an anchor. */\n url?: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* CVA — row */\n/* -------------------------------------------------------------------- */\n\nconst appointmentCardVariants = cva(\n [\n 'ds:relative ds:flex ds:items-start ds:gap-[var(--spacing-sm)]',\n 'ds:rounded-[var(--radius-md)]',\n 'ds:text-start ds:group',\n 'ds:bg-[var(--card)] ds:text-[color:var(--card-foreground)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:transition-none',\n 'ds:forced-colors:border ds:forced-colors:border-[CanvasText]',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:p-[var(--spacing-sm)]',\n md: 'ds:p-[var(--spacing-md)]',\n },\n interactive: {\n true: 'ds:hover:bg-[color:var(--muted)]/40 ds:cursor-pointer',\n false: '',\n },\n surface: {\n // `flat` (default) is the chrome-less row used inside a bordered\n // AppointmentTray. `elevated` gives each row its own card chrome\n // (border + shadow) for use directly on a rail, e.g. AppointmentTimeline.\n // Uses `--shadow-sm` (a row lift), not `--shadow-card`/`--shadow-lg`:\n // the rail's `overflow-y:auto` scroll region clips `overflow-x` at a\n // narrow gutter, so a wide 30px-blur shadow gets a hard-edged clip.\n flat: '',\n elevated:\n 'ds:border ds:border-[color:var(--card-border)] ds:[.theme-accessible_&]:border-2 ds:shadow-[var(--shadow-sm)]',\n },\n },\n defaultVariants: {\n size: 'sm',\n interactive: false,\n surface: 'flat',\n },\n },\n);\n\nconst stretchedLinkClass = [\n 'ds:focus-visible:outline-none',\n \"ds:after:content-[''] ds:after:absolute ds:after:inset-0 ds:after:rounded-[var(--radius-md)]\",\n 'ds:after:pointer-events-auto',\n 'ds:focus-visible:after:outline-[length:var(--focus-ring-width)]',\n 'ds:focus-visible:after:outline-solid',\n 'ds:focus-visible:after:outline-[color:var(--ring)]',\n 'ds:focus-visible:after:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:after:outline-[CanvasText]',\n].join(' ');\n\nconst TYPE_ICON: Record<AppointmentType, typeof MapPin> = {\n inPractice: MapPin,\n telehealth: Video,\n};\n\n/* -------------------------------------------------------------------- */\n/* Props */\n/* -------------------------------------------------------------------- */\n\nexport interface AppointmentCardProps\n extends\n Omit<HTMLAttributes<HTMLDivElement>, 'onClick' | 'title'>,\n VariantProps<typeof appointmentCardVariants> {\n /** The appointment to render. */\n item: AppointmentItem;\n /** Visual density. */\n size?: 'sm' | 'md';\n /**\n * `'flat'` (default) — chrome-less row for use inside a bordered\n * `AppointmentTray`. `'elevated'` — each row carries its own card chrome\n * (border + shadow), for placing rows directly on a rail (AppointmentTimeline).\n */\n surface?: 'flat' | 'elevated';\n /** BCP-47 locale override for the timestamp + currency. */\n locale?: string;\n /** ISO-4217 currency code for the advance-deposit chip. Defaults to `EUR`. */\n currency?: string;\n /**\n * Show the patient identity (avatar + name as the row title). Default `true`.\n * Set `false` in single-patient surfaces (e.g. a patient's own\n * `AppointmentTimeline`) where repeating the name is redundant — the row then\n * leads with the operator as its title, falling back to a generic label.\n */\n showPatient?: boolean;\n /** Fires when the row is activated. */\n onActivate?: (item: AppointmentItem) => void;\n}\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\nexport const AppointmentCard = forwardRef<HTMLDivElement, AppointmentCardProps>(\n (\n {\n item,\n size = 'sm',\n surface = 'flat',\n locale,\n currency = 'EUR',\n showPatient = true,\n onActivate,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const interactive = !!onActivate;\n const hasSafeUrl = !!item.url && isSafeEntityUrl(item.url);\n\n const patientName = item.patient?.name ?? t('appointmentCard.noPatient');\n const operatorName = item.operator?.name;\n const stateLabel = t(`appointmentCard.state.${item.state}`);\n\n const avatarSize = size === 'md' ? 'md' : 'sm';\n const TypeIcon = TYPE_ICON[item.type];\n\n // In single-patient context the patient name is redundant, so the row leads\n // with the operator (the \"who am I seeing\") and falls back to a generic\n // label when no operator is set.\n const title = showPatient\n ? patientName\n : (operatorName ?? t('appointmentCard.appointment'));\n\n const ariaLabel = showPatient\n ? t('appointmentCard.itemLabel', {\n patient: patientName,\n state: stateLabel,\n })\n : t('appointmentCard.itemLabelGeneric', { title, state: stateLabel });\n\n const titleNode = (\n <span className=\"ds:min-w-0 ds:truncate type-title-item ds:text-[color:var(--foreground)]\">\n {title}\n </span>\n );\n\n let activator: ReactNode;\n if (interactive && hasSafeUrl) {\n activator = (\n <a\n href={item.url}\n aria-label={ariaLabel}\n className={stretchedLinkClass + ' ds:contents'}\n onClick={(e) => {\n if (e.defaultPrevented || e.metaKey || e.ctrlKey || e.shiftKey)\n return;\n onActivate?.(item);\n }}\n >\n {titleNode}\n </a>\n );\n } else if (interactive) {\n activator = (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n className={\n stretchedLinkClass +\n ' ds:contents ds:bg-transparent ds:border-0 ds:p-0 ds:text-start'\n }\n onClick={() => onActivate?.(item)}\n >\n {titleNode}\n </button>\n );\n } else {\n activator = titleNode;\n }\n\n // Treatment / tooth-element Tags. Each treatment becomes one Tag; the\n // tooth elements (when present) are appended to the visible label.\n const treatmentTags = (item.treatments ?? []).map((tx, index) => {\n const label =\n tx.elements && tx.elements.length > 0\n ? t('appointmentCard.treatmentWithElements', {\n care: tx.careName,\n elements: tx.elements.join(', '),\n })\n : tx.careName;\n return (\n <Tag\n key={`${tx.careName}-${index}`}\n label={label}\n variant=\"neutral\"\n fill=\"outline\"\n size=\"sm\"\n maxInlineSize=\"12rem\"\n />\n );\n });\n\n const advanceCents = item.bookingReason?.advanceAmountCents;\n const hasAdvance = typeof advanceCents === 'number' && advanceCents > 0;\n\n return (\n <div\n ref={ref}\n /* `listitem` by default — the card is normally a list child. A\n consumer composing a richer row (card plus its own action row)\n owns the semantics instead and passes `role=\"presentation\"`,\n so the two do not nest. `rest` is spread after this. */\n role=\"listitem\"\n data-component=\"appointment-card\"\n data-component-id={item.id}\n data-state={item.state}\n data-type={item.type}\n className={appointmentCardVariants({\n size,\n interactive,\n surface,\n className,\n })}\n {...rest}\n >\n {/* Leading rail: type glyph + start time + duration */}\n <span className=\"ds:flex ds:w-16 ds:shrink-0 ds:flex-col ds:items-start ds:gap-[var(--spacing-2xs)]\">\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)] ds:text-[color:var(--muted-foreground)]\">\n <TypeIcon aria-hidden=\"true\" className=\"ds:size-3.5 ds:shrink-0\" />\n <span className=\"ds:sr-only\">\n {t(`appointmentCard.type.${item.type}`)}\n </span>\n <Timestamp\n value={item.startIso}\n format=\"absolute\"\n shape=\"bare\"\n locale={locale}\n absoluteFormat={{ hour: 'numeric', minute: '2-digit' }}\n className=\"type-body-sm ds:font-medium ds:text-[color:var(--foreground)] ds:tabular-nums\"\n />\n </span>\n <span className=\"type-eyebrow ds:text-[color:var(--muted-foreground)]\">\n {t('appointmentCard.duration', { count: item.durationMin })}\n </span>\n </span>\n\n {/* Operator avatar with colour dot */}\n {operatorName ? (\n <span className=\"ds:relative ds:shrink-0\">\n <Avatar\n name={operatorName}\n color={colorIdToAvatarToken(item.operator?.colorId)}\n size={avatarSize}\n aria-hidden=\"true\"\n role=\"presentation\"\n aria-label={undefined}\n />\n <span\n aria-hidden=\"true\"\n className={\n 'ds:absolute ds:bottom-0 ds:end-0 ds:size-2.5 ds:rounded-[var(--radius-full)] ds:border ds:border-[color:var(--card)] ' +\n operatorDotClass(item.operator?.colorId)\n }\n />\n </span>\n ) : null}\n\n {/* Main content column */}\n <div className=\"ds:flex-1 ds:min-w-0 ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <div className=\"ds:flex ds:items-start ds:gap-[var(--spacing-sm)]\">\n <div className=\"ds:flex-1 ds:min-w-0 ds:flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n {showPatient && item.patient?.avatarUrl ? (\n <Avatar\n name={patientName}\n src={item.patient.avatarUrl}\n size={avatarSize}\n className=\"ds:shrink-0\"\n aria-hidden=\"true\"\n role=\"presentation\"\n aria-label={undefined}\n />\n ) : null}\n {activator}\n </div>\n <Badge\n variant={appointmentStateBadge(item.state)}\n size=\"sm\"\n withDot\n className=\"ds:shrink-0\"\n >\n {stateLabel}\n </Badge>\n </div>\n\n {/* Meta line: operator name · specialty · first visit\n (chair moved to a chip below). When the patient is hidden the\n operator is promoted to the title, so it drops out of the meta. */}\n {(showPatient && operatorName) ||\n item.bookingReason?.specialty ||\n item.firstVisit === 'yes' ? (\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-x-[var(--spacing-sm)] ds:gap-y-[var(--spacing-2xs)] type-eyebrow ds:text-[color:var(--muted-foreground)]\">\n {showPatient && operatorName ? <span>{operatorName}</span> : null}\n {item.bookingReason?.specialty ? (\n <span>{item.bookingReason.specialty}</span>\n ) : null}\n {item.firstVisit === 'yes' ? (\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]\">\n <Star aria-hidden=\"true\" className=\"ds:size-3 ds:shrink-0\" />\n {t('appointmentCard.firstVisit')}\n </span>\n ) : null}\n </div>\n ) : null}\n\n {/* Booking reason */}\n {item.bookingReason?.description ? (\n <span className=\"type-body-sm ds:text-[color:var(--foreground)] ds:truncate\">\n {item.bookingReason.description}\n </span>\n ) : null}\n\n {/* Treatment / tooth Tags */}\n {treatmentTags.length > 0 ? (\n <div className=\"ds:flex ds:flex-wrap ds:gap-[var(--spacing-2xs)]\">\n {treatmentTags}\n </div>\n ) : null}\n\n {/* Chips: care plan · chair · flags · advance-deposit */}\n {item.carePlan ||\n item.chair ||\n item.flags?.bookedOnline ||\n item.flags?.highlightedAnamnesis ||\n hasAdvance ? (\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-xs)]\">\n {item.carePlan ? (\n <Badge\n variant=\"accentSoft\"\n size=\"sm\"\n leading={<ClipboardPlus />}\n >\n {t('appointmentCard.carePlan', {\n number: item.carePlan.number,\n })}\n </Badge>\n ) : null}\n {item.chair ? (\n <Badge variant=\"neutral\" size=\"sm\" leading={<Armchair />}>\n {t('appointmentCard.chair', { chair: item.chair })}\n </Badge>\n ) : null}\n {item.flags?.bookedOnline ? (\n <Badge variant=\"info\" size=\"sm\" leading={<Globe />}>\n {t('appointmentCard.flag.bookedOnline')}\n </Badge>\n ) : null}\n {item.flags?.highlightedAnamnesis ? (\n <Badge variant=\"warning\" size=\"sm\" withDot>\n {t('appointmentCard.flag.highlightedAnamnesis')}\n </Badge>\n ) : null}\n {hasAdvance ? (\n <TransactionChip\n state=\"to-invoice\"\n amount={(advanceCents as number) / 100}\n currency={currency}\n locale={locale}\n size=\"sm\"\n showLabel\n />\n ) : null}\n </div>\n ) : null}\n </div>\n\n {interactive ? (\n <ChevronRight\n aria-hidden=\"true\"\n className=\"ds:size-4 ds:shrink-0 ds:self-center ds:text-[color:var(--muted-foreground)] ds:opacity-0 ds:transition-opacity ds:motion-reduce:transition-none ds:group-hover:opacity-100 ds:group-focus-within:opacity-100 ds:rtl:rotate-180\"\n />\n ) : null}\n </div>\n );\n },\n);\n\nAppointmentCard.displayName = 'AppointmentCard';\n"],"names":["__iconNode","ClipboardPlus","createLucideIcon","OPERATOR_DOT_CLASS","operatorDotClass","colorId","colorIdToToken","appointmentCardVariants","cva","stretchedLinkClass","TYPE_ICON","MapPin","Video","AppointmentCard","forwardRef","item","size","surface","locale","currency","showPatient","onActivate","className","rest","ref","t","useTranslation","interactive","hasSafeUrl","isSafeEntityUrl","patientName","_a","operatorName","_b","stateLabel","avatarSize","TypeIcon","title","ariaLabel","titleNode","jsx","activator","treatmentTags","tx","index","label","Tag","advanceCents","_c","hasAdvance","jsxs","Timestamp","Avatar","colorIdToAvatarToken","_d","_e","_f","Badge","appointmentStateBadge","_g","_h","Star","_i","_j","_k","Armchair","_l","Globe","_m","TransactionChip","ChevronRight"],"mappings":"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","x_google_ignoreList":[0]}
@@ -0,0 +1,403 @@
1
+ import { jsxs as S, jsx as o, Fragment as ke } from "react/jsx-runtime";
2
+ import { forwardRef as be, useId as Te, useMemo as F, useRef as T, useState as Z, useCallback as x, useImperativeHandle as xe } from "react";
3
+ import { c as De } from "./index-D2ZczOXr.js";
4
+ import { useTranslation as we } from "react-i18next";
5
+ import { A as Re } from "./appointment-card-Dzr3GeVz.js";
6
+ import { B as A } from "./button-nJaPh_HC.js";
7
+ import { B as Ie } from "./button-group-CaxRJsNG.js";
8
+ import { D as Se } from "./date-picker-CepI_oI2.js";
9
+ import { P as je } from "./panel-header-D313n_9-.js";
10
+ import { S as Ce } from "./skeleton-CZbwyJAA.js";
11
+ import { E as He } from "./empty-state-X0sVPfVo.js";
12
+ import { u as O } from "./use-isomorphic-layout-effect-BGfaCOP1.js";
13
+ import { u as Fe } from "./use-prefers-reduced-motion-BMwIQRjB.js";
14
+ import { u as Me } from "./registry-DvAUVLHh.js";
15
+ import { C as Ne } from "./calendar-days-BGWklvE-.js";
16
+ const _e = {
17
+ id: "appointment-timeline",
18
+ // 'pick' backs select_item (non-persisting activation through
19
+ // onOpenAppointment); 'scroll' backs jump_to_today, which re-anchors the
20
+ // scroll viewport (mirrors practice-results' scroll_to_result).
21
+ capabilities: ["scroll", "pick"],
22
+ state: {
23
+ items: {
24
+ type: "Array<{ id: string; state: string }>",
25
+ descriptionKey: "ui.agent.appointmentTimeline.state.items",
26
+ description: "Currently-rendered appointments. Ids + lifecycle state only — never patient identity.",
27
+ read: (n) => n.getItems()
28
+ }
29
+ },
30
+ actions: {
31
+ select_item: {
32
+ safety: "read",
33
+ argsType: "{ id: string }",
34
+ argsSchema: {
35
+ type: "object",
36
+ properties: { id: { type: "string" } },
37
+ required: ["id"]
38
+ },
39
+ idempotent: !0,
40
+ descriptionKey: "ui.agent.appointmentTimeline.actions.selectItem",
41
+ description: "Activate the appointment with the given id.",
42
+ invoke: (n, i) => {
43
+ n.selectItem(i.id);
44
+ }
45
+ },
46
+ jump_to_today: {
47
+ safety: "read",
48
+ idempotent: !0,
49
+ descriptionKey: "ui.agent.appointmentTimeline.actions.jumpToToday",
50
+ description: "Re-anchor the timeline scroll on today's appointments.",
51
+ invoke: (n) => {
52
+ n.jumpToToday();
53
+ }
54
+ }
55
+ },
56
+ domHooks: {
57
+ root: {
58
+ attr: "data-component",
59
+ value: "appointment-timeline",
60
+ description: "Marks the AppointmentTimeline wrapper."
61
+ },
62
+ instanceId: {
63
+ attr: "data-component-id",
64
+ sourceProp: "id",
65
+ description: "Sourced from the id prop. Required to address a specific timeline from the agent."
66
+ },
67
+ item: {
68
+ attr: "data-appointment-id",
69
+ description: "Stable appointment id emitted on each rendered AppointmentCard inside the timeline."
70
+ }
71
+ }
72
+ }, L = 96, ze = De(
73
+ [
74
+ "ds:flex ds:flex-col ds:h-full ds:min-h-0",
75
+ "ds:bg-[color:var(--card)] ds:text-[color:var(--card-foreground)]"
76
+ ].join(" "),
77
+ { variants: { size: { sm: "", md: "" } }, defaultVariants: { size: "sm" } }
78
+ ), Be = [
79
+ "ds:flex-1 ds:min-h-0 ds:overflow-y-auto",
80
+ "ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]",
81
+ "ds:px-[var(--spacing-sm)] ds:py-[var(--spacing-xs)]",
82
+ "ds:[--atl-mask-top:black] ds:[--atl-mask-bottom:black]",
83
+ "ds:data-[fade-top=true]:[--atl-mask-top:transparent]",
84
+ "ds:data-[fade-bottom=true]:[--atl-mask-bottom:transparent]",
85
+ "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%)]",
86
+ "ds:motion-reduce:[mask-image:none]",
87
+ "ds:[.theme-accessible_&]:[mask-image:none]",
88
+ "ds:forced-colors:[mask-image:none]",
89
+ "ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid",
90
+ "ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:[outline-offset:-2px]",
91
+ "ds:forced-colors:focus-visible:outline-[CanvasText]"
92
+ ].join(" ");
93
+ function y(n) {
94
+ const i = `${n.getMonth() + 1}`.padStart(2, "0"), l = `${n.getDate()}`.padStart(2, "0");
95
+ return `${n.getFullYear()}-${i}-${l}`;
96
+ }
97
+ function Ke(n) {
98
+ const i = [...n].sort(
99
+ (u, m) => +new Date(u.startIso) - +new Date(m.startIso)
100
+ ), l = /* @__PURE__ */ new Map();
101
+ for (const u of i) {
102
+ const m = new Date(u.startIso), h = y(m);
103
+ let c = l.get(h);
104
+ c || (c = {
105
+ key: h,
106
+ date: new Date(m.getFullYear(), m.getMonth(), m.getDate()),
107
+ items: []
108
+ }, l.set(h, c)), c.items.push(u);
109
+ }
110
+ return [...l.values()];
111
+ }
112
+ const Ae = be(
113
+ ({
114
+ id: n,
115
+ items: i,
116
+ nowIso: l,
117
+ title: u,
118
+ locale: m,
119
+ currency: h,
120
+ size: c = "sm",
121
+ showPatient: M = !1,
122
+ onOpenAppointment: f,
123
+ onLoadEarlier: v,
124
+ onLoadLater: p,
125
+ hasMoreEarlier: k,
126
+ hasMoreLater: N,
127
+ loadingEarlier: j = !1,
128
+ loadingLater: C = !1,
129
+ showFilters: J = !0,
130
+ onJumpToDate: H,
131
+ emptyTitle: ee,
132
+ emptyDescription: te,
133
+ className: se,
134
+ "aria-label": ne,
135
+ ...ie
136
+ }, oe) => {
137
+ const { t: a, i18n: ae } = we(), Y = Te(), P = Fe(), $ = m ?? ae.language, E = u ?? a("appointmentTimeline.title"), r = F(
138
+ () => l ? new Date(l) : /* @__PURE__ */ new Date(),
139
+ [l]
140
+ ), g = F(() => Ke(i), [i]), q = k ?? !!v, G = N ?? !!p, _ = F(() => {
141
+ if (g.length === 0) return;
142
+ const e = y(r), t = g.map((s) => s.key);
143
+ return t.includes(e) ? e : t.find((s) => s >= e) ?? t[t.length - 1];
144
+ }, [g, r]), D = T(null), w = T(/* @__PURE__ */ new Map()), V = T(!1), z = T(void 0), W = T(0), [re, le] = Z(!1), [ce, de] = Z(!1), R = x(() => {
145
+ const e = D.current;
146
+ if (!e) return;
147
+ const { scrollTop: t, scrollHeight: s, clientHeight: d } = e;
148
+ le(t > 1), de(t + d < s - 1);
149
+ }, []), I = x(
150
+ (e, t) => {
151
+ const s = D.current;
152
+ if (!s) return;
153
+ let d = w.current.get(e);
154
+ if (!d) {
155
+ const K = [...w.current.keys()].sort(), X = K.find((ve) => ve >= e) ?? K[K.length - 1];
156
+ d = X ? w.current.get(X) : void 0;
157
+ }
158
+ if (!d) return;
159
+ const he = d.getBoundingClientRect().top - s.getBoundingClientRect().top;
160
+ s.scrollTo({
161
+ top: s.scrollTop + he,
162
+ behavior: t && !P ? "smooth" : "auto"
163
+ });
164
+ },
165
+ [P]
166
+ );
167
+ O(() => {
168
+ var s;
169
+ const e = D.current;
170
+ if (!e) return;
171
+ const t = (s = g[0]) == null ? void 0 : s.key;
172
+ if (!V.current)
173
+ g.length > 0 && _ && (I(_, !1), V.current = !0);
174
+ else if (z.current && t && t < z.current) {
175
+ const d = e.scrollHeight - W.current;
176
+ d > 0 && (e.scrollTop += d);
177
+ }
178
+ z.current = t, W.current = e.scrollHeight, R();
179
+ }, [g, _, I, R]), O(() => {
180
+ const e = D.current;
181
+ if (!e || typeof ResizeObserver > "u") return;
182
+ const t = new ResizeObserver(R);
183
+ return t.observe(e), () => t.disconnect();
184
+ }, [R]);
185
+ const me = x(() => {
186
+ R();
187
+ const e = D.current;
188
+ if (!e) return;
189
+ const { scrollTop: t, scrollHeight: s, clientHeight: d } = e;
190
+ v && q && !j && t <= L && v(), p && G && !C && s - t - d <= L && p();
191
+ }, [
192
+ R,
193
+ v,
194
+ p,
195
+ q,
196
+ G,
197
+ j,
198
+ C
199
+ ]), b = x(
200
+ (e) => {
201
+ const t = y(e), s = w.current.has(t);
202
+ I(t, !0), s || H == null || H(e.toISOString());
203
+ },
204
+ [I, H]
205
+ ), pe = x(() => b(r), [b, r]), ue = x(() => {
206
+ const e = new Date(r), t = (e.getDay() + 6) % 7;
207
+ e.setDate(e.getDate() - t), b(e);
208
+ }, [b, r]), fe = x(() => {
209
+ b(new Date(r.getFullYear(), r.getMonth(), 1));
210
+ }, [b, r]), B = T(i);
211
+ B.current = i;
212
+ const Q = T(null);
213
+ xe(oe, () => Q.current, []);
214
+ const ge = F(
215
+ () => ({
216
+ getItems: () => B.current.map((e) => ({ id: e.id, state: e.state })),
217
+ selectItem: (e) => {
218
+ const t = B.current.find((s) => s.id === e);
219
+ t && (f == null || f(t));
220
+ },
221
+ jumpToToday: () => I(y(r), !0)
222
+ }),
223
+ [f, I, r]
224
+ );
225
+ Me(_e, ge, n);
226
+ const ye = g.length === 0 && !j && !C, U = (e) => /* @__PURE__ */ o(
227
+ "div",
228
+ {
229
+ "aria-hidden": "true",
230
+ className: "ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)] ds:py-[var(--spacing-xs)]",
231
+ children: /* @__PURE__ */ o(Ce, { variant: "rectangular", height: "3.5rem" })
232
+ },
233
+ e
234
+ );
235
+ return /* @__PURE__ */ S(
236
+ "section",
237
+ {
238
+ ref: Q,
239
+ "data-component": "appointment-timeline",
240
+ "data-component-id": n,
241
+ "aria-label": ne ?? E,
242
+ className: ze({ size: c, className: se }),
243
+ ...ie,
244
+ children: [
245
+ /* @__PURE__ */ o(
246
+ je,
247
+ {
248
+ icon: /* @__PURE__ */ o(Ne, {}),
249
+ title: E,
250
+ filters: J ? /* @__PURE__ */ S(ke, { children: [
251
+ /* @__PURE__ */ S(Ie, { label: a("appointmentTimeline.filtersLabel"), children: [
252
+ /* @__PURE__ */ o(A, { intent: "outline", size: "sm", onClick: pe, children: a("appointmentTimeline.today") }),
253
+ /* @__PURE__ */ o(A, { intent: "outline", size: "sm", onClick: ue, children: a("appointmentTimeline.thisWeek") }),
254
+ /* @__PURE__ */ o(A, { intent: "outline", size: "sm", onClick: fe, children: a("appointmentTimeline.thisMonth") })
255
+ ] }),
256
+ /* @__PURE__ */ o("label", { htmlFor: Y, className: "ds:sr-only", children: a("appointmentTimeline.jumpToDate") }),
257
+ /* @__PURE__ */ o(
258
+ Se,
259
+ {
260
+ id: Y,
261
+ size: "sm",
262
+ placeholder: a("appointmentTimeline.jumpToDate"),
263
+ onChange: (e) => {
264
+ e && b(e);
265
+ }
266
+ }
267
+ )
268
+ ] }) : void 0
269
+ }
270
+ ),
271
+ ye ? /* @__PURE__ */ o("div", { className: "ds:flex-1 ds:p-[var(--spacing-md)]", children: /* @__PURE__ */ o(
272
+ He,
273
+ {
274
+ variant: "first-use",
275
+ size: "sm",
276
+ title: ee ?? a("appointmentTimeline.empty.title"),
277
+ description: te ?? a("appointmentTimeline.empty.description")
278
+ }
279
+ ) }) : /* @__PURE__ */ S(
280
+ "div",
281
+ {
282
+ ref: D,
283
+ tabIndex: 0,
284
+ onScroll: me,
285
+ "data-fade-top": re ? "true" : void 0,
286
+ "data-fade-bottom": ce ? "true" : void 0,
287
+ className: Be,
288
+ children: [
289
+ j ? U("loading-earlier") : null,
290
+ /* @__PURE__ */ o(
291
+ "ol",
292
+ {
293
+ role: "list",
294
+ className: "ds:m-0 ds:flex ds:list-none ds:flex-col ds:gap-[var(--spacing-md)] ds:p-0",
295
+ children: g.map((e) => /* @__PURE__ */ S(
296
+ "li",
297
+ {
298
+ ref: (t) => {
299
+ t ? w.current.set(e.key, t) : w.current.delete(e.key);
300
+ },
301
+ className: "ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]",
302
+ children: [
303
+ /* @__PURE__ */ o(
304
+ Ye,
305
+ {
306
+ date: e.date,
307
+ now: r,
308
+ locale: $,
309
+ todayLabel: a("appointmentTimeline.today"),
310
+ tomorrowLabel: a("appointmentTimeline.tomorrow"),
311
+ yesterdayLabel: a("appointmentTimeline.yesterday")
312
+ }
313
+ ),
314
+ /* @__PURE__ */ o(
315
+ "ul",
316
+ {
317
+ role: "list",
318
+ className: "ds:m-0 ds:flex ds:list-none ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-0",
319
+ children: e.items.map((t) => /* @__PURE__ */ o(
320
+ Re,
321
+ {
322
+ item: t,
323
+ size: c,
324
+ surface: "elevated",
325
+ showPatient: M,
326
+ locale: $,
327
+ currency: h,
328
+ onActivate: f,
329
+ "data-appointment-id": t.id
330
+ },
331
+ t.id
332
+ ))
333
+ }
334
+ )
335
+ ]
336
+ },
337
+ e.key
338
+ ))
339
+ }
340
+ ),
341
+ C ? U("loading-later") : null
342
+ ]
343
+ }
344
+ )
345
+ ]
346
+ }
347
+ );
348
+ }
349
+ );
350
+ Ae.displayName = "AppointmentTimeline";
351
+ function Ye({
352
+ date: n,
353
+ now: i,
354
+ locale: l,
355
+ todayLabel: u,
356
+ tomorrowLabel: m,
357
+ yesterdayLabel: h
358
+ }) {
359
+ const c = y(n), M = y(i), f = new Date(i);
360
+ f.setDate(i.getDate() + 1);
361
+ const v = new Date(i);
362
+ v.setDate(i.getDate() - 1);
363
+ const p = c === M;
364
+ let k;
365
+ if (p) k = u;
366
+ else if (c === y(f)) k = m;
367
+ else if (c === y(v)) k = h;
368
+ else {
369
+ const N = n.getFullYear() === i.getFullYear();
370
+ k = new Intl.DateTimeFormat(l, {
371
+ weekday: "short",
372
+ day: "numeric",
373
+ month: "short",
374
+ ...N ? {} : { year: "numeric" }
375
+ }).format(n);
376
+ }
377
+ return /* @__PURE__ */ S(
378
+ "div",
379
+ {
380
+ "data-today": p ? "true" : void 0,
381
+ className: [
382
+ "ds:sticky ds:top-0 ds:z-[1] ds:bg-[color:var(--card)] ds:py-[var(--spacing-2xs)]",
383
+ "ds:flex ds:items-center ds:gap-[var(--spacing-2xs)] type-eyebrow",
384
+ p ? "ds:text-[color:var(--primary)] ds:font-semibold" : "ds:text-[color:var(--muted-foreground)]"
385
+ ].join(" "),
386
+ children: [
387
+ p ? /* @__PURE__ */ o(
388
+ "span",
389
+ {
390
+ "aria-hidden": "true",
391
+ className: "ds:size-1.5 ds:shrink-0 ds:rounded-[var(--radius-full)] ds:bg-[color:var(--primary)]"
392
+ }
393
+ ) : null,
394
+ k
395
+ ]
396
+ }
397
+ );
398
+ }
399
+ export {
400
+ Ae as A,
401
+ _e as a
402
+ };
403
+ //# sourceMappingURL=appointment-timeline-DCn0Jx55.js.map