@mieweb/ui 0.6.1-dev.159 → 0.6.1-dev.164

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 (119) hide show
  1. package/dist/MedicationEditor-Bp4NLy7y.d.cts +352 -0
  2. package/dist/MedicationEditor-Bp4NLy7y.d.ts +352 -0
  3. package/dist/ag-grid.cjs +5 -5
  4. package/dist/ag-grid.js +1 -1
  5. package/dist/{chunk-H4T5T65N.cjs → chunk-2JQBPU3Y.cjs} +3 -3
  6. package/dist/{chunk-H4T5T65N.cjs.map → chunk-2JQBPU3Y.cjs.map} +1 -1
  7. package/dist/{chunk-6555OYRU.js → chunk-5BQE6BEL.js} +2 -2
  8. package/dist/{chunk-6555OYRU.js.map → chunk-5BQE6BEL.js.map} +1 -1
  9. package/dist/chunk-67CEEDAP.cjs +1605 -0
  10. package/dist/chunk-67CEEDAP.cjs.map +1 -0
  11. package/dist/{chunk-ULF4PWHW.cjs → chunk-7BARESXI.cjs} +3 -3
  12. package/dist/{chunk-ULF4PWHW.cjs.map → chunk-7BARESXI.cjs.map} +1 -1
  13. package/dist/chunk-7ZVTLTJF.js +109 -0
  14. package/dist/chunk-7ZVTLTJF.js.map +1 -0
  15. package/dist/{chunk-KX4NWZIR.cjs → chunk-CDMWZBDD.cjs} +3 -3
  16. package/dist/{chunk-KX4NWZIR.cjs.map → chunk-CDMWZBDD.cjs.map} +1 -1
  17. package/dist/chunk-DHKYGVP7.js +3 -0
  18. package/dist/{chunk-LEMY57MI.js.map → chunk-DHKYGVP7.js.map} +1 -1
  19. package/dist/{chunk-FSBFQBNE.cjs → chunk-DKGUOMBC.cjs} +2 -2
  20. package/dist/{chunk-FSBFQBNE.cjs.map → chunk-DKGUOMBC.cjs.map} +1 -1
  21. package/dist/{chunk-4FALCEJI.cjs → chunk-EL2RSQTJ.cjs} +2 -2
  22. package/dist/{chunk-4FALCEJI.cjs.map → chunk-EL2RSQTJ.cjs.map} +1 -1
  23. package/dist/{chunk-TQV5CGSA.js → chunk-ETGZ5DSA.js} +3 -3
  24. package/dist/{chunk-TQV5CGSA.js.map → chunk-ETGZ5DSA.js.map} +1 -1
  25. package/dist/{chunk-EYJDPYOP.js → chunk-FJG7KUDJ.js} +3 -3
  26. package/dist/{chunk-EYJDPYOP.js.map → chunk-FJG7KUDJ.js.map} +1 -1
  27. package/dist/{chunk-HMQ2PRTB.cjs → chunk-GBRPZGSI.cjs} +7 -7
  28. package/dist/{chunk-HMQ2PRTB.cjs.map → chunk-GBRPZGSI.cjs.map} +1 -1
  29. package/dist/{chunk-M7BLVBL4.js → chunk-GSOOPTGM.js} +3 -3
  30. package/dist/{chunk-M7BLVBL4.js.map → chunk-GSOOPTGM.js.map} +1 -1
  31. package/dist/chunk-HUXOASEB.js +1568 -0
  32. package/dist/chunk-HUXOASEB.js.map +1 -0
  33. package/dist/chunk-IQ4KQJAO.js +3 -0
  34. package/dist/{chunk-4ZU53GNR.cjs.map → chunk-IQ4KQJAO.js.map} +1 -1
  35. package/dist/{chunk-RUGWUQ47.js → chunk-NBD236EH.js} +48 -2
  36. package/dist/chunk-NBD236EH.js.map +1 -0
  37. package/dist/chunk-OF7NTZ2I.js +3 -0
  38. package/dist/chunk-OF7NTZ2I.js.map +1 -0
  39. package/dist/chunk-RHNPILZQ.cjs +4 -0
  40. package/dist/chunk-RHNPILZQ.cjs.map +1 -0
  41. package/dist/chunk-RI56YPED.cjs +201 -0
  42. package/dist/chunk-RI56YPED.cjs.map +1 -0
  43. package/dist/{chunk-DCER2QQB.js → chunk-T42BCIUA.js} +2 -2
  44. package/dist/{chunk-DCER2QQB.js.map → chunk-T42BCIUA.js.map} +1 -1
  45. package/dist/{chunk-4ZU53GNR.cjs → chunk-UTYUT5YY.cjs} +62 -2
  46. package/dist/chunk-UTYUT5YY.cjs.map +1 -0
  47. package/dist/{chunk-Q7NBJFEB.js → chunk-UVQEHZRF.js} +2 -2
  48. package/dist/{chunk-Q7NBJFEB.js.map → chunk-UVQEHZRF.js.map} +1 -1
  49. package/dist/chunk-VBWRH4UW.cjs +4 -0
  50. package/dist/chunk-VBWRH4UW.cjs.map +1 -0
  51. package/dist/{chunk-WJIANZPI.cjs → chunk-VJOVJTFP.cjs} +3 -3
  52. package/dist/chunk-VJOVJTFP.cjs.map +1 -0
  53. package/dist/{chunk-OVQKCUTZ.js → chunk-WFS6R2F5.js} +3 -3
  54. package/dist/{chunk-OVQKCUTZ.js.map → chunk-WFS6R2F5.js.map} +1 -1
  55. package/dist/chunk-Y5KEFV5O.js +192 -0
  56. package/dist/chunk-Y5KEFV5O.js.map +1 -0
  57. package/dist/{chunk-4N72FAQW.js → chunk-YBLIDESO.js} +3 -3
  58. package/dist/chunk-YBLIDESO.js.map +1 -0
  59. package/dist/chunk-YVPC65ZJ.cjs +134 -0
  60. package/dist/chunk-YVPC65ZJ.cjs.map +1 -0
  61. package/dist/{chunk-3AMNAMJA.cjs → chunk-YYDW3ZZS.cjs} +48 -2
  62. package/dist/chunk-YYDW3ZZS.cjs.map +1 -0
  63. package/dist/{chunk-3M4P2KMG.cjs → chunk-ZHBANUBI.cjs} +2 -2
  64. package/dist/{chunk-3M4P2KMG.cjs.map → chunk-ZHBANUBI.cjs.map} +1 -1
  65. package/dist/components/Badge/index.cjs +4 -3
  66. package/dist/components/Badge/index.js +2 -1
  67. package/dist/components/Card/index.cjs +16 -15
  68. package/dist/components/Card/index.js +2 -1
  69. package/dist/components/CountryCodeDropdown/index.cjs +5 -5
  70. package/dist/components/CountryCodeDropdown/index.js +2 -2
  71. package/dist/components/Dropdown/index.cjs +8 -8
  72. package/dist/components/Dropdown/index.js +2 -2
  73. package/dist/components/RichTextEditor/index.cjs +7 -7
  74. package/dist/components/RichTextEditor/index.js +4 -4
  75. package/dist/components/Skeleton/index.d.cts +1 -1
  76. package/dist/components/Skeleton/index.d.ts +1 -1
  77. package/dist/components/SuperChat/index.cjs +10 -9
  78. package/dist/components/SuperChat/index.cjs.map +1 -1
  79. package/dist/components/SuperChat/index.js +5 -4
  80. package/dist/components/SuperChat/index.js.map +1 -1
  81. package/dist/components/SuperChat/plugins/index.cjs +3 -3
  82. package/dist/components/SuperChat/plugins/index.js +2 -2
  83. package/dist/datavis.cjs +273 -5
  84. package/dist/datavis.cjs.map +1 -1
  85. package/dist/datavis.d.cts +61 -1
  86. package/dist/datavis.d.ts +61 -1
  87. package/dist/datavis.js +251 -1
  88. package/dist/datavis.js.map +1 -1
  89. package/dist/esheet.cjs +182 -0
  90. package/dist/esheet.cjs.map +1 -1
  91. package/dist/esheet.d.cts +117 -0
  92. package/dist/esheet.d.ts +117 -0
  93. package/dist/esheet.js +161 -0
  94. package/dist/esheet.js.map +1 -1
  95. package/dist/hooks/index.cjs +33 -16
  96. package/dist/hooks/index.d.cts +62 -1
  97. package/dist/hooks/index.d.ts +62 -1
  98. package/dist/hooks/index.js +3 -2
  99. package/dist/index.cjs +3923 -938
  100. package/dist/index.cjs.map +1 -1
  101. package/dist/index.d.cts +981 -2
  102. package/dist/index.d.ts +981 -2
  103. package/dist/index.js +3313 -452
  104. package/dist/index.js.map +1 -1
  105. package/dist/{nitroTableGrid-RQNSZQO4.cjs → nitroTableGrid-3J2GY6CK.cjs} +4 -4
  106. package/dist/{nitroTableGrid-RQNSZQO4.cjs.map → nitroTableGrid-3J2GY6CK.cjs.map} +1 -1
  107. package/dist/{nitroTableGrid-W6XWRFA7.js → nitroTableGrid-SIMT7635.js} +3 -3
  108. package/dist/{nitroTableGrid-W6XWRFA7.js.map → nitroTableGrid-SIMT7635.js.map} +1 -1
  109. package/dist/orderRows-BUJYgSc7.d.cts +221 -0
  110. package/dist/orderRows-BUJYgSc7.d.ts +221 -0
  111. package/dist/styles.css +1 -1
  112. package/dist/tailwind-preset.cjs +4 -4
  113. package/dist/tailwind-preset.js +1 -1
  114. package/package.json +1 -1
  115. package/dist/chunk-3AMNAMJA.cjs.map +0 -1
  116. package/dist/chunk-4N72FAQW.js.map +0 -1
  117. package/dist/chunk-LEMY57MI.js +0 -3
  118. package/dist/chunk-RUGWUQ47.js.map +0 -1
  119. package/dist/chunk-WJIANZPI.cjs.map +0 -1
