@mieweb/ui 0.11.0-dev.32 → 0.11.0-dev.34
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.
- package/dist/{chunk-PJN4HM2X.cjs → chunk-3O3Q3DX7.cjs} +21 -21
- package/dist/{chunk-PJN4HM2X.cjs.map → chunk-3O3Q3DX7.cjs.map} +1 -1
- package/dist/{chunk-M42FTWY2.cjs → chunk-46RYJYN2.cjs} +6 -6
- package/dist/{chunk-M42FTWY2.cjs.map → chunk-46RYJYN2.cjs.map} +1 -1
- package/dist/{chunk-JMAZ2GKT.js → chunk-4GSFVD5V.js} +3 -3
- package/dist/{chunk-JMAZ2GKT.js.map → chunk-4GSFVD5V.js.map} +1 -1
- package/dist/{chunk-2R3ILG4Y.cjs → chunk-4M2KE4O5.cjs} +7 -7
- package/dist/{chunk-2R3ILG4Y.cjs.map → chunk-4M2KE4O5.cjs.map} +1 -1
- package/dist/{chunk-L52HKEUD.js → chunk-4SUEBKTY.js} +3 -3
- package/dist/{chunk-L52HKEUD.js.map → chunk-4SUEBKTY.js.map} +1 -1
- package/dist/{chunk-SCYLVQWA.cjs → chunk-6O6EYKOG.cjs} +130 -874
- package/dist/chunk-6O6EYKOG.cjs.map +1 -0
- package/dist/{chunk-INMDLVUB.cjs → chunk-6UYMO3PF.cjs} +7 -2
- package/dist/chunk-6UYMO3PF.cjs.map +1 -0
- package/dist/{chunk-2Z2GFDRS.cjs → chunk-DXNVQQYN.cjs} +4 -4
- package/dist/{chunk-2Z2GFDRS.cjs.map → chunk-DXNVQQYN.cjs.map} +1 -1
- package/dist/{chunk-JN2CL7MK.js → chunk-EWPNM6OO.js} +25 -5
- package/dist/chunk-EWPNM6OO.js.map +1 -0
- package/dist/{chunk-JQAGNAY2.js → chunk-EX7Q5545.js} +4 -4
- package/dist/{chunk-JQAGNAY2.js.map → chunk-EX7Q5545.js.map} +1 -1
- package/dist/{chunk-YG7BR76Y.js → chunk-GMNF4E7B.js} +3 -42
- package/dist/chunk-GMNF4E7B.js.map +1 -0
- package/dist/chunk-HTZ27DGZ.js +46 -0
- package/dist/chunk-HTZ27DGZ.js.map +1 -0
- package/dist/{chunk-SNAMNKFH.js → chunk-JEXEFWEM.js} +7 -2
- package/dist/chunk-JEXEFWEM.js.map +1 -0
- package/dist/{chunk-KIVEYQGA.cjs → chunk-MSX2P5JE.cjs} +2 -48
- package/dist/chunk-MSX2P5JE.cjs.map +1 -0
- package/dist/{chunk-VV2U36T6.cjs → chunk-MZR634HI.cjs} +9 -9
- package/dist/{chunk-VV2U36T6.cjs.map → chunk-MZR634HI.cjs.map} +1 -1
- package/dist/{chunk-JCO62XMU.cjs → chunk-NCPDUJKJ.cjs} +43 -26
- package/dist/chunk-NCPDUJKJ.cjs.map +1 -0
- package/dist/{chunk-DG3CXR24.js → chunk-NW6D6AFL.js} +80 -819
- package/dist/chunk-NW6D6AFL.js.map +1 -0
- package/dist/{chunk-BP4LXRC6.cjs → chunk-PGJLW2NO.cjs} +7 -7
- package/dist/{chunk-BP4LXRC6.cjs.map → chunk-PGJLW2NO.cjs.map} +1 -1
- package/dist/chunk-PHYQSCP6.cjs +54 -0
- package/dist/chunk-PHYQSCP6.cjs.map +1 -0
- package/dist/{chunk-ZFJJMDV3.js → chunk-RTADGBBN.js} +3 -3
- package/dist/{chunk-ZFJJMDV3.js.map → chunk-RTADGBBN.js.map} +1 -1
- package/dist/{chunk-FQUSSPXX.cjs → chunk-S2CJ57BX.cjs} +25 -5
- package/dist/chunk-S2CJ57BX.cjs.map +1 -0
- package/dist/{chunk-KSQQBFDQ.js → chunk-SPGP252Q.js} +43 -26
- package/dist/chunk-SPGP252Q.js.map +1 -0
- package/dist/{chunk-67SD6VPF.cjs → chunk-SVC7NPQ6.cjs} +6 -6
- package/dist/{chunk-67SD6VPF.cjs.map → chunk-SVC7NPQ6.cjs.map} +1 -1
- package/dist/{chunk-TWQSMUE6.js → chunk-VMEVLBI3.js} +46 -38
- package/dist/chunk-VMEVLBI3.js.map +1 -0
- package/dist/{chunk-QNJEDBIN.js → chunk-X5HLVACK.js} +4 -4
- package/dist/{chunk-QNJEDBIN.js.map → chunk-X5HLVACK.js.map} +1 -1
- package/dist/{chunk-LC3JL2HB.cjs → chunk-XEHD2HA5.cjs} +46 -38
- package/dist/chunk-XEHD2HA5.cjs.map +1 -0
- package/dist/{chunk-R3MJKY63.js → chunk-YGSPIDE4.js} +3 -3
- package/dist/{chunk-R3MJKY63.js.map → chunk-YGSPIDE4.js.map} +1 -1
- package/dist/{chunk-7LKTIZLR.js → chunk-YR45POH2.js} +3 -3
- package/dist/{chunk-7LKTIZLR.js.map → chunk-YR45POH2.js.map} +1 -1
- package/dist/components/AlertDialog/index.cjs +6 -6
- package/dist/components/AlertDialog/index.js +2 -2
- package/dist/components/EmailEditor/index.cjs +14 -12
- package/dist/components/EmailEditor/index.js +5 -3
- package/dist/components/KeyboardShortcutsOverlay/index.cjs +4 -4
- package/dist/components/KeyboardShortcutsOverlay/index.js +2 -2
- package/dist/components/MediaEditor/index.cjs +7 -7
- package/dist/components/MediaEditor/index.js +2 -2
- package/dist/components/MediaFeed/index.cjs +3 -3
- package/dist/components/MediaFeed/index.js +2 -2
- package/dist/components/Modal/index.cjs +9 -9
- package/dist/components/Modal/index.js +1 -1
- package/dist/components/RichTextEditor/index.cjs +6 -4
- package/dist/components/RichTextEditor/index.js +3 -1
- package/dist/components/SuperChat/index.cjs +3 -3
- package/dist/components/SuperChat/index.js +2 -2
- package/dist/components/Textarea/index.cjs +7 -5
- package/dist/components/Textarea/index.js +4 -2
- package/dist/esheet.cjs +1119 -1393
- package/dist/esheet.cjs.map +1 -1
- package/dist/esheet.d.cts +211 -4
- package/dist/esheet.d.ts +211 -4
- package/dist/esheet.js +946 -1239
- package/dist/esheet.js.map +1 -1
- package/dist/hooks/index.cjs +41 -40
- package/dist/hooks/index.js +2 -1
- package/dist/index.cjs +1058 -285
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +809 -20
- package/dist/index.js.map +1 -1
- package/dist/{model-NPW2JMHZ.cjs → model-EFXX5JZN.cjs} +18 -18
- package/dist/{model-NPW2JMHZ.cjs.map → model-EFXX5JZN.cjs.map} +1 -1
- package/dist/{model-SG5H3TCJ.js → model-TAARSYDY.js} +4 -4
- package/dist/{model-SG5H3TCJ.js.map → model-TAARSYDY.js.map} +1 -1
- package/dist/styles.css +1 -1
- package/dist/tailwind-preset.cjs +4 -4
- package/dist/tailwind-preset.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-DG3CXR24.js.map +0 -1
- package/dist/chunk-FQUSSPXX.cjs.map +0 -1
- package/dist/chunk-INMDLVUB.cjs.map +0 -1
- package/dist/chunk-JCO62XMU.cjs.map +0 -1
- package/dist/chunk-JN2CL7MK.js.map +0 -1
- package/dist/chunk-KIVEYQGA.cjs.map +0 -1
- package/dist/chunk-KSQQBFDQ.js.map +0 -1
- package/dist/chunk-LC3JL2HB.cjs.map +0 -1
- package/dist/chunk-SCYLVQWA.cjs.map +0 -1
- package/dist/chunk-SNAMNKFH.js.map +0 -1
- package/dist/chunk-TWQSMUE6.js.map +0 -1
- package/dist/chunk-YG7BR76Y.js.map +0 -1
package/dist/esheet.js
CHANGED
|
@@ -1,32 +1,33 @@
|
|
|
1
1
|
import './chunk-GADJASST.js';
|
|
2
|
-
import { parseEncounterMdy, DEFAULT_ENCOUNTER_VISIT_DEFINITION, createEncounterFormDefinition, ENCOUNTER_DOCUMENT_FIELD_ID, encounterDocumentResponse, createEncounterSnapshot, updateEncounterDocumentBody, getEncounterSectionFieldId, getEncounterMdyFields, getEncounterSectionFieldIds, getEncounterDocumentBody, renderEncounterMdy, validateEncounterVitals, validateEncounterPlan, validateEncounterConditions, validAssessmentItem, isEncounterDate, validateEncounterAllergyValue, validateEncounterMedicationValue, validateEncounterAssessment, validateEncounterResponses, getEncounterFieldId, validateEncounterDefinition } from './chunk-
|
|
3
|
-
export { DEFAULT_ENCOUNTER_VISIT_DEFINITION, ENCOUNTER_DOCUMENT_FIELD_ID, EXAMPLE_ENCOUNTER_VISIT_RESPONSES, createEncounterFormDefinition, createEncounterMdy, createEncounterSnapshot, encounterDocumentResponse, getEncounterConcerns, getEncounterDocumentBody, getEncounterFieldId, getEncounterMdyFields, getEncounterSectionFieldId, getEncounterSectionFieldIds, parseEncounterAssessment, parseEncounterConditions, parseEncounterMdy, parseEncounterPlan, parseEncounterVitals, refreshEncounterMdyLinks, renderEncounterMdy, updateEncounterDocumentBody, validAssessmentItem, validateEncounterAllergyValue, validateEncounterAssessment, validateEncounterConditions, validateEncounterDefinition, validateEncounterMedicationValue, validateEncounterPlan, validateEncounterResponses, validateEncounterVitals } from './chunk-
|
|
4
|
-
import { useCodeLookupConfig, CodeLookupContext, Assessment, ConditionEditor,
|
|
5
|
-
import { ProblemList, currentAssertion } from './chunk-
|
|
2
|
+
import { parseEncounterMdy, DEFAULT_ENCOUNTER_VISIT_DEFINITION, createEncounterFormDefinition, ENCOUNTER_DOCUMENT_FIELD_ID, encounterDocumentResponse, createEncounterSnapshot, updateEncounterDocumentBody, getEncounterSectionFieldId, getEncounterMdyFields, getEncounterSectionFieldIds, getEncounterDocumentBody, renderEncounterMdy, validateEncounterVitals, validateEncounterPlan, validateEncounterConditions, validAssessmentItem, isEncounterDate, validateEncounterAllergyValue, validateEncounterMedicationValue, validateEncounterAssessment, validateEncounterResponses, getEncounterFieldId, validateEncounterDefinition } from './chunk-X5HLVACK.js';
|
|
3
|
+
export { DEFAULT_ENCOUNTER_VISIT_DEFINITION, ENCOUNTER_DOCUMENT_FIELD_ID, EXAMPLE_ENCOUNTER_VISIT_RESPONSES, createEncounterFormDefinition, createEncounterMdy, createEncounterSnapshot, encounterDocumentResponse, getEncounterConcerns, getEncounterDocumentBody, getEncounterFieldId, getEncounterMdyFields, getEncounterSectionFieldId, getEncounterSectionFieldIds, parseEncounterAssessment, parseEncounterConditions, parseEncounterMdy, parseEncounterPlan, parseEncounterVitals, refreshEncounterMdyLinks, renderEncounterMdy, updateEncounterDocumentBody, validAssessmentItem, validateEncounterAllergyValue, validateEncounterAssessment, validateEncounterConditions, validateEncounterDefinition, validateEncounterMedicationValue, validateEncounterPlan, validateEncounterResponses, validateEncounterVitals } from './chunk-X5HLVACK.js';
|
|
4
|
+
import { useCodeLookupConfig, CodeLookupContext, Assessment, ConditionEditor, AllergyManager, MedicationReconciliation, normalizeConditionCodingSystem } from './chunk-NW6D6AFL.js';
|
|
5
|
+
import { ProblemList, currentAssertion } from './chunk-SPGP252Q.js';
|
|
6
6
|
import './chunk-YFWDS6MX.js';
|
|
7
7
|
import { ButtonGroup } from './chunk-BBLEGF5L.js';
|
|
8
|
-
import
|
|
9
|
-
import
|
|
8
|
+
import './chunk-EPSB4WTL.js';
|
|
9
|
+
import './chunk-I3WGZPI6.js';
|
|
10
10
|
import { DateInput } from './chunk-I6Y2KLTZ.js';
|
|
11
|
-
import { TextAreaRich
|
|
12
|
-
import './chunk-
|
|
11
|
+
import { TextAreaRich } from './chunk-4SUEBKTY.js';
|
|
12
|
+
import './chunk-EWPNM6OO.js';
|
|
13
13
|
import { MessagesProvider, useMessages } from './chunk-5YOT4DVH.js';
|
|
14
14
|
import './chunk-OQWZYKJM.js';
|
|
15
15
|
import { Select } from './chunk-VCI2Y7ST.js';
|
|
16
16
|
import './chunk-AMYFRY7K.js';
|
|
17
17
|
import './chunk-OYUIEJPN.js';
|
|
18
18
|
import './chunk-QYSUYB3S.js';
|
|
19
|
-
import
|
|
20
|
-
import './chunk-
|
|
19
|
+
import './chunk-753Z3NXL.js';
|
|
20
|
+
import './chunk-VMEVLBI3.js';
|
|
21
21
|
import './chunk-6NZXWKUE.js';
|
|
22
|
-
import {
|
|
22
|
+
import { Input, RequiredMark } from './chunk-3IMM2PM4.js';
|
|
23
23
|
import './chunk-YUMOM3KE.js';
|
|
24
24
|
import './chunk-HNXLLDPN.js';
|
|
25
|
+
import './chunk-HTZ27DGZ.js';
|
|
25
26
|
import './chunk-4SMSH4OY.js';
|
|
26
27
|
import { useEscapeKey } from './chunk-T4ME7QCT.js';
|
|
27
28
|
import './chunk-62ZOA6PX.js';
|
|
28
29
|
import './chunk-WSNMQQ4P.js';
|
|
29
|
-
import
|
|
30
|
+
import './chunk-RC2YMOMS.js';
|
|
30
31
|
import './chunk-ERLPVXOV.js';
|
|
31
32
|
import './chunk-VSQF22GL.js';
|
|
32
33
|
import './chunk-BKSPDTX4.js';
|
|
@@ -40,12 +41,13 @@ import './chunk-F4N67NAF.js';
|
|
|
40
41
|
import { cn } from './chunk-TQONQSJO.js';
|
|
41
42
|
import './chunk-PX6F3LHL.js';
|
|
42
43
|
import * as React11 from 'react';
|
|
43
|
-
import { Save,
|
|
44
|
+
import { Save, Trash2, Plus } from 'lucide-react';
|
|
44
45
|
import { EsheetRenderer } from '@esheet/renderer';
|
|
45
46
|
export { EsheetRenderer } from '@esheet/renderer';
|
|
46
47
|
import DOMPurify from 'dompurify';
|
|
47
48
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
48
|
-
import { getFieldComponent, SectionField, registerFieldComponents
|
|
49
|
+
import { getFieldComponent, registerCustomFieldTypes, SectionField, registerFieldComponents } from '@esheet/fields';
|
|
50
|
+
import { stringify } from 'yaml';
|
|
49
51
|
export { EsheetBuilder } from '@esheet/builder';
|
|
50
52
|
|
|
51
53
|
var HEADINGS = "h1,h2,h3,h4,h5,h6";
|
|
@@ -258,6 +260,31 @@ function EncounterMdyView({ source }) {
|
|
|
258
260
|
}
|
|
259
261
|
);
|
|
260
262
|
}
|
|
263
|
+
var EsheetSectionContext = React11.createContext(false);
|
|
264
|
+
function EsheetSectionField(props) {
|
|
265
|
+
const inDocument = React11.useContext(EsheetSectionContext);
|
|
266
|
+
const { field, isPreview, isRequired, isSoftRequired, nestedChildren } = props;
|
|
267
|
+
const def = field.definition;
|
|
268
|
+
if (!inDocument || !isPreview) return /* @__PURE__ */ jsx(SectionField, { ...props });
|
|
269
|
+
const title = def.title || "Section";
|
|
270
|
+
return /* @__PURE__ */ jsxs("div", { className: "section-field-preview grid gap-1.5", children: [
|
|
271
|
+
/* @__PURE__ */ jsx("h3", { className: "text-foreground m-0 flex items-center gap-2 text-sm font-medium", children: /* @__PURE__ */ jsxs("span", { className: "min-w-0 break-words", children: [
|
|
272
|
+
title,
|
|
273
|
+
(isRequired || isSoftRequired) && /* @__PURE__ */ jsx(
|
|
274
|
+
RequiredMark,
|
|
275
|
+
{
|
|
276
|
+
variant: isSoftRequired && !isRequired ? "warning" : void 0
|
|
277
|
+
}
|
|
278
|
+
)
|
|
279
|
+
] }) }),
|
|
280
|
+
nestedChildren
|
|
281
|
+
] });
|
|
282
|
+
}
|
|
283
|
+
function registerEsheetSectionFieldType() {
|
|
284
|
+
const section = getFieldComponent("section");
|
|
285
|
+
if (section === void 0 || section === SectionField)
|
|
286
|
+
registerFieldComponents({ section: EsheetSectionField });
|
|
287
|
+
}
|
|
261
288
|
var measurements = [
|
|
262
289
|
{ key: "pulse", label: "Pulse", unit: "bpm" },
|
|
263
290
|
{ key: "respiratoryRate", label: "Respiratory rate", unit: "/min" },
|
|
@@ -631,103 +658,6 @@ function EncounterVitalsField({
|
|
|
631
658
|
}
|
|
632
659
|
);
|
|
633
660
|
}
|
|
634
|
-
function FreeClinicalEntry({
|
|
635
|
-
placeholder,
|
|
636
|
-
translate,
|
|
637
|
-
onFreeText
|
|
638
|
-
}) {
|
|
639
|
-
const [text, setText] = React11.useState("");
|
|
640
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap gap-2", children: [
|
|
641
|
-
/* @__PURE__ */ jsx(
|
|
642
|
-
Input,
|
|
643
|
-
{
|
|
644
|
-
"aria-label": translate("Clinical entry"),
|
|
645
|
-
placeholder,
|
|
646
|
-
value: text,
|
|
647
|
-
onChange: (event) => setText(event.target.value)
|
|
648
|
-
}
|
|
649
|
-
),
|
|
650
|
-
/* @__PURE__ */ jsx(
|
|
651
|
-
Button,
|
|
652
|
-
{
|
|
653
|
-
type: "button",
|
|
654
|
-
disabled: !text.trim(),
|
|
655
|
-
onClick: () => {
|
|
656
|
-
onFreeText?.(text.trim());
|
|
657
|
-
setText("");
|
|
658
|
-
},
|
|
659
|
-
children: translate("Add")
|
|
660
|
-
}
|
|
661
|
-
)
|
|
662
|
-
] });
|
|
663
|
-
}
|
|
664
|
-
function codedOrderSearch(config, requireCoding = true, translate = (message) => message) {
|
|
665
|
-
const CodedOrderSearch = ({
|
|
666
|
-
domains,
|
|
667
|
-
preferDomains,
|
|
668
|
-
preferCodetypes,
|
|
669
|
-
billableOnly,
|
|
670
|
-
placeholder,
|
|
671
|
-
onPick,
|
|
672
|
-
onFreeText
|
|
673
|
-
}) => {
|
|
674
|
-
if (!config)
|
|
675
|
-
return /* @__PURE__ */ jsx(
|
|
676
|
-
FreeClinicalEntry,
|
|
677
|
-
{
|
|
678
|
-
placeholder,
|
|
679
|
-
translate,
|
|
680
|
-
onFreeText
|
|
681
|
-
}
|
|
682
|
-
);
|
|
683
|
-
const Lookup = config.component;
|
|
684
|
-
const allowedDomains = domains;
|
|
685
|
-
return /* @__PURE__ */ jsx(
|
|
686
|
-
Lookup,
|
|
687
|
-
{
|
|
688
|
-
indexUrl: config.indexUrl,
|
|
689
|
-
locale: config.locale,
|
|
690
|
-
domains: allowedDomains,
|
|
691
|
-
preferDomains,
|
|
692
|
-
preferCodetypes,
|
|
693
|
-
billableOnly,
|
|
694
|
-
bare: true,
|
|
695
|
-
clearOnSelect: true,
|
|
696
|
-
placeholder,
|
|
697
|
-
onFreeText: requireCoding ? void 0 : onFreeText,
|
|
698
|
-
onSelect: (result) => {
|
|
699
|
-
if (!result.fullid?.trim() || !result.codetype?.trim() || !result.fullcode?.trim() || !result.label?.trim() || allowedDomains !== void 0 && !allowedDomains.includes(result.domain))
|
|
700
|
-
return;
|
|
701
|
-
onPick({
|
|
702
|
-
fullid: result.fullid,
|
|
703
|
-
label: result.label,
|
|
704
|
-
codetype: result.codetype,
|
|
705
|
-
fullcode: result.fullcode
|
|
706
|
-
});
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
);
|
|
710
|
-
};
|
|
711
|
-
return CodedOrderSearch;
|
|
712
|
-
}
|
|
713
|
-
var clinicalAssessmentClasses = "rounded-none border-0 bg-transparent shadow-none [&_.border-dashed]:rounded-none [&_.border-dashed]:border-0 [&_.border-dashed]:bg-transparent [&_.border-dashed]:p-0 [&_.min-w-64]:min-w-0 [&_[data-slot=assessment-problem]]:rounded-none [&_[data-slot=assessment-problem]]:border-0 [&_[data-slot=assessment-problem]]:px-0 [&_[data-slot=assessment-problem]>ul]:ms-0 [&_[data-slot=assessment-problem]>ul]:border-0 [&_[data-slot=assessment-problem]>ul]:ps-4 [&_button]:min-h-11 [&_button]:min-w-11 [&_input]:min-h-11 [&_select]:min-h-11 [&>[data-slot=card-content]]:p-0 [&>[data-slot=card-content]>section]:border-0 [&>[data-slot=card-content]>section]:bg-transparent [&>[data-slot=card-content]>section]:px-0";
|
|
714
|
-
var clinicalEntryCardClasses = "bg-muted/40 min-w-0 space-y-3 rounded-lg p-3";
|
|
715
|
-
function ClinicalAddButton({
|
|
716
|
-
label,
|
|
717
|
-
onClick
|
|
718
|
-
}) {
|
|
719
|
-
return /* @__PURE__ */ jsx(
|
|
720
|
-
Button,
|
|
721
|
-
{
|
|
722
|
-
type: "button",
|
|
723
|
-
variant: "ghost",
|
|
724
|
-
leftIcon: /* @__PURE__ */ jsx(Plus, { "aria-hidden": "true" }),
|
|
725
|
-
className: "text-primary-800 hover:bg-primary-50 hover:text-primary-900 active:bg-primary-100 dark:text-primary-400 dark:hover:bg-primary-950 dark:hover:text-primary-200 min-h-11",
|
|
726
|
-
onClick,
|
|
727
|
-
children: label
|
|
728
|
-
}
|
|
729
|
-
);
|
|
730
|
-
}
|
|
731
661
|
function InvalidClinicalAnswer({
|
|
732
662
|
label,
|
|
733
663
|
answer
|
|
@@ -881,9 +811,6 @@ function matchingConditionCode(concern, picked) {
|
|
|
881
811
|
)
|
|
882
812
|
);
|
|
883
813
|
}
|
|
884
|
-
function clinicalRequiresCoding(field) {
|
|
885
|
-
return field.definition._sourceData?.encounterRequireCoding ?? true;
|
|
886
|
-
}
|
|
887
814
|
function clinicalAnswerResponse(response, answer) {
|
|
888
815
|
const next = { ...response, answer, _ai: false };
|
|
889
816
|
delete next.selected;
|
|
@@ -897,37 +824,6 @@ function useEncounterAssertionDate(field) {
|
|
|
897
824
|
}, [hostDate]);
|
|
898
825
|
return { date, setDate, valid: isEncounterDate(date) };
|
|
899
826
|
}
|
|
900
|
-
function EncounterAssertionDateInput({
|
|
901
|
-
date,
|
|
902
|
-
setDate
|
|
903
|
-
}) {
|
|
904
|
-
const m = useMessages();
|
|
905
|
-
const id = React11.useId();
|
|
906
|
-
const [display, setDisplay] = React11.useState(() => dateToDisplayFormat(date));
|
|
907
|
-
React11.useEffect(() => {
|
|
908
|
-
setDisplay(
|
|
909
|
-
(current) => displayFormatToDateString(current) === date ? current : dateToDisplayFormat(date)
|
|
910
|
-
);
|
|
911
|
-
}, [date]);
|
|
912
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
913
|
-
/* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-sm font-medium", children: m("Assertion date") }),
|
|
914
|
-
/* @__PURE__ */ jsx(
|
|
915
|
-
DateInput,
|
|
916
|
-
{
|
|
917
|
-
id,
|
|
918
|
-
value: display,
|
|
919
|
-
onChange: (value) => {
|
|
920
|
-
setDisplay(value);
|
|
921
|
-
setDate(displayFormatToDateString(value));
|
|
922
|
-
},
|
|
923
|
-
showCalendar: true,
|
|
924
|
-
width: "fixed",
|
|
925
|
-
className: "min-h-11"
|
|
926
|
-
}
|
|
927
|
-
),
|
|
928
|
-
!isEncounterDate(date) && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: m("Enter the encounter date before adding or changing a diagnosis.") })
|
|
929
|
-
] });
|
|
930
|
-
}
|
|
931
827
|
function readAssessment(answer, externalConcerns) {
|
|
932
828
|
if (!answer) return { value: { concerns: [], items: [], orders: [] } };
|
|
933
829
|
try {
|
|
@@ -940,7 +836,7 @@ function readAssessment(answer, externalConcerns) {
|
|
|
940
836
|
} catch {
|
|
941
837
|
return {
|
|
942
838
|
value: { concerns: [], items: [], orders: [] },
|
|
943
|
-
error: "
|
|
839
|
+
error: "unreadable"
|
|
944
840
|
};
|
|
945
841
|
}
|
|
946
842
|
}
|
|
@@ -961,11 +857,9 @@ function EncounterAssessmentField({
|
|
|
961
857
|
onResponse
|
|
962
858
|
}) {
|
|
963
859
|
const m = useMessages();
|
|
964
|
-
const requireCoding = clinicalRequiresCoding(field);
|
|
965
860
|
const assertionDate = useEncounterAssertionDate(field);
|
|
966
861
|
const currentDate = React11.useRef(assertionDate);
|
|
967
862
|
currentDate.current = assertionDate;
|
|
968
|
-
const codeLookup = useCodeLookupConfig();
|
|
969
863
|
const state = useClinicalFormState(form);
|
|
970
864
|
const externalConcerns = React11.useMemo(
|
|
971
865
|
() => getExternalEncounterConditions(state),
|
|
@@ -990,10 +884,6 @@ function EncounterAssessmentField({
|
|
|
990
884
|
currentValue.current = value;
|
|
991
885
|
const canEdit = React11.useRef(!readOnly);
|
|
992
886
|
canEdit.current = !readOnly;
|
|
993
|
-
const renderSearch = React11.useMemo(
|
|
994
|
-
() => codeLookup || !requireCoding ? codedOrderSearch(codeLookup, requireCoding, m) : void 0,
|
|
995
|
-
[codeLookup, requireCoding, m]
|
|
996
|
-
);
|
|
997
887
|
const definition = field.definition;
|
|
998
888
|
const commit = (update) => {
|
|
999
889
|
if (!canEdit.current || form.getState().readOnly || form.getState().disposed)
|
|
@@ -1008,124 +898,92 @@ function EncounterAssessmentField({
|
|
|
1008
898
|
);
|
|
1009
899
|
};
|
|
1010
900
|
if (error) {
|
|
1011
|
-
return /* @__PURE__ */ jsx(
|
|
1012
|
-
|
|
901
|
+
return /* @__PURE__ */ jsx(InvalidClinicalAnswer, { label: "assessment", answer: response?.answer });
|
|
902
|
+
}
|
|
903
|
+
return /* @__PURE__ */ jsxs("section", { className: "space-y-2", children: [
|
|
904
|
+
/* @__PURE__ */ jsx(
|
|
905
|
+
Assessment,
|
|
1013
906
|
{
|
|
907
|
+
concerns,
|
|
908
|
+
items: value.items,
|
|
909
|
+
orders: value.orders,
|
|
1014
910
|
"aria-label": definition.question ?? m("Assessment and plan"),
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
911
|
+
readOnly,
|
|
912
|
+
addFormLabel: "Add assessment or order",
|
|
913
|
+
onAddAssessment: !assertionDate.valid ? void 0 : (pick) => {
|
|
914
|
+
if (!currentDate.current.valid) return;
|
|
915
|
+
const existingConcern = pick.code ? concerns.find(
|
|
916
|
+
(concern) => matchingConditionCode(concern, pick.code)
|
|
917
|
+
) : void 0;
|
|
918
|
+
const existingAssertion = existingConcern && currentAssertion(existingConcern);
|
|
919
|
+
if (existingConcern && existingAssertion) {
|
|
920
|
+
commit((next) => ({
|
|
921
|
+
...next,
|
|
922
|
+
items: [
|
|
923
|
+
...next.items.filter(
|
|
924
|
+
(item) => item.concernId !== existingConcern.concernId
|
|
925
|
+
),
|
|
1025
926
|
{
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
className: "mt-2 max-h-48 overflow-auto text-xs break-all whitespace-pre-wrap",
|
|
1029
|
-
children: response?.answer
|
|
927
|
+
concernId: existingConcern.concernId,
|
|
928
|
+
assertionId: existingAssertion.id
|
|
1030
929
|
}
|
|
1031
|
-
|
|
1032
|
-
|
|
930
|
+
]
|
|
931
|
+
}));
|
|
932
|
+
return;
|
|
1033
933
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
);
|
|
1037
|
-
}
|
|
1038
|
-
return /* @__PURE__ */ jsx("section", { className: "space-y-2", children: /* @__PURE__ */ jsx(
|
|
1039
|
-
Assessment,
|
|
1040
|
-
{
|
|
1041
|
-
concerns,
|
|
1042
|
-
items: value.items,
|
|
1043
|
-
orders: value.orders,
|
|
1044
|
-
title: null,
|
|
1045
|
-
addFormStart: !readOnly && /* @__PURE__ */ jsx(EncounterAssertionDateInput, { ...assertionDate }),
|
|
1046
|
-
"aria-label": definition.question ?? m("Assessment and plan"),
|
|
1047
|
-
className: "rounded-none border-0 bg-transparent shadow-none [&_.border-dashed]:rounded-none [&_.border-dashed]:border-0 [&_.border-dashed]:bg-transparent [&_.border-dashed]:p-0 [&_.min-w-64]:min-w-0 [&_[data-slot=assessment-problem]]:rounded-none [&_[data-slot=assessment-problem]]:border-0 [&_[data-slot=assessment-problem]]:px-0 [&_[data-slot=assessment-problem]>ul]:ms-0 [&_[data-slot=assessment-problem]>ul]:border-0 [&_[data-slot=assessment-problem]>ul]:ps-4 [&_button]:min-h-11 [&_button]:min-w-11 [&_input]:min-h-11 [&_select]:min-h-11 [&>[data-slot=card-content]]:p-0 [&>[data-slot=card-content]>section]:border-0 [&>[data-slot=card-content]>section]:bg-transparent [&>[data-slot=card-content]>section]:px-0",
|
|
1048
|
-
readOnly,
|
|
1049
|
-
defaultAddMode: "problem",
|
|
1050
|
-
addFormLabel: "Add assessment or order",
|
|
1051
|
-
renderOrderSearch: renderSearch,
|
|
1052
|
-
onAddAssessment: (codeLookup || !requireCoding) && assertionDate.valid ? (pick) => {
|
|
1053
|
-
if (!currentDate.current.valid || requireCoding && !pick.code)
|
|
1054
|
-
return;
|
|
1055
|
-
const existingConcern = pick.code ? concerns.find(
|
|
1056
|
-
(concern) => matchingConditionCode(concern, pick.code)
|
|
1057
|
-
) : void 0;
|
|
1058
|
-
const existingAssertion = existingConcern && currentAssertion(existingConcern);
|
|
1059
|
-
if (existingConcern && existingAssertion) {
|
|
934
|
+
const concernId = globalThis.crypto.randomUUID();
|
|
935
|
+
const assertionId = globalThis.crypto.randomUUID();
|
|
1060
936
|
commit((next) => ({
|
|
1061
937
|
...next,
|
|
1062
|
-
|
|
1063
|
-
...next.
|
|
1064
|
-
(item) => item.concernId !== existingConcern.concernId
|
|
1065
|
-
),
|
|
938
|
+
concerns: [
|
|
939
|
+
...next.concerns,
|
|
1066
940
|
{
|
|
1067
|
-
concernId
|
|
1068
|
-
|
|
941
|
+
concernId,
|
|
942
|
+
clinicalStatus: "active",
|
|
943
|
+
source: "manuallyAdded",
|
|
944
|
+
assertions: [
|
|
945
|
+
{
|
|
946
|
+
id: assertionId,
|
|
947
|
+
date: currentDate.current.date,
|
|
948
|
+
text: pick.label,
|
|
949
|
+
verificationStatus: "unconfirmed",
|
|
950
|
+
coding: pick.code ? [
|
|
951
|
+
{
|
|
952
|
+
system: pick.code.codetype,
|
|
953
|
+
code: pick.code.fullcode,
|
|
954
|
+
display: pick.label
|
|
955
|
+
}
|
|
956
|
+
] : void 0
|
|
957
|
+
}
|
|
958
|
+
]
|
|
1069
959
|
}
|
|
1070
|
-
]
|
|
960
|
+
],
|
|
961
|
+
items: [...next.items, { concernId, assertionId }]
|
|
1071
962
|
}));
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
] : void 0
|
|
1097
|
-
}
|
|
1098
|
-
]
|
|
1099
|
-
}
|
|
1100
|
-
],
|
|
1101
|
-
items: [...next.items, { concernId, assertionId }]
|
|
1102
|
-
}));
|
|
1103
|
-
} : void 0,
|
|
1104
|
-
onRemoveAssessment: (item) => {
|
|
1105
|
-
const wasLocal = currentValue.current.concerns.some(
|
|
1106
|
-
(concern) => concern.concernId === item.concernId
|
|
1107
|
-
);
|
|
1108
|
-
commit((next) => ({
|
|
1109
|
-
...next,
|
|
1110
|
-
// Removing the row also drops its local concern record — nothing
|
|
1111
|
-
// else can reference it once the orders below are unlinked, and an
|
|
1112
|
-
// orphaned uncoded concern would block Save with no visible repair.
|
|
1113
|
-
concerns: next.concerns.filter(
|
|
1114
|
-
(concern) => concern.concernId !== item.concernId
|
|
1115
|
-
),
|
|
1116
|
-
items: next.items.filter(
|
|
1117
|
-
(candidate) => candidate.concernId !== item.concernId
|
|
1118
|
-
),
|
|
1119
|
-
orders: next.orders.map(
|
|
1120
|
-
(order) => order.concernId === item.concernId ? { ...order, concernId: void 0 } : order
|
|
1121
|
-
)
|
|
1122
|
-
}));
|
|
1123
|
-
if (wasLocal && canEdit.current)
|
|
1124
|
-
repairEncounterConcernReferences(form.getState(), item.concernId);
|
|
1125
|
-
},
|
|
1126
|
-
onAddOrder: codeLookup || !requireCoding ? (item, order) => {
|
|
1127
|
-
if (requireCoding && !order.code) return;
|
|
1128
|
-
commit((next) => ({
|
|
963
|
+
},
|
|
964
|
+
onRemoveAssessment: (item) => {
|
|
965
|
+
const wasLocal = currentValue.current.concerns.some(
|
|
966
|
+
(concern) => concern.concernId === item.concernId
|
|
967
|
+
);
|
|
968
|
+
commit((next) => ({
|
|
969
|
+
...next,
|
|
970
|
+
// Removing the row also drops its local concern record — nothing
|
|
971
|
+
// else can reference it once the orders below are unlinked, and an
|
|
972
|
+
// orphaned uncoded concern would block Save with no visible repair.
|
|
973
|
+
concerns: next.concerns.filter(
|
|
974
|
+
(concern) => concern.concernId !== item.concernId
|
|
975
|
+
),
|
|
976
|
+
items: next.items.filter(
|
|
977
|
+
(candidate) => candidate.concernId !== item.concernId
|
|
978
|
+
),
|
|
979
|
+
orders: next.orders.map(
|
|
980
|
+
(order) => order.concernId === item.concernId ? { ...order, concernId: void 0 } : order
|
|
981
|
+
)
|
|
982
|
+
}));
|
|
983
|
+
if (wasLocal && canEdit.current)
|
|
984
|
+
repairEncounterConcernReferences(form.getState(), item.concernId);
|
|
985
|
+
},
|
|
986
|
+
onAddOrder: (item, order) => commit((next) => ({
|
|
1129
987
|
...next,
|
|
1130
988
|
orders: [
|
|
1131
989
|
...next.orders,
|
|
@@ -1135,59 +993,64 @@ function EncounterAssessmentField({
|
|
|
1135
993
|
concernId: item?.concernId
|
|
1136
994
|
}
|
|
1137
995
|
]
|
|
1138
|
-
}))
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
)
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
)
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
)
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
|
|
996
|
+
})),
|
|
997
|
+
onEditOrder: (order, changes) => commit((next) => ({
|
|
998
|
+
...next,
|
|
999
|
+
orders: next.orders.map(
|
|
1000
|
+
(candidate) => candidate.orderId === order.orderId ? { ...candidate, ...changes } : candidate
|
|
1001
|
+
)
|
|
1002
|
+
})),
|
|
1003
|
+
onRemoveOrder: (order) => commit((next) => ({
|
|
1004
|
+
...next,
|
|
1005
|
+
orders: next.orders.filter(
|
|
1006
|
+
(candidate) => candidate.orderId !== order.orderId
|
|
1007
|
+
)
|
|
1008
|
+
})),
|
|
1009
|
+
onLinkOrder: (order, concernId) => commit((next) => ({
|
|
1010
|
+
...next,
|
|
1011
|
+
orders: next.orders.map(
|
|
1012
|
+
(candidate) => candidate.orderId === order.orderId ? { ...candidate, concernId } : candidate
|
|
1013
|
+
)
|
|
1014
|
+
})),
|
|
1015
|
+
onReorderItems: (ids) => commit((next) => ({
|
|
1016
|
+
...next,
|
|
1017
|
+
items: reorder(next.items, ids, (item) => item.concernId)
|
|
1018
|
+
})),
|
|
1019
|
+
onReorderOrders: (ids) => commit((next) => ({
|
|
1020
|
+
...next,
|
|
1021
|
+
orders: reorder(next.orders, ids, (order) => order.orderId)
|
|
1022
|
+
}))
|
|
1023
|
+
}
|
|
1024
|
+
),
|
|
1025
|
+
!readOnly && !assertionDate.valid && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: m(
|
|
1026
|
+
"No encounter date \u2014 new diagnoses are disabled; orders can still be added."
|
|
1027
|
+
) })
|
|
1028
|
+
] });
|
|
1168
1029
|
}
|
|
1169
1030
|
|
|
1170
|
-
// src/
|
|
1171
|
-
var
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1031
|
+
// src/esheet-fields/narrativeFindings.ts
|
|
1032
|
+
var NARRATIVE_ATTRIBUTES = {
|
|
1033
|
+
html: "narrativeHtml",
|
|
1034
|
+
text: "narrativeText",
|
|
1035
|
+
findings: "narrativeFindings",
|
|
1036
|
+
findingsText: "narrativeFindingsText"
|
|
1037
|
+
};
|
|
1175
1038
|
function escapedTextHtml(text) {
|
|
1176
1039
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'").replace(/\n/g, "<br>");
|
|
1177
1040
|
}
|
|
1178
1041
|
function hasNarrativeTagsOnly(html) {
|
|
1179
1042
|
return !html.replace(/<\/?(?:p|div|strong|b|em|i|u|ul|ol|li)\s*>|<br\s*\/?>/gi, "").includes("<");
|
|
1180
1043
|
}
|
|
1181
|
-
function appendedRichAttributes(response, answer, finding) {
|
|
1044
|
+
function appendedRichAttributes(response, answer, finding, attributes) {
|
|
1182
1045
|
const previous = response?.answer ?? "";
|
|
1183
|
-
const html = response?.attributes?.[
|
|
1184
|
-
if (answer === previous || response?.attributes?.[
|
|
1046
|
+
const html = response?.attributes?.[attributes.html];
|
|
1047
|
+
if (answer === previous || response?.attributes?.[attributes.text] !== previous || typeof html !== "string" || !hasNarrativeTagsOnly(html))
|
|
1185
1048
|
return {};
|
|
1186
1049
|
const aligned = previous.endsWith("\n") ? html.replace(/<br\s*\/?>((?:<\/(?:p|div|ul|ol|li)\s*>|\s)*)$/i, "$1") : html;
|
|
1187
1050
|
const next = `${previous.trim() ? aligned : ""}<p>${escapedTextHtml(finding.text)}${finding.text.endsWith("\n") ? "<br>" : ""}</p>`;
|
|
1188
|
-
return { [
|
|
1051
|
+
return { [attributes.html]: next, [attributes.text]: answer };
|
|
1189
1052
|
}
|
|
1190
|
-
function
|
|
1053
|
+
function validNarrativeFindings(value) {
|
|
1191
1054
|
if (!Array.isArray(value)) return [];
|
|
1192
1055
|
const ids = /* @__PURE__ */ new Set();
|
|
1193
1056
|
return value.flatMap((finding) => {
|
|
@@ -1212,7 +1075,7 @@ function validEncounterNarrativeFindings(value) {
|
|
|
1212
1075
|
];
|
|
1213
1076
|
});
|
|
1214
1077
|
}
|
|
1215
|
-
function
|
|
1078
|
+
function hasNarrativeFindingText(answer, text) {
|
|
1216
1079
|
const normalize = (value) => value.replace(/\r\n?/g, "\n").trim();
|
|
1217
1080
|
return `
|
|
1218
1081
|
${normalize(answer)}
|
|
@@ -1220,46 +1083,39 @@ ${normalize(answer)}
|
|
|
1220
1083
|
${normalize(text)}
|
|
1221
1084
|
`);
|
|
1222
1085
|
}
|
|
1223
|
-
function
|
|
1086
|
+
function getNarrativeFindings(response, attributes = NARRATIVE_ATTRIBUTES) {
|
|
1224
1087
|
const answer = response?.answer ?? "";
|
|
1225
|
-
if (response?.attributes?.[
|
|
1226
|
-
const selected = response.attributes[
|
|
1088
|
+
if (response?.attributes?.[attributes.findingsText] !== answer) return [];
|
|
1089
|
+
const selected = response.attributes[attributes.findings];
|
|
1227
1090
|
if (typeof selected !== "string") return [];
|
|
1228
1091
|
try {
|
|
1229
|
-
return
|
|
1230
|
-
(finding) =>
|
|
1092
|
+
return validNarrativeFindings(JSON.parse(selected)).filter(
|
|
1093
|
+
(finding) => hasNarrativeFindingText(answer, finding.text)
|
|
1231
1094
|
);
|
|
1232
1095
|
} catch {
|
|
1233
1096
|
return [];
|
|
1234
1097
|
}
|
|
1235
1098
|
}
|
|
1236
|
-
function
|
|
1237
|
-
const validated =
|
|
1099
|
+
function applyNarrativeFinding(response, finding, attributes = NARRATIVE_ATTRIBUTES) {
|
|
1100
|
+
const validated = validNarrativeFindings([finding])[0];
|
|
1238
1101
|
if (!validated) return response ?? {};
|
|
1239
|
-
const selected =
|
|
1102
|
+
const selected = getNarrativeFindings(response, attributes);
|
|
1240
1103
|
if (selected.some((item) => item.id === validated.id)) return response ?? {};
|
|
1241
1104
|
const previousAnswer = response?.answer ?? "";
|
|
1242
|
-
const answer =
|
|
1243
|
-
previousAnswer,
|
|
1244
|
-
validated.text
|
|
1245
|
-
) ? previousAnswer : `${previousAnswer.trim() ? `${previousAnswer}${previousAnswer.endsWith("\n") ? "" : "\n"}` : ""}${validated.text}`;
|
|
1105
|
+
const answer = hasNarrativeFindingText(previousAnswer, validated.text) ? previousAnswer : `${previousAnswer.trim() ? `${previousAnswer}${previousAnswer.endsWith("\n") ? "" : "\n"}` : ""}${validated.text}`;
|
|
1246
1106
|
return {
|
|
1247
1107
|
...response,
|
|
1248
1108
|
answer,
|
|
1249
1109
|
_ai: false,
|
|
1250
1110
|
attributes: {
|
|
1251
1111
|
...response?.attributes,
|
|
1252
|
-
...appendedRichAttributes(response, answer, validated),
|
|
1253
|
-
[
|
|
1254
|
-
[
|
|
1112
|
+
...appendedRichAttributes(response, answer, validated, attributes),
|
|
1113
|
+
[attributes.findings]: JSON.stringify([...selected, validated]),
|
|
1114
|
+
[attributes.findingsText]: answer
|
|
1255
1115
|
}
|
|
1256
1116
|
};
|
|
1257
1117
|
}
|
|
1258
|
-
|
|
1259
|
-
var TEXT_ATTRIBUTE2 = "encounterNarrativeText";
|
|
1260
|
-
var FINDINGS_ATTRIBUTE2 = "encounterNarrativeFindings";
|
|
1261
|
-
var FINDINGS_TEXT_ATTRIBUTE2 = "encounterNarrativeFindingsText";
|
|
1262
|
-
function sanitizeEncounterNarrativeHtml(html) {
|
|
1118
|
+
function sanitizeNarrativeHtml(html) {
|
|
1263
1119
|
return DOMPurify.sanitize(html, {
|
|
1264
1120
|
ALLOWED_TAGS: [
|
|
1265
1121
|
"p",
|
|
@@ -1286,9 +1142,9 @@ function sanitizeEncounterNarrativeHtml(html) {
|
|
|
1286
1142
|
function textToHtml(text) {
|
|
1287
1143
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'").replace(/\n/g, "<br>");
|
|
1288
1144
|
}
|
|
1289
|
-
function
|
|
1145
|
+
function narrativeText(html) {
|
|
1290
1146
|
const documentBody = document.createElement("div");
|
|
1291
|
-
documentBody.innerHTML =
|
|
1147
|
+
documentBody.innerHTML = sanitizeNarrativeHtml(html);
|
|
1292
1148
|
let text = "";
|
|
1293
1149
|
const lineBreak = () => {
|
|
1294
1150
|
if (text && !text.endsWith("\n")) text += "\n";
|
|
@@ -1326,12 +1182,12 @@ function encounterNarrativeText(html) {
|
|
|
1326
1182
|
documentBody.childNodes.forEach(visit);
|
|
1327
1183
|
return text.replace(/\u00a0/g, " ").replace(/\n$/, "");
|
|
1328
1184
|
}
|
|
1329
|
-
function responseHtml(response) {
|
|
1185
|
+
function responseHtml(response, attributes) {
|
|
1330
1186
|
const answer = response?.answer ?? "";
|
|
1331
|
-
const
|
|
1332
|
-
if (typeof window !== "undefined" &&
|
|
1333
|
-
const html =
|
|
1334
|
-
if (
|
|
1187
|
+
const responseAttributes = response?.attributes;
|
|
1188
|
+
if (typeof window !== "undefined" && responseAttributes?.[attributes.text] === answer && typeof responseAttributes[attributes.html] === "string") {
|
|
1189
|
+
const html = sanitizeNarrativeHtml(responseAttributes[attributes.html]);
|
|
1190
|
+
if (narrativeText(html) === answer) return html;
|
|
1335
1191
|
}
|
|
1336
1192
|
return textToHtml(answer);
|
|
1337
1193
|
}
|
|
@@ -1348,7 +1204,7 @@ function findingShortcuts(findings) {
|
|
|
1348
1204
|
}
|
|
1349
1205
|
return shortcuts;
|
|
1350
1206
|
}
|
|
1351
|
-
function
|
|
1207
|
+
function EsheetNarrativeField({
|
|
1352
1208
|
field,
|
|
1353
1209
|
response,
|
|
1354
1210
|
isPreview,
|
|
@@ -1356,7 +1212,11 @@ function EncounterNarrativeField({
|
|
|
1356
1212
|
isReadOnly,
|
|
1357
1213
|
isRequired,
|
|
1358
1214
|
isSoftRequired,
|
|
1359
|
-
onResponse
|
|
1215
|
+
onResponse,
|
|
1216
|
+
attributes = NARRATIVE_ATTRIBUTES,
|
|
1217
|
+
findingsKey = "narrativeFindings",
|
|
1218
|
+
hideLabelKey = "narrativeHideLabel",
|
|
1219
|
+
slot = "esheet-narrative"
|
|
1360
1220
|
}) {
|
|
1361
1221
|
const m = useMessages();
|
|
1362
1222
|
const editorRef = React11.useRef(null);
|
|
@@ -1366,9 +1226,12 @@ function EncounterNarrativeField({
|
|
|
1366
1226
|
const definition = field.definition;
|
|
1367
1227
|
const question = definition.question || m("Narrative");
|
|
1368
1228
|
const readOnly = !isPreview || !isEnabled || isReadOnly;
|
|
1369
|
-
const html = React11.useMemo(
|
|
1370
|
-
|
|
1371
|
-
|
|
1229
|
+
const html = React11.useMemo(
|
|
1230
|
+
() => responseHtml(response, attributes),
|
|
1231
|
+
[response, attributes]
|
|
1232
|
+
);
|
|
1233
|
+
const findings = validNarrativeFindings(
|
|
1234
|
+
definition._sourceData?.[findingsKey]
|
|
1372
1235
|
);
|
|
1373
1236
|
const shortcuts = findingShortcuts(findings);
|
|
1374
1237
|
const macros = findings.length ? [
|
|
@@ -1384,18 +1247,23 @@ function EncounterNarrativeField({
|
|
|
1384
1247
|
}
|
|
1385
1248
|
] : void 0;
|
|
1386
1249
|
const publish = (value, previous, selected) => {
|
|
1387
|
-
const sanitized =
|
|
1388
|
-
const answer =
|
|
1389
|
-
const
|
|
1250
|
+
const sanitized = sanitizeNarrativeHtml(value);
|
|
1251
|
+
const answer = narrativeText(sanitized);
|
|
1252
|
+
const nextAttributes = {
|
|
1390
1253
|
...previous?.attributes,
|
|
1391
|
-
[
|
|
1392
|
-
[
|
|
1393
|
-
...selected.length || previous?.attributes?.[
|
|
1394
|
-
[
|
|
1395
|
-
[
|
|
1254
|
+
[attributes.html]: sanitized,
|
|
1255
|
+
[attributes.text]: answer,
|
|
1256
|
+
...selected.length || previous?.attributes?.[attributes.findings] !== void 0 ? {
|
|
1257
|
+
[attributes.findings]: JSON.stringify(selected),
|
|
1258
|
+
[attributes.findingsText]: answer
|
|
1396
1259
|
} : {}
|
|
1397
1260
|
};
|
|
1398
|
-
const next = {
|
|
1261
|
+
const next = {
|
|
1262
|
+
...previous,
|
|
1263
|
+
answer,
|
|
1264
|
+
_ai: false,
|
|
1265
|
+
attributes: nextAttributes
|
|
1266
|
+
};
|
|
1399
1267
|
delete next.selected;
|
|
1400
1268
|
responseRef.current = next;
|
|
1401
1269
|
onResponse(next);
|
|
@@ -1409,12 +1277,12 @@ function EncounterNarrativeField({
|
|
|
1409
1277
|
String(isRequired || isSoftRequired)
|
|
1410
1278
|
);
|
|
1411
1279
|
}, [isRequired, isSoftRequired, labelId, readOnly]);
|
|
1412
|
-
return /* @__PURE__ */ jsxs("div", { "data-slot":
|
|
1280
|
+
return /* @__PURE__ */ jsxs("div", { "data-slot": slot, className: "min-w-0", children: [
|
|
1413
1281
|
/* @__PURE__ */ jsxs(
|
|
1414
1282
|
"div",
|
|
1415
1283
|
{
|
|
1416
1284
|
id: labelId,
|
|
1417
|
-
className: definition._sourceData?.
|
|
1285
|
+
className: definition._sourceData?.[hideLabelKey] ? "sr-only" : "text-muted-foreground text-sm font-medium",
|
|
1418
1286
|
children: [
|
|
1419
1287
|
question,
|
|
1420
1288
|
definition.unit && ` (${definition.unit})`,
|
|
@@ -1429,14 +1297,14 @@ function EncounterNarrativeField({
|
|
|
1429
1297
|
value: html,
|
|
1430
1298
|
onChange: (value) => {
|
|
1431
1299
|
if (readOnly) return;
|
|
1432
|
-
const sanitized =
|
|
1433
|
-
const answer =
|
|
1300
|
+
const sanitized = sanitizeNarrativeHtml(value);
|
|
1301
|
+
const answer = narrativeText(sanitized);
|
|
1434
1302
|
const previous = responseRef.current;
|
|
1435
1303
|
publish(
|
|
1436
1304
|
sanitized,
|
|
1437
1305
|
previous,
|
|
1438
|
-
|
|
1439
|
-
(finding) =>
|
|
1306
|
+
getNarrativeFindings(previous, attributes).filter(
|
|
1307
|
+
(finding) => hasNarrativeFindingText(answer, finding.text)
|
|
1440
1308
|
)
|
|
1441
1309
|
);
|
|
1442
1310
|
},
|
|
@@ -1444,14 +1312,11 @@ function EncounterNarrativeField({
|
|
|
1444
1312
|
const finding = findings.find((item) => item.id === macro.id);
|
|
1445
1313
|
if (!finding || readOnly) return true;
|
|
1446
1314
|
const previous = responseRef.current;
|
|
1447
|
-
const next =
|
|
1315
|
+
const next = applyNarrativeFinding(previous, finding, attributes);
|
|
1448
1316
|
if (next === previous) return true;
|
|
1449
1317
|
const answer = previous?.answer ?? "";
|
|
1450
|
-
const nextHtml =
|
|
1451
|
-
|
|
1452
|
-
finding.text
|
|
1453
|
-
) ? responseHtml(previous) : `${answer.trim() ? responseHtml(previous) : ""}<p>${textToHtml(finding.text)}</p>`;
|
|
1454
|
-
publish(nextHtml, previous, getEncounterNarrativeFindings(next));
|
|
1318
|
+
const nextHtml = hasNarrativeFindingText(answer, finding.text) ? responseHtml(previous, attributes) : `${answer.trim() ? responseHtml(previous, attributes) : ""}<p>${textToHtml(finding.text)}</p>`;
|
|
1319
|
+
publish(nextHtml, previous, getNarrativeFindings(next, attributes));
|
|
1455
1320
|
return true;
|
|
1456
1321
|
},
|
|
1457
1322
|
"aria-label": question,
|
|
@@ -1489,12 +1354,53 @@ function EncounterNarrativeField({
|
|
|
1489
1354
|
showToolbar: m("Show toolbar"),
|
|
1490
1355
|
hideToolbar: m("Hide toolbar")
|
|
1491
1356
|
},
|
|
1492
|
-
sanitizeHtml:
|
|
1357
|
+
sanitizeHtml: sanitizeNarrativeHtml,
|
|
1493
1358
|
className: "mt-1"
|
|
1494
1359
|
}
|
|
1495
1360
|
)
|
|
1496
1361
|
] });
|
|
1497
1362
|
}
|
|
1363
|
+
function registerEsheetNarrativeFieldType() {
|
|
1364
|
+
if (getFieldComponent("esheetNarrative")) return;
|
|
1365
|
+
registerCustomFieldTypes({
|
|
1366
|
+
esheetNarrative: {
|
|
1367
|
+
label: "Narrative",
|
|
1368
|
+
category: "rich",
|
|
1369
|
+
answerType: "text",
|
|
1370
|
+
hasOptions: false,
|
|
1371
|
+
hasMatrix: false,
|
|
1372
|
+
defaultProps: { question: "Narrative" },
|
|
1373
|
+
component: EsheetNarrativeField
|
|
1374
|
+
}
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// src/components/EncounterVisit/narrativeFindings.ts
|
|
1379
|
+
var ENCOUNTER_NARRATIVE_ATTRIBUTES = {
|
|
1380
|
+
html: "encounterNarrativeHtml",
|
|
1381
|
+
text: "encounterNarrativeText",
|
|
1382
|
+
findings: "encounterNarrativeFindings",
|
|
1383
|
+
findingsText: "encounterNarrativeFindingsText"
|
|
1384
|
+
};
|
|
1385
|
+
function applyEncounterNarrativeFinding(response, finding) {
|
|
1386
|
+
return applyNarrativeFinding(
|
|
1387
|
+
response,
|
|
1388
|
+
finding,
|
|
1389
|
+
ENCOUNTER_NARRATIVE_ATTRIBUTES
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
function EncounterNarrativeField(props) {
|
|
1393
|
+
return /* @__PURE__ */ jsx(
|
|
1394
|
+
EsheetNarrativeField,
|
|
1395
|
+
{
|
|
1396
|
+
...props,
|
|
1397
|
+
attributes: ENCOUNTER_NARRATIVE_ATTRIBUTES,
|
|
1398
|
+
findingsKey: "encounterFindings",
|
|
1399
|
+
hideLabelKey: "encounterHideLabel",
|
|
1400
|
+
slot: "encounter-narrative"
|
|
1401
|
+
}
|
|
1402
|
+
);
|
|
1403
|
+
}
|
|
1498
1404
|
function readValue(answer) {
|
|
1499
1405
|
if (!answer) return { value: { medications: [] } };
|
|
1500
1406
|
try {
|
|
@@ -1508,250 +1414,6 @@ function readValue(answer) {
|
|
|
1508
1414
|
};
|
|
1509
1415
|
}
|
|
1510
1416
|
}
|
|
1511
|
-
function parseAnswerDate(value) {
|
|
1512
|
-
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value ?? "");
|
|
1513
|
-
return match ? new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])) : null;
|
|
1514
|
-
}
|
|
1515
|
-
function toAnswerDate(date) {
|
|
1516
|
-
if (!date) return void 0;
|
|
1517
|
-
const pad = (part) => String(part).padStart(2, "0");
|
|
1518
|
-
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
1519
|
-
}
|
|
1520
|
-
function MedicationCard({
|
|
1521
|
-
index,
|
|
1522
|
-
medication,
|
|
1523
|
-
readOnly,
|
|
1524
|
-
requireCoding,
|
|
1525
|
-
codeLookup,
|
|
1526
|
-
onPatch,
|
|
1527
|
-
onRemove
|
|
1528
|
-
}) {
|
|
1529
|
-
const m = useMessages();
|
|
1530
|
-
const id = React11.useId();
|
|
1531
|
-
const Lookup = codeLookup?.component;
|
|
1532
|
-
const text = (value) => value.trim() && value;
|
|
1533
|
-
return /* @__PURE__ */ jsxs("fieldset", { className: clinicalEntryCardClasses, children: [
|
|
1534
|
-
/* @__PURE__ */ jsx("legend", { className: "sr-only", children: m("Medication {number}", { number: index }) }),
|
|
1535
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
1536
|
-
/* @__PURE__ */ jsx("h4", { className: "font-semibold", children: m("Medication {number}", { number: index }) }),
|
|
1537
|
-
!readOnly && /* @__PURE__ */ jsx(
|
|
1538
|
-
Button,
|
|
1539
|
-
{
|
|
1540
|
-
type: "button",
|
|
1541
|
-
variant: "ghost",
|
|
1542
|
-
size: "icon",
|
|
1543
|
-
className: "shrink-0",
|
|
1544
|
-
"aria-label": m("Remove medication {number}", { number: index }),
|
|
1545
|
-
onClick: onRemove,
|
|
1546
|
-
children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
1547
|
-
}
|
|
1548
|
-
)
|
|
1549
|
-
] }),
|
|
1550
|
-
!readOnly && Lookup && codeLookup ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1551
|
-
/* @__PURE__ */ jsx(
|
|
1552
|
-
Lookup,
|
|
1553
|
-
{
|
|
1554
|
-
indexUrl: codeLookup.indexUrl,
|
|
1555
|
-
locale: codeLookup.locale,
|
|
1556
|
-
domains: ["med"],
|
|
1557
|
-
bare: true,
|
|
1558
|
-
clearOnSelect: false,
|
|
1559
|
-
initialQuery: medication.name || void 0,
|
|
1560
|
-
initialSearch: false,
|
|
1561
|
-
"aria-label": m("Medication"),
|
|
1562
|
-
placeholder: m("Search RxNorm / FDB \u2014 e.g. lisinopril"),
|
|
1563
|
-
onSelect: (pick) => {
|
|
1564
|
-
if (requireCoding && (!pick.codetype.trim() || !pick.fullcode.trim()))
|
|
1565
|
-
return;
|
|
1566
|
-
onPatch(lookupToMedicationFields(pick));
|
|
1567
|
-
},
|
|
1568
|
-
onFreeText: requireCoding ? void 0 : (name) => {
|
|
1569
|
-
if (name.trim()) onPatch({ name, code: void 0 });
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
),
|
|
1573
|
-
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: medication.code ? m("Coded: {system} {code}", {
|
|
1574
|
-
system: medication.code.system,
|
|
1575
|
-
code: medication.code.code
|
|
1576
|
-
}) : medication.name ? m('Uncoded free text: "{name}" \u2014 pick a result to code it', {
|
|
1577
|
-
name: medication.name
|
|
1578
|
-
}) : requireCoding ? m("Pick a catalog result to code the medication") : m(
|
|
1579
|
-
"Pick a result to code the medication, or press Enter for free text"
|
|
1580
|
-
) })
|
|
1581
|
-
] }) : /* @__PURE__ */ jsx(
|
|
1582
|
-
Input,
|
|
1583
|
-
{
|
|
1584
|
-
label: m("Medication"),
|
|
1585
|
-
className: "min-h-11",
|
|
1586
|
-
value: medication.name,
|
|
1587
|
-
readOnly: readOnly || requireCoding,
|
|
1588
|
-
onChange: (event) => {
|
|
1589
|
-
if (event.target.value.trim())
|
|
1590
|
-
onPatch({ name: event.target.value, code: void 0 });
|
|
1591
|
-
}
|
|
1592
|
-
}
|
|
1593
|
-
),
|
|
1594
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
|
|
1595
|
-
/* @__PURE__ */ jsx(
|
|
1596
|
-
Input,
|
|
1597
|
-
{
|
|
1598
|
-
label: m("Strength"),
|
|
1599
|
-
className: "min-h-11",
|
|
1600
|
-
value: medication.strength ?? "",
|
|
1601
|
-
readOnly,
|
|
1602
|
-
onChange: (event) => onPatch({ strength: text(event.target.value) || void 0 })
|
|
1603
|
-
}
|
|
1604
|
-
),
|
|
1605
|
-
/* @__PURE__ */ jsx(
|
|
1606
|
-
Input,
|
|
1607
|
-
{
|
|
1608
|
-
label: m("Dose form"),
|
|
1609
|
-
className: "min-h-11",
|
|
1610
|
-
value: medication.doseForm ?? "",
|
|
1611
|
-
readOnly,
|
|
1612
|
-
onChange: (event) => (
|
|
1613
|
-
// Quantity unit follows the dose form (tablet → tablet,
|
|
1614
|
-
// solution → milliliter, …)
|
|
1615
|
-
onPatch({
|
|
1616
|
-
doseForm: event.target.value || void 0,
|
|
1617
|
-
quantityUnit: doseFormToQuantityUnit(event.target.value)
|
|
1618
|
-
})
|
|
1619
|
-
)
|
|
1620
|
-
}
|
|
1621
|
-
),
|
|
1622
|
-
/* @__PURE__ */ jsx(
|
|
1623
|
-
Input,
|
|
1624
|
-
{
|
|
1625
|
-
label: medication.quantityUnit ? `${m("Quantity")} (${medication.quantityUnit}s)` : m("Quantity"),
|
|
1626
|
-
className: "min-h-11",
|
|
1627
|
-
inputMode: "decimal",
|
|
1628
|
-
value: medication.quantity ?? "",
|
|
1629
|
-
readOnly,
|
|
1630
|
-
onChange: (event) => onPatch({ quantity: text(event.target.value) || void 0 })
|
|
1631
|
-
}
|
|
1632
|
-
)
|
|
1633
|
-
] }),
|
|
1634
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
|
|
1635
|
-
/* @__PURE__ */ jsx(
|
|
1636
|
-
Input,
|
|
1637
|
-
{
|
|
1638
|
-
label: m("Days supply"),
|
|
1639
|
-
className: "min-h-11",
|
|
1640
|
-
inputMode: "numeric",
|
|
1641
|
-
value: medication.daysSupply ?? "",
|
|
1642
|
-
readOnly,
|
|
1643
|
-
onChange: (event) => onPatch({ daysSupply: text(event.target.value) || void 0 })
|
|
1644
|
-
}
|
|
1645
|
-
),
|
|
1646
|
-
/* @__PURE__ */ jsx(
|
|
1647
|
-
Input,
|
|
1648
|
-
{
|
|
1649
|
-
label: m("Refills"),
|
|
1650
|
-
className: "min-h-11",
|
|
1651
|
-
inputMode: "numeric",
|
|
1652
|
-
value: medication.refills ?? "",
|
|
1653
|
-
readOnly,
|
|
1654
|
-
onChange: (event) => onPatch({ refills: text(event.target.value) || void 0 })
|
|
1655
|
-
}
|
|
1656
|
-
),
|
|
1657
|
-
readOnly ? /* @__PURE__ */ jsx(
|
|
1658
|
-
Input,
|
|
1659
|
-
{
|
|
1660
|
-
label: m("Start \u2013 end date"),
|
|
1661
|
-
className: "min-h-11",
|
|
1662
|
-
value: [medication.startDate, medication.endDate].filter(Boolean).join(" \u2013 "),
|
|
1663
|
-
readOnly: true
|
|
1664
|
-
}
|
|
1665
|
-
) : /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1.5", children: [
|
|
1666
|
-
/* @__PURE__ */ jsx("span", { className: "text-foreground block text-sm font-medium", children: m("Start \u2013 end date") }),
|
|
1667
|
-
/* @__PURE__ */ jsx(
|
|
1668
|
-
DateRangePicker,
|
|
1669
|
-
{
|
|
1670
|
-
className: "w-full [&_[data-slot=date-range-trigger]]:min-h-11 [&_[data-slot=date-range-trigger]]:w-full",
|
|
1671
|
-
variant: "responsive",
|
|
1672
|
-
showPresets: false,
|
|
1673
|
-
placeholder: m("Start \u2013 end date"),
|
|
1674
|
-
value: {
|
|
1675
|
-
start: parseAnswerDate(medication.startDate),
|
|
1676
|
-
end: parseAnswerDate(medication.endDate)
|
|
1677
|
-
},
|
|
1678
|
-
onChange: (range) => onPatch({
|
|
1679
|
-
startDate: toAnswerDate(range.start),
|
|
1680
|
-
endDate: toAnswerDate(range.end)
|
|
1681
|
-
})
|
|
1682
|
-
}
|
|
1683
|
-
)
|
|
1684
|
-
] })
|
|
1685
|
-
] }),
|
|
1686
|
-
/* @__PURE__ */ jsxs(
|
|
1687
|
-
RadioGroup,
|
|
1688
|
-
{
|
|
1689
|
-
name: `${id}-substitution`,
|
|
1690
|
-
label: m("Substitution"),
|
|
1691
|
-
value: medication.substitution ?? "0",
|
|
1692
|
-
onValueChange: (value) => {
|
|
1693
|
-
if (!readOnly) onPatch({ substitution: value });
|
|
1694
|
-
},
|
|
1695
|
-
orientation: "horizontal",
|
|
1696
|
-
size: "sm",
|
|
1697
|
-
disabled: readOnly,
|
|
1698
|
-
children: [
|
|
1699
|
-
/* @__PURE__ */ jsx(Radio, { value: "0", label: m("Substitution permitted") }),
|
|
1700
|
-
/* @__PURE__ */ jsx(Radio, { value: "1", label: m("Dispense as written (DAW)") })
|
|
1701
|
-
]
|
|
1702
|
-
}
|
|
1703
|
-
),
|
|
1704
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1705
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: `${id}-sig`, children: m("Sig (patient directions)") }),
|
|
1706
|
-
/* @__PURE__ */ jsx(
|
|
1707
|
-
Textarea,
|
|
1708
|
-
{
|
|
1709
|
-
id: `${id}-sig`,
|
|
1710
|
-
rows: 2,
|
|
1711
|
-
value: medication.sig ?? "",
|
|
1712
|
-
readOnly,
|
|
1713
|
-
onChange: (event) => {
|
|
1714
|
-
const sig = event.target.value;
|
|
1715
|
-
const { route, frequency, prn } = parseSig(sig);
|
|
1716
|
-
onPatch({ sig, route, frequency, prn: prn || void 0 });
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
),
|
|
1720
|
-
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", "aria-live": "polite", children: medication.route || medication.frequency || medication.prn ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1721
|
-
m("Derived:"),
|
|
1722
|
-
" ",
|
|
1723
|
-
[
|
|
1724
|
-
medication.route && m("route {route}", { route: medication.route }),
|
|
1725
|
-
medication.frequency && m(medication.frequency.toLowerCase()),
|
|
1726
|
-
medication.prn && m("PRN (as needed)")
|
|
1727
|
-
].filter(Boolean).join(" \xB7 ")
|
|
1728
|
-
] }) : m("Route, frequency, and PRN are derived from the directions") })
|
|
1729
|
-
] }),
|
|
1730
|
-
/* @__PURE__ */ jsx(
|
|
1731
|
-
Input,
|
|
1732
|
-
{
|
|
1733
|
-
label: m("Indication"),
|
|
1734
|
-
className: "min-h-11",
|
|
1735
|
-
value: medication.indication ?? "",
|
|
1736
|
-
readOnly,
|
|
1737
|
-
onChange: (event) => onPatch({ indication: text(event.target.value) || void 0 })
|
|
1738
|
-
}
|
|
1739
|
-
),
|
|
1740
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1741
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: `${id}-pharmacy-notes`, children: m("Pharmacy notes") }),
|
|
1742
|
-
/* @__PURE__ */ jsx(
|
|
1743
|
-
Textarea,
|
|
1744
|
-
{
|
|
1745
|
-
id: `${id}-pharmacy-notes`,
|
|
1746
|
-
rows: 2,
|
|
1747
|
-
value: medication.pharmacyNotes ?? "",
|
|
1748
|
-
readOnly,
|
|
1749
|
-
onChange: (event) => onPatch({ pharmacyNotes: event.target.value || void 0 })
|
|
1750
|
-
}
|
|
1751
|
-
)
|
|
1752
|
-
] })
|
|
1753
|
-
] });
|
|
1754
|
-
}
|
|
1755
1417
|
function EncounterMedicationField({
|
|
1756
1418
|
field,
|
|
1757
1419
|
form,
|
|
@@ -1765,24 +1427,16 @@ function EncounterMedicationField({
|
|
|
1765
1427
|
() => readValue(response?.answer),
|
|
1766
1428
|
[response?.answer]
|
|
1767
1429
|
);
|
|
1768
|
-
const m = useMessages();
|
|
1769
|
-
const requireCoding = clinicalRequiresCoding(field);
|
|
1770
|
-
const codeLookup = useCodeLookupConfig();
|
|
1771
1430
|
const state = useClinicalFormState(form);
|
|
1772
|
-
const [draft, setDraft] = React11.useState(null);
|
|
1773
1431
|
const readOnly = !(isPreview && isEnabled) || Boolean(isReadOnly) || state.readOnly || state.disposed || Boolean(error);
|
|
1774
1432
|
const canEdit = React11.useRef(!readOnly);
|
|
1775
1433
|
canEdit.current = !readOnly;
|
|
1776
1434
|
const currentValue = React11.useRef(value);
|
|
1777
1435
|
currentValue.current = value;
|
|
1778
|
-
const
|
|
1779
|
-
const commit = (update) => {
|
|
1436
|
+
const commit = (medications) => {
|
|
1780
1437
|
if (!canEdit.current || form.getState().readOnly || form.getState().disposed)
|
|
1781
1438
|
return;
|
|
1782
|
-
const next = {
|
|
1783
|
-
...currentValue.current,
|
|
1784
|
-
medications: update(currentValue.current)
|
|
1785
|
-
};
|
|
1439
|
+
const next = { ...currentValue.current, medications };
|
|
1786
1440
|
currentValue.current = next;
|
|
1787
1441
|
onResponse(
|
|
1788
1442
|
clinicalAnswerResponse(
|
|
@@ -1791,77 +1445,15 @@ function EncounterMedicationField({
|
|
|
1791
1445
|
)
|
|
1792
1446
|
);
|
|
1793
1447
|
};
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
const next = { ...draft, ...patch };
|
|
1797
|
-
if (next.name.trim()) {
|
|
1798
|
-
setDraft(null);
|
|
1799
|
-
commit((current) => [...current.medications, next]);
|
|
1800
|
-
} else setDraft(next);
|
|
1801
|
-
} else {
|
|
1802
|
-
commit(
|
|
1803
|
-
(current) => current.medications.map(
|
|
1804
|
-
(candidate) => candidate.id === medication.id ? { ...candidate, ...patch } : candidate
|
|
1805
|
-
)
|
|
1806
|
-
);
|
|
1807
|
-
}
|
|
1808
|
-
};
|
|
1809
|
-
const cards = !readOnly && draft ? [...value.medications, draft] : value.medications;
|
|
1448
|
+
if (error)
|
|
1449
|
+
return /* @__PURE__ */ jsx(InvalidClinicalAnswer, { label: "medication", answer: response?.answer });
|
|
1810
1450
|
return /* @__PURE__ */ jsx(
|
|
1811
|
-
|
|
1451
|
+
MedicationReconciliation,
|
|
1812
1452
|
{
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
/* @__PURE__ */ jsx(
|
|
1818
|
-
"pre",
|
|
1819
|
-
{
|
|
1820
|
-
tabIndex: 0,
|
|
1821
|
-
"aria-label": m("Original medication answer"),
|
|
1822
|
-
className: "max-h-48 overflow-auto text-xs break-all whitespace-pre-wrap",
|
|
1823
|
-
children: response?.answer
|
|
1824
|
-
}
|
|
1825
|
-
)
|
|
1826
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1827
|
-
!readOnly && requireCoding && !codeLookup && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: m(
|
|
1828
|
-
"Connect a medication terminology catalog to add coded medications."
|
|
1829
|
-
) }),
|
|
1830
|
-
requireCoding && value.medications.some((medication) => !medication.code) && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: m(
|
|
1831
|
-
"Imported medications without a code are retained. Pick a catalog result to code them."
|
|
1832
|
-
) }),
|
|
1833
|
-
readOnly && value.medications.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: m("No medications recorded.") }),
|
|
1834
|
-
cards.map((medication, index) => /* @__PURE__ */ jsx(
|
|
1835
|
-
MedicationCard,
|
|
1836
|
-
{
|
|
1837
|
-
index: index + 1,
|
|
1838
|
-
medication,
|
|
1839
|
-
readOnly,
|
|
1840
|
-
requireCoding,
|
|
1841
|
-
codeLookup,
|
|
1842
|
-
onPatch: (patch) => patchMedication(medication, patch),
|
|
1843
|
-
onRemove: () => {
|
|
1844
|
-
if (draft && medication.id === draft.id) setDraft(null);
|
|
1845
|
-
else
|
|
1846
|
-
commit(
|
|
1847
|
-
(current) => current.medications.filter(({ id }) => id !== medication.id)
|
|
1848
|
-
);
|
|
1849
|
-
}
|
|
1850
|
-
},
|
|
1851
|
-
medication.id
|
|
1852
|
-
)),
|
|
1853
|
-
!readOnly && !draft && (codeLookup || !requireCoding) && /* @__PURE__ */ jsx(
|
|
1854
|
-
ClinicalAddButton,
|
|
1855
|
-
{
|
|
1856
|
-
label: cards.length > 0 ? m("Add another medication") : m("Add medication"),
|
|
1857
|
-
onClick: () => setDraft({
|
|
1858
|
-
id: globalThis.crypto.randomUUID(),
|
|
1859
|
-
name: "",
|
|
1860
|
-
status: "unreconciled"
|
|
1861
|
-
})
|
|
1862
|
-
}
|
|
1863
|
-
)
|
|
1864
|
-
] })
|
|
1453
|
+
medications: value.medications,
|
|
1454
|
+
onChange: commit,
|
|
1455
|
+
inlineAddSearch: true,
|
|
1456
|
+
readOnly
|
|
1865
1457
|
}
|
|
1866
1458
|
);
|
|
1867
1459
|
}
|
|
@@ -1878,201 +1470,6 @@ function readValue2(answer) {
|
|
|
1878
1470
|
};
|
|
1879
1471
|
}
|
|
1880
1472
|
}
|
|
1881
|
-
function AllergyCard({
|
|
1882
|
-
index,
|
|
1883
|
-
allergy,
|
|
1884
|
-
readOnly,
|
|
1885
|
-
requireCoding,
|
|
1886
|
-
codeLookup,
|
|
1887
|
-
onPatch,
|
|
1888
|
-
onRemove
|
|
1889
|
-
}) {
|
|
1890
|
-
const m = useMessages();
|
|
1891
|
-
const id = React11.useId();
|
|
1892
|
-
const Lookup = codeLookup?.component;
|
|
1893
|
-
const isDrug = (allergy.type ?? "drug") === "drug";
|
|
1894
|
-
const text = (value) => value.trim() && value;
|
|
1895
|
-
return /* @__PURE__ */ jsxs("fieldset", { className: clinicalEntryCardClasses, children: [
|
|
1896
|
-
/* @__PURE__ */ jsx("legend", { className: "sr-only", children: m("Allergy {number}", { number: index }) }),
|
|
1897
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
1898
|
-
/* @__PURE__ */ jsx("h4", { className: "font-semibold", children: m("Allergy {number}", { number: index }) }),
|
|
1899
|
-
!readOnly && /* @__PURE__ */ jsx(
|
|
1900
|
-
Button,
|
|
1901
|
-
{
|
|
1902
|
-
type: "button",
|
|
1903
|
-
variant: "ghost",
|
|
1904
|
-
size: "icon",
|
|
1905
|
-
className: "shrink-0",
|
|
1906
|
-
"aria-label": m("Remove allergy {number}", { number: index }),
|
|
1907
|
-
onClick: onRemove,
|
|
1908
|
-
children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
1909
|
-
}
|
|
1910
|
-
)
|
|
1911
|
-
] }),
|
|
1912
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
|
|
1913
|
-
readOnly ? /* @__PURE__ */ jsx(
|
|
1914
|
-
Input,
|
|
1915
|
-
{
|
|
1916
|
-
label: m("Type"),
|
|
1917
|
-
className: "min-h-11",
|
|
1918
|
-
value: m(ALLERGY_TYPE_LABELS[allergy.type ?? "drug"]),
|
|
1919
|
-
readOnly: true
|
|
1920
|
-
}
|
|
1921
|
-
) : /* @__PURE__ */ jsx(
|
|
1922
|
-
Select,
|
|
1923
|
-
{
|
|
1924
|
-
label: m("Type"),
|
|
1925
|
-
className: "[&_[data-slot=select-trigger]]:min-h-11",
|
|
1926
|
-
options: Object.keys(ALLERGY_TYPE_LABELS).map(
|
|
1927
|
-
(type) => ({ value: type, label: m(ALLERGY_TYPE_LABELS[type]) })
|
|
1928
|
-
),
|
|
1929
|
-
value: allergy.type ?? "drug",
|
|
1930
|
-
onValueChange: (next) => {
|
|
1931
|
-
const type = next;
|
|
1932
|
-
onPatch({
|
|
1933
|
-
type,
|
|
1934
|
-
...type !== (allergy.type ?? "drug") && { code: void 0 }
|
|
1935
|
-
});
|
|
1936
|
-
}
|
|
1937
|
-
}
|
|
1938
|
-
),
|
|
1939
|
-
/* @__PURE__ */ jsx("div", { className: "min-w-0 sm:col-span-2", children: !readOnly && Lookup && codeLookup && (isDrug || requireCoding) ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1940
|
-
/* @__PURE__ */ jsx("span", { className: "text-foreground block text-sm font-medium", children: m("Allergen") }),
|
|
1941
|
-
/* @__PURE__ */ jsx(
|
|
1942
|
-
Lookup,
|
|
1943
|
-
{
|
|
1944
|
-
indexUrl: codeLookup.indexUrl,
|
|
1945
|
-
locale: codeLookup.locale,
|
|
1946
|
-
domains: isDrug ? ["med"] : ["condition"],
|
|
1947
|
-
searchCodetypes: isDrug ? void 0 : ["SNOMED US"],
|
|
1948
|
-
bare: true,
|
|
1949
|
-
clearOnSelect: false,
|
|
1950
|
-
initialQuery: allergy.allergen || void 0,
|
|
1951
|
-
initialSearch: false,
|
|
1952
|
-
"aria-label": m("Allergen"),
|
|
1953
|
-
placeholder: isDrug ? m("Search drug allergens \u2014 e.g. penicillin") : m("Search SNOMED allergen substances"),
|
|
1954
|
-
onSelect: (pick) => {
|
|
1955
|
-
if (requireCoding && (!pick.codetype.trim() || !pick.fullcode.trim()))
|
|
1956
|
-
return;
|
|
1957
|
-
onPatch({
|
|
1958
|
-
allergen: pick.label,
|
|
1959
|
-
code: {
|
|
1960
|
-
system: pick.codetype,
|
|
1961
|
-
code: pick.fullcode,
|
|
1962
|
-
display: pick.label
|
|
1963
|
-
}
|
|
1964
|
-
});
|
|
1965
|
-
},
|
|
1966
|
-
onFreeText: requireCoding ? void 0 : (allergen) => {
|
|
1967
|
-
if (allergen.trim())
|
|
1968
|
-
onPatch({ allergen, code: void 0 });
|
|
1969
|
-
}
|
|
1970
|
-
}
|
|
1971
|
-
),
|
|
1972
|
-
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: allergy.code ? m("Coded: {system} {code}", {
|
|
1973
|
-
system: allergy.code.system,
|
|
1974
|
-
code: allergy.code.code
|
|
1975
|
-
}) : allergy.allergen ? m(
|
|
1976
|
-
'Uncoded free text: "{name}" \u2014 pick a result to code it',
|
|
1977
|
-
{ name: allergy.allergen }
|
|
1978
|
-
) : requireCoding ? m("Pick a catalog result to code the allergen") : m(
|
|
1979
|
-
"Pick a result to code the allergen, or press Enter for free text"
|
|
1980
|
-
) })
|
|
1981
|
-
] }) : /* @__PURE__ */ jsx(
|
|
1982
|
-
Input,
|
|
1983
|
-
{
|
|
1984
|
-
label: m("Allergen"),
|
|
1985
|
-
className: "min-h-11",
|
|
1986
|
-
value: allergy.allergen,
|
|
1987
|
-
readOnly: readOnly || requireCoding,
|
|
1988
|
-
onChange: (event) => {
|
|
1989
|
-
if (event.target.value.trim())
|
|
1990
|
-
onPatch({ allergen: event.target.value, code: void 0 });
|
|
1991
|
-
}
|
|
1992
|
-
}
|
|
1993
|
-
) })
|
|
1994
|
-
] }),
|
|
1995
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
|
|
1996
|
-
/* @__PURE__ */ jsx(
|
|
1997
|
-
Input,
|
|
1998
|
-
{
|
|
1999
|
-
label: m("Reaction"),
|
|
2000
|
-
className: "min-h-11",
|
|
2001
|
-
value: allergy.reaction ?? "",
|
|
2002
|
-
readOnly,
|
|
2003
|
-
onChange: (event) => onPatch({ reaction: text(event.target.value) || void 0 })
|
|
2004
|
-
}
|
|
2005
|
-
),
|
|
2006
|
-
/* @__PURE__ */ jsx(
|
|
2007
|
-
DateInput,
|
|
2008
|
-
{
|
|
2009
|
-
label: m("Onset date"),
|
|
2010
|
-
className: "min-h-11",
|
|
2011
|
-
value: allergy.onsetDate ?? "",
|
|
2012
|
-
onChange: (next) => {
|
|
2013
|
-
if (!readOnly) onPatch({ onsetDate: next || void 0 });
|
|
2014
|
-
}
|
|
2015
|
-
}
|
|
2016
|
-
)
|
|
2017
|
-
] }),
|
|
2018
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
|
|
2019
|
-
/* @__PURE__ */ jsxs(
|
|
2020
|
-
RadioGroup,
|
|
2021
|
-
{
|
|
2022
|
-
name: `${id}-kind`,
|
|
2023
|
-
label: m("Mechanism"),
|
|
2024
|
-
description: m(
|
|
2025
|
-
"A true immune-mediated allergy, or a non-immune intolerance (e.g. GI upset)"
|
|
2026
|
-
),
|
|
2027
|
-
value: allergy.kind ?? "allergy",
|
|
2028
|
-
onValueChange: (next) => {
|
|
2029
|
-
if (!readOnly) onPatch({ kind: next });
|
|
2030
|
-
},
|
|
2031
|
-
orientation: "horizontal",
|
|
2032
|
-
size: "sm",
|
|
2033
|
-
disabled: readOnly,
|
|
2034
|
-
children: [
|
|
2035
|
-
/* @__PURE__ */ jsx(Radio, { value: "allergy", label: m("Allergy") }),
|
|
2036
|
-
/* @__PURE__ */ jsx(Radio, { value: "intolerance", label: m("Intolerance") })
|
|
2037
|
-
]
|
|
2038
|
-
}
|
|
2039
|
-
),
|
|
2040
|
-
/* @__PURE__ */ jsxs(
|
|
2041
|
-
RadioGroup,
|
|
2042
|
-
{
|
|
2043
|
-
name: `${id}-severity`,
|
|
2044
|
-
label: m("Severity"),
|
|
2045
|
-
value: allergy.severity ?? "",
|
|
2046
|
-
onValueChange: (next) => {
|
|
2047
|
-
if (!readOnly)
|
|
2048
|
-
onPatch({ severity: next || void 0 });
|
|
2049
|
-
},
|
|
2050
|
-
orientation: "horizontal",
|
|
2051
|
-
size: "sm",
|
|
2052
|
-
disabled: readOnly,
|
|
2053
|
-
children: [
|
|
2054
|
-
/* @__PURE__ */ jsx(Radio, { value: "mild", label: m("Mild") }),
|
|
2055
|
-
/* @__PURE__ */ jsx(Radio, { value: "moderate", label: m("Moderate") }),
|
|
2056
|
-
/* @__PURE__ */ jsx(Radio, { value: "severe", label: m("Severe") })
|
|
2057
|
-
]
|
|
2058
|
-
}
|
|
2059
|
-
)
|
|
2060
|
-
] }),
|
|
2061
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
2062
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: `${id}-note`, children: m("Note") }),
|
|
2063
|
-
/* @__PURE__ */ jsx(
|
|
2064
|
-
Textarea,
|
|
2065
|
-
{
|
|
2066
|
-
id: `${id}-note`,
|
|
2067
|
-
rows: 2,
|
|
2068
|
-
value: allergy.note ?? "",
|
|
2069
|
-
readOnly,
|
|
2070
|
-
onChange: (event) => onPatch({ note: event.target.value || void 0 })
|
|
2071
|
-
}
|
|
2072
|
-
)
|
|
2073
|
-
] })
|
|
2074
|
-
] });
|
|
2075
|
-
}
|
|
2076
1473
|
function EncounterAllergyField({
|
|
2077
1474
|
field,
|
|
2078
1475
|
form,
|
|
@@ -2086,17 +1483,12 @@ function EncounterAllergyField({
|
|
|
2086
1483
|
() => readValue2(response?.answer),
|
|
2087
1484
|
[response?.answer]
|
|
2088
1485
|
);
|
|
2089
|
-
const m = useMessages();
|
|
2090
|
-
const requireCoding = clinicalRequiresCoding(field);
|
|
2091
|
-
const codeLookup = useCodeLookupConfig();
|
|
2092
1486
|
const state = useClinicalFormState(form);
|
|
2093
|
-
const [draft, setDraft] = React11.useState(null);
|
|
2094
1487
|
const readOnly = !(isPreview && isEnabled) || Boolean(isReadOnly) || state.readOnly || state.disposed || Boolean(error);
|
|
2095
1488
|
const canEdit = React11.useRef(!readOnly);
|
|
2096
1489
|
canEdit.current = !readOnly;
|
|
2097
1490
|
const currentValue = React11.useRef(value);
|
|
2098
1491
|
currentValue.current = value;
|
|
2099
|
-
const definition = field.definition;
|
|
2100
1492
|
const commitValue = (update) => {
|
|
2101
1493
|
if (!canEdit.current || form.getState().readOnly || form.getState().disposed)
|
|
2102
1494
|
return;
|
|
@@ -2109,97 +1501,25 @@ function EncounterAllergyField({
|
|
|
2109
1501
|
)
|
|
2110
1502
|
);
|
|
2111
1503
|
};
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
return {
|
|
2115
|
-
...current,
|
|
2116
|
-
allergies,
|
|
2117
|
-
// Any recorded allergy clears the no-known-allergies flag.
|
|
2118
|
-
noKnownAllergies: allergies.length > 0 ? void 0 : current.noKnownAllergies
|
|
2119
|
-
};
|
|
2120
|
-
});
|
|
2121
|
-
const patchAllergy = (allergy, patch) => {
|
|
2122
|
-
if (draft && allergy.id === draft.id) {
|
|
2123
|
-
const next = { ...draft, ...patch };
|
|
2124
|
-
if (next.allergen.trim()) {
|
|
2125
|
-
setDraft(null);
|
|
2126
|
-
commit((current) => [...current.allergies, next]);
|
|
2127
|
-
} else setDraft(next);
|
|
2128
|
-
} else {
|
|
2129
|
-
commit(
|
|
2130
|
-
(current) => current.allergies.map(
|
|
2131
|
-
(candidate) => candidate.id === allergy.id ? { ...candidate, ...patch } : candidate
|
|
2132
|
-
)
|
|
2133
|
-
);
|
|
2134
|
-
}
|
|
2135
|
-
};
|
|
2136
|
-
const cards = !readOnly && draft ? [...value.allergies, draft] : value.allergies;
|
|
1504
|
+
if (error)
|
|
1505
|
+
return /* @__PURE__ */ jsx(InvalidClinicalAnswer, { label: "allergy", answer: response?.answer });
|
|
2137
1506
|
return /* @__PURE__ */ jsx(
|
|
2138
|
-
|
|
1507
|
+
AllergyManager,
|
|
2139
1508
|
{
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
!readOnly && requireCoding && !codeLookup && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: m(
|
|
2155
|
-
"Connect an allergen terminology catalog to add coded allergies."
|
|
2156
|
-
) }),
|
|
2157
|
-
requireCoding && value.allergies.some((allergy) => !allergy.code) && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: m(
|
|
2158
|
-
"Imported allergies without a code are retained. Pick a catalog result to code them."
|
|
2159
|
-
) }),
|
|
2160
|
-
readOnly && value.allergies.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: value.noKnownAllergies ? m("No known allergies (NKA).") : m("No allergies recorded.") }),
|
|
2161
|
-
cards.map((allergy, index) => /* @__PURE__ */ jsx(
|
|
2162
|
-
AllergyCard,
|
|
2163
|
-
{
|
|
2164
|
-
index: index + 1,
|
|
2165
|
-
allergy,
|
|
2166
|
-
readOnly,
|
|
2167
|
-
requireCoding,
|
|
2168
|
-
codeLookup,
|
|
2169
|
-
onPatch: (patch) => patchAllergy(allergy, patch),
|
|
2170
|
-
onRemove: () => {
|
|
2171
|
-
if (draft && allergy.id === draft.id) setDraft(null);
|
|
2172
|
-
else
|
|
2173
|
-
commit(
|
|
2174
|
-
(current) => current.allergies.filter(({ id }) => id !== allergy.id)
|
|
2175
|
-
);
|
|
2176
|
-
}
|
|
2177
|
-
},
|
|
2178
|
-
allergy.id
|
|
2179
|
-
)),
|
|
2180
|
-
!readOnly && value.allergies.length === 0 && !draft && /* @__PURE__ */ jsx(
|
|
2181
|
-
Checkbox,
|
|
2182
|
-
{
|
|
2183
|
-
label: m("No known allergies (NKA)"),
|
|
2184
|
-
checked: value.noKnownAllergies ?? false,
|
|
2185
|
-
onChange: (event) => commitValue((current) => ({
|
|
2186
|
-
...current,
|
|
2187
|
-
noKnownAllergies: event.target.checked
|
|
2188
|
-
}))
|
|
2189
|
-
}
|
|
2190
|
-
),
|
|
2191
|
-
!readOnly && !draft && (codeLookup || !requireCoding) && /* @__PURE__ */ jsx(
|
|
2192
|
-
ClinicalAddButton,
|
|
2193
|
-
{
|
|
2194
|
-
label: cards.length > 0 ? m("Add another allergy") : m("Add allergy"),
|
|
2195
|
-
onClick: () => setDraft({
|
|
2196
|
-
id: globalThis.crypto.randomUUID(),
|
|
2197
|
-
allergen: "",
|
|
2198
|
-
type: "drug"
|
|
2199
|
-
})
|
|
2200
|
-
}
|
|
2201
|
-
)
|
|
2202
|
-
] })
|
|
1509
|
+
allergies: value.allergies,
|
|
1510
|
+
onChange: (allergies) => commitValue((current) => ({
|
|
1511
|
+
...current,
|
|
1512
|
+
allergies,
|
|
1513
|
+
// Any recorded allergy clears the no-known-allergies flag.
|
|
1514
|
+
noKnownAllergies: allergies.length > 0 ? void 0 : current.noKnownAllergies
|
|
1515
|
+
})),
|
|
1516
|
+
noKnownAllergies: value.noKnownAllergies ?? false,
|
|
1517
|
+
onNoKnownAllergiesChange: (noKnownAllergies) => commitValue((current) => ({
|
|
1518
|
+
...current,
|
|
1519
|
+
noKnownAllergies: current.allergies.length > 0 ? void 0 : noKnownAllergies
|
|
1520
|
+
})),
|
|
1521
|
+
inlineAddSearch: true,
|
|
1522
|
+
readOnly
|
|
2203
1523
|
}
|
|
2204
1524
|
);
|
|
2205
1525
|
}
|
|
@@ -2228,14 +1548,11 @@ function EncounterConditionsField({
|
|
|
2228
1548
|
[response?.answer]
|
|
2229
1549
|
);
|
|
2230
1550
|
const m = useMessages();
|
|
2231
|
-
const
|
|
1551
|
+
const codeLookup = useCodeLookupConfig();
|
|
2232
1552
|
const assertionDate = useEncounterAssertionDate(field);
|
|
2233
1553
|
const currentDate = React11.useRef(assertionDate);
|
|
2234
1554
|
currentDate.current = assertionDate;
|
|
2235
|
-
const [adding, setAdding] = React11.useState(false);
|
|
2236
|
-
const [manualEditing, setManualEditing] = React11.useState(false);
|
|
2237
1555
|
const state = useClinicalFormState(form);
|
|
2238
|
-
const codeLookup = useCodeLookupConfig();
|
|
2239
1556
|
const readOnly = !(isPreview && isEnabled) || Boolean(isReadOnly) || state.readOnly || state.disposed || !value;
|
|
2240
1557
|
const currentValue = React11.useRef(value);
|
|
2241
1558
|
const currentResponse = React11.useRef(response);
|
|
@@ -2243,211 +1560,210 @@ function EncounterConditionsField({
|
|
|
2243
1560
|
const canEdit = React11.useRef(!readOnly);
|
|
2244
1561
|
currentValue.current = value;
|
|
2245
1562
|
canEdit.current = !readOnly;
|
|
2246
|
-
const [
|
|
2247
|
-
null
|
|
2248
|
-
);
|
|
2249
|
-
const definition = field.definition;
|
|
2250
|
-
const label = definition.question ?? m("Conditions");
|
|
1563
|
+
const [editor, setEditor] = React11.useState(null);
|
|
2251
1564
|
const commit = (update) => {
|
|
2252
1565
|
if (!canEdit.current || form.getState().readOnly || form.getState().disposed || !currentValue.current)
|
|
2253
1566
|
return;
|
|
1567
|
+
const next = update(currentValue.current);
|
|
1568
|
+
currentValue.current = next;
|
|
2254
1569
|
onResponse(
|
|
2255
1570
|
clinicalAnswerResponse(
|
|
2256
1571
|
form.getState().responses[field.definition.id] ?? currentResponse.current,
|
|
2257
|
-
JSON.stringify(
|
|
1572
|
+
JSON.stringify(next)
|
|
2258
1573
|
)
|
|
2259
1574
|
);
|
|
2260
1575
|
};
|
|
2261
|
-
const
|
|
2262
|
-
if (!currentDate.current.valid
|
|
2263
|
-
|
|
1576
|
+
const saveAssertion = (concernId, draft) => {
|
|
1577
|
+
if (!currentDate.current.valid) return;
|
|
1578
|
+
const assertion = {
|
|
1579
|
+
...draft,
|
|
1580
|
+
id: globalThis.crypto.randomUUID(),
|
|
1581
|
+
date: currentDate.current.date
|
|
1582
|
+
};
|
|
2264
1583
|
commit((next) => {
|
|
2265
|
-
|
|
2266
|
-
...draft,
|
|
2267
|
-
id: globalThis.crypto.randomUUID(),
|
|
2268
|
-
date: currentDate.current.date
|
|
2269
|
-
};
|
|
2270
|
-
if (editing)
|
|
1584
|
+
if (!concernId)
|
|
2271
1585
|
return {
|
|
2272
1586
|
...next,
|
|
2273
|
-
concerns:
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
}
|
|
2283
|
-
]
|
|
2284
|
-
} : concern
|
|
2285
|
-
)
|
|
1587
|
+
concerns: [
|
|
1588
|
+
...next.concerns,
|
|
1589
|
+
{
|
|
1590
|
+
concernId: globalThis.crypto.randomUUID(),
|
|
1591
|
+
clinicalStatus: "active",
|
|
1592
|
+
source: "manuallyAdded",
|
|
1593
|
+
assertions: [assertion]
|
|
1594
|
+
}
|
|
1595
|
+
]
|
|
2286
1596
|
};
|
|
2287
|
-
if (pick && next.concerns.some((concern) => matchingConditionCode(concern, pick)))
|
|
2288
|
-
return next;
|
|
2289
1597
|
return {
|
|
2290
1598
|
...next,
|
|
2291
|
-
concerns:
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
1599
|
+
concerns: next.concerns.map((concern) => {
|
|
1600
|
+
if (concern.concernId !== concernId) return concern;
|
|
1601
|
+
const prior = currentAssertion(concern);
|
|
1602
|
+
const assertions = draft.changeType === "revision" && prior ? concern.assertions.map(
|
|
1603
|
+
(candidate) => candidate.id === prior.id ? { ...candidate, verificationStatus: "refuted" } : candidate
|
|
1604
|
+
) : concern.assertions;
|
|
1605
|
+
return {
|
|
1606
|
+
...concern,
|
|
1607
|
+
assertions: [
|
|
1608
|
+
...assertions,
|
|
1609
|
+
{ ...assertion, supersedes: prior?.id }
|
|
1610
|
+
]
|
|
1611
|
+
};
|
|
1612
|
+
})
|
|
2300
1613
|
};
|
|
2301
1614
|
});
|
|
2302
|
-
if (canEdit.current) {
|
|
2303
|
-
setEditing(null);
|
|
2304
|
-
setManualEditing(false);
|
|
2305
|
-
}
|
|
2306
1615
|
};
|
|
2307
1616
|
if (!value)
|
|
2308
|
-
return /* @__PURE__ */ jsx(InvalidClinicalAnswer, { label, answer: response?.answer });
|
|
2309
|
-
const
|
|
2310
|
-
const
|
|
2311
|
-
const
|
|
2312
|
-
|
|
1617
|
+
return /* @__PURE__ */ jsx(InvalidClinicalAnswer, { label: "condition", answer: response?.answer });
|
|
1618
|
+
const editorConcern = editor?.concernId ? value.concerns.find(({ concernId }) => concernId === editor.concernId) : void 0;
|
|
1619
|
+
const canAssert = assertionDate.valid;
|
|
1620
|
+
const addSearch = codeLookup && !readOnly && canAssert ? /* @__PURE__ */ jsx(
|
|
1621
|
+
codeLookup.component,
|
|
2313
1622
|
{
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
}
|
|
2341
|
-
setEditing({ concernId: concern.concernId });
|
|
2342
|
-
if (!codeLookup) setManualEditing(true);
|
|
2343
|
-
}
|
|
1623
|
+
indexUrl: codeLookup.indexUrl,
|
|
1624
|
+
locale: codeLookup.locale,
|
|
1625
|
+
domains: ["condition"],
|
|
1626
|
+
preferCodetypes: ["ICD10"],
|
|
1627
|
+
bare: true,
|
|
1628
|
+
clearOnSelect: true,
|
|
1629
|
+
placeholder: m("Search conditions to add"),
|
|
1630
|
+
onSelect: (result) => {
|
|
1631
|
+
const coded = result.codetype.trim() && result.fullcode.trim();
|
|
1632
|
+
if (coded && (currentValue.current ?? value).concerns.some(
|
|
1633
|
+
(concern) => matchingConditionCode(concern, result)
|
|
1634
|
+
))
|
|
1635
|
+
return;
|
|
1636
|
+
saveAssertion(void 0, {
|
|
1637
|
+
text: result.label,
|
|
1638
|
+
verificationStatus: "unconfirmed",
|
|
1639
|
+
...coded ? {
|
|
1640
|
+
coding: [
|
|
1641
|
+
{
|
|
1642
|
+
system: result.codetype,
|
|
1643
|
+
code: result.fullcode,
|
|
1644
|
+
display: result.label,
|
|
1645
|
+
primary: true
|
|
1646
|
+
}
|
|
1647
|
+
]
|
|
1648
|
+
} : {}
|
|
1649
|
+
});
|
|
2344
1650
|
},
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
ids,
|
|
2350
|
-
({ concernId }) => concernId
|
|
2351
|
-
)
|
|
2352
|
-
}))
|
|
1651
|
+
onFreeText: (text) => saveAssertion(void 0, {
|
|
1652
|
+
text,
|
|
1653
|
+
verificationStatus: "unconfirmed"
|
|
1654
|
+
})
|
|
2353
1655
|
}
|
|
2354
|
-
);
|
|
2355
|
-
return /* @__PURE__ */ jsxs(
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
},
|
|
2376
|
-
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
2377
|
-
}
|
|
2378
|
-
)
|
|
2379
|
-
] }),
|
|
2380
|
-
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-end", children: [
|
|
2381
|
-
/* @__PURE__ */ jsx(EncounterAssertionDateInput, { ...assertionDate }),
|
|
2382
|
-
assertionDate.valid && Lookup && codeLookup && /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 sm:basis-56", children: /* @__PURE__ */ jsx(
|
|
2383
|
-
Lookup,
|
|
2384
|
-
{
|
|
2385
|
-
indexUrl: codeLookup.indexUrl,
|
|
2386
|
-
locale: codeLookup.locale,
|
|
2387
|
-
domains: ["condition"],
|
|
2388
|
-
preferCodetypes: ["ICD10"],
|
|
2389
|
-
bare: true,
|
|
2390
|
-
clearOnSelect: true,
|
|
2391
|
-
initialQuery: editingConcern ? currentAssertion(editingConcern)?.text : "",
|
|
2392
|
-
"aria-label": editingConcern ? m("Recode condition") : m("Add condition"),
|
|
2393
|
-
placeholder: editingConcern ? m("Search replacement diagnosis\u2026") : m("Search conditions\u2026"),
|
|
2394
|
-
onSelect: (pick) => {
|
|
2395
|
-
if (!pick.fullid?.trim() || !pick.codetype?.trim() || !pick.fullcode?.trim() || !pick.label?.trim() || pick.domain !== "condition")
|
|
2396
|
-
return;
|
|
2397
|
-
recordAssertion(
|
|
2398
|
-
{
|
|
2399
|
-
text: pick.label,
|
|
2400
|
-
verificationStatus: "unconfirmed",
|
|
2401
|
-
coding: [
|
|
2402
|
-
{
|
|
2403
|
-
system: pick.codetype,
|
|
2404
|
-
code: pick.fullcode,
|
|
2405
|
-
display: pick.label,
|
|
2406
|
-
primary: true
|
|
2407
|
-
}
|
|
2408
|
-
]
|
|
2409
|
-
},
|
|
2410
|
-
pick
|
|
1656
|
+
) : void 0;
|
|
1657
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1658
|
+
/* @__PURE__ */ jsx(
|
|
1659
|
+
ProblemList,
|
|
1660
|
+
{
|
|
1661
|
+
concerns: value.concerns,
|
|
1662
|
+
readOnly,
|
|
1663
|
+
actions: canAssert ? ["observe", "refine", "revise", "relate", "resolve", "remove"] : ["relate", "resolve", "remove"],
|
|
1664
|
+
onAction: (concern, action) => {
|
|
1665
|
+
if (action === "remove") {
|
|
1666
|
+
setEditor(null);
|
|
1667
|
+
commit((next) => ({
|
|
1668
|
+
...next,
|
|
1669
|
+
concerns: next.concerns.filter(
|
|
1670
|
+
(candidate) => candidate.concernId !== concern.concernId
|
|
1671
|
+
)
|
|
1672
|
+
}));
|
|
1673
|
+
if (canEdit.current)
|
|
1674
|
+
repairEncounterConcernReferences(
|
|
1675
|
+
form.getState(),
|
|
1676
|
+
concern.concernId
|
|
2411
1677
|
);
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
type: "button",
|
|
2422
|
-
onClick: () => {
|
|
2423
|
-
setManualEditing(true);
|
|
2424
|
-
},
|
|
2425
|
-
children: m("Other\u2026")
|
|
2426
|
-
}
|
|
2427
|
-
),
|
|
2428
|
-
/* @__PURE__ */ jsx(
|
|
2429
|
-
ConditionEditor,
|
|
2430
|
-
{
|
|
2431
|
-
mode: editing ? "refine" : "add",
|
|
2432
|
-
open: manualEditing,
|
|
2433
|
-
onOpenChange: setManualEditing,
|
|
2434
|
-
concern: editingConcern,
|
|
2435
|
-
renderCodeSearch: false,
|
|
2436
|
-
onSave: (draft) => recordAssertion(draft)
|
|
1678
|
+
} else if (action === "resolve") {
|
|
1679
|
+
commit((next) => ({
|
|
1680
|
+
...next,
|
|
1681
|
+
concerns: next.concerns.map(
|
|
1682
|
+
(candidate) => candidate.concernId === concern.concernId ? { ...candidate, clinicalStatus: "resolved" } : candidate
|
|
1683
|
+
)
|
|
1684
|
+
}));
|
|
1685
|
+
} else if (action === "observe" || action === "refine" || action === "revise" || action === "relate") {
|
|
1686
|
+
setEditor({ mode: action, concernId: concern.concernId });
|
|
2437
1687
|
}
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
1688
|
+
},
|
|
1689
|
+
addSearch,
|
|
1690
|
+
onAddProblem: addSearch || !canAssert ? void 0 : (text) => saveAssertion(void 0, {
|
|
1691
|
+
text,
|
|
1692
|
+
verificationStatus: "unconfirmed"
|
|
1693
|
+
}),
|
|
1694
|
+
onAdd: canAssert ? () => setEditor({ mode: "add" }) : void 0,
|
|
1695
|
+
onReorder: (ids) => commit((next) => ({
|
|
1696
|
+
...next,
|
|
1697
|
+
concerns: reorderClinicalEntries(
|
|
1698
|
+
next.concerns,
|
|
1699
|
+
ids,
|
|
1700
|
+
({ concernId }) => concernId
|
|
1701
|
+
)
|
|
1702
|
+
}))
|
|
1703
|
+
}
|
|
1704
|
+
),
|
|
1705
|
+
/* @__PURE__ */ jsx(
|
|
1706
|
+
ConditionEditor,
|
|
2444
1707
|
{
|
|
2445
|
-
|
|
2446
|
-
|
|
1708
|
+
mode: editor?.mode ?? "add",
|
|
1709
|
+
open: editor !== null,
|
|
1710
|
+
onOpenChange: (open) => {
|
|
1711
|
+
if (!open) setEditor(null);
|
|
1712
|
+
},
|
|
1713
|
+
concern: editor?.mode === "add" ? void 0 : editorConcern,
|
|
1714
|
+
relatableConcerns: value.concerns.filter(
|
|
1715
|
+
({ concernId }) => concernId !== editor?.concernId
|
|
1716
|
+
),
|
|
1717
|
+
onSave: (draft) => {
|
|
1718
|
+
saveAssertion(
|
|
1719
|
+
editor?.mode === "add" ? void 0 : editor?.concernId,
|
|
1720
|
+
draft
|
|
1721
|
+
);
|
|
1722
|
+
setEditor(null);
|
|
1723
|
+
},
|
|
1724
|
+
onRelate: (relationship) => {
|
|
1725
|
+
const target = editor?.concernId;
|
|
1726
|
+
if (target)
|
|
1727
|
+
commit((next) => ({
|
|
1728
|
+
...next,
|
|
1729
|
+
concerns: next.concerns.map(
|
|
1730
|
+
(concern) => concern.concernId === target ? {
|
|
1731
|
+
...concern,
|
|
1732
|
+
relationships: [
|
|
1733
|
+
...concern.relationships ?? [],
|
|
1734
|
+
relationship
|
|
1735
|
+
]
|
|
1736
|
+
} : concern
|
|
1737
|
+
)
|
|
1738
|
+
}));
|
|
1739
|
+
setEditor(null);
|
|
1740
|
+
},
|
|
1741
|
+
onAddObservation: (text) => {
|
|
1742
|
+
const target = editor?.concernId;
|
|
1743
|
+
if (target && currentDate.current.valid)
|
|
1744
|
+
commit((next) => ({
|
|
1745
|
+
...next,
|
|
1746
|
+
concerns: next.concerns.map(
|
|
1747
|
+
(concern) => concern.concernId === target ? {
|
|
1748
|
+
...concern,
|
|
1749
|
+
observations: [
|
|
1750
|
+
...concern.observations ?? [],
|
|
1751
|
+
{
|
|
1752
|
+
id: globalThis.crypto.randomUUID(),
|
|
1753
|
+
date: currentDate.current.date,
|
|
1754
|
+
text
|
|
1755
|
+
}
|
|
1756
|
+
]
|
|
1757
|
+
} : concern
|
|
1758
|
+
)
|
|
1759
|
+
}));
|
|
1760
|
+
setEditor(null);
|
|
1761
|
+
}
|
|
2447
1762
|
}
|
|
2448
1763
|
),
|
|
2449
|
-
!readOnly &&
|
|
2450
|
-
|
|
1764
|
+
!readOnly && !canAssert && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mt-2 text-xs", children: m(
|
|
1765
|
+
"No encounter date \u2014 conditions cannot be added or revised until the visit provides one."
|
|
1766
|
+
) })
|
|
2451
1767
|
] });
|
|
2452
1768
|
}
|
|
2453
1769
|
function readPlan(answer) {
|
|
@@ -2516,9 +1832,7 @@ function EncounterPlanField({
|
|
|
2516
1832
|
[response?.answer]
|
|
2517
1833
|
);
|
|
2518
1834
|
const m = useMessages();
|
|
2519
|
-
const requireCoding = clinicalRequiresCoding(field);
|
|
2520
1835
|
const state = useClinicalFormState(form);
|
|
2521
|
-
const codeLookup = useCodeLookupConfig();
|
|
2522
1836
|
const readOnly = !(isPreview && isEnabled) || Boolean(isReadOnly) || state.readOnly || state.disposed || !value;
|
|
2523
1837
|
const currentValue = React11.useRef(value);
|
|
2524
1838
|
const currentResponse = React11.useRef(response);
|
|
@@ -2544,98 +1858,63 @@ function EncounterPlanField({
|
|
|
2544
1858
|
)
|
|
2545
1859
|
);
|
|
2546
1860
|
};
|
|
2547
|
-
const renderSearch = React11.useMemo(
|
|
2548
|
-
() => codeLookup || !requireCoding ? codedOrderSearch(codeLookup, requireCoding, m) : void 0,
|
|
2549
|
-
[codeLookup, requireCoding, m]
|
|
2550
|
-
);
|
|
2551
1861
|
if (!value)
|
|
2552
1862
|
return /* @__PURE__ */ jsx(InvalidClinicalAnswer, { label, answer: response?.answer });
|
|
2553
|
-
return /* @__PURE__ */
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
)
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
next.orders,
|
|
2604
|
-
ids,
|
|
2605
|
-
({ orderId }) => orderId
|
|
2606
|
-
)
|
|
2607
|
-
}))
|
|
2608
|
-
}
|
|
2609
|
-
),
|
|
2610
|
-
!readOnly && requireCoding && !codeLookup && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: m("Coded order search is unavailable.") })
|
|
2611
|
-
] });
|
|
2612
|
-
}
|
|
2613
|
-
var EncounterSectionContext = React11.createContext(false);
|
|
2614
|
-
function EncounterSectionField(props) {
|
|
2615
|
-
const inEncounter = React11.useContext(EncounterSectionContext);
|
|
2616
|
-
const { field, isPreview, isRequired, isSoftRequired, nestedChildren } = props;
|
|
2617
|
-
const def = field.definition;
|
|
2618
|
-
if (!inEncounter || !isPreview) return /* @__PURE__ */ jsx(SectionField, { ...props });
|
|
2619
|
-
const title = def.title || "Section";
|
|
2620
|
-
return /* @__PURE__ */ jsxs("div", { className: "section-field-preview grid gap-1.5", children: [
|
|
2621
|
-
/* @__PURE__ */ jsx("h3", { className: "text-foreground m-0 flex items-center gap-2 text-sm font-medium", children: /* @__PURE__ */ jsxs("span", { className: "min-w-0 break-words", children: [
|
|
2622
|
-
title,
|
|
2623
|
-
(isRequired || isSoftRequired) && /* @__PURE__ */ jsx(
|
|
2624
|
-
RequiredMark,
|
|
2625
|
-
{
|
|
2626
|
-
variant: isSoftRequired && !isRequired ? "warning" : void 0
|
|
2627
|
-
}
|
|
2628
|
-
)
|
|
2629
|
-
] }) }),
|
|
2630
|
-
nestedChildren
|
|
2631
|
-
] });
|
|
1863
|
+
return /* @__PURE__ */ jsx("section", { "aria-label": label, className: "min-w-0 space-y-2", children: /* @__PURE__ */ jsx(
|
|
1864
|
+
Assessment,
|
|
1865
|
+
{
|
|
1866
|
+
concerns,
|
|
1867
|
+
items,
|
|
1868
|
+
orders: value.orders,
|
|
1869
|
+
title: label,
|
|
1870
|
+
readOnly,
|
|
1871
|
+
rowActions: ["add-order"],
|
|
1872
|
+
addModes: ["order"],
|
|
1873
|
+
addFormLabel: "Add plan order",
|
|
1874
|
+
onAddOrder: (item, order) => commit((next) => ({
|
|
1875
|
+
...next,
|
|
1876
|
+
orders: [
|
|
1877
|
+
...next.orders,
|
|
1878
|
+
{
|
|
1879
|
+
...order,
|
|
1880
|
+
orderId: globalThis.crypto.randomUUID(),
|
|
1881
|
+
concernId: item?.concernId
|
|
1882
|
+
}
|
|
1883
|
+
]
|
|
1884
|
+
})),
|
|
1885
|
+
onEditOrder: (order, changes) => commit((next) => ({
|
|
1886
|
+
...next,
|
|
1887
|
+
orders: next.orders.map(
|
|
1888
|
+
(candidate) => candidate.orderId === order.orderId ? { ...candidate, ...changes } : candidate
|
|
1889
|
+
)
|
|
1890
|
+
})),
|
|
1891
|
+
onRemoveOrder: (order) => commit((next) => ({
|
|
1892
|
+
...next,
|
|
1893
|
+
orders: next.orders.filter(
|
|
1894
|
+
(candidate) => candidate.orderId !== order.orderId
|
|
1895
|
+
)
|
|
1896
|
+
})),
|
|
1897
|
+
onLinkOrder: (order, concernId) => commit((next) => ({
|
|
1898
|
+
...next,
|
|
1899
|
+
orders: next.orders.map(
|
|
1900
|
+
(candidate) => candidate.orderId === order.orderId ? { ...candidate, concernId } : candidate
|
|
1901
|
+
)
|
|
1902
|
+
})),
|
|
1903
|
+
onReorderOrders: (ids) => commit((next) => ({
|
|
1904
|
+
...next,
|
|
1905
|
+
orders: reorderClinicalEntries(
|
|
1906
|
+
next.orders,
|
|
1907
|
+
ids,
|
|
1908
|
+
({ orderId }) => orderId
|
|
1909
|
+
)
|
|
1910
|
+
}))
|
|
1911
|
+
}
|
|
1912
|
+
) });
|
|
2632
1913
|
}
|
|
2633
1914
|
|
|
2634
1915
|
// src/components/EncounterVisit/fields.ts
|
|
2635
1916
|
function registerEncounterFieldTypes() {
|
|
2636
|
-
|
|
2637
|
-
if (section === void 0 || section === SectionField)
|
|
2638
|
-
registerFieldComponents({ section: EncounterSectionField });
|
|
1917
|
+
registerEsheetSectionFieldType();
|
|
2639
1918
|
const clinicalFields = {
|
|
2640
1919
|
encounterMedications: {
|
|
2641
1920
|
label: "Encounter medications",
|
|
@@ -4056,7 +3335,7 @@ var EncounterVisitSession = React11.forwardRef(function EncounterVisitSession2({
|
|
|
4056
3335
|
}
|
|
4057
3336
|
)
|
|
4058
3337
|
] }),
|
|
4059
|
-
/* @__PURE__ */ jsx(
|
|
3338
|
+
/* @__PURE__ */ jsx(EsheetSectionContext.Provider, { value: true, children: /* @__PURE__ */ jsx(
|
|
4060
3339
|
EsheetRenderer,
|
|
4061
3340
|
{
|
|
4062
3341
|
ref: renderer,
|
|
@@ -4114,6 +3393,434 @@ var EncounterVisitSession = React11.forwardRef(function EncounterVisitSession2({
|
|
|
4114
3393
|
}
|
|
4115
3394
|
);
|
|
4116
3395
|
});
|
|
3396
|
+
function getRichTextEsheetFieldId(sectionId) {
|
|
3397
|
+
return `richtext:${encodeURIComponent(sectionId)}:value`;
|
|
3398
|
+
}
|
|
3399
|
+
function getRichTextEsheetSectionFieldId(sectionId) {
|
|
3400
|
+
return `richtext:${encodeURIComponent(sectionId)}:section`;
|
|
3401
|
+
}
|
|
3402
|
+
function nonempty(value) {
|
|
3403
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
3404
|
+
}
|
|
3405
|
+
function invalid(message) {
|
|
3406
|
+
throw new Error(`Invalid RichTextEsheet definition: ${message}`);
|
|
3407
|
+
}
|
|
3408
|
+
function validateRichTextEsheetDefinition(definition) {
|
|
3409
|
+
if (!definition || typeof definition !== "object")
|
|
3410
|
+
invalid("expected an object");
|
|
3411
|
+
if (!nonempty(definition.id) || !nonempty(definition.title))
|
|
3412
|
+
invalid("id and title must be nonempty strings");
|
|
3413
|
+
if (!Array.isArray(definition.sections) || definition.sections.length === 0)
|
|
3414
|
+
invalid("at least one section is required");
|
|
3415
|
+
const sectionIds = /* @__PURE__ */ new Set();
|
|
3416
|
+
for (const section of definition.sections) {
|
|
3417
|
+
if (!section || typeof section !== "object")
|
|
3418
|
+
invalid("expected a section object");
|
|
3419
|
+
if (!nonempty(section.id) || !nonempty(section.title))
|
|
3420
|
+
invalid("each section requires an id and title");
|
|
3421
|
+
if (sectionIds.has(section.id))
|
|
3422
|
+
invalid(`duplicate section id ${section.id}`);
|
|
3423
|
+
sectionIds.add(section.id);
|
|
3424
|
+
if (section.description !== void 0 && typeof section.description !== "string")
|
|
3425
|
+
invalid(`invalid description in ${section.id}`);
|
|
3426
|
+
if (section.required !== void 0 && typeof section.required !== "boolean")
|
|
3427
|
+
invalid(`required must be boolean in ${section.id}`);
|
|
3428
|
+
if (section.fieldType !== void 0 && !nonempty(section.fieldType))
|
|
3429
|
+
invalid(`fieldType must be a nonempty string in ${section.id}`);
|
|
3430
|
+
if (section.findings === void 0) continue;
|
|
3431
|
+
if (!Array.isArray(section.findings))
|
|
3432
|
+
invalid(`findings must be an array in ${section.id}`);
|
|
3433
|
+
const findingIds = /* @__PURE__ */ new Set();
|
|
3434
|
+
for (const finding of section.findings) {
|
|
3435
|
+
if (!finding || typeof finding !== "object" || !nonempty(finding.id) || !nonempty(finding.label) || !nonempty(finding.text))
|
|
3436
|
+
invalid(`findings require id, label and text in ${section.id}`);
|
|
3437
|
+
if (findingIds.has(finding.id))
|
|
3438
|
+
invalid(`duplicate finding id ${finding.id} in ${section.id}`);
|
|
3439
|
+
findingIds.add(finding.id);
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
function partyFindings(parties) {
|
|
3444
|
+
return (parties ?? []).map((party) => ({
|
|
3445
|
+
// Content-derived identity: editing or reordering a party yields a new
|
|
3446
|
+
// macro id, so the updated text stays insertable after an earlier
|
|
3447
|
+
// version was applied (selection dedupe is by id).
|
|
3448
|
+
id: `party:${encodeURIComponent(party.role)}:${encodeURIComponent(
|
|
3449
|
+
party.name
|
|
3450
|
+
)}:${encodeURIComponent(party.detail ?? "")}`,
|
|
3451
|
+
label: `${party.role}: ${party.name}`,
|
|
3452
|
+
text: party.detail ? `${party.name} (${party.detail})` : party.name
|
|
3453
|
+
}));
|
|
3454
|
+
}
|
|
3455
|
+
function createRichTextEsheetFormDefinition(definition, parties) {
|
|
3456
|
+
validateRichTextEsheetDefinition(definition);
|
|
3457
|
+
const partyMacros = partyFindings(parties);
|
|
3458
|
+
return {
|
|
3459
|
+
id: definition.id,
|
|
3460
|
+
title: definition.title,
|
|
3461
|
+
pages: [
|
|
3462
|
+
{
|
|
3463
|
+
id: `${definition.id}:document`,
|
|
3464
|
+
title: definition.title,
|
|
3465
|
+
fields: definition.sections.map((section) => {
|
|
3466
|
+
const fields = [];
|
|
3467
|
+
if (section.description) {
|
|
3468
|
+
fields.push({
|
|
3469
|
+
id: `${getRichTextEsheetSectionFieldId(section.id)}:description`,
|
|
3470
|
+
fieldType: "display",
|
|
3471
|
+
content: section.description,
|
|
3472
|
+
width: "full"
|
|
3473
|
+
});
|
|
3474
|
+
}
|
|
3475
|
+
const fieldType = section.fieldType ?? "esheetNarrative";
|
|
3476
|
+
fields.push({
|
|
3477
|
+
id: getRichTextEsheetFieldId(section.id),
|
|
3478
|
+
fieldType,
|
|
3479
|
+
question: section.title,
|
|
3480
|
+
required: section.required,
|
|
3481
|
+
width: "full",
|
|
3482
|
+
// Keyed off the resolved type: an explicit 'esheetNarrative'
|
|
3483
|
+
// override keeps its findings, macros and hidden label.
|
|
3484
|
+
_sourceData: fieldType === "esheetNarrative" ? {
|
|
3485
|
+
narrativeHideLabel: true,
|
|
3486
|
+
narrativeFindings: [
|
|
3487
|
+
...section.findings ?? [],
|
|
3488
|
+
...partyMacros
|
|
3489
|
+
]
|
|
3490
|
+
} : void 0
|
|
3491
|
+
});
|
|
3492
|
+
return {
|
|
3493
|
+
id: getRichTextEsheetSectionFieldId(section.id),
|
|
3494
|
+
fieldType: "section",
|
|
3495
|
+
title: section.title,
|
|
3496
|
+
sectionCollapse: "expanded",
|
|
3497
|
+
width: "full",
|
|
3498
|
+
fields
|
|
3499
|
+
};
|
|
3500
|
+
})
|
|
3501
|
+
}
|
|
3502
|
+
]
|
|
3503
|
+
};
|
|
3504
|
+
}
|
|
3505
|
+
function record(value) {
|
|
3506
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3507
|
+
}
|
|
3508
|
+
function hasValue(value) {
|
|
3509
|
+
if (typeof value === "string") return value.trim().length > 0;
|
|
3510
|
+
if (typeof value === "number" || typeof value === "boolean") return true;
|
|
3511
|
+
if (Array.isArray(value)) return value.some(hasValue);
|
|
3512
|
+
if (record(value)) return Object.values(value).some(hasValue);
|
|
3513
|
+
return false;
|
|
3514
|
+
}
|
|
3515
|
+
function valueText(value) {
|
|
3516
|
+
if (typeof value === "string") return value.trim();
|
|
3517
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
3518
|
+
return String(value);
|
|
3519
|
+
if (Array.isArray(value))
|
|
3520
|
+
return value.map(valueText).filter(Boolean).join(", ");
|
|
3521
|
+
return "";
|
|
3522
|
+
}
|
|
3523
|
+
function selectedText(selected) {
|
|
3524
|
+
if (Array.isArray(selected))
|
|
3525
|
+
return selected.map(selectedText).filter(Boolean).join(", ");
|
|
3526
|
+
if (!record(selected)) return "";
|
|
3527
|
+
if ("value" in selected) {
|
|
3528
|
+
if (!hasValue(selected.value)) return "";
|
|
3529
|
+
const text = valueText(selected.value);
|
|
3530
|
+
const label = typeof selected.label === "string" ? selected.label.trim() : "";
|
|
3531
|
+
return label && label !== text ? `${label}: ${text}` : text;
|
|
3532
|
+
}
|
|
3533
|
+
return Object.values(selected).map(selectedText).filter(Boolean).join(", ");
|
|
3534
|
+
}
|
|
3535
|
+
function sectionText(responses, sectionId) {
|
|
3536
|
+
const response = responses[getRichTextEsheetFieldId(sectionId)];
|
|
3537
|
+
return valueText(response?.answer) || selectedText(response?.selected);
|
|
3538
|
+
}
|
|
3539
|
+
function markdownText(value) {
|
|
3540
|
+
return value.replace(/&/g, "&").replace(/[\\[\]`*_~<>]/g, "\\$&");
|
|
3541
|
+
}
|
|
3542
|
+
function markdownHeading(value) {
|
|
3543
|
+
return markdownText(value).replace(/\r?\n/g, " \xB7 ");
|
|
3544
|
+
}
|
|
3545
|
+
function markdownBody(value) {
|
|
3546
|
+
return value.split(/\r?\n/).map(
|
|
3547
|
+
(line) => markdownText(line.trim()).replace(/^([#+-])/, "\\$1").replace(/^(\d+)([.)])/, "$1\\$2")
|
|
3548
|
+
).filter(Boolean).join("\n\n");
|
|
3549
|
+
}
|
|
3550
|
+
function createRichTextEsheetSnapshot(definition, responses, parties) {
|
|
3551
|
+
validateRichTextEsheetDefinition(definition);
|
|
3552
|
+
const documentedSections = definition.sections.filter((section) => sectionText(responses, section.id).length > 0).map((section) => section.id);
|
|
3553
|
+
const errors = definition.sections.filter(
|
|
3554
|
+
(section) => section.required && !documentedSections.includes(section.id)
|
|
3555
|
+
).map((section) => ({
|
|
3556
|
+
sectionId: section.id,
|
|
3557
|
+
message: `${section.title} is required.`
|
|
3558
|
+
}));
|
|
3559
|
+
const frontMatter = {
|
|
3560
|
+
richTextEsheet: { id: definition.id, title: definition.title },
|
|
3561
|
+
...parties?.length ? { parties } : {}
|
|
3562
|
+
};
|
|
3563
|
+
const body = definition.sections.filter((section) => documentedSections.includes(section.id)).map(
|
|
3564
|
+
(section) => `## ${markdownHeading(section.title)}
|
|
3565
|
+
|
|
3566
|
+
${markdownBody(
|
|
3567
|
+
sectionText(responses, section.id)
|
|
3568
|
+
)}`
|
|
3569
|
+
).join("\n\n");
|
|
3570
|
+
const mdy = `---
|
|
3571
|
+
${stringify(frontMatter)}---
|
|
3572
|
+
|
|
3573
|
+
# ${markdownHeading(
|
|
3574
|
+
definition.title
|
|
3575
|
+
)}
|
|
3576
|
+
${body ? `
|
|
3577
|
+
${body}
|
|
3578
|
+
` : ""}`;
|
|
3579
|
+
return { definition, responses, documentedSections, errors, mdy };
|
|
3580
|
+
}
|
|
3581
|
+
var RichTextEsheet = React11.forwardRef(function RichTextEsheet2({ messages, ...props }, ref) {
|
|
3582
|
+
return /* @__PURE__ */ jsx(MessagesProvider, { messages, children: /* @__PURE__ */ jsx(RichTextEsheetSession, { ...props, ref }, props.definition.id) });
|
|
3583
|
+
});
|
|
3584
|
+
function ensureFields2() {
|
|
3585
|
+
registerEsheetSectionFieldType();
|
|
3586
|
+
registerEsheetNarrativeFieldType();
|
|
3587
|
+
}
|
|
3588
|
+
var esheetDefaultsSettled2 = false;
|
|
3589
|
+
var RichTextEsheetSession = React11.forwardRef(function RichTextEsheetSession2({
|
|
3590
|
+
definition,
|
|
3591
|
+
parties,
|
|
3592
|
+
initialResponses,
|
|
3593
|
+
onChange,
|
|
3594
|
+
onSubmit,
|
|
3595
|
+
readOnly = false,
|
|
3596
|
+
className,
|
|
3597
|
+
...props
|
|
3598
|
+
}, ref) {
|
|
3599
|
+
ensureFields2();
|
|
3600
|
+
const [fieldsSettled, setFieldsSettled] = React11.useState(
|
|
3601
|
+
() => esheetDefaultsSettled2
|
|
3602
|
+
);
|
|
3603
|
+
React11.useLayoutEffect(() => {
|
|
3604
|
+
if (!esheetDefaultsSettled2) {
|
|
3605
|
+
esheetDefaultsSettled2 = true;
|
|
3606
|
+
ensureFields2();
|
|
3607
|
+
}
|
|
3608
|
+
setFieldsSettled(true);
|
|
3609
|
+
}, []);
|
|
3610
|
+
const m = useMessages();
|
|
3611
|
+
const configJson = JSON.stringify({ definition, parties });
|
|
3612
|
+
const stable = React11.useMemo(
|
|
3613
|
+
() => JSON.parse(configJson),
|
|
3614
|
+
[configJson]
|
|
3615
|
+
);
|
|
3616
|
+
const form = React11.useMemo(
|
|
3617
|
+
() => createRichTextEsheetFormDefinition(stable.definition, stable.parties),
|
|
3618
|
+
[stable]
|
|
3619
|
+
);
|
|
3620
|
+
const [initialSeed] = React11.useState(() => initialResponses ?? {});
|
|
3621
|
+
const [snapshot, setSnapshot] = React11.useState(
|
|
3622
|
+
() => createRichTextEsheetSnapshot(stable.definition, initialSeed, stable.parties)
|
|
3623
|
+
);
|
|
3624
|
+
const [showErrors, setShowErrors] = React11.useState(false);
|
|
3625
|
+
const [saving, setSaving] = React11.useState(false);
|
|
3626
|
+
const submissionPending = React11.useRef(false);
|
|
3627
|
+
const [saveMessage, setSaveMessage] = React11.useState("");
|
|
3628
|
+
const renderer = React11.useRef(null);
|
|
3629
|
+
const root = React11.useRef(null);
|
|
3630
|
+
const unsubscribe = React11.useRef(null);
|
|
3631
|
+
const responses = React11.useRef(initialSeed);
|
|
3632
|
+
const latest = React11.useRef({ stable, onChange, onSubmit, readOnly, m });
|
|
3633
|
+
latest.current = { stable, onChange, onSubmit, readOnly, m };
|
|
3634
|
+
const rendererInput = React11.useMemo(
|
|
3635
|
+
() => ({ definition: form, responses: responses.current }),
|
|
3636
|
+
[form]
|
|
3637
|
+
);
|
|
3638
|
+
const currentSnapshot = React11.useCallback(() => {
|
|
3639
|
+
const { stable: stable2 } = latest.current;
|
|
3640
|
+
return createRichTextEsheetSnapshot(
|
|
3641
|
+
stable2.definition,
|
|
3642
|
+
renderer.current?.getRawResponse() ?? responses.current,
|
|
3643
|
+
stable2.parties
|
|
3644
|
+
);
|
|
3645
|
+
}, []);
|
|
3646
|
+
const focusSection = React11.useCallback((sectionId) => {
|
|
3647
|
+
const fieldId = getRichTextEsheetSectionFieldId(sectionId);
|
|
3648
|
+
const section = Array.from(
|
|
3649
|
+
root.current?.querySelectorAll("[data-field-id]") ?? []
|
|
3650
|
+
).find((element) => element.dataset.fieldId === fieldId);
|
|
3651
|
+
if (!section) return;
|
|
3652
|
+
section.tabIndex = -1;
|
|
3653
|
+
section.focus({ preventScroll: true });
|
|
3654
|
+
section.scrollIntoView?.({ block: "start", behavior: "auto" });
|
|
3655
|
+
}, []);
|
|
3656
|
+
React11.useImperativeHandle(
|
|
3657
|
+
ref,
|
|
3658
|
+
() => ({ getSnapshot: currentSnapshot, focusSection }),
|
|
3659
|
+
[currentSnapshot, focusSection]
|
|
3660
|
+
);
|
|
3661
|
+
const handleReady = React11.useCallback(() => {
|
|
3662
|
+
const store = renderer.current?.getFormStore();
|
|
3663
|
+
if (!store) return;
|
|
3664
|
+
unsubscribe.current?.();
|
|
3665
|
+
const publish = () => {
|
|
3666
|
+
responses.current = store.getState().responses;
|
|
3667
|
+
const next = currentSnapshot();
|
|
3668
|
+
setSnapshot(next);
|
|
3669
|
+
latest.current.onChange?.(next);
|
|
3670
|
+
};
|
|
3671
|
+
unsubscribe.current = store.subscribe((state, previous) => {
|
|
3672
|
+
if (state.responses !== previous.responses) {
|
|
3673
|
+
setSaveMessage("");
|
|
3674
|
+
publish();
|
|
3675
|
+
}
|
|
3676
|
+
});
|
|
3677
|
+
publish();
|
|
3678
|
+
}, [currentSnapshot]);
|
|
3679
|
+
React11.useLayoutEffect(() => {
|
|
3680
|
+
unsubscribe.current?.();
|
|
3681
|
+
unsubscribe.current = null;
|
|
3682
|
+
}, [rendererInput]);
|
|
3683
|
+
React11.useEffect(() => () => unsubscribe.current?.(), []);
|
|
3684
|
+
const submit = async () => {
|
|
3685
|
+
const context = latest.current;
|
|
3686
|
+
if (!context.onSubmit || context.readOnly || saving || submissionPending.current)
|
|
3687
|
+
return;
|
|
3688
|
+
submissionPending.current = true;
|
|
3689
|
+
try {
|
|
3690
|
+
const next = currentSnapshot();
|
|
3691
|
+
setShowErrors(true);
|
|
3692
|
+
if (next.errors.length > 0) {
|
|
3693
|
+
setSnapshot(next);
|
|
3694
|
+
setSaveMessage(m("Resolve the highlighted sections before saving."));
|
|
3695
|
+
focusSection(next.errors[0].sectionId);
|
|
3696
|
+
return;
|
|
3697
|
+
}
|
|
3698
|
+
renderer.current?.getFormStore().getState().setReadOnly(true);
|
|
3699
|
+
setSaving(true);
|
|
3700
|
+
setSaveMessage("");
|
|
3701
|
+
try {
|
|
3702
|
+
await context.onSubmit(next);
|
|
3703
|
+
setSaveMessage(latest.current.m("Document saved."));
|
|
3704
|
+
} catch (error) {
|
|
3705
|
+
setSaveMessage(
|
|
3706
|
+
error instanceof Error ? error.message : latest.current.m(
|
|
3707
|
+
"Unable to save this document. Your draft is still here."
|
|
3708
|
+
)
|
|
3709
|
+
);
|
|
3710
|
+
} finally {
|
|
3711
|
+
renderer.current?.getFormStore().getState().setReadOnly(latest.current.readOnly);
|
|
3712
|
+
setSaving(false);
|
|
3713
|
+
}
|
|
3714
|
+
} finally {
|
|
3715
|
+
submissionPending.current = false;
|
|
3716
|
+
}
|
|
3717
|
+
};
|
|
3718
|
+
return /* @__PURE__ */ jsxs(
|
|
3719
|
+
"div",
|
|
3720
|
+
{
|
|
3721
|
+
...props,
|
|
3722
|
+
ref: root,
|
|
3723
|
+
"data-slot": "richtext-esheet",
|
|
3724
|
+
className: cn(
|
|
3725
|
+
"richtext-esheet bg-background text-foreground min-w-0",
|
|
3726
|
+
className
|
|
3727
|
+
),
|
|
3728
|
+
children: [
|
|
3729
|
+
/* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-start justify-between gap-2 px-2 pt-3 pb-2 sm:px-3", children: [
|
|
3730
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
3731
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: m(stable.definition.title) }),
|
|
3732
|
+
stable.parties && stable.parties.length > 0 && /* @__PURE__ */ jsx("dl", { className: "mt-1 space-y-0.5 text-sm", children: stable.parties.map((party, index) => /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-x-1.5", children: [
|
|
3733
|
+
/* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: m(party.role) }),
|
|
3734
|
+
/* @__PURE__ */ jsxs("dd", { dir: "auto", className: "break-words", children: [
|
|
3735
|
+
party.name,
|
|
3736
|
+
party.detail && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
3737
|
+
" ",
|
|
3738
|
+
"(",
|
|
3739
|
+
party.detail,
|
|
3740
|
+
")"
|
|
3741
|
+
] })
|
|
3742
|
+
] })
|
|
3743
|
+
] }, index)) })
|
|
3744
|
+
] }),
|
|
3745
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: /* @__PURE__ */ jsx("bdi", { children: m("{count} of {total} sections documented", {
|
|
3746
|
+
count: snapshot.documentedSections.length,
|
|
3747
|
+
total: stable.definition.sections.length
|
|
3748
|
+
}) }) })
|
|
3749
|
+
] }),
|
|
3750
|
+
/* @__PURE__ */ jsxs(
|
|
3751
|
+
"div",
|
|
3752
|
+
{
|
|
3753
|
+
"data-slot": "richtext-esheet-document",
|
|
3754
|
+
className: "min-w-0 px-2 pt-2 pb-4 sm:px-3",
|
|
3755
|
+
children: [
|
|
3756
|
+
showErrors && snapshot.errors.length > 0 && /* @__PURE__ */ jsxs(
|
|
3757
|
+
"div",
|
|
3758
|
+
{
|
|
3759
|
+
role: "alert",
|
|
3760
|
+
className: "border-destructive/40 bg-destructive/5 mb-4 rounded-lg border p-3 text-sm",
|
|
3761
|
+
children: [
|
|
3762
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold", children: m("Sections need attention") }),
|
|
3763
|
+
/* @__PURE__ */ jsx("ul", { className: "mt-2 space-y-1", children: snapshot.errors.map((issue) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
3764
|
+
"button",
|
|
3765
|
+
{
|
|
3766
|
+
type: "button",
|
|
3767
|
+
className: "text-destructive min-h-11 text-start underline underline-offset-2",
|
|
3768
|
+
onClick: () => focusSection(issue.sectionId),
|
|
3769
|
+
children: m("{section} is required.", {
|
|
3770
|
+
section: stable.definition.sections.find(
|
|
3771
|
+
(section) => section.id === issue.sectionId
|
|
3772
|
+
)?.title ?? issue.sectionId
|
|
3773
|
+
})
|
|
3774
|
+
}
|
|
3775
|
+
) }, issue.sectionId)) })
|
|
3776
|
+
]
|
|
3777
|
+
}
|
|
3778
|
+
),
|
|
3779
|
+
/* @__PURE__ */ jsx(EsheetSectionContext.Provider, { value: true, children: /* @__PURE__ */ jsx(
|
|
3780
|
+
EsheetRenderer,
|
|
3781
|
+
{
|
|
3782
|
+
ref: renderer,
|
|
3783
|
+
formDataInput: rendererInput.definition,
|
|
3784
|
+
initialResponses: rendererInput.responses,
|
|
3785
|
+
onReady: handleReady,
|
|
3786
|
+
strict: true,
|
|
3787
|
+
readOnly: readOnly || saving,
|
|
3788
|
+
fitToContainer: true,
|
|
3789
|
+
topNavigation: false,
|
|
3790
|
+
bottomNavigation: false,
|
|
3791
|
+
className: "richtext-esheet-renderer"
|
|
3792
|
+
},
|
|
3793
|
+
fieldsSettled ? "settled" : "priming"
|
|
3794
|
+
) })
|
|
3795
|
+
]
|
|
3796
|
+
}
|
|
3797
|
+
),
|
|
3798
|
+
/* @__PURE__ */ jsxs("footer", { className: "bg-background sticky bottom-0 z-10 flex flex-wrap items-center justify-between gap-2 px-2 pt-2 pb-[max(0.5rem,env(safe-area-inset-bottom))] sm:px-3", children: [
|
|
3799
|
+
/* @__PURE__ */ jsx(
|
|
3800
|
+
"p",
|
|
3801
|
+
{
|
|
3802
|
+
role: "status",
|
|
3803
|
+
"aria-live": "polite",
|
|
3804
|
+
className: "text-muted-foreground min-w-0 text-sm",
|
|
3805
|
+
children: saveMessage || (readOnly ? m("Read-only document") : "")
|
|
3806
|
+
}
|
|
3807
|
+
),
|
|
3808
|
+
onSubmit && /* @__PURE__ */ jsx(
|
|
3809
|
+
Button,
|
|
3810
|
+
{
|
|
3811
|
+
type: "button",
|
|
3812
|
+
className: "min-h-11",
|
|
3813
|
+
disabled: readOnly || saving,
|
|
3814
|
+
leftIcon: /* @__PURE__ */ jsx(Save, { size: 16, "aria-hidden": "true" }),
|
|
3815
|
+
onClick: () => void submit(),
|
|
3816
|
+
children: m(saving ? "Saving\u2026" : "Save document")
|
|
3817
|
+
}
|
|
3818
|
+
)
|
|
3819
|
+
] })
|
|
3820
|
+
]
|
|
3821
|
+
}
|
|
3822
|
+
);
|
|
3823
|
+
});
|
|
4117
3824
|
function parseValue(answer) {
|
|
4118
3825
|
if (!answer) return { medications: [] };
|
|
4119
3826
|
try {
|
|
@@ -4252,6 +3959,6 @@ function registerMieEsheetFields(options) {
|
|
|
4252
3959
|
registerAllergyListFieldType({ codeLookup: options?.codeLookup });
|
|
4253
3960
|
}
|
|
4254
3961
|
|
|
4255
|
-
export { AllergyListField, ENCOUNTER_VISIT_SYSTEM_PROMPT, ENCOUNTER_VISIT_TOOL_DEFINITIONS, EncounterAllergyField, EncounterAssessmentField, EncounterConditionsField, EncounterMedicationField, EncounterNarrativeField, EncounterPlanField, EncounterVisit, EncounterVitalsField, MedicationListField, createEncounterVisitTools, executeEncounterVisitToolCall, registerAllergyListFieldType, registerEncounterFieldTypes, registerMedicationListFieldType, registerMieEsheetFields };
|
|
3962
|
+
export { AllergyListField, ENCOUNTER_VISIT_SYSTEM_PROMPT, ENCOUNTER_VISIT_TOOL_DEFINITIONS, EncounterAllergyField, EncounterAssessmentField, EncounterConditionsField, EncounterMedicationField, EncounterNarrativeField, EncounterPlanField, EncounterVisit, EncounterVitalsField, EsheetNarrativeField, EsheetSectionContext, EsheetSectionField, MedicationListField, NARRATIVE_ATTRIBUTES, RichTextEsheet, applyNarrativeFinding, createEncounterVisitTools, createRichTextEsheetFormDefinition, createRichTextEsheetSnapshot, executeEncounterVisitToolCall, getNarrativeFindings, getRichTextEsheetFieldId, getRichTextEsheetSectionFieldId, hasNarrativeFindingText, narrativeText, partyFindings, registerAllergyListFieldType, registerEncounterFieldTypes, registerEsheetNarrativeFieldType, registerEsheetSectionFieldType, registerMedicationListFieldType, registerMieEsheetFields, sanitizeNarrativeHtml, validNarrativeFindings, validateRichTextEsheetDefinition };
|
|
4256
3963
|
//# sourceMappingURL=esheet.js.map
|
|
4257
3964
|
//# sourceMappingURL=esheet.js.map
|