@@ -0,0 +1,1568 @@
1
+ import { Textarea } from './chunk-6LFG4JFF.js';
2
+ import { Tooltip } from './chunk-FZJBFJJR.js';
3
+ import { RadioGroup, Radio } from './chunk-EZKRAW2X.js';
4
+ import { Select } from './chunk-R25H4N4Z.js';
5
+ import { Label } from './chunk-DUPR6FEL.js';
6
+ import { Card, CardHeader, CardContent } from './chunk-5BQE6BEL.js';
7
+ import { DateInput } from './chunk-HYEJLKXI.js';
8
+ import { Modal, ModalHeader, ModalTitle, ModalClose, ModalBody, ModalFooter } from './chunk-IUFDGB5O.js';
9
+ import { Input } from './chunk-PVUDXJAI.js';
10
+ import { AlertCircle, Plus, GripVertical, StickyNote, Ban, Pencil, CalendarCheck, ExternalLink, ThumbsUp, HandMetal, ThumbsDown, HelpCircle, ChevronDown, ChevronUp, RefreshCw } from './chunk-DHKYGVP7.js';
11
+ import { Button } from './chunk-ZVSW2KS6.js';
12
+ import { Badge } from './chunk-T42BCIUA.js';
13
+ import { useLiveAnnouncement, useDragReorder, dragIndicatorClasses } from './chunk-7ZVTLTJF.js';
14
+ import { cn } from './chunk-F3SOEIN2.js';
15
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
16
+ import * as React3 from 'react';
17
+
18
+ function toolbarKeyNav(e) {
19
+ if (e.altKey || e.ctrlKey || e.metaKey) return;
20
+ if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(e.key)) return;
21
+ const buttons = Array.from(
22
+ e.currentTarget.querySelectorAll(
23
+ "button:not([disabled])"
24
+ )
25
+ );
26
+ const i = buttons.indexOf(document.activeElement);
27
+ if (i === -1 || buttons.length === 0) return;
28
+ e.preventDefault();
29
+ e.stopPropagation();
30
+ const next = e.key === "ArrowLeft" ? (i - 1 + buttons.length) % buttons.length : e.key === "ArrowRight" ? (i + 1) % buttons.length : e.key === "Home" ? 0 : buttons.length - 1;
31
+ buttons[next]?.focus();
32
+ }
33
+ var ALIGN = {
34
+ /** Vertically centered on the row's right edge (single-line rows). */
35
+ center: "pointer-fine:top-1/2 pointer-fine:right-0 pointer-fine:-translate-y-1/2",
36
+ /** Pinned near the row's top-right corner (multi-line rows). */
37
+ top: "pointer-fine:top-1.5 pointer-fine:right-1.5"
38
+ };
39
+ var REVEAL = {
40
+ row: cn(
41
+ "group-hover:pointer-events-auto group-hover:opacity-100",
42
+ "group-focus-within:pointer-events-auto group-focus-within:opacity-100",
43
+ "group-focus:pointer-events-auto group-focus:opacity-100"
44
+ ),
45
+ order: cn(
46
+ "group-hover/order:pointer-events-auto group-hover/order:opacity-100",
47
+ "group-focus-within/order:pointer-events-auto group-focus-within/order:opacity-100",
48
+ "group-focus/order:pointer-events-auto group-focus/order:opacity-100"
49
+ ),
50
+ grid: cn(
51
+ "[.wcdv-tr:hover_&]:pointer-events-auto [.wcdv-tr:hover_&]:opacity-100",
52
+ "[.wcdv-tr:focus-within_&]:pointer-events-auto [.wcdv-tr:focus-within_&]:opacity-100"
53
+ )
54
+ };
55
+ function RowActionToolbar({
56
+ label,
57
+ align = "center",
58
+ group = "row",
59
+ className,
60
+ children
61
+ }) {
62
+ return /* @__PURE__ */ jsx(
63
+ "div",
64
+ {
65
+ role: "toolbar",
66
+ "aria-label": label,
67
+ onKeyDown: toolbarKeyNav,
68
+ className: cn(
69
+ "z-10 ml-auto flex items-center gap-0.5 transition-opacity",
70
+ // Inside a grid cell the column already reserves the space, so the
71
+ // toolbar stays in flow — no absolute overlay. It keeps the card
72
+ // chrome, though: pair with a `sticky right-0` actions column and it
73
+ // floats over scrolled-under content at the grid's right edge.
74
+ group !== "grid" && [
75
+ "pointer-fine:absolute",
76
+ ALIGN[align],
77
+ "pointer-fine:bg-card pointer-fine:border-border pointer-fine:rounded-md pointer-fine:border pointer-fine:p-0.5 pointer-fine:shadow-sm"
78
+ ],
79
+ group === "grid" && "bg-card border-border rounded-md border p-0.5 shadow-sm",
80
+ "pointer-fine:pointer-events-none pointer-fine:opacity-0",
81
+ REVEAL[group],
82
+ "focus-within:pointer-events-auto focus-within:opacity-100",
83
+ className
84
+ ),
85
+ children
86
+ }
87
+ );
88
+ }
89
+ function RowIconButton({
90
+ label,
91
+ icon: Icon,
92
+ onClick,
93
+ active,
94
+ size = "md"
95
+ }) {
96
+ return /* @__PURE__ */ jsx(Tooltip, { content: label, children: /* @__PURE__ */ jsx(
97
+ Button,
98
+ {
99
+ type: "button",
100
+ variant: "ghost",
101
+ size: "icon",
102
+ "aria-label": label,
103
+ "aria-pressed": active,
104
+ onClick: (e) => {
105
+ e.stopPropagation();
106
+ onClick();
107
+ },
108
+ className: cn(
109
+ size === "sm" ? "h-7 w-7" : "h-8 w-8",
110
+ "shrink-0",
111
+ active && "bg-primary-50 text-primary-700 dark:bg-primary-950 dark:text-primary-400"
112
+ ),
113
+ children: /* @__PURE__ */ jsx(Icon, { size: size === "sm" ? 14 : 16 })
114
+ }
115
+ ) });
116
+ }
117
+ var TYPE_ORDER = ["drug", "food", "environmental", "other"];
118
+ var ALLERGY_TYPE_LABELS = {
119
+ drug: "Drug",
120
+ food: "Food",
121
+ environmental: "Environmental",
122
+ other: "Other"
123
+ };
124
+ var SEVERITY_BADGE = {
125
+ severe: "danger",
126
+ moderate: "warning",
127
+ mild: "secondary"
128
+ };
129
+ var DEFAULT_ACTIONS = ["correct", "note", "remove"];
130
+ var ACTION_META = {
131
+ correct: { label: "Correct", icon: Pencil },
132
+ note: { label: "Notes", icon: StickyNote },
133
+ remove: { label: "Remove", icon: Ban }
134
+ };
135
+ function AllergyRow({
136
+ allergy,
137
+ actions,
138
+ readOnly,
139
+ drag,
140
+ onMove,
141
+ onAction
142
+ }) {
143
+ const handleRowKeyDown = (e) => {
144
+ if (e.target !== e.currentTarget) return;
145
+ if (e.altKey && (e.key === "ArrowUp" || e.key === "ArrowDown")) {
146
+ e.preventDefault();
147
+ onMove?.(allergy, e.key === "ArrowUp" ? -1 : 1);
148
+ } else if (e.key === "ArrowUp" || e.key === "ArrowDown") {
149
+ e.preventDefault();
150
+ const list = e.currentTarget.closest("ul");
151
+ if (!list) return;
152
+ const rows = Array.from(
153
+ list.querySelectorAll("li[data-allergy-id]")
154
+ );
155
+ const i = rows.indexOf(e.currentTarget);
156
+ rows[e.key === "ArrowUp" ? Math.max(0, i - 1) : Math.min(rows.length - 1, i + 1)]?.focus();
157
+ }
158
+ };
159
+ return (
160
+ // Rows are focus stops when reordering is enabled so drag & drop has a
161
+ // keyboard equivalent (Alt+↑/↓) — 508.
162
+ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/no-noninteractive-tabindex */
163
+ /* @__PURE__ */ jsxs(
164
+ "li",
165
+ {
166
+ "data-allergy-id": allergy.id,
167
+ tabIndex: drag.enabled ? 0 : void 0,
168
+ onKeyDown: drag.enabled ? handleRowKeyDown : void 0,
169
+ "aria-label": drag.enabled ? `${allergy.allergen}. Alt plus arrow keys to reorder.` : void 0,
170
+ ...drag.rowProps(allergy.id),
171
+ className: cn(
172
+ "group border-border/60 relative flex min-h-11 flex-wrap items-center gap-x-2 gap-y-1 border-b px-1 py-2",
173
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none",
174
+ drag.enabled && "cursor-grab active:cursor-grabbing",
175
+ dragIndicatorClasses(drag, allergy.id)
176
+ ),
177
+ children: [
178
+ drag.enabled ? /* @__PURE__ */ jsx(
179
+ GripVertical,
180
+ {
181
+ size: 14,
182
+ "aria-hidden": true,
183
+ className: "text-muted-foreground/50 shrink-0"
184
+ }
185
+ ) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/60 select-none", children: "\u2022" }),
186
+ /* @__PURE__ */ jsx(
187
+ "span",
188
+ {
189
+ className: cn(
190
+ "text-foreground font-medium",
191
+ allergy.inactive && "text-muted-foreground line-through"
192
+ ),
193
+ children: allergy.allergen
194
+ }
195
+ ),
196
+ allergy.code && /* @__PURE__ */ jsxs(
197
+ "span",
198
+ {
199
+ className: "text-muted-foreground/70 text-xs",
200
+ title: allergy.code.display,
201
+ children: [
202
+ allergy.code.system,
203
+ " ",
204
+ allergy.code.code
205
+ ]
206
+ }
207
+ ),
208
+ allergy.severity && /* @__PURE__ */ jsx(Badge, { variant: SEVERITY_BADGE[allergy.severity], size: "sm", children: allergy.severity }),
209
+ allergy.kind === "intolerance" && /* @__PURE__ */ jsx(Badge, { variant: "secondary", size: "sm", children: "intolerance" }),
210
+ allergy.reaction && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground text-sm", children: [
211
+ "\u2014 ",
212
+ allergy.reaction
213
+ ] }),
214
+ allergy.onsetDate && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground text-xs", children: [
215
+ "since ",
216
+ allergy.onsetDate
217
+ ] }),
218
+ allergy.inactive && /* @__PURE__ */ jsx(Badge, { variant: "secondary", size: "sm", children: "inactive" }),
219
+ allergy.note && /* @__PURE__ */ jsxs("span", { className: "flex w-full basis-full items-center gap-1.5 pl-4", children: [
220
+ /* @__PURE__ */ jsx(StickyNote, { size: 12, className: "text-muted-foreground" }),
221
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs", children: allergy.note })
222
+ ] }),
223
+ !readOnly && actions.length > 0 && /* @__PURE__ */ jsx(RowActionToolbar, { label: `Actions for ${allergy.allergen}`, children: actions.map((action) => /* @__PURE__ */ jsx(
224
+ RowIconButton,
225
+ {
226
+ label: ACTION_META[action].label,
227
+ icon: ACTION_META[action].icon,
228
+ onClick: () => onAction?.(allergy, action)
229
+ },
230
+ action
231
+ )) })
232
+ ]
233
+ }
234
+ )
235
+ );
236
+ }
237
+ var AllergyList = React3.forwardRef(
238
+ ({
239
+ allergies,
240
+ noKnownAllergies,
241
+ onNoKnownAllergiesChange,
242
+ title = "Allergies",
243
+ actions = DEFAULT_ACTIONS,
244
+ onAction,
245
+ onReorder,
246
+ addSearch,
247
+ onAdd,
248
+ readOnly = false,
249
+ className,
250
+ "data-testid": dataTestId,
251
+ ...props
252
+ }, ref) => {
253
+ const [announcement, setAnnouncement] = useLiveAnnouncement();
254
+ const groups = TYPE_ORDER.map((type) => ({
255
+ type,
256
+ label: ALLERGY_TYPE_LABELS[type],
257
+ items: allergies.filter((a) => (a.type ?? "other") === type)
258
+ })).filter((g) => g.items.length > 0);
259
+ const empty = allergies.length === 0;
260
+ const typeOf = React3.useCallback(
261
+ (id) => allergies.find((a) => a.id === id)?.type ?? "other",
262
+ [allergies]
263
+ );
264
+ const drag = useDragReorder({
265
+ ids: allergies.map((a) => a.id),
266
+ onReorder: readOnly || !onReorder ? void 0 : (ids) => {
267
+ setAnnouncement("Allergy list reordered");
268
+ onReorder(ids);
269
+ },
270
+ canDropOn: (dragged, target) => typeOf(dragged) === typeOf(target)
271
+ });
272
+ const moveAllergy = React3.useCallback(
273
+ (allergy, dir) => {
274
+ if (readOnly || !onReorder) return;
275
+ const ids = allergies.map((a) => a.id);
276
+ const from = ids.indexOf(allergy.id);
277
+ let to = from + dir;
278
+ while (to >= 0 && to < allergies.length && (allergies[to].type ?? "other") !== (allergy.type ?? "other")) {
279
+ to += dir;
280
+ }
281
+ if (to < 0 || to >= allergies.length) return;
282
+ [ids[from], ids[to]] = [ids[to], ids[from]];
283
+ setAnnouncement(
284
+ `${allergy.allergen} moved ${dir === -1 ? "up" : "down"}`
285
+ );
286
+ onReorder(ids);
287
+ },
288
+ [allergies, onReorder, readOnly, setAnnouncement]
289
+ );
290
+ return /* @__PURE__ */ jsxs(
291
+ Card,
292
+ {
293
+ ref,
294
+ padding: "none",
295
+ className: cn("w-full", className),
296
+ "data-testid": dataTestId,
297
+ ...props,
298
+ children: [
299
+ title !== null && /* @__PURE__ */ jsx(CardHeader, { className: "border-border bg-muted/50 border-b px-4 py-3", children: /* @__PURE__ */ jsxs("h3", { className: "text-foreground flex items-center gap-2 text-sm font-semibold tracking-wide uppercase", children: [
300
+ /* @__PURE__ */ jsx(
301
+ AlertCircle,
302
+ {
303
+ size: 16,
304
+ className: "text-red-600 dark:text-red-400"
305
+ }
306
+ ),
307
+ title
308
+ ] }) }),
309
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-5 px-4 py-4", children: [
310
+ empty ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
311
+ noKnownAllergies ? /* @__PURE__ */ jsxs("p", { className: "text-foreground flex items-center gap-1.5 text-sm", children: [
312
+ /* @__PURE__ */ jsx(Badge, { variant: "success", size: "sm", children: "\u2713" }),
313
+ "No known allergies (NKA)"
314
+ ] }) : /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: "Allergy status not recorded." }),
315
+ !readOnly && onNoKnownAllergiesChange && /* @__PURE__ */ jsx(
316
+ Button,
317
+ {
318
+ variant: noKnownAllergies ? "secondary" : "outline",
319
+ size: "sm",
320
+ onClick: () => onNoKnownAllergiesChange(!noKnownAllergies),
321
+ children: noKnownAllergies ? "Clear no-known-allergies" : "Confirm: no known allergies"
322
+ }
323
+ )
324
+ ] }) : groups.map(({ type, label, items }) => /* @__PURE__ */ jsxs("section", { "aria-label": label, children: [
325
+ /* @__PURE__ */ jsx("h4", { className: "border-border text-foreground border-b pb-1 text-sm font-semibold tracking-wide uppercase", children: label }),
326
+ /* @__PURE__ */ jsx("ul", { className: "mt-1", children: items.map((allergy) => /* @__PURE__ */ jsx(
327
+ AllergyRow,
328
+ {
329
+ allergy,
330
+ actions,
331
+ readOnly,
332
+ drag,
333
+ onMove: moveAllergy,
334
+ onAction
335
+ },
336
+ allergy.id
337
+ )) })
338
+ ] }, type)),
339
+ !readOnly && (addSearch || onAdd) && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
340
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs font-semibold tracking-wide uppercase", children: "Add allergy" }),
341
+ addSearch,
342
+ onAdd && /* @__PURE__ */ jsx(
343
+ Button,
344
+ {
345
+ variant: "outline",
346
+ size: "sm",
347
+ onClick: onAdd,
348
+ leftIcon: /* @__PURE__ */ jsx(Plus, { size: 12 }),
349
+ className: "h-auto rounded-full px-2.5 py-1 text-xs",
350
+ children: "Add allergy\u2026"
351
+ }
352
+ )
353
+ ] })
354
+ ] }),
355
+ /* @__PURE__ */ jsx("div", { "aria-live": "polite", className: "sr-only", children: announcement })
356
+ ]
357
+ }
358
+ );
359
+ }
360
+ );
361
+ AllergyList.displayName = "AllergyList";
362
+ var CodeLookupContext = React3.createContext(
363
+ null
364
+ );
365
+ function CodeLookupProvider({
366
+ component,
367
+ indexUrl = "/codify",
368
+ locale = "en",
369
+ children
370
+ }) {
371
+ const value = React3.useMemo(
372
+ () => ({ component, indexUrl, locale }),
373
+ [component, indexUrl, locale]
374
+ );
375
+ return /* @__PURE__ */ jsx(CodeLookupContext.Provider, { value, children });
376
+ }
377
+ function useCodeLookupConfig() {
378
+ return React3.useContext(CodeLookupContext);
379
+ }
380
+ function newId() {
381
+ return `alg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
382
+ }
383
+ var TYPE_OPTIONS = [
384
+ { value: "drug", label: "Drug" },
385
+ { value: "food", label: "Food" },
386
+ { value: "environmental", label: "Environmental" },
387
+ { value: "other", label: "Other" }
388
+ ];
389
+ function AllergyEditor({
390
+ allergy,
391
+ codeLookup,
392
+ onClose,
393
+ onSave
394
+ }) {
395
+ const [draft, setDraft] = React3.useState(
396
+ () => allergy ?? { id: newId(), allergen: "", type: "drug" }
397
+ );
398
+ const patch = (p) => setDraft((prev) => ({ ...prev, ...p }));
399
+ const bodyRef = React3.useRef(null);
400
+ React3.useEffect(() => {
401
+ bodyRef.current?.querySelector("input")?.focus();
402
+ }, []);
403
+ const canSave = draft.allergen.trim().length > 0;
404
+ const isDrug = (draft.type ?? "drug") === "drug";
405
+ return /* @__PURE__ */ jsxs(Modal, { open: true, onOpenChange: (o) => !o && onClose(), size: "md", children: [
406
+ /* @__PURE__ */ jsxs(ModalHeader, { children: [
407
+ /* @__PURE__ */ jsx(ModalTitle, { children: allergy ? "Correct Allergy" : "Add Allergy" }),
408
+ /* @__PURE__ */ jsx(ModalClose, {})
409
+ ] }),
410
+ /* @__PURE__ */ jsx(ModalBody, { className: "space-y-4", children: /* @__PURE__ */ jsxs("div", { ref: bodyRef, className: "contents", children: [
411
+ /* @__PURE__ */ jsxs(
412
+ RadioGroup,
413
+ {
414
+ name: "alg-kind",
415
+ label: "Mechanism",
416
+ description: "A true immune-mediated allergy, or a non-immune intolerance (e.g. GI upset)",
417
+ value: draft.kind ?? "allergy",
418
+ onValueChange: (v) => patch({ kind: v }),
419
+ orientation: "horizontal",
420
+ size: "sm",
421
+ children: [
422
+ /* @__PURE__ */ jsx(Radio, { value: "allergy", label: "Allergy" }),
423
+ /* @__PURE__ */ jsx(Radio, { value: "intolerance", label: "Intolerance" })
424
+ ]
425
+ }
426
+ ),
427
+ /* @__PURE__ */ jsx(
428
+ Select,
429
+ {
430
+ label: "Type",
431
+ options: TYPE_OPTIONS,
432
+ value: draft.type ?? "drug",
433
+ onValueChange: (v) => {
434
+ const type = v;
435
+ patch({
436
+ type,
437
+ ...type !== "drug" && { code: void 0 }
438
+ });
439
+ }
440
+ }
441
+ ),
442
+ isDrug && codeLookup ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
443
+ /* @__PURE__ */ jsx(Label, { htmlFor: "alg-search", children: "Allergen" }),
444
+ /* @__PURE__ */ jsx(
445
+ codeLookup.component,
446
+ {
447
+ indexUrl: codeLookup.indexUrl,
448
+ locale: codeLookup.locale,
449
+ domains: ["med"],
450
+ bare: true,
451
+ clearOnSelect: false,
452
+ placeholder: "Search drug allergens \u2014 e.g. penicillin",
453
+ onSelect: (result) => patch({
454
+ allergen: result.label,
455
+ code: {
456
+ system: result.codetype,
457
+ code: result.fullcode,
458
+ display: result.label
459
+ }
460
+ }),
461
+ onFreeText: (text) => patch({ allergen: text, code: void 0 })
462
+ }
463
+ ),
464
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: draft.code ? `Coded: ${draft.code.system} ${draft.code.code}` : draft.allergen ? `Uncoded free text: "${draft.allergen}"` : "Pick a result to code the allergen, or press Enter for free text" })
465
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
466
+ /* @__PURE__ */ jsx(Label, { htmlFor: "alg-name", children: "Allergen" }),
467
+ /* @__PURE__ */ jsx(
468
+ Input,
469
+ {
470
+ id: "alg-name",
471
+ value: draft.allergen,
472
+ onChange: (e) => patch({ allergen: e.target.value })
473
+ }
474
+ )
475
+ ] }),
476
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
477
+ /* @__PURE__ */ jsx(Label, { htmlFor: "alg-reaction", children: "Reaction" }),
478
+ /* @__PURE__ */ jsx(
479
+ Input,
480
+ {
481
+ id: "alg-reaction",
482
+ value: draft.reaction ?? "",
483
+ onChange: (e) => patch({ reaction: e.target.value })
484
+ }
485
+ )
486
+ ] }),
487
+ /* @__PURE__ */ jsxs(
488
+ RadioGroup,
489
+ {
490
+ name: "alg-severity",
491
+ label: "Severity",
492
+ value: draft.severity ?? "",
493
+ onValueChange: (v) => patch({ severity: v || void 0 }),
494
+ orientation: "horizontal",
495
+ size: "sm",
496
+ children: [
497
+ /* @__PURE__ */ jsx(Radio, { value: "mild", label: "Mild" }),
498
+ /* @__PURE__ */ jsx(Radio, { value: "moderate", label: "Moderate" }),
499
+ /* @__PURE__ */ jsx(Radio, { value: "severe", label: "Severe" })
500
+ ]
501
+ }
502
+ ),
503
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
504
+ /* @__PURE__ */ jsx(Label, { htmlFor: "alg-onset", children: "Onset date" }),
505
+ /* @__PURE__ */ jsx(
506
+ DateInput,
507
+ {
508
+ id: "alg-onset",
509
+ value: draft.onsetDate ?? "",
510
+ onChange: (v) => patch({ onsetDate: v })
511
+ }
512
+ )
513
+ ] }),
514
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
515
+ /* @__PURE__ */ jsx(Label, { htmlFor: "alg-note", children: "Note" }),
516
+ /* @__PURE__ */ jsx(
517
+ Textarea,
518
+ {
519
+ id: "alg-note",
520
+ value: draft.note ?? "",
521
+ onChange: (e) => patch({ note: e.target.value }),
522
+ rows: 2
523
+ }
524
+ )
525
+ ] })
526
+ ] }) }),
527
+ /* @__PURE__ */ jsxs(ModalFooter, { children: [
528
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onClose, children: "Cancel" }),
529
+ /* @__PURE__ */ jsx(
530
+ Button,
531
+ {
532
+ onClick: () => {
533
+ onSave({ ...draft, allergen: draft.allergen.trim() });
534
+ onClose();
535
+ },
536
+ disabled: !canSave,
537
+ children: "Save"
538
+ }
539
+ )
540
+ ] })
541
+ ] });
542
+ }
543
+ function AllergyManager({
544
+ allergies: controlled,
545
+ defaultAllergies,
546
+ onChange,
547
+ noKnownAllergies,
548
+ onNoKnownAllergiesChange,
549
+ title = "Allergies",
550
+ codeLookup,
551
+ inlineAddSearch = false,
552
+ readOnly = false,
553
+ className,
554
+ "data-testid": dataTestId
555
+ }) {
556
+ const isControlled = controlled !== void 0;
557
+ const [internal, setInternal] = React3.useState(
558
+ defaultAllergies ?? []
559
+ );
560
+ const allergies = isControlled ? controlled : internal;
561
+ const ambientCodeLookup = useCodeLookupConfig();
562
+ const effectiveCodeLookup = codeLookup === false ? void 0 : codeLookup ?? ambientCodeLookup ?? void 0;
563
+ const [dialog, setDialog] = React3.useState(null);
564
+ const [noteDraft, setNoteDraft] = React3.useState("");
565
+ const commit = (next) => {
566
+ if (!isControlled) setInternal(next);
567
+ onChange?.(next);
568
+ if (next.length > 0 && noKnownAllergies) {
569
+ onNoKnownAllergiesChange?.(false);
570
+ }
571
+ };
572
+ const handleAction = (allergy, action) => {
573
+ switch (action) {
574
+ case "remove":
575
+ commit(allergies.filter((a) => a.id !== allergy.id));
576
+ break;
577
+ case "correct":
578
+ setDialog({ kind: "correct", allergy });
579
+ break;
580
+ case "note":
581
+ setNoteDraft(allergy.note ?? "");
582
+ setDialog({ kind: "note", allergy });
583
+ break;
584
+ }
585
+ };
586
+ const addSearch = inlineAddSearch && effectiveCodeLookup && !readOnly ? /* @__PURE__ */ jsx(
587
+ effectiveCodeLookup.component,
588
+ {
589
+ indexUrl: effectiveCodeLookup.indexUrl,
590
+ locale: effectiveCodeLookup.locale,
591
+ domains: ["med"],
592
+ bare: true,
593
+ clearOnSelect: true,
594
+ placeholder: "Search drug allergens to add \u2014 use Add allergy\u2026 for food/environmental",
595
+ onSelect: (result) => commit([
596
+ ...allergies,
597
+ {
598
+ id: newId(),
599
+ allergen: result.label,
600
+ type: "drug",
601
+ code: {
602
+ system: result.codetype,
603
+ code: result.fullcode,
604
+ display: result.label
605
+ }
606
+ }
607
+ ]),
608
+ onFreeText: (text) => (
609
+ // this search bar is explicitly for drug allergens — free-text
610
+ // entries carry type 'drug' like the coded path
611
+ commit([...allergies, { id: newId(), allergen: text, type: "drug" }])
612
+ )
613
+ }
614
+ ) : void 0;
615
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
616
+ /* @__PURE__ */ jsx(
617
+ AllergyList,
618
+ {
619
+ allergies,
620
+ noKnownAllergies,
621
+ onNoKnownAllergiesChange,
622
+ title,
623
+ onAction: handleAction,
624
+ onReorder: (ids) => commit(
625
+ [...allergies].sort((a, b) => ids.indexOf(a.id) - ids.indexOf(b.id))
626
+ ),
627
+ addSearch,
628
+ onAdd: () => setDialog({ kind: "add" }),
629
+ readOnly,
630
+ className,
631
+ "data-testid": dataTestId
632
+ }
633
+ ),
634
+ dialog && (dialog.kind === "correct" || dialog.kind === "add") && /* @__PURE__ */ jsx(
635
+ AllergyEditor,
636
+ {
637
+ allergy: dialog.kind === "correct" ? dialog.allergy : void 0,
638
+ codeLookup: effectiveCodeLookup,
639
+ onClose: () => setDialog(null),
640
+ onSave: (saved) => {
641
+ if (dialog.kind === "correct") {
642
+ commit(allergies.map((a) => a.id === saved.id ? saved : a));
643
+ } else {
644
+ commit([...allergies, saved]);
645
+ }
646
+ }
647
+ },
648
+ dialog.kind === "correct" ? dialog.allergy.id : "add"
649
+ ),
650
+ dialog?.kind === "note" && /* @__PURE__ */ jsxs(Modal, { open: true, onOpenChange: (o) => !o && setDialog(null), size: "md", children: [
651
+ /* @__PURE__ */ jsxs(ModalHeader, { children: [
652
+ /* @__PURE__ */ jsx(ModalTitle, { children: "Allergy Notes" }),
653
+ /* @__PURE__ */ jsx(ModalClose, {})
654
+ ] }),
655
+ /* @__PURE__ */ jsxs(ModalBody, { className: "space-y-4", children: [
656
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: dialog.allergy.allergen }),
657
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
658
+ /* @__PURE__ */ jsx(Label, { htmlFor: "alg-note-dlg", children: "Note" }),
659
+ /* @__PURE__ */ jsx(
660
+ Textarea,
661
+ {
662
+ id: "alg-note-dlg",
663
+ value: noteDraft,
664
+ onChange: (e) => setNoteDraft(e.target.value),
665
+ rows: 4
666
+ }
667
+ )
668
+ ] })
669
+ ] }),
670
+ /* @__PURE__ */ jsxs(ModalFooter, { children: [
671
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => setDialog(null), children: "Cancel" }),
672
+ /* @__PURE__ */ jsx(
673
+ Button,
674
+ {
675
+ onClick: () => {
676
+ commit(
677
+ allergies.map(
678
+ (a) => a.id === dialog.allergy.id ? { ...a, note: noteDraft.trim() || void 0 } : a
679
+ )
680
+ );
681
+ setDialog(null);
682
+ },
683
+ children: "Save"
684
+ }
685
+ )
686
+ ] })
687
+ ] })
688
+ ] });
689
+ }
690
+ var GROUP_ORDER = [
691
+ "unreconciled",
692
+ "taking",
693
+ "taking-noncompliant",
694
+ "not-taking",
695
+ "unknown"
696
+ ];
697
+ var MEDICATION_STATUS_LABELS = {
698
+ unreconciled: "Unreconciled",
699
+ taking: "Taking as Directed",
700
+ "taking-noncompliant": "Not Taking as Directed",
701
+ "not-taking": "Not Taking",
702
+ unknown: "Unknown"
703
+ };
704
+ var DEFAULT_ACTIONS2 = [
705
+ "open",
706
+ "correct",
707
+ "refill",
708
+ "add-task",
709
+ "note",
710
+ "remove"
711
+ ];
712
+ var ACTION_META2 = {
713
+ open: { label: "Open", icon: ExternalLink },
714
+ correct: { label: "Correct", icon: Pencil },
715
+ refill: { label: "Refill", icon: RefreshCw },
716
+ "add-task": { label: "Add Task", icon: CalendarCheck },
717
+ note: { label: "Notes", icon: StickyNote },
718
+ remove: { label: "Remove", icon: Ban },
719
+ "move-up": { label: "Move Up", icon: ChevronUp },
720
+ "move-down": { label: "Move Down", icon: ChevronDown }
721
+ };
722
+ var STATUS_BUTTON_META = [
723
+ { status: "taking", label: "Taking", icon: ThumbsUp },
724
+ {
725
+ status: "taking-noncompliant",
726
+ label: "Taking \u2013 Noncompliant",
727
+ icon: HandMetal
728
+ },
729
+ { status: "not-taking", label: "Not Taking", icon: ThumbsDown },
730
+ { status: "unknown", label: "Unknown", icon: HelpCircle }
731
+ ];
732
+ function MedicationRow({
733
+ medication,
734
+ actions,
735
+ readOnly,
736
+ drag,
737
+ onMove,
738
+ onStatusChange,
739
+ onAction
740
+ }) {
741
+ const handleRowKeyDown = (e) => {
742
+ if (e.target !== e.currentTarget) return;
743
+ if (e.altKey && (e.key === "ArrowUp" || e.key === "ArrowDown")) {
744
+ e.preventDefault();
745
+ onMove?.(medication, e.key === "ArrowUp" ? -1 : 1);
746
+ } else if (e.key === "ArrowUp" || e.key === "ArrowDown") {
747
+ e.preventDefault();
748
+ const list = e.currentTarget.closest("ul");
749
+ if (!list) return;
750
+ const rows = Array.from(
751
+ list.querySelectorAll("li[data-medication-id]")
752
+ );
753
+ const i = rows.indexOf(e.currentTarget);
754
+ rows[e.key === "ArrowUp" ? Math.max(0, i - 1) : Math.min(rows.length - 1, i + 1)]?.focus();
755
+ }
756
+ };
757
+ return (
758
+ // Rows are focus stops when reordering is enabled so drag & drop has a
759
+ // keyboard equivalent (Alt+↑/↓) — 508.
760
+ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/no-noninteractive-tabindex */
761
+ /* @__PURE__ */ jsxs(
762
+ "li",
763
+ {
764
+ "data-medication-id": medication.id,
765
+ tabIndex: drag.enabled ? 0 : void 0,
766
+ onKeyDown: drag.enabled ? handleRowKeyDown : void 0,
767
+ "aria-label": drag.enabled ? `${medication.name}. Alt plus arrow keys to reorder.` : void 0,
768
+ ...drag.rowProps(medication.id),
769
+ className: cn(
770
+ "group border-border/60 relative flex min-h-11 flex-wrap items-center gap-x-2 gap-y-1 border-b px-1 py-2",
771
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none",
772
+ drag.enabled && "cursor-grab active:cursor-grabbing",
773
+ dragIndicatorClasses(drag, medication.id)
774
+ ),
775
+ children: [
776
+ drag.enabled ? /* @__PURE__ */ jsx(
777
+ GripVertical,
778
+ {
779
+ size: 14,
780
+ "aria-hidden": true,
781
+ className: "text-muted-foreground/50 shrink-0"
782
+ }
783
+ ) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/60 select-none", children: "\u2022" }),
784
+ /* @__PURE__ */ jsx("span", { className: "text-foreground font-medium", children: medication.name }),
785
+ medication.code && /* @__PURE__ */ jsxs(
786
+ "span",
787
+ {
788
+ className: "text-muted-foreground/70 text-xs",
789
+ title: medication.code.display,
790
+ children: [
791
+ medication.code.system,
792
+ " ",
793
+ medication.code.code
794
+ ]
795
+ }
796
+ ),
797
+ medication.sig && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: medication.sig }),
798
+ medication.expired && /* @__PURE__ */ jsx("span", { className: "text-sm font-bold text-red-600 dark:text-red-400", children: "EXPIRED" }),
799
+ medication.discontinuedDate && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground text-sm", children: [
800
+ "Discontinued on: ",
801
+ medication.discontinuedDate
802
+ ] }),
803
+ (medication.note || medication.task) && /* @__PURE__ */ jsxs("span", { className: "flex w-full basis-full flex-col gap-0.5 pl-4", children: [
804
+ medication.note && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground flex items-center gap-1.5 text-xs", children: [
805
+ /* @__PURE__ */ jsx(StickyNote, { size: 12 }),
806
+ medication.note
807
+ ] }),
808
+ medication.task && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground flex items-center gap-1.5 text-xs", children: [
809
+ /* @__PURE__ */ jsx(CalendarCheck, { size: 12 }),
810
+ "Task: ",
811
+ medication.task
812
+ ] })
813
+ ] }),
814
+ !readOnly && /* @__PURE__ */ jsxs(RowActionToolbar, { label: `Actions for ${medication.name}`, children: [
815
+ actions.includes("open") && /* @__PURE__ */ jsx(
816
+ RowIconButton,
817
+ {
818
+ label: ACTION_META2.open.label,
819
+ icon: ACTION_META2.open.icon,
820
+ onClick: () => onAction?.(medication, "open")
821
+ }
822
+ ),
823
+ STATUS_BUTTON_META.map(({ status, label, icon }) => /* @__PURE__ */ jsx(
824
+ RowIconButton,
825
+ {
826
+ label,
827
+ icon,
828
+ active: medication.status === status,
829
+ onClick: () => onStatusChange?.(medication, status)
830
+ },
831
+ status
832
+ )),
833
+ actions.filter((a) => a !== "open").map((action) => /* @__PURE__ */ jsx(
834
+ RowIconButton,
835
+ {
836
+ label: ACTION_META2[action].label,
837
+ icon: ACTION_META2[action].icon,
838
+ onClick: () => onAction?.(medication, action)
839
+ },
840
+ action
841
+ ))
842
+ ] })
843
+ ]
844
+ }
845
+ )
846
+ );
847
+ }
848
+ var MedicationList = React3.forwardRef(
849
+ ({
850
+ medications,
851
+ title = "Presenting medications",
852
+ onStatusChange,
853
+ onAction,
854
+ onReorder,
855
+ actions = DEFAULT_ACTIONS2,
856
+ quickAddOptions,
857
+ onQuickAdd,
858
+ onAddOther,
859
+ readOnly = false,
860
+ reconciledMessage = "All medications reconciled.",
861
+ emptyMessage = "No medications recorded.",
862
+ addSearch,
863
+ className,
864
+ "data-testid": dataTestId,
865
+ ...props
866
+ }, ref) => {
867
+ const [announcement, setAnnouncement] = useLiveAnnouncement();
868
+ const handleStatusChange = React3.useCallback(
869
+ (medication, status) => {
870
+ setAnnouncement(
871
+ `${medication.name} marked ${MEDICATION_STATUS_LABELS[status]}`
872
+ );
873
+ onStatusChange?.(medication, status);
874
+ },
875
+ [onStatusChange, setAnnouncement]
876
+ );
877
+ const groups = GROUP_ORDER.map((status) => ({
878
+ status,
879
+ label: MEDICATION_STATUS_LABELS[status],
880
+ items: medications.filter((m) => m.status === status)
881
+ })).filter((g) => g.items.length > 0 || g.status === "unreconciled");
882
+ const statusById = React3.useMemo(
883
+ () => new Map(medications.map((m) => [m.id, m.status])),
884
+ [medications]
885
+ );
886
+ const statusOf = React3.useCallback(
887
+ (id) => statusById.get(id),
888
+ [statusById]
889
+ );
890
+ const drag = useDragReorder({
891
+ ids: medications.map((m) => m.id),
892
+ onReorder: readOnly || !onReorder ? void 0 : (ids) => {
893
+ setAnnouncement("Medication list reordered");
894
+ onReorder(ids);
895
+ },
896
+ canDropOn: (dragged, target) => statusOf(dragged) === statusOf(target)
897
+ });
898
+ const moveMedication = React3.useCallback(
899
+ (medication, dir) => {
900
+ if (readOnly || !onReorder) return;
901
+ const ids = medications.map((m) => m.id);
902
+ const from = ids.indexOf(medication.id);
903
+ let to = from + dir;
904
+ while (to >= 0 && to < medications.length && medications[to].status !== medication.status) {
905
+ to += dir;
906
+ }
907
+ if (to < 0 || to >= medications.length) return;
908
+ [ids[from], ids[to]] = [ids[to], ids[from]];
909
+ setAnnouncement(
910
+ `${medication.name} moved ${dir === -1 ? "up" : "down"}`
911
+ );
912
+ onReorder(ids);
913
+ },
914
+ [medications, onReorder, readOnly, setAnnouncement]
915
+ );
916
+ return /* @__PURE__ */ jsxs(
917
+ Card,
918
+ {
919
+ ref,
920
+ padding: "none",
921
+ className: cn("w-full", className),
922
+ "data-testid": dataTestId,
923
+ ...props,
924
+ children: [
925
+ title !== null && /* @__PURE__ */ jsx(CardHeader, { className: "border-border bg-muted/50 border-b px-4 py-3", children: /* @__PURE__ */ jsx("h3", { className: "text-foreground text-sm font-semibold tracking-wide uppercase", children: title }) }),
926
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-6 px-4 py-4", children: [
927
+ groups.map(({ status, label, items }) => /* @__PURE__ */ jsxs("section", { "aria-label": label, children: [
928
+ /* @__PURE__ */ jsx("h4", { className: "border-border text-foreground border-b pb-1 text-sm font-semibold tracking-wide uppercase", children: label }),
929
+ items.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "mt-1", children: items.map((medication) => /* @__PURE__ */ jsx(
930
+ MedicationRow,
931
+ {
932
+ medication,
933
+ actions,
934
+ readOnly,
935
+ drag,
936
+ onMove: moveMedication,
937
+ onStatusChange: handleStatusChange,
938
+ onAction
939
+ },
940
+ medication.id
941
+ )) }) : medications.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mt-2 text-sm", children: emptyMessage }) : /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground mt-2 flex items-center gap-1.5 text-sm", children: [
942
+ /* @__PURE__ */ jsx(Badge, { variant: "success", size: "sm", children: "\u2713" }),
943
+ reconciledMessage
944
+ ] })
945
+ ] }, status)),
946
+ !readOnly && (addSearch || quickAddOptions?.length || onAddOther) && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
947
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs font-semibold tracking-wide uppercase", children: "Add medication" }),
948
+ addSearch,
949
+ (quickAddOptions?.length || onAddOther) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [
950
+ quickAddOptions?.map((name) => /* @__PURE__ */ jsx(
951
+ Button,
952
+ {
953
+ variant: "ghost",
954
+ size: "sm",
955
+ onClick: () => onQuickAdd?.(name),
956
+ leftIcon: /* @__PURE__ */ jsx(Plus, { size: 12 }),
957
+ className: "border-border h-auto rounded-full border px-2.5 py-1 text-xs",
958
+ children: name
959
+ },
960
+ name
961
+ )),
962
+ onAddOther && /* @__PURE__ */ jsx(
963
+ Button,
964
+ {
965
+ variant: "outline",
966
+ size: "sm",
967
+ onClick: onAddOther,
968
+ leftIcon: /* @__PURE__ */ jsx(Plus, { size: 12 }),
969
+ className: "h-auto rounded-full px-2.5 py-1 text-xs",
970
+ children: "Other\u2026"
971
+ }
972
+ )
973
+ ] })
974
+ ] })
975
+ ] }),
976
+ /* @__PURE__ */ jsx("div", { "aria-live": "polite", className: "sr-only", children: announcement })
977
+ ]
978
+ }
979
+ );
980
+ }
981
+ );
982
+ MedicationList.displayName = "MedicationList";
983
+ var DOSE_FORMS = [
984
+ "tablet",
985
+ "capsule",
986
+ "solution",
987
+ "suspension",
988
+ "syrup",
989
+ "cream",
990
+ "ointment",
991
+ "gel",
992
+ "patch",
993
+ "suppository",
994
+ "spray",
995
+ "drops",
996
+ "inhaler",
997
+ "injection",
998
+ "lozenge",
999
+ "powder",
1000
+ "film"
1001
+ ];
1002
+ var FORM_TO_UNIT = {
1003
+ tablet: "tablet",
1004
+ capsule: "capsule",
1005
+ solution: "milliliter",
1006
+ suspension: "milliliter",
1007
+ syrup: "milliliter",
1008
+ cream: "gram",
1009
+ ointment: "gram",
1010
+ gel: "gram",
1011
+ patch: "patch",
1012
+ suppository: "suppository",
1013
+ spray: "spray",
1014
+ drops: "milliliter",
1015
+ inhaler: "each",
1016
+ injection: "milliliter",
1017
+ lozenge: "each",
1018
+ powder: "gram",
1019
+ film: "each"
1020
+ };
1021
+ function parseMedicationLabel(label) {
1022
+ const lower = label.toLowerCase();
1023
+ const strengthMatch = lower.match(
1024
+ /(\d{1,7}(?:\.\d{1,4})?(?:\s{0,4}\/\s{0,4}\d{1,7}(?:\.\d{1,4})?)?)\s{0,4}(mg\/ml|mcg\/ml|mg|mcg|g|ml|units?|%|meq)\b/
1025
+ );
1026
+ const doseForm = DOSE_FORMS.find((f) => lower.includes(f));
1027
+ return {
1028
+ strength: strengthMatch ? `${strengthMatch[1].replace(/\s+/g, "")} ${strengthMatch[2]}` : void 0,
1029
+ doseForm
1030
+ };
1031
+ }
1032
+ var SIG_ROUTES = [
1033
+ [/\bby mouth\b|\boral(ly)?\b|\bpo\b/, "oral"],
1034
+ [/\bsublingual(ly)?\b|\bunder the tongue\b|\bsl\b/, "sublingual"],
1035
+ [/\bsubcutaneous(ly)?\b|\bsubq\b|\bsc\b|\bsq\b/, "subcutaneous"],
1036
+ [/\bintramuscular(ly)?\b|\bim\b/, "intramuscular"],
1037
+ [/\bintravenous(ly)?\b|\biv\b/, "intravenous"],
1038
+ [/\binhal(e|ation|ed)\b|\bpuffs?\b|\bnebuliz/, "inhalation"],
1039
+ [/\beyes?\b|\bophthalmic\b/, "ophthalmic"],
1040
+ [/\bears?\b|\botic\b/, "otic"],
1041
+ [/\bnostrils?\b|\bnasal(ly)?\b|\bintranasal/, "nasal"],
1042
+ [/\brectal(ly)?\b|\bpr\b/, "rectal"],
1043
+ [/\btransdermal\b|\bto (the )?skin\b|\bapply\b|\btopical(ly)?\b/, "topical"]
1044
+ ];
1045
+ var SIG_FREQUENCIES = [
1046
+ [/\bevery other day\b|\bqod\b/, "Every other day"],
1047
+ [/\btwice (a |per )?day\b|\btwice daily\b|\bbid\b/, "Twice daily"],
1048
+ [
1049
+ /\b(three times|3 times)( a| per)? day\b|\b(three times|3 times) daily\b|\btid\b/,
1050
+ "Three times daily"
1051
+ ],
1052
+ [
1053
+ /\b(four times|4 times)( a| per)? day\b|\b(four times|4 times) daily\b|\bqid\b/,
1054
+ "Four times daily"
1055
+ ],
1056
+ [/\bevery morning\b|\bqam\b/, "Every morning"],
1057
+ [/\b(at )?bedtime\b|\bqhs\b|\bat night\b/, "Every bedtime"],
1058
+ [/\bweekly\b|\bonce a week\b|\bevery week\b/, "Weekly"],
1059
+ [
1060
+ /\bonce (a |per )?day\b|\bonce daily\b|\bdaily\b|\bevery day\b|\bqd\b/,
1061
+ "Once daily"
1062
+ ]
1063
+ ];
1064
+ function parseSig(sig) {
1065
+ const lower = sig.toLowerCase();
1066
+ const route = SIG_ROUTES.find(([re]) => re.test(lower))?.[1];
1067
+ const frequency = SIG_FREQUENCIES.find(([re]) => re.test(lower))?.[1];
1068
+ const prn = /\bas needed\b|\bprn\b/.test(lower);
1069
+ return { route, frequency, prn };
1070
+ }
1071
+ function labelToMedicationFields(label) {
1072
+ const { strength, doseForm } = parseMedicationLabel(label);
1073
+ return {
1074
+ ...strength && { strength },
1075
+ ...doseForm && {
1076
+ doseForm,
1077
+ quantityUnit: FORM_TO_UNIT[doseForm] ?? doseForm
1078
+ }
1079
+ };
1080
+ }
1081
+ function lookupToMedicationFields(result) {
1082
+ return {
1083
+ name: result.label,
1084
+ code: {
1085
+ system: result.codetype,
1086
+ code: result.fullcode,
1087
+ display: result.label
1088
+ },
1089
+ ...labelToMedicationFields(result.label)
1090
+ };
1091
+ }
1092
+ function newId2() {
1093
+ return `med-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1094
+ }
1095
+ function MedicationEditor({
1096
+ open,
1097
+ medication,
1098
+ codeLookup,
1099
+ onClose,
1100
+ onSave
1101
+ }) {
1102
+ const ambientCodeLookup = useCodeLookupConfig();
1103
+ const effectiveCodeLookup = codeLookup === false ? void 0 : codeLookup ?? ambientCodeLookup ?? void 0;
1104
+ const [draft, setDraft] = React3.useState(
1105
+ () => medication ?? {
1106
+ id: newId2(),
1107
+ name: "",
1108
+ status: "unreconciled"
1109
+ }
1110
+ );
1111
+ const bodyRef = React3.useRef(null);
1112
+ React3.useEffect(() => {
1113
+ const input = bodyRef.current?.querySelector("input");
1114
+ input?.focus();
1115
+ }, []);
1116
+ const patch = (p) => setDraft((prev) => ({ ...prev, ...p }));
1117
+ const handleCodeSelect = (result) => {
1118
+ patch(lookupToMedicationFields(result));
1119
+ };
1120
+ const handleSigChange = (sig) => {
1121
+ const { route, frequency, prn } = parseSig(sig);
1122
+ patch({ sig, route, frequency, prn: prn || void 0 });
1123
+ };
1124
+ const canSave = draft.name.trim().length > 0;
1125
+ if (!open) return null;
1126
+ return /* @__PURE__ */ jsxs(Modal, { open: true, onOpenChange: (o) => !o && onClose(), size: "lg", children: [
1127
+ /* @__PURE__ */ jsxs(ModalHeader, { children: [
1128
+ /* @__PURE__ */ jsx(ModalTitle, { children: medication ? "Correct Medication" : "Add Medication" }),
1129
+ /* @__PURE__ */ jsx(ModalClose, {})
1130
+ ] }),
1131
+ /* @__PURE__ */ jsx(ModalBody, { className: "space-y-5", children: /* @__PURE__ */ jsxs("div", { ref: bodyRef, className: "contents", children: [
1132
+ /* @__PURE__ */ jsxs("section", { className: "space-y-3", "aria-label": "Medication", children: [
1133
+ effectiveCodeLookup ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1134
+ /* @__PURE__ */ jsx(Label, { htmlFor: "med-search", children: "Medication" }),
1135
+ /* @__PURE__ */ jsx(
1136
+ effectiveCodeLookup.component,
1137
+ {
1138
+ indexUrl: effectiveCodeLookup.indexUrl,
1139
+ locale: effectiveCodeLookup.locale,
1140
+ domains: ["med"],
1141
+ bare: true,
1142
+ clearOnSelect: false,
1143
+ placeholder: "Search RxNorm / FDB \u2014 e.g. lisinopril",
1144
+ initialQuery: medication?.name || void 0,
1145
+ initialSearch: medication ? !medication.code : void 0,
1146
+ onSelect: handleCodeSelect,
1147
+ onFreeText: (text) => patch({ name: text, code: void 0 })
1148
+ }
1149
+ ),
1150
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: draft.code ? `Coded: ${draft.code.system} ${draft.code.code}` : draft.name ? `Uncoded free text: "${draft.name}" \u2014 pick a result to code it` : "Pick a result to code the medication, or press Enter for free text" })
1151
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1152
+ /* @__PURE__ */ jsx(Label, { htmlFor: "med-name", children: "Medication" }),
1153
+ /* @__PURE__ */ jsx(
1154
+ Input,
1155
+ {
1156
+ id: "med-name",
1157
+ value: draft.name,
1158
+ onChange: (e) => patch({ name: e.target.value })
1159
+ }
1160
+ )
1161
+ ] }),
1162
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
1163
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1164
+ /* @__PURE__ */ jsx(Label, { htmlFor: "med-strength", children: "Strength" }),
1165
+ /* @__PURE__ */ jsx(
1166
+ Input,
1167
+ {
1168
+ id: "med-strength",
1169
+ value: draft.strength ?? "",
1170
+ onChange: (e) => patch({ strength: e.target.value })
1171
+ }
1172
+ )
1173
+ ] }),
1174
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1175
+ /* @__PURE__ */ jsx(Label, { htmlFor: "med-form", children: "Dose form" }),
1176
+ /* @__PURE__ */ jsx(
1177
+ Input,
1178
+ {
1179
+ id: "med-form",
1180
+ value: draft.doseForm ?? "",
1181
+ onChange: (e) => {
1182
+ const doseForm = e.target.value;
1183
+ const normalized = doseForm.toLowerCase().trim();
1184
+ patch({
1185
+ doseForm,
1186
+ quantityUnit: (FORM_TO_UNIT[normalized] ?? normalized) || void 0
1187
+ });
1188
+ }
1189
+ }
1190
+ )
1191
+ ] })
1192
+ ] })
1193
+ ] }),
1194
+ /* @__PURE__ */ jsxs("section", { className: "space-y-3", "aria-label": "Dispensing", children: [
1195
+ /* @__PURE__ */ jsx("h4", { className: "text-muted-foreground text-xs font-semibold tracking-wide uppercase", children: "Dispensing" }),
1196
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-3", children: [
1197
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1198
+ /* @__PURE__ */ jsxs(Label, { htmlFor: "med-qty", children: [
1199
+ "Quantity",
1200
+ draft.quantityUnit ? ` (${draft.quantityUnit}s)` : ""
1201
+ ] }),
1202
+ /* @__PURE__ */ jsx(
1203
+ Input,
1204
+ {
1205
+ id: "med-qty",
1206
+ inputMode: "decimal",
1207
+ value: draft.quantity ?? "",
1208
+ onChange: (e) => patch({ quantity: e.target.value })
1209
+ }
1210
+ )
1211
+ ] }),
1212
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1213
+ /* @__PURE__ */ jsx(Label, { htmlFor: "med-days", children: "Days supply" }),
1214
+ /* @__PURE__ */ jsx(
1215
+ Input,
1216
+ {
1217
+ id: "med-days",
1218
+ inputMode: "numeric",
1219
+ value: draft.daysSupply ?? "",
1220
+ onChange: (e) => patch({ daysSupply: e.target.value })
1221
+ }
1222
+ )
1223
+ ] }),
1224
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1225
+ /* @__PURE__ */ jsx(Label, { htmlFor: "med-refills", children: "Refills" }),
1226
+ /* @__PURE__ */ jsx(
1227
+ Input,
1228
+ {
1229
+ id: "med-refills",
1230
+ inputMode: "numeric",
1231
+ value: draft.refills ?? "",
1232
+ onChange: (e) => patch({ refills: e.target.value })
1233
+ }
1234
+ )
1235
+ ] })
1236
+ ] }),
1237
+ /* @__PURE__ */ jsxs(
1238
+ RadioGroup,
1239
+ {
1240
+ name: "med-substitution",
1241
+ label: "Substitution",
1242
+ value: draft.substitution ?? "0",
1243
+ onValueChange: (v) => patch({ substitution: v }),
1244
+ orientation: "horizontal",
1245
+ size: "sm",
1246
+ children: [
1247
+ /* @__PURE__ */ jsx(Radio, { value: "0", label: "Substitution permitted" }),
1248
+ /* @__PURE__ */ jsx(Radio, { value: "1", label: "Dispense as written (DAW)" })
1249
+ ]
1250
+ }
1251
+ )
1252
+ ] }),
1253
+ /* @__PURE__ */ jsxs("section", { className: "space-y-3", "aria-label": "Directions", children: [
1254
+ /* @__PURE__ */ jsx("h4", { className: "text-muted-foreground text-xs font-semibold tracking-wide uppercase", children: "Directions" }),
1255
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1256
+ /* @__PURE__ */ jsx(Label, { htmlFor: "med-sig", children: "Sig (patient directions)" }),
1257
+ /* @__PURE__ */ jsx(
1258
+ Textarea,
1259
+ {
1260
+ id: "med-sig",
1261
+ value: draft.sig ?? "",
1262
+ onChange: (e) => handleSigChange(e.target.value),
1263
+ rows: 2
1264
+ }
1265
+ ),
1266
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", "aria-live": "polite", children: draft.route || draft.frequency || draft.prn ? /* @__PURE__ */ jsxs(Fragment, { children: [
1267
+ "Derived:",
1268
+ " ",
1269
+ [
1270
+ draft.route && `route ${draft.route}`,
1271
+ draft.frequency && draft.frequency.toLowerCase(),
1272
+ draft.prn && "PRN (as needed)"
1273
+ ].filter(Boolean).join(" \xB7 ")
1274
+ ] }) : "Route, frequency, and PRN are derived from the directions" })
1275
+ ] })
1276
+ ] }),
1277
+ /* @__PURE__ */ jsxs("section", { className: "space-y-3", "aria-label": "Dates and context", children: [
1278
+ /* @__PURE__ */ jsx("h4", { className: "text-muted-foreground text-xs font-semibold tracking-wide uppercase", children: "Dates & context" }),
1279
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
1280
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1281
+ /* @__PURE__ */ jsx(Label, { htmlFor: "med-start", children: "Start date" }),
1282
+ /* @__PURE__ */ jsx(
1283
+ DateInput,
1284
+ {
1285
+ id: "med-start",
1286
+ value: draft.startDate ?? "",
1287
+ onChange: (v) => patch({ startDate: v })
1288
+ }
1289
+ )
1290
+ ] }),
1291
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1292
+ /* @__PURE__ */ jsx(Label, { htmlFor: "med-end", children: "End date" }),
1293
+ /* @__PURE__ */ jsx(
1294
+ DateInput,
1295
+ {
1296
+ id: "med-end",
1297
+ value: draft.endDate ?? "",
1298
+ onChange: (v) => patch({ endDate: v })
1299
+ }
1300
+ )
1301
+ ] })
1302
+ ] }),
1303
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1304
+ /* @__PURE__ */ jsx(Label, { htmlFor: "med-indication", children: "Indication" }),
1305
+ /* @__PURE__ */ jsx(
1306
+ Input,
1307
+ {
1308
+ id: "med-indication",
1309
+ value: draft.indication ?? "",
1310
+ onChange: (e) => patch({ indication: e.target.value })
1311
+ }
1312
+ )
1313
+ ] }),
1314
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1315
+ /* @__PURE__ */ jsx(Label, { htmlFor: "med-pharmacy-notes", children: "Pharmacy notes" }),
1316
+ /* @__PURE__ */ jsx(
1317
+ Textarea,
1318
+ {
1319
+ id: "med-pharmacy-notes",
1320
+ value: draft.pharmacyNotes ?? "",
1321
+ onChange: (e) => patch({ pharmacyNotes: e.target.value }),
1322
+ rows: 2
1323
+ }
1324
+ )
1325
+ ] })
1326
+ ] })
1327
+ ] }) }),
1328
+ /* @__PURE__ */ jsxs(ModalFooter, { children: [
1329
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onClose, children: "Cancel" }),
1330
+ /* @__PURE__ */ jsx(
1331
+ Button,
1332
+ {
1333
+ onClick: () => {
1334
+ onSave({ ...draft, name: draft.name.trim() });
1335
+ onClose();
1336
+ },
1337
+ disabled: !canSave,
1338
+ children: "Save"
1339
+ }
1340
+ )
1341
+ ] })
1342
+ ] });
1343
+ }
1344
+ var MANAGED_ACTIONS = [
1345
+ "correct",
1346
+ "add-task",
1347
+ "note",
1348
+ "remove"
1349
+ ];
1350
+ function newId3() {
1351
+ return `med-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1352
+ }
1353
+ function MedicationDialog({
1354
+ dialog,
1355
+ onClose,
1356
+ onSave
1357
+ }) {
1358
+ const [form, setForm] = React3.useState(
1359
+ () => dialog.kind === "note" ? { note: dialog.medication.note ?? "" } : { task: dialog.medication.task ?? "" }
1360
+ );
1361
+ const set = (key) => (e) => setForm((prev) => ({ ...prev, [key]: e.target.value }));
1362
+ const handleSave = () => {
1363
+ onSave(form);
1364
+ onClose();
1365
+ };
1366
+ return /* @__PURE__ */ jsxs(Modal, { open: true, onOpenChange: (open) => !open && onClose(), size: "md", children: [
1367
+ /* @__PURE__ */ jsxs(ModalHeader, { children: [
1368
+ /* @__PURE__ */ jsx(ModalTitle, { children: dialog.kind === "note" ? "Medication Notes" : "Add Task" }),
1369
+ /* @__PURE__ */ jsx(ModalClose, {})
1370
+ ] }),
1371
+ /* @__PURE__ */ jsxs(ModalBody, { className: "space-y-4", children: [
1372
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: dialog.medication.name }),
1373
+ dialog.kind === "note" ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1374
+ /* @__PURE__ */ jsx(Label, { htmlFor: "med-note", children: "Note" }),
1375
+ /* @__PURE__ */ jsx(
1376
+ Textarea,
1377
+ {
1378
+ id: "med-note",
1379
+ value: form.note,
1380
+ onChange: set("note"),
1381
+ rows: 4
1382
+ }
1383
+ )
1384
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1385
+ /* @__PURE__ */ jsx(Label, { htmlFor: "med-task", children: "Task" }),
1386
+ /* @__PURE__ */ jsx(
1387
+ Textarea,
1388
+ {
1389
+ id: "med-task",
1390
+ value: form.task,
1391
+ onChange: set("task"),
1392
+ rows: 3,
1393
+ placeholder: "Follow-up task for this medication"
1394
+ }
1395
+ )
1396
+ ] })
1397
+ ] }),
1398
+ /* @__PURE__ */ jsxs(ModalFooter, { children: [
1399
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onClose, children: "Cancel" }),
1400
+ /* @__PURE__ */ jsx(Button, { onClick: handleSave, children: "Save" })
1401
+ ] })
1402
+ ] });
1403
+ }
1404
+ function MedicationReconciliation({
1405
+ medications: controlledMedications,
1406
+ defaultMedications,
1407
+ onChange,
1408
+ title = "Presenting medications",
1409
+ quickAddOptions,
1410
+ actions = MANAGED_ACTIONS,
1411
+ onAction,
1412
+ codeLookup,
1413
+ inlineAddSearch = false,
1414
+ readOnly = false,
1415
+ reconciledMessage,
1416
+ emptyMessage,
1417
+ className,
1418
+ "data-testid": dataTestId
1419
+ }) {
1420
+ const isControlled = controlledMedications !== void 0;
1421
+ const [internal, setInternal] = React3.useState(
1422
+ defaultMedications ?? []
1423
+ );
1424
+ const medications = isControlled ? controlledMedications : internal;
1425
+ const ambientCodeLookup = useCodeLookupConfig();
1426
+ const effectiveCodeLookup = codeLookup === false ? void 0 : codeLookup ?? ambientCodeLookup ?? void 0;
1427
+ const [dialog, setDialog] = React3.useState(null);
1428
+ const commit = (next) => {
1429
+ if (!isControlled) setInternal(next);
1430
+ onChange?.(next);
1431
+ };
1432
+ const patchMedication = (id, patch) => {
1433
+ commit(medications.map((m) => m.id === id ? { ...m, ...patch } : m));
1434
+ };
1435
+ const moveMedication = (med, direction) => {
1436
+ const meds = [...medications];
1437
+ const group = meds.filter((m) => m.status === med.status);
1438
+ const groupIndex = group.findIndex((m) => m.id === med.id);
1439
+ const swapWith = group[groupIndex + direction];
1440
+ if (!swapWith) return;
1441
+ const i = meds.findIndex((m) => m.id === med.id);
1442
+ const j = meds.findIndex((m) => m.id === swapWith.id);
1443
+ [meds[i], meds[j]] = [meds[j], meds[i]];
1444
+ commit(meds);
1445
+ };
1446
+ const handleAction = (med, action) => {
1447
+ switch (action) {
1448
+ case "remove":
1449
+ commit(medications.filter((m) => m.id !== med.id));
1450
+ break;
1451
+ case "move-up":
1452
+ moveMedication(med, -1);
1453
+ break;
1454
+ case "move-down":
1455
+ moveMedication(med, 1);
1456
+ break;
1457
+ case "correct":
1458
+ case "note":
1459
+ case "add-task":
1460
+ setDialog({ kind: action, medication: med });
1461
+ break;
1462
+ default:
1463
+ onAction?.(med, action);
1464
+ break;
1465
+ }
1466
+ };
1467
+ const addMedication = (name, sig) => {
1468
+ commit([
1469
+ ...medications,
1470
+ { id: newId3(), name, sig: void 0, status: "unreconciled" }
1471
+ ]);
1472
+ };
1473
+ const handleDialogSave = (form) => {
1474
+ if (!dialog) return;
1475
+ switch (dialog.kind) {
1476
+ case "note":
1477
+ patchMedication(dialog.medication.id, {
1478
+ note: form.note.trim() || void 0
1479
+ });
1480
+ break;
1481
+ case "add-task":
1482
+ patchMedication(dialog.medication.id, {
1483
+ task: form.task.trim() || void 0
1484
+ });
1485
+ break;
1486
+ }
1487
+ };
1488
+ const editorOpen = dialog?.kind === "correct" || dialog?.kind === "add";
1489
+ const editorTarget = dialog?.kind === "correct" ? dialog.medication : void 0;
1490
+ const handleEditorSave = (med) => {
1491
+ if (dialog?.kind === "correct") {
1492
+ commit(medications.map((m) => m.id === med.id ? med : m));
1493
+ } else {
1494
+ commit([...medications, { ...med, status: "unreconciled" }]);
1495
+ }
1496
+ };
1497
+ const addSearch = inlineAddSearch && effectiveCodeLookup && !readOnly ? /* @__PURE__ */ jsx(
1498
+ effectiveCodeLookup.component,
1499
+ {
1500
+ indexUrl: effectiveCodeLookup.indexUrl,
1501
+ locale: effectiveCodeLookup.locale,
1502
+ domains: ["med"],
1503
+ bare: true,
1504
+ clearOnSelect: true,
1505
+ placeholder: "Search medications to add",
1506
+ onSelect: (result) => commit([
1507
+ ...medications,
1508
+ {
1509
+ id: newId3(),
1510
+ name: result.label,
1511
+ status: "unreconciled",
1512
+ ...lookupToMedicationFields(result)
1513
+ }
1514
+ ]),
1515
+ onFreeText: (text) => addMedication(text)
1516
+ }
1517
+ ) : void 0;
1518
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1519
+ /* @__PURE__ */ jsx(
1520
+ MedicationList,
1521
+ {
1522
+ medications,
1523
+ title,
1524
+ readOnly,
1525
+ actions,
1526
+ onStatusChange: (med, status) => patchMedication(med.id, { status }),
1527
+ onAction: handleAction,
1528
+ onReorder: (ids) => commit(
1529
+ [...medications].sort(
1530
+ (a, b) => ids.indexOf(a.id) - ids.indexOf(b.id)
1531
+ )
1532
+ ),
1533
+ quickAddOptions,
1534
+ onQuickAdd: (name) => addMedication(name),
1535
+ onAddOther: () => setDialog({ kind: "add" }),
1536
+ addSearch,
1537
+ reconciledMessage,
1538
+ emptyMessage,
1539
+ className,
1540
+ "data-testid": dataTestId
1541
+ }
1542
+ ),
1543
+ dialog && (dialog.kind === "note" || dialog.kind === "add-task") && /* @__PURE__ */ jsx(
1544
+ MedicationDialog,
1545
+ {
1546
+ dialog,
1547
+ onClose: () => setDialog(null),
1548
+ onSave: handleDialogSave
1549
+ },
1550
+ dialog.kind + dialog.medication.id
1551
+ ),
1552
+ editorOpen && /* @__PURE__ */ jsx(
1553
+ MedicationEditor,
1554
+ {
1555
+ open: true,
1556
+ medication: editorTarget,
1557
+ codeLookup: codeLookup === false ? false : effectiveCodeLookup,
1558
+ onClose: () => setDialog(null),
1559
+ onSave: handleEditorSave
1560
+ },
1561
+ editorTarget?.id ?? "add"
1562
+ )
1563
+ ] });
1564
+ }
1565
+
1566
+ export { ALLERGY_TYPE_LABELS, AllergyList, AllergyManager, CodeLookupProvider, MEDICATION_STATUS_LABELS, MedicationEditor, MedicationList, MedicationReconciliation, RowActionToolbar, RowIconButton, labelToMedicationFields, lookupToMedicationFields, parseMedicationLabel, parseSig, toolbarKeyNav, useCodeLookupConfig };
1567
+ //# sourceMappingURL=chunk-HUXOASEB.js.map
1568
+ //# sourceMappingURL=chunk-HUXOASEB.js.map