@mieweb/ui 0.11.0-dev.33 → 0.11.0-dev.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/{chunk-PJN4HM2X.cjs → chunk-3O3Q3DX7.cjs} +21 -21
  2. package/dist/{chunk-PJN4HM2X.cjs.map → chunk-3O3Q3DX7.cjs.map} +1 -1
  3. package/dist/{chunk-M42FTWY2.cjs → chunk-46RYJYN2.cjs} +6 -6
  4. package/dist/{chunk-M42FTWY2.cjs.map → chunk-46RYJYN2.cjs.map} +1 -1
  5. package/dist/{chunk-JMAZ2GKT.js → chunk-4GSFVD5V.js} +3 -3
  6. package/dist/{chunk-JMAZ2GKT.js.map → chunk-4GSFVD5V.js.map} +1 -1
  7. package/dist/{chunk-2R3ILG4Y.cjs → chunk-4M2KE4O5.cjs} +7 -7
  8. package/dist/{chunk-2R3ILG4Y.cjs.map → chunk-4M2KE4O5.cjs.map} +1 -1
  9. package/dist/{chunk-NAQMIFUF.js → chunk-4SUEBKTY.js} +3 -3
  10. package/dist/{chunk-NAQMIFUF.js.map → chunk-4SUEBKTY.js.map} +1 -1
  11. package/dist/{chunk-X6HEWIYP.cjs → chunk-6O6EYKOG.cjs} +130 -874
  12. package/dist/chunk-6O6EYKOG.cjs.map +1 -0
  13. package/dist/{chunk-ZNYBLM4O.cjs → chunk-DXNVQQYN.cjs} +4 -4
  14. package/dist/{chunk-ZNYBLM4O.cjs.map → chunk-DXNVQQYN.cjs.map} +1 -1
  15. package/dist/{chunk-WEGKY2LY.js → chunk-EWPNM6OO.js} +5 -3
  16. package/dist/chunk-EWPNM6OO.js.map +1 -0
  17. package/dist/{chunk-IBEQ54XW.js → chunk-EX7Q5545.js} +4 -4
  18. package/dist/{chunk-IBEQ54XW.js.map → chunk-EX7Q5545.js.map} +1 -1
  19. package/dist/{chunk-VV2U36T6.cjs → chunk-MZR634HI.cjs} +9 -9
  20. package/dist/{chunk-VV2U36T6.cjs.map → chunk-MZR634HI.cjs.map} +1 -1
  21. package/dist/{chunk-JCO62XMU.cjs → chunk-NCPDUJKJ.cjs} +43 -26
  22. package/dist/chunk-NCPDUJKJ.cjs.map +1 -0
  23. package/dist/{chunk-PYAK7XXD.js → chunk-NW6D6AFL.js} +80 -819
  24. package/dist/chunk-NW6D6AFL.js.map +1 -0
  25. package/dist/{chunk-ZPPMEUGR.cjs → chunk-PGJLW2NO.cjs} +7 -7
  26. package/dist/{chunk-ZPPMEUGR.cjs.map → chunk-PGJLW2NO.cjs.map} +1 -1
  27. package/dist/{chunk-ZFJJMDV3.js → chunk-RTADGBBN.js} +3 -3
  28. package/dist/{chunk-ZFJJMDV3.js.map → chunk-RTADGBBN.js.map} +1 -1
  29. package/dist/{chunk-LJ4CCM3V.cjs → chunk-S2CJ57BX.cjs} +5 -3
  30. package/dist/chunk-S2CJ57BX.cjs.map +1 -0
  31. package/dist/{chunk-KSQQBFDQ.js → chunk-SPGP252Q.js} +43 -26
  32. package/dist/chunk-SPGP252Q.js.map +1 -0
  33. package/dist/{chunk-67SD6VPF.cjs → chunk-SVC7NPQ6.cjs} +6 -6
  34. package/dist/{chunk-67SD6VPF.cjs.map → chunk-SVC7NPQ6.cjs.map} +1 -1
  35. package/dist/{chunk-TWQSMUE6.js → chunk-VMEVLBI3.js} +46 -38
  36. package/dist/chunk-VMEVLBI3.js.map +1 -0
  37. package/dist/{chunk-QNJEDBIN.js → chunk-X5HLVACK.js} +4 -4
  38. package/dist/{chunk-QNJEDBIN.js.map → chunk-X5HLVACK.js.map} +1 -1
  39. package/dist/{chunk-LC3JL2HB.cjs → chunk-XEHD2HA5.cjs} +46 -38
  40. package/dist/chunk-XEHD2HA5.cjs.map +1 -0
  41. package/dist/{chunk-R3MJKY63.js → chunk-YGSPIDE4.js} +3 -3
  42. package/dist/{chunk-R3MJKY63.js.map → chunk-YGSPIDE4.js.map} +1 -1
  43. package/dist/{chunk-7LKTIZLR.js → chunk-YR45POH2.js} +3 -3
  44. package/dist/{chunk-7LKTIZLR.js.map → chunk-YR45POH2.js.map} +1 -1
  45. package/dist/components/AlertDialog/index.cjs +6 -6
  46. package/dist/components/AlertDialog/index.js +2 -2
  47. package/dist/components/EmailEditor/index.cjs +12 -12
  48. package/dist/components/EmailEditor/index.js +3 -3
  49. package/dist/components/KeyboardShortcutsOverlay/index.cjs +4 -4
  50. package/dist/components/KeyboardShortcutsOverlay/index.js +2 -2
  51. package/dist/components/MediaEditor/index.cjs +7 -7
  52. package/dist/components/MediaEditor/index.js +2 -2
  53. package/dist/components/MediaFeed/index.cjs +3 -3
  54. package/dist/components/MediaFeed/index.js +2 -2
  55. package/dist/components/Modal/index.cjs +9 -9
  56. package/dist/components/Modal/index.js +1 -1
  57. package/dist/components/RichTextEditor/index.cjs +4 -4
  58. package/dist/components/RichTextEditor/index.js +1 -1
  59. package/dist/components/SuperChat/index.cjs +3 -3
  60. package/dist/components/SuperChat/index.js +2 -2
  61. package/dist/components/Textarea/index.cjs +5 -5
  62. package/dist/components/Textarea/index.js +2 -2
  63. package/dist/esheet.cjs +462 -1233
  64. package/dist/esheet.cjs.map +1 -1
  65. package/dist/esheet.d.cts +2 -2
  66. package/dist/esheet.d.ts +2 -2
  67. package/dist/esheet.js +375 -1146
  68. package/dist/esheet.js.map +1 -1
  69. package/dist/index.cjs +1011 -239
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.d.cts +6 -2
  72. package/dist/index.d.ts +6 -2
  73. package/dist/index.js +804 -17
  74. package/dist/index.js.map +1 -1
  75. package/dist/{model-NPW2JMHZ.cjs → model-EFXX5JZN.cjs} +18 -18
  76. package/dist/{model-NPW2JMHZ.cjs.map → model-EFXX5JZN.cjs.map} +1 -1
  77. package/dist/{model-SG5H3TCJ.js → model-TAARSYDY.js} +4 -4
  78. package/dist/{model-SG5H3TCJ.js.map → model-TAARSYDY.js.map} +1 -1
  79. package/dist/styles.css +1 -1
  80. package/package.json +1 -1
  81. package/dist/chunk-JCO62XMU.cjs.map +0 -1
  82. package/dist/chunk-KSQQBFDQ.js.map +0 -1
  83. package/dist/chunk-LC3JL2HB.cjs.map +0 -1
  84. package/dist/chunk-LJ4CCM3V.cjs.map +0 -1
  85. package/dist/chunk-PYAK7XXD.js.map +0 -1
  86. package/dist/chunk-TWQSMUE6.js.map +0 -1
  87. package/dist/chunk-WEGKY2LY.js.map +0 -1
  88. package/dist/chunk-X6HEWIYP.cjs.map +0 -1
package/dist/esheet.js CHANGED
@@ -1,23 +1,23 @@
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-QNJEDBIN.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-QNJEDBIN.js';
4
- import { useCodeLookupConfig, CodeLookupContext, Assessment, ConditionEditor, ALLERGY_TYPE_LABELS, lookupToMedicationFields, doseFormToQuantityUnit, DateRangePicker, parseSig, normalizeConditionCodingSystem, MedicationReconciliation, AllergyManager } from './chunk-PYAK7XXD.js';
5
- import { ProblemList, currentAssertion } from './chunk-KSQQBFDQ.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-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 { RadioGroup, Radio } from './chunk-EPSB4WTL.js';
9
- import { Label } from './chunk-I3WGZPI6.js';
8
+ import './chunk-EPSB4WTL.js';
9
+ import './chunk-I3WGZPI6.js';
10
10
  import { DateInput } from './chunk-I6Y2KLTZ.js';
11
- import { TextAreaRich, Textarea } from './chunk-NAQMIFUF.js';
12
- import './chunk-WEGKY2LY.js';
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 { Checkbox } from './chunk-753Z3NXL.js';
20
- import './chunk-TWQSMUE6.js';
19
+ import './chunk-753Z3NXL.js';
20
+ import './chunk-VMEVLBI3.js';
21
21
  import './chunk-6NZXWKUE.js';
22
22
  import { Input, RequiredMark } from './chunk-3IMM2PM4.js';
23
23
  import './chunk-YUMOM3KE.js';
@@ -27,7 +27,7 @@ import './chunk-4SMSH4OY.js';
27
27
  import { useEscapeKey } from './chunk-T4ME7QCT.js';
28
28
  import './chunk-62ZOA6PX.js';
29
29
  import './chunk-WSNMQQ4P.js';
30
- import { dateToDisplayFormat, displayFormatToDateString } from './chunk-RC2YMOMS.js';
30
+ import './chunk-RC2YMOMS.js';
31
31
  import './chunk-ERLPVXOV.js';
32
32
  import './chunk-VSQF22GL.js';
33
33
  import './chunk-BKSPDTX4.js';
@@ -41,7 +41,7 @@ import './chunk-F4N67NAF.js';
41
41
  import { cn } from './chunk-TQONQSJO.js';
42
42
  import './chunk-PX6F3LHL.js';
43
43
  import * as React11 from 'react';
44
- import { Save, X, Trash2, Plus } from 'lucide-react';
44
+ import { Save, Trash2, Plus } from 'lucide-react';
45
45
  import { EsheetRenderer } from '@esheet/renderer';
46
46
  export { EsheetRenderer } from '@esheet/renderer';
47
47
  import DOMPurify from 'dompurify';
@@ -658,103 +658,6 @@ function EncounterVitalsField({
658
658
  }
659
659
  );
660
660
  }
661
- function FreeClinicalEntry({
662
- placeholder,
663
- translate,
664
- onFreeText
665
- }) {
666
- const [text, setText] = React11.useState("");
667
- return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap gap-2", children: [
668
- /* @__PURE__ */ jsx(
669
- Input,
670
- {
671
- "aria-label": translate("Clinical entry"),
672
- placeholder,
673
- value: text,
674
- onChange: (event) => setText(event.target.value)
675
- }
676
- ),
677
- /* @__PURE__ */ jsx(
678
- Button,
679
- {
680
- type: "button",
681
- disabled: !text.trim(),
682
- onClick: () => {
683
- onFreeText?.(text.trim());
684
- setText("");
685
- },
686
- children: translate("Add")
687
- }
688
- )
689
- ] });
690
- }
691
- function codedOrderSearch(config, requireCoding = true, translate = (message) => message) {
692
- const CodedOrderSearch = ({
693
- domains,
694
- preferDomains,
695
- preferCodetypes,
696
- billableOnly,
697
- placeholder,
698
- onPick,
699
- onFreeText
700
- }) => {
701
- if (!config)
702
- return /* @__PURE__ */ jsx(
703
- FreeClinicalEntry,
704
- {
705
- placeholder,
706
- translate,
707
- onFreeText
708
- }
709
- );
710
- const Lookup = config.component;
711
- const allowedDomains = domains;
712
- return /* @__PURE__ */ jsx(
713
- Lookup,
714
- {
715
- indexUrl: config.indexUrl,
716
- locale: config.locale,
717
- domains: allowedDomains,
718
- preferDomains,
719
- preferCodetypes,
720
- billableOnly,
721
- bare: true,
722
- clearOnSelect: true,
723
- placeholder,
724
- onFreeText: requireCoding ? void 0 : onFreeText,
725
- onSelect: (result) => {
726
- if (!result.fullid?.trim() || !result.codetype?.trim() || !result.fullcode?.trim() || !result.label?.trim() || allowedDomains !== void 0 && !allowedDomains.includes(result.domain))
727
- return;
728
- onPick({
729
- fullid: result.fullid,
730
- label: result.label,
731
- codetype: result.codetype,
732
- fullcode: result.fullcode
733
- });
734
- }
735
- }
736
- );
737
- };
738
- return CodedOrderSearch;
739
- }
740
- 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";
741
- var clinicalEntryCardClasses = "bg-muted/40 min-w-0 space-y-3 rounded-lg p-3";
742
- function ClinicalAddButton({
743
- label,
744
- onClick
745
- }) {
746
- return /* @__PURE__ */ jsx(
747
- Button,
748
- {
749
- type: "button",
750
- variant: "ghost",
751
- leftIcon: /* @__PURE__ */ jsx(Plus, { "aria-hidden": "true" }),
752
- 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",
753
- onClick,
754
- children: label
755
- }
756
- );
757
- }
758
661
  function InvalidClinicalAnswer({
759
662
  label,
760
663
  answer
@@ -908,9 +811,6 @@ function matchingConditionCode(concern, picked) {
908
811
  )
909
812
  );
910
813
  }
911
- function clinicalRequiresCoding(field) {
912
- return field.definition._sourceData?.encounterRequireCoding ?? true;
913
- }
914
814
  function clinicalAnswerResponse(response, answer) {
915
815
  const next = { ...response, answer, _ai: false };
916
816
  delete next.selected;
@@ -924,37 +824,6 @@ function useEncounterAssertionDate(field) {
924
824
  }, [hostDate]);
925
825
  return { date, setDate, valid: isEncounterDate(date) };
926
826
  }
927
- function EncounterAssertionDateInput({
928
- date,
929
- setDate
930
- }) {
931
- const m = useMessages();
932
- const id = React11.useId();
933
- const [display, setDisplay] = React11.useState(() => dateToDisplayFormat(date));
934
- React11.useEffect(() => {
935
- setDisplay(
936
- (current) => displayFormatToDateString(current) === date ? current : dateToDisplayFormat(date)
937
- );
938
- }, [date]);
939
- return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
940
- /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-sm font-medium", children: m("Assertion date") }),
941
- /* @__PURE__ */ jsx(
942
- DateInput,
943
- {
944
- id,
945
- value: display,
946
- onChange: (value) => {
947
- setDisplay(value);
948
- setDate(displayFormatToDateString(value));
949
- },
950
- showCalendar: true,
951
- width: "fixed",
952
- className: "min-h-11"
953
- }
954
- ),
955
- !isEncounterDate(date) && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: m("Enter the encounter date before adding or changing a diagnosis.") })
956
- ] });
957
- }
958
827
  function readAssessment(answer, externalConcerns) {
959
828
  if (!answer) return { value: { concerns: [], items: [], orders: [] } };
960
829
  try {
@@ -967,7 +836,7 @@ function readAssessment(answer, externalConcerns) {
967
836
  } catch {
968
837
  return {
969
838
  value: { concerns: [], items: [], orders: [] },
970
- error: "The saved assessment answer could not be read. The original answer is retained below; correct it through the visit data before editing."
839
+ error: "unreadable"
971
840
  };
972
841
  }
973
842
  }
@@ -988,11 +857,9 @@ function EncounterAssessmentField({
988
857
  onResponse
989
858
  }) {
990
859
  const m = useMessages();
991
- const requireCoding = clinicalRequiresCoding(field);
992
860
  const assertionDate = useEncounterAssertionDate(field);
993
861
  const currentDate = React11.useRef(assertionDate);
994
862
  currentDate.current = assertionDate;
995
- const codeLookup = useCodeLookupConfig();
996
863
  const state = useClinicalFormState(form);
997
864
  const externalConcerns = React11.useMemo(
998
865
  () => getExternalEncounterConditions(state),
@@ -1017,10 +884,6 @@ function EncounterAssessmentField({
1017
884
  currentValue.current = value;
1018
885
  const canEdit = React11.useRef(!readOnly);
1019
886
  canEdit.current = !readOnly;
1020
- const renderSearch = React11.useMemo(
1021
- () => codeLookup || !requireCoding ? codedOrderSearch(codeLookup, requireCoding, m) : void 0,
1022
- [codeLookup, requireCoding, m]
1023
- );
1024
887
  const definition = field.definition;
1025
888
  const commit = (update) => {
1026
889
  if (!canEdit.current || form.getState().readOnly || form.getState().disposed)
@@ -1035,124 +898,92 @@ function EncounterAssessmentField({
1035
898
  );
1036
899
  };
1037
900
  if (error) {
1038
- return /* @__PURE__ */ jsx(
1039
- "section",
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,
1040
906
  {
907
+ concerns,
908
+ items: value.items,
909
+ orders: value.orders,
1041
910
  "aria-label": definition.question ?? m("Assessment and plan"),
1042
- className: "space-y-3",
1043
- children: /* @__PURE__ */ jsxs(
1044
- "div",
1045
- {
1046
- role: "alert",
1047
- className: "border-destructive text-destructive rounded-lg border p-3",
1048
- children: [
1049
- /* @__PURE__ */ jsx("p", { children: m(error) }),
1050
- /* @__PURE__ */ jsx(
1051
- "pre",
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
+ ),
1052
926
  {
1053
- tabIndex: 0,
1054
- "aria-label": m("Original assessment answer"),
1055
- className: "mt-2 max-h-48 overflow-auto text-xs break-all whitespace-pre-wrap",
1056
- children: response?.answer
927
+ concernId: existingConcern.concernId,
928
+ assertionId: existingAssertion.id
1057
929
  }
1058
- )
1059
- ]
930
+ ]
931
+ }));
932
+ return;
1060
933
  }
1061
- )
1062
- }
1063
- );
1064
- }
1065
- return /* @__PURE__ */ jsx("section", { className: "space-y-2", children: /* @__PURE__ */ jsx(
1066
- Assessment,
1067
- {
1068
- concerns,
1069
- items: value.items,
1070
- orders: value.orders,
1071
- title: null,
1072
- addFormStart: !readOnly && /* @__PURE__ */ jsx(EncounterAssertionDateInput, { ...assertionDate }),
1073
- "aria-label": definition.question ?? m("Assessment and plan"),
1074
- 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",
1075
- readOnly,
1076
- defaultAddMode: "problem",
1077
- addFormLabel: "Add assessment or order",
1078
- renderOrderSearch: renderSearch,
1079
- onAddAssessment: (codeLookup || !requireCoding) && assertionDate.valid ? (pick) => {
1080
- if (!currentDate.current.valid || requireCoding && !pick.code)
1081
- return;
1082
- const existingConcern = pick.code ? concerns.find(
1083
- (concern) => matchingConditionCode(concern, pick.code)
1084
- ) : void 0;
1085
- const existingAssertion = existingConcern && currentAssertion(existingConcern);
1086
- if (existingConcern && existingAssertion) {
934
+ const concernId = globalThis.crypto.randomUUID();
935
+ const assertionId = globalThis.crypto.randomUUID();
1087
936
  commit((next) => ({
1088
937
  ...next,
1089
- items: [
1090
- ...next.items.filter(
1091
- (item) => item.concernId !== existingConcern.concernId
1092
- ),
938
+ concerns: [
939
+ ...next.concerns,
1093
940
  {
1094
- concernId: existingConcern.concernId,
1095
- assertionId: existingAssertion.id
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
+ ]
1096
959
  }
1097
- ]
960
+ ],
961
+ items: [...next.items, { concernId, assertionId }]
1098
962
  }));
1099
- return;
1100
- }
1101
- const concernId = globalThis.crypto.randomUUID();
1102
- const assertionId = globalThis.crypto.randomUUID();
1103
- commit((next) => ({
1104
- ...next,
1105
- concerns: [
1106
- ...next.concerns,
1107
- {
1108
- concernId,
1109
- clinicalStatus: "active",
1110
- source: "manuallyAdded",
1111
- assertions: [
1112
- {
1113
- id: assertionId,
1114
- date: currentDate.current.date,
1115
- text: pick.label,
1116
- verificationStatus: "unconfirmed",
1117
- coding: pick.code ? [
1118
- {
1119
- system: pick.code.codetype,
1120
- code: pick.code.fullcode,
1121
- display: pick.label
1122
- }
1123
- ] : void 0
1124
- }
1125
- ]
1126
- }
1127
- ],
1128
- items: [...next.items, { concernId, assertionId }]
1129
- }));
1130
- } : void 0,
1131
- onRemoveAssessment: (item) => {
1132
- const wasLocal = currentValue.current.concerns.some(
1133
- (concern) => concern.concernId === item.concernId
1134
- );
1135
- commit((next) => ({
1136
- ...next,
1137
- // Removing the row also drops its local concern record — nothing
1138
- // else can reference it once the orders below are unlinked, and an
1139
- // orphaned uncoded concern would block Save with no visible repair.
1140
- concerns: next.concerns.filter(
1141
- (concern) => concern.concernId !== item.concernId
1142
- ),
1143
- items: next.items.filter(
1144
- (candidate) => candidate.concernId !== item.concernId
1145
- ),
1146
- orders: next.orders.map(
1147
- (order) => order.concernId === item.concernId ? { ...order, concernId: void 0 } : order
1148
- )
1149
- }));
1150
- if (wasLocal && canEdit.current)
1151
- repairEncounterConcernReferences(form.getState(), item.concernId);
1152
- },
1153
- onAddOrder: codeLookup || !requireCoding ? (item, order) => {
1154
- if (requireCoding && !order.code) return;
1155
- 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) => ({
1156
987
  ...next,
1157
988
  orders: [
1158
989
  ...next.orders,
@@ -1162,36 +993,39 @@ function EncounterAssessmentField({
1162
993
  concernId: item?.concernId
1163
994
  }
1164
995
  ]
1165
- }));
1166
- } : void 0,
1167
- onEditOrder: (order, changes) => commit((next) => ({
1168
- ...next,
1169
- orders: next.orders.map(
1170
- (candidate) => candidate.orderId === order.orderId ? { ...candidate, ...changes } : candidate
1171
- )
1172
- })),
1173
- onRemoveOrder: (order) => commit((next) => ({
1174
- ...next,
1175
- orders: next.orders.filter(
1176
- (candidate) => candidate.orderId !== order.orderId
1177
- )
1178
- })),
1179
- onLinkOrder: (order, concernId) => commit((next) => ({
1180
- ...next,
1181
- orders: next.orders.map(
1182
- (candidate) => candidate.orderId === order.orderId ? { ...candidate, concernId } : candidate
1183
- )
1184
- })),
1185
- onReorderItems: (ids) => commit((next) => ({
1186
- ...next,
1187
- items: reorder(next.items, ids, (item) => item.concernId)
1188
- })),
1189
- onReorderOrders: (ids) => commit((next) => ({
1190
- ...next,
1191
- orders: reorder(next.orders, ids, (order) => order.orderId)
1192
- }))
1193
- }
1194
- ) });
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
+ ] });
1195
1029
  }
1196
1030
 
1197
1031
  // src/esheet-fields/narrativeFindings.ts
@@ -1580,250 +1414,6 @@ function readValue(answer) {
1580
1414
  };
1581
1415
  }
1582
1416
  }
1583
- function parseAnswerDate(value) {
1584
- const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value ?? "");
1585
- return match ? new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])) : null;
1586
- }
1587
- function toAnswerDate(date) {
1588
- if (!date) return void 0;
1589
- const pad = (part) => String(part).padStart(2, "0");
1590
- return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
1591
- }
1592
- function MedicationCard({
1593
- index,
1594
- medication,
1595
- readOnly,
1596
- requireCoding,
1597
- codeLookup,
1598
- onPatch,
1599
- onRemove
1600
- }) {
1601
- const m = useMessages();
1602
- const id = React11.useId();
1603
- const Lookup = codeLookup?.component;
1604
- const text = (value) => value.trim() && value;
1605
- return /* @__PURE__ */ jsxs("fieldset", { className: clinicalEntryCardClasses, children: [
1606
- /* @__PURE__ */ jsx("legend", { className: "sr-only", children: m("Medication {number}", { number: index }) }),
1607
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
1608
- /* @__PURE__ */ jsx("h4", { className: "font-semibold", children: m("Medication {number}", { number: index }) }),
1609
- !readOnly && /* @__PURE__ */ jsx(
1610
- Button,
1611
- {
1612
- type: "button",
1613
- variant: "ghost",
1614
- size: "icon",
1615
- className: "shrink-0",
1616
- "aria-label": m("Remove medication {number}", { number: index }),
1617
- onClick: onRemove,
1618
- children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" })
1619
- }
1620
- )
1621
- ] }),
1622
- !readOnly && Lookup && codeLookup ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1623
- /* @__PURE__ */ jsx(
1624
- Lookup,
1625
- {
1626
- indexUrl: codeLookup.indexUrl,
1627
- locale: codeLookup.locale,
1628
- domains: ["med"],
1629
- bare: true,
1630
- clearOnSelect: false,
1631
- initialQuery: medication.name || void 0,
1632
- initialSearch: false,
1633
- "aria-label": m("Medication"),
1634
- placeholder: m("Search RxNorm / FDB \u2014 e.g. lisinopril"),
1635
- onSelect: (pick) => {
1636
- if (requireCoding && (!pick.codetype.trim() || !pick.fullcode.trim()))
1637
- return;
1638
- onPatch(lookupToMedicationFields(pick));
1639
- },
1640
- onFreeText: requireCoding ? void 0 : (name) => {
1641
- if (name.trim()) onPatch({ name, code: void 0 });
1642
- }
1643
- }
1644
- ),
1645
- /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: medication.code ? m("Coded: {system} {code}", {
1646
- system: medication.code.system,
1647
- code: medication.code.code
1648
- }) : medication.name ? m('Uncoded free text: "{name}" \u2014 pick a result to code it', {
1649
- name: medication.name
1650
- }) : requireCoding ? m("Pick a catalog result to code the medication") : m(
1651
- "Pick a result to code the medication, or press Enter for free text"
1652
- ) })
1653
- ] }) : /* @__PURE__ */ jsx(
1654
- Input,
1655
- {
1656
- label: m("Medication"),
1657
- className: "min-h-11",
1658
- value: medication.name,
1659
- readOnly: readOnly || requireCoding,
1660
- onChange: (event) => {
1661
- if (event.target.value.trim())
1662
- onPatch({ name: event.target.value, code: void 0 });
1663
- }
1664
- }
1665
- ),
1666
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
1667
- /* @__PURE__ */ jsx(
1668
- Input,
1669
- {
1670
- label: m("Strength"),
1671
- className: "min-h-11",
1672
- value: medication.strength ?? "",
1673
- readOnly,
1674
- onChange: (event) => onPatch({ strength: text(event.target.value) || void 0 })
1675
- }
1676
- ),
1677
- /* @__PURE__ */ jsx(
1678
- Input,
1679
- {
1680
- label: m("Dose form"),
1681
- className: "min-h-11",
1682
- value: medication.doseForm ?? "",
1683
- readOnly,
1684
- onChange: (event) => (
1685
- // Quantity unit follows the dose form (tablet → tablet,
1686
- // solution → milliliter, …)
1687
- onPatch({
1688
- doseForm: event.target.value || void 0,
1689
- quantityUnit: doseFormToQuantityUnit(event.target.value)
1690
- })
1691
- )
1692
- }
1693
- ),
1694
- /* @__PURE__ */ jsx(
1695
- Input,
1696
- {
1697
- label: medication.quantityUnit ? `${m("Quantity")} (${medication.quantityUnit}s)` : m("Quantity"),
1698
- className: "min-h-11",
1699
- inputMode: "decimal",
1700
- value: medication.quantity ?? "",
1701
- readOnly,
1702
- onChange: (event) => onPatch({ quantity: text(event.target.value) || void 0 })
1703
- }
1704
- )
1705
- ] }),
1706
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
1707
- /* @__PURE__ */ jsx(
1708
- Input,
1709
- {
1710
- label: m("Days supply"),
1711
- className: "min-h-11",
1712
- inputMode: "numeric",
1713
- value: medication.daysSupply ?? "",
1714
- readOnly,
1715
- onChange: (event) => onPatch({ daysSupply: text(event.target.value) || void 0 })
1716
- }
1717
- ),
1718
- /* @__PURE__ */ jsx(
1719
- Input,
1720
- {
1721
- label: m("Refills"),
1722
- className: "min-h-11",
1723
- inputMode: "numeric",
1724
- value: medication.refills ?? "",
1725
- readOnly,
1726
- onChange: (event) => onPatch({ refills: text(event.target.value) || void 0 })
1727
- }
1728
- ),
1729
- readOnly ? /* @__PURE__ */ jsx(
1730
- Input,
1731
- {
1732
- label: m("Start \u2013 end date"),
1733
- className: "min-h-11",
1734
- value: [medication.startDate, medication.endDate].filter(Boolean).join(" \u2013 "),
1735
- readOnly: true
1736
- }
1737
- ) : /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1.5", children: [
1738
- /* @__PURE__ */ jsx("span", { className: "text-foreground block text-sm font-medium", children: m("Start \u2013 end date") }),
1739
- /* @__PURE__ */ jsx(
1740
- DateRangePicker,
1741
- {
1742
- className: "w-full [&_[data-slot=date-range-trigger]]:min-h-11 [&_[data-slot=date-range-trigger]]:w-full",
1743
- variant: "responsive",
1744
- showPresets: false,
1745
- placeholder: m("Start \u2013 end date"),
1746
- value: {
1747
- start: parseAnswerDate(medication.startDate),
1748
- end: parseAnswerDate(medication.endDate)
1749
- },
1750
- onChange: (range) => onPatch({
1751
- startDate: toAnswerDate(range.start),
1752
- endDate: toAnswerDate(range.end)
1753
- })
1754
- }
1755
- )
1756
- ] })
1757
- ] }),
1758
- /* @__PURE__ */ jsxs(
1759
- RadioGroup,
1760
- {
1761
- name: `${id}-substitution`,
1762
- label: m("Substitution"),
1763
- value: medication.substitution ?? "0",
1764
- onValueChange: (value) => {
1765
- if (!readOnly) onPatch({ substitution: value });
1766
- },
1767
- orientation: "horizontal",
1768
- size: "sm",
1769
- disabled: readOnly,
1770
- children: [
1771
- /* @__PURE__ */ jsx(Radio, { value: "0", label: m("Substitution permitted") }),
1772
- /* @__PURE__ */ jsx(Radio, { value: "1", label: m("Dispense as written (DAW)") })
1773
- ]
1774
- }
1775
- ),
1776
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1777
- /* @__PURE__ */ jsx(Label, { htmlFor: `${id}-sig`, children: m("Sig (patient directions)") }),
1778
- /* @__PURE__ */ jsx(
1779
- Textarea,
1780
- {
1781
- id: `${id}-sig`,
1782
- rows: 2,
1783
- value: medication.sig ?? "",
1784
- readOnly,
1785
- onChange: (event) => {
1786
- const sig = event.target.value;
1787
- const { route, frequency, prn } = parseSig(sig);
1788
- onPatch({ sig, route, frequency, prn: prn || void 0 });
1789
- }
1790
- }
1791
- ),
1792
- /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", "aria-live": "polite", children: medication.route || medication.frequency || medication.prn ? /* @__PURE__ */ jsxs(Fragment, { children: [
1793
- m("Derived:"),
1794
- " ",
1795
- [
1796
- medication.route && m("route {route}", { route: medication.route }),
1797
- medication.frequency && m(medication.frequency.toLowerCase()),
1798
- medication.prn && m("PRN (as needed)")
1799
- ].filter(Boolean).join(" \xB7 ")
1800
- ] }) : m("Route, frequency, and PRN are derived from the directions") })
1801
- ] }),
1802
- /* @__PURE__ */ jsx(
1803
- Input,
1804
- {
1805
- label: m("Indication"),
1806
- className: "min-h-11",
1807
- value: medication.indication ?? "",
1808
- readOnly,
1809
- onChange: (event) => onPatch({ indication: text(event.target.value) || void 0 })
1810
- }
1811
- ),
1812
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1813
- /* @__PURE__ */ jsx(Label, { htmlFor: `${id}-pharmacy-notes`, children: m("Pharmacy notes") }),
1814
- /* @__PURE__ */ jsx(
1815
- Textarea,
1816
- {
1817
- id: `${id}-pharmacy-notes`,
1818
- rows: 2,
1819
- value: medication.pharmacyNotes ?? "",
1820
- readOnly,
1821
- onChange: (event) => onPatch({ pharmacyNotes: event.target.value || void 0 })
1822
- }
1823
- )
1824
- ] })
1825
- ] });
1826
- }
1827
1417
  function EncounterMedicationField({
1828
1418
  field,
1829
1419
  form,
@@ -1837,24 +1427,16 @@ function EncounterMedicationField({
1837
1427
  () => readValue(response?.answer),
1838
1428
  [response?.answer]
1839
1429
  );
1840
- const m = useMessages();
1841
- const requireCoding = clinicalRequiresCoding(field);
1842
- const codeLookup = useCodeLookupConfig();
1843
1430
  const state = useClinicalFormState(form);
1844
- const [draft, setDraft] = React11.useState(null);
1845
1431
  const readOnly = !(isPreview && isEnabled) || Boolean(isReadOnly) || state.readOnly || state.disposed || Boolean(error);
1846
1432
  const canEdit = React11.useRef(!readOnly);
1847
1433
  canEdit.current = !readOnly;
1848
1434
  const currentValue = React11.useRef(value);
1849
1435
  currentValue.current = value;
1850
- const definition = field.definition;
1851
- const commit = (update) => {
1436
+ const commit = (medications) => {
1852
1437
  if (!canEdit.current || form.getState().readOnly || form.getState().disposed)
1853
1438
  return;
1854
- const next = {
1855
- ...currentValue.current,
1856
- medications: update(currentValue.current)
1857
- };
1439
+ const next = { ...currentValue.current, medications };
1858
1440
  currentValue.current = next;
1859
1441
  onResponse(
1860
1442
  clinicalAnswerResponse(
@@ -1863,77 +1445,15 @@ function EncounterMedicationField({
1863
1445
  )
1864
1446
  );
1865
1447
  };
1866
- const patchMedication = (medication, patch) => {
1867
- if (draft && medication.id === draft.id) {
1868
- const next = { ...draft, ...patch };
1869
- if (next.name.trim()) {
1870
- setDraft(null);
1871
- commit((current) => [...current.medications, next]);
1872
- } else setDraft(next);
1873
- } else {
1874
- commit(
1875
- (current) => current.medications.map(
1876
- (candidate) => candidate.id === medication.id ? { ...candidate, ...patch } : candidate
1877
- )
1878
- );
1879
- }
1880
- };
1881
- const cards = !readOnly && draft ? [...value.medications, draft] : value.medications;
1448
+ if (error)
1449
+ return /* @__PURE__ */ jsx(InvalidClinicalAnswer, { label: "medication", answer: response?.answer });
1882
1450
  return /* @__PURE__ */ jsx(
1883
- "section",
1451
+ MedicationReconciliation,
1884
1452
  {
1885
- "aria-label": definition.question ?? m("Presenting medications"),
1886
- className: "min-w-0 space-y-3",
1887
- children: error ? /* @__PURE__ */ jsxs("div", { role: "alert", className: "text-destructive space-y-2 text-sm", children: [
1888
- /* @__PURE__ */ jsx("p", { children: m(error) }),
1889
- /* @__PURE__ */ jsx(
1890
- "pre",
1891
- {
1892
- tabIndex: 0,
1893
- "aria-label": m("Original medication answer"),
1894
- className: "max-h-48 overflow-auto text-xs break-all whitespace-pre-wrap",
1895
- children: response?.answer
1896
- }
1897
- )
1898
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1899
- !readOnly && requireCoding && !codeLookup && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: m(
1900
- "Connect a medication terminology catalog to add coded medications."
1901
- ) }),
1902
- requireCoding && value.medications.some((medication) => !medication.code) && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: m(
1903
- "Imported medications without a code are retained. Pick a catalog result to code them."
1904
- ) }),
1905
- readOnly && value.medications.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: m("No medications recorded.") }),
1906
- cards.map((medication, index) => /* @__PURE__ */ jsx(
1907
- MedicationCard,
1908
- {
1909
- index: index + 1,
1910
- medication,
1911
- readOnly,
1912
- requireCoding,
1913
- codeLookup,
1914
- onPatch: (patch) => patchMedication(medication, patch),
1915
- onRemove: () => {
1916
- if (draft && medication.id === draft.id) setDraft(null);
1917
- else
1918
- commit(
1919
- (current) => current.medications.filter(({ id }) => id !== medication.id)
1920
- );
1921
- }
1922
- },
1923
- medication.id
1924
- )),
1925
- !readOnly && !draft && (codeLookup || !requireCoding) && /* @__PURE__ */ jsx(
1926
- ClinicalAddButton,
1927
- {
1928
- label: cards.length > 0 ? m("Add another medication") : m("Add medication"),
1929
- onClick: () => setDraft({
1930
- id: globalThis.crypto.randomUUID(),
1931
- name: "",
1932
- status: "unreconciled"
1933
- })
1934
- }
1935
- )
1936
- ] })
1453
+ medications: value.medications,
1454
+ onChange: commit,
1455
+ inlineAddSearch: true,
1456
+ readOnly
1937
1457
  }
1938
1458
  );
1939
1459
  }
@@ -1950,201 +1470,6 @@ function readValue2(answer) {
1950
1470
  };
1951
1471
  }
1952
1472
  }
1953
- function AllergyCard({
1954
- index,
1955
- allergy,
1956
- readOnly,
1957
- requireCoding,
1958
- codeLookup,
1959
- onPatch,
1960
- onRemove
1961
- }) {
1962
- const m = useMessages();
1963
- const id = React11.useId();
1964
- const Lookup = codeLookup?.component;
1965
- const isDrug = (allergy.type ?? "drug") === "drug";
1966
- const text = (value) => value.trim() && value;
1967
- return /* @__PURE__ */ jsxs("fieldset", { className: clinicalEntryCardClasses, children: [
1968
- /* @__PURE__ */ jsx("legend", { className: "sr-only", children: m("Allergy {number}", { number: index }) }),
1969
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
1970
- /* @__PURE__ */ jsx("h4", { className: "font-semibold", children: m("Allergy {number}", { number: index }) }),
1971
- !readOnly && /* @__PURE__ */ jsx(
1972
- Button,
1973
- {
1974
- type: "button",
1975
- variant: "ghost",
1976
- size: "icon",
1977
- className: "shrink-0",
1978
- "aria-label": m("Remove allergy {number}", { number: index }),
1979
- onClick: onRemove,
1980
- children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" })
1981
- }
1982
- )
1983
- ] }),
1984
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
1985
- readOnly ? /* @__PURE__ */ jsx(
1986
- Input,
1987
- {
1988
- label: m("Type"),
1989
- className: "min-h-11",
1990
- value: m(ALLERGY_TYPE_LABELS[allergy.type ?? "drug"]),
1991
- readOnly: true
1992
- }
1993
- ) : /* @__PURE__ */ jsx(
1994
- Select,
1995
- {
1996
- label: m("Type"),
1997
- className: "[&_[data-slot=select-trigger]]:min-h-11",
1998
- options: Object.keys(ALLERGY_TYPE_LABELS).map(
1999
- (type) => ({ value: type, label: m(ALLERGY_TYPE_LABELS[type]) })
2000
- ),
2001
- value: allergy.type ?? "drug",
2002
- onValueChange: (next) => {
2003
- const type = next;
2004
- onPatch({
2005
- type,
2006
- ...type !== (allergy.type ?? "drug") && { code: void 0 }
2007
- });
2008
- }
2009
- }
2010
- ),
2011
- /* @__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: [
2012
- /* @__PURE__ */ jsx("span", { className: "text-foreground block text-sm font-medium", children: m("Allergen") }),
2013
- /* @__PURE__ */ jsx(
2014
- Lookup,
2015
- {
2016
- indexUrl: codeLookup.indexUrl,
2017
- locale: codeLookup.locale,
2018
- domains: isDrug ? ["med"] : ["condition"],
2019
- searchCodetypes: isDrug ? void 0 : ["SNOMED US"],
2020
- bare: true,
2021
- clearOnSelect: false,
2022
- initialQuery: allergy.allergen || void 0,
2023
- initialSearch: false,
2024
- "aria-label": m("Allergen"),
2025
- placeholder: isDrug ? m("Search drug allergens \u2014 e.g. penicillin") : m("Search SNOMED allergen substances"),
2026
- onSelect: (pick) => {
2027
- if (requireCoding && (!pick.codetype.trim() || !pick.fullcode.trim()))
2028
- return;
2029
- onPatch({
2030
- allergen: pick.label,
2031
- code: {
2032
- system: pick.codetype,
2033
- code: pick.fullcode,
2034
- display: pick.label
2035
- }
2036
- });
2037
- },
2038
- onFreeText: requireCoding ? void 0 : (allergen) => {
2039
- if (allergen.trim())
2040
- onPatch({ allergen, code: void 0 });
2041
- }
2042
- }
2043
- ),
2044
- /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: allergy.code ? m("Coded: {system} {code}", {
2045
- system: allergy.code.system,
2046
- code: allergy.code.code
2047
- }) : allergy.allergen ? m(
2048
- 'Uncoded free text: "{name}" \u2014 pick a result to code it',
2049
- { name: allergy.allergen }
2050
- ) : requireCoding ? m("Pick a catalog result to code the allergen") : m(
2051
- "Pick a result to code the allergen, or press Enter for free text"
2052
- ) })
2053
- ] }) : /* @__PURE__ */ jsx(
2054
- Input,
2055
- {
2056
- label: m("Allergen"),
2057
- className: "min-h-11",
2058
- value: allergy.allergen,
2059
- readOnly: readOnly || requireCoding,
2060
- onChange: (event) => {
2061
- if (event.target.value.trim())
2062
- onPatch({ allergen: event.target.value, code: void 0 });
2063
- }
2064
- }
2065
- ) })
2066
- ] }),
2067
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
2068
- /* @__PURE__ */ jsx(
2069
- Input,
2070
- {
2071
- label: m("Reaction"),
2072
- className: "min-h-11",
2073
- value: allergy.reaction ?? "",
2074
- readOnly,
2075
- onChange: (event) => onPatch({ reaction: text(event.target.value) || void 0 })
2076
- }
2077
- ),
2078
- /* @__PURE__ */ jsx(
2079
- DateInput,
2080
- {
2081
- label: m("Onset date"),
2082
- className: "min-h-11",
2083
- value: allergy.onsetDate ?? "",
2084
- onChange: (next) => {
2085
- if (!readOnly) onPatch({ onsetDate: next || void 0 });
2086
- }
2087
- }
2088
- )
2089
- ] }),
2090
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
2091
- /* @__PURE__ */ jsxs(
2092
- RadioGroup,
2093
- {
2094
- name: `${id}-kind`,
2095
- label: m("Mechanism"),
2096
- description: m(
2097
- "A true immune-mediated allergy, or a non-immune intolerance (e.g. GI upset)"
2098
- ),
2099
- value: allergy.kind ?? "allergy",
2100
- onValueChange: (next) => {
2101
- if (!readOnly) onPatch({ kind: next });
2102
- },
2103
- orientation: "horizontal",
2104
- size: "sm",
2105
- disabled: readOnly,
2106
- children: [
2107
- /* @__PURE__ */ jsx(Radio, { value: "allergy", label: m("Allergy") }),
2108
- /* @__PURE__ */ jsx(Radio, { value: "intolerance", label: m("Intolerance") })
2109
- ]
2110
- }
2111
- ),
2112
- /* @__PURE__ */ jsxs(
2113
- RadioGroup,
2114
- {
2115
- name: `${id}-severity`,
2116
- label: m("Severity"),
2117
- value: allergy.severity ?? "",
2118
- onValueChange: (next) => {
2119
- if (!readOnly)
2120
- onPatch({ severity: next || void 0 });
2121
- },
2122
- orientation: "horizontal",
2123
- size: "sm",
2124
- disabled: readOnly,
2125
- children: [
2126
- /* @__PURE__ */ jsx(Radio, { value: "mild", label: m("Mild") }),
2127
- /* @__PURE__ */ jsx(Radio, { value: "moderate", label: m("Moderate") }),
2128
- /* @__PURE__ */ jsx(Radio, { value: "severe", label: m("Severe") })
2129
- ]
2130
- }
2131
- )
2132
- ] }),
2133
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
2134
- /* @__PURE__ */ jsx(Label, { htmlFor: `${id}-note`, children: m("Note") }),
2135
- /* @__PURE__ */ jsx(
2136
- Textarea,
2137
- {
2138
- id: `${id}-note`,
2139
- rows: 2,
2140
- value: allergy.note ?? "",
2141
- readOnly,
2142
- onChange: (event) => onPatch({ note: event.target.value || void 0 })
2143
- }
2144
- )
2145
- ] })
2146
- ] });
2147
- }
2148
1473
  function EncounterAllergyField({
2149
1474
  field,
2150
1475
  form,
@@ -2158,17 +1483,12 @@ function EncounterAllergyField({
2158
1483
  () => readValue2(response?.answer),
2159
1484
  [response?.answer]
2160
1485
  );
2161
- const m = useMessages();
2162
- const requireCoding = clinicalRequiresCoding(field);
2163
- const codeLookup = useCodeLookupConfig();
2164
1486
  const state = useClinicalFormState(form);
2165
- const [draft, setDraft] = React11.useState(null);
2166
1487
  const readOnly = !(isPreview && isEnabled) || Boolean(isReadOnly) || state.readOnly || state.disposed || Boolean(error);
2167
1488
  const canEdit = React11.useRef(!readOnly);
2168
1489
  canEdit.current = !readOnly;
2169
1490
  const currentValue = React11.useRef(value);
2170
1491
  currentValue.current = value;
2171
- const definition = field.definition;
2172
1492
  const commitValue = (update) => {
2173
1493
  if (!canEdit.current || form.getState().readOnly || form.getState().disposed)
2174
1494
  return;
@@ -2181,97 +1501,25 @@ function EncounterAllergyField({
2181
1501
  )
2182
1502
  );
2183
1503
  };
2184
- const commit = (update) => commitValue((current) => {
2185
- const allergies = update(current);
2186
- return {
2187
- ...current,
2188
- allergies,
2189
- // Any recorded allergy clears the no-known-allergies flag.
2190
- noKnownAllergies: allergies.length > 0 ? void 0 : current.noKnownAllergies
2191
- };
2192
- });
2193
- const patchAllergy = (allergy, patch) => {
2194
- if (draft && allergy.id === draft.id) {
2195
- const next = { ...draft, ...patch };
2196
- if (next.allergen.trim()) {
2197
- setDraft(null);
2198
- commit((current) => [...current.allergies, next]);
2199
- } else setDraft(next);
2200
- } else {
2201
- commit(
2202
- (current) => current.allergies.map(
2203
- (candidate) => candidate.id === allergy.id ? { ...candidate, ...patch } : candidate
2204
- )
2205
- );
2206
- }
2207
- };
2208
- const cards = !readOnly && draft ? [...value.allergies, draft] : value.allergies;
1504
+ if (error)
1505
+ return /* @__PURE__ */ jsx(InvalidClinicalAnswer, { label: "allergy", answer: response?.answer });
2209
1506
  return /* @__PURE__ */ jsx(
2210
- "section",
1507
+ AllergyManager,
2211
1508
  {
2212
- "aria-label": definition.question ?? m("Allergies and intolerances"),
2213
- className: "min-w-0 space-y-3",
2214
- children: error ? /* @__PURE__ */ jsxs("div", { role: "alert", className: "text-destructive space-y-2 text-sm", children: [
2215
- /* @__PURE__ */ jsx("p", { children: m(error) }),
2216
- /* @__PURE__ */ jsx(
2217
- "pre",
2218
- {
2219
- tabIndex: 0,
2220
- "aria-label": m("Original allergy answer"),
2221
- className: "max-h-48 overflow-auto text-xs break-all whitespace-pre-wrap",
2222
- children: response?.answer
2223
- }
2224
- )
2225
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2226
- !readOnly && requireCoding && !codeLookup && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: m(
2227
- "Connect an allergen terminology catalog to add coded allergies."
2228
- ) }),
2229
- requireCoding && value.allergies.some((allergy) => !allergy.code) && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: m(
2230
- "Imported allergies without a code are retained. Pick a catalog result to code them."
2231
- ) }),
2232
- 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.") }),
2233
- cards.map((allergy, index) => /* @__PURE__ */ jsx(
2234
- AllergyCard,
2235
- {
2236
- index: index + 1,
2237
- allergy,
2238
- readOnly,
2239
- requireCoding,
2240
- codeLookup,
2241
- onPatch: (patch) => patchAllergy(allergy, patch),
2242
- onRemove: () => {
2243
- if (draft && allergy.id === draft.id) setDraft(null);
2244
- else
2245
- commit(
2246
- (current) => current.allergies.filter(({ id }) => id !== allergy.id)
2247
- );
2248
- }
2249
- },
2250
- allergy.id
2251
- )),
2252
- !readOnly && value.allergies.length === 0 && !draft && /* @__PURE__ */ jsx(
2253
- Checkbox,
2254
- {
2255
- label: m("No known allergies (NKA)"),
2256
- checked: value.noKnownAllergies ?? false,
2257
- onChange: (event) => commitValue((current) => ({
2258
- ...current,
2259
- noKnownAllergies: event.target.checked
2260
- }))
2261
- }
2262
- ),
2263
- !readOnly && !draft && (codeLookup || !requireCoding) && /* @__PURE__ */ jsx(
2264
- ClinicalAddButton,
2265
- {
2266
- label: cards.length > 0 ? m("Add another allergy") : m("Add allergy"),
2267
- onClick: () => setDraft({
2268
- id: globalThis.crypto.randomUUID(),
2269
- allergen: "",
2270
- type: "drug"
2271
- })
2272
- }
2273
- )
2274
- ] })
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
2275
1523
  }
2276
1524
  );
2277
1525
  }
@@ -2300,14 +1548,11 @@ function EncounterConditionsField({
2300
1548
  [response?.answer]
2301
1549
  );
2302
1550
  const m = useMessages();
2303
- const requireCoding = clinicalRequiresCoding(field);
1551
+ const codeLookup = useCodeLookupConfig();
2304
1552
  const assertionDate = useEncounterAssertionDate(field);
2305
1553
  const currentDate = React11.useRef(assertionDate);
2306
1554
  currentDate.current = assertionDate;
2307
- const [adding, setAdding] = React11.useState(false);
2308
- const [manualEditing, setManualEditing] = React11.useState(false);
2309
1555
  const state = useClinicalFormState(form);
2310
- const codeLookup = useCodeLookupConfig();
2311
1556
  const readOnly = !(isPreview && isEnabled) || Boolean(isReadOnly) || state.readOnly || state.disposed || !value;
2312
1557
  const currentValue = React11.useRef(value);
2313
1558
  const currentResponse = React11.useRef(response);
@@ -2315,211 +1560,210 @@ function EncounterConditionsField({
2315
1560
  const canEdit = React11.useRef(!readOnly);
2316
1561
  currentValue.current = value;
2317
1562
  canEdit.current = !readOnly;
2318
- const [editing, setEditing] = React11.useState(
2319
- null
2320
- );
2321
- const definition = field.definition;
2322
- const label = definition.question ?? m("Conditions");
1563
+ const [editor, setEditor] = React11.useState(null);
2323
1564
  const commit = (update) => {
2324
1565
  if (!canEdit.current || form.getState().readOnly || form.getState().disposed || !currentValue.current)
2325
1566
  return;
1567
+ const next = update(currentValue.current);
1568
+ currentValue.current = next;
2326
1569
  onResponse(
2327
1570
  clinicalAnswerResponse(
2328
1571
  form.getState().responses[field.definition.id] ?? currentResponse.current,
2329
- JSON.stringify(update(currentValue.current))
1572
+ JSON.stringify(next)
2330
1573
  )
2331
1574
  );
2332
1575
  };
2333
- const recordAssertion = (draft, pick) => {
2334
- if (!currentDate.current.valid || requireCoding && !draft.coding?.length)
2335
- return;
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
+ };
2336
1583
  commit((next) => {
2337
- const assertion = {
2338
- ...draft,
2339
- id: globalThis.crypto.randomUUID(),
2340
- date: currentDate.current.date
2341
- };
2342
- if (editing)
1584
+ if (!concernId)
2343
1585
  return {
2344
1586
  ...next,
2345
- concerns: next.concerns.map(
2346
- (concern) => concern.concernId === editing.concernId ? {
2347
- ...concern,
2348
- assertions: [
2349
- ...concern.assertions,
2350
- {
2351
- ...assertion,
2352
- changeType: draft.changeType ?? "refinement",
2353
- supersedes: currentAssertion(concern)?.id
2354
- }
2355
- ]
2356
- } : concern
2357
- )
1587
+ concerns: [
1588
+ ...next.concerns,
1589
+ {
1590
+ concernId: globalThis.crypto.randomUUID(),
1591
+ clinicalStatus: "active",
1592
+ source: "manuallyAdded",
1593
+ assertions: [assertion]
1594
+ }
1595
+ ]
2358
1596
  };
2359
- if (pick && next.concerns.some((concern) => matchingConditionCode(concern, pick)))
2360
- return next;
2361
1597
  return {
2362
1598
  ...next,
2363
- concerns: [
2364
- ...next.concerns,
2365
- {
2366
- concernId: globalThis.crypto.randomUUID(),
2367
- clinicalStatus: "active",
2368
- source: "manuallyAdded",
2369
- assertions: [assertion]
2370
- }
2371
- ]
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
+ })
2372
1613
  };
2373
1614
  });
2374
- if (canEdit.current) {
2375
- setEditing(null);
2376
- setManualEditing(false);
2377
- }
2378
1615
  };
2379
1616
  if (!value)
2380
- return /* @__PURE__ */ jsx(InvalidClinicalAnswer, { label, answer: response?.answer });
2381
- const editingConcern = editing ? value.concerns.find(({ concernId }) => concernId === editing.concernId) : void 0;
2382
- const Lookup = codeLookup?.component;
2383
- const problemList = /* @__PURE__ */ jsx(
2384
- ProblemList,
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,
2385
1622
  {
2386
- concerns: value.concerns,
2387
- title: null,
2388
- readOnly,
2389
- actions: [
2390
- ...assertionDate.valid && (codeLookup || !requireCoding) ? ["refine"] : [],
2391
- "remove"
2392
- ],
2393
- inlineActions: true,
2394
- className: "rounded-none border-0 bg-transparent shadow-none [&_[data-slot=clinical-list-heading]]:hidden [&_button]:min-h-11 [&_button]:min-w-11 [&>[data-slot=card-content]]:space-y-2 [&>[data-slot=card-content]]:p-0",
2395
- onAction: (concern, action) => {
2396
- if (readOnly) return;
2397
- if (action === "remove") {
2398
- setEditing(
2399
- (current) => current?.concernId === concern.concernId ? null : current
2400
- );
2401
- commit((next) => ({
2402
- ...next,
2403
- concerns: next.concerns.filter(
2404
- (candidate) => candidate.concernId !== concern.concernId
2405
- )
2406
- }));
2407
- if (canEdit.current)
2408
- repairEncounterConcernReferences(
2409
- form.getState(),
2410
- concern.concernId
2411
- );
2412
- } else if (action === "refine") {
2413
- setEditing({ concernId: concern.concernId });
2414
- if (!codeLookup) setManualEditing(true);
2415
- }
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
+ });
2416
1650
  },
2417
- onReorder: (ids) => commit((next) => ({
2418
- ...next,
2419
- concerns: reorderClinicalEntries(
2420
- next.concerns,
2421
- ids,
2422
- ({ concernId }) => concernId
2423
- )
2424
- }))
1651
+ onFreeText: (text) => saveAssertion(void 0, {
1652
+ text,
1653
+ verificationStatus: "unconfirmed"
1654
+ })
2425
1655
  }
2426
- );
2427
- return /* @__PURE__ */ jsxs("section", { "aria-label": label, className: "min-w-0 space-y-2", children: [
2428
- !readOnly && (adding || editingConcern || value.concerns.length > 0) && /* @__PURE__ */ jsxs("div", { className: clinicalEntryCardClasses, children: [
2429
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
2430
- /* @__PURE__ */ jsx("h4", { className: "min-w-0 font-semibold break-words", children: editingConcern ? /* @__PURE__ */ jsxs(Fragment, { children: [
2431
- m("Refine"),
2432
- " ",
2433
- currentAssertion(editingConcern)?.text
2434
- ] }) : m("Add condition") }),
2435
- (editingConcern || value.concerns.length === 0) && /* @__PURE__ */ jsx(
2436
- Button,
2437
- {
2438
- type: "button",
2439
- variant: "ghost",
2440
- size: "icon",
2441
- className: "shrink-0",
2442
- "aria-label": m("Cancel"),
2443
- onClick: () => {
2444
- setEditing(null);
2445
- setAdding(false);
2446
- setManualEditing(false);
2447
- },
2448
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4", "aria-hidden": "true" })
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
1677
+ );
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 });
2449
1687
  }
2450
- )
2451
- ] }),
2452
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-end", children: [
2453
- /* @__PURE__ */ jsx(EncounterAssertionDateInput, { ...assertionDate }),
2454
- assertionDate.valid && Lookup && codeLookup && /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 sm:basis-56", children: /* @__PURE__ */ jsx(
2455
- Lookup,
2456
- {
2457
- indexUrl: codeLookup.indexUrl,
2458
- locale: codeLookup.locale,
2459
- domains: ["condition"],
2460
- preferCodetypes: ["ICD10"],
2461
- bare: true,
2462
- clearOnSelect: true,
2463
- initialQuery: editingConcern ? currentAssertion(editingConcern)?.text : "",
2464
- "aria-label": editingConcern ? m("Recode condition") : m("Add condition"),
2465
- placeholder: editingConcern ? m("Search replacement diagnosis\u2026") : m("Search conditions\u2026"),
2466
- onSelect: (pick) => {
2467
- if (!pick.fullid?.trim() || !pick.codetype?.trim() || !pick.fullcode?.trim() || !pick.label?.trim() || pick.domain !== "condition")
2468
- return;
2469
- recordAssertion(
2470
- {
2471
- text: pick.label,
2472
- verificationStatus: "unconfirmed",
2473
- coding: [
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,
1707
+ {
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 ?? [],
2474
1751
  {
2475
- system: pick.codetype,
2476
- code: pick.fullcode,
2477
- display: pick.label,
2478
- primary: true
1752
+ id: globalThis.crypto.randomUUID(),
1753
+ date: currentDate.current.date,
1754
+ text
2479
1755
  }
2480
1756
  ]
2481
- },
2482
- pick
2483
- );
2484
- }
2485
- },
2486
- editing ? editing.concernId : "add"
2487
- ) })
2488
- ] }),
2489
- assertionDate.valid && !requireCoding && /* @__PURE__ */ jsxs(Fragment, { children: [
2490
- /* @__PURE__ */ jsx(
2491
- Button,
2492
- {
2493
- type: "button",
2494
- onClick: () => {
2495
- setManualEditing(true);
2496
- },
2497
- children: m("Other\u2026")
2498
- }
2499
- ),
2500
- /* @__PURE__ */ jsx(
2501
- ConditionEditor,
2502
- {
2503
- mode: editing ? "refine" : "add",
2504
- open: manualEditing,
2505
- onOpenChange: setManualEditing,
2506
- concern: editingConcern,
2507
- renderCodeSearch: false,
2508
- onSave: (draft) => recordAssertion(draft)
2509
- }
2510
- )
2511
- ] }),
2512
- value.concerns.length > 0 && problemList
2513
- ] }),
2514
- !readOnly && !adding && !editingConcern && value.concerns.length === 0 && (codeLookup || !requireCoding) && /* @__PURE__ */ jsx(
2515
- ClinicalAddButton,
2516
- {
2517
- label: m("Add condition"),
2518
- onClick: () => setAdding(true)
1757
+ } : concern
1758
+ )
1759
+ }));
1760
+ setEditor(null);
1761
+ }
2519
1762
  }
2520
1763
  ),
2521
- !readOnly && requireCoding && !codeLookup && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: m("Coded condition search is unavailable.") }),
2522
- readOnly && problemList
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
+ ) })
2523
1767
  ] });
2524
1768
  }
2525
1769
  function readPlan(answer) {
@@ -2588,9 +1832,7 @@ function EncounterPlanField({
2588
1832
  [response?.answer]
2589
1833
  );
2590
1834
  const m = useMessages();
2591
- const requireCoding = clinicalRequiresCoding(field);
2592
1835
  const state = useClinicalFormState(form);
2593
- const codeLookup = useCodeLookupConfig();
2594
1836
  const readOnly = !(isPreview && isEnabled) || Boolean(isReadOnly) || state.readOnly || state.disposed || !value;
2595
1837
  const currentValue = React11.useRef(value);
2596
1838
  const currentResponse = React11.useRef(response);
@@ -2616,71 +1858,58 @@ function EncounterPlanField({
2616
1858
  )
2617
1859
  );
2618
1860
  };
2619
- const renderSearch = React11.useMemo(
2620
- () => codeLookup || !requireCoding ? codedOrderSearch(codeLookup, requireCoding, m) : void 0,
2621
- [codeLookup, requireCoding, m]
2622
- );
2623
1861
  if (!value)
2624
1862
  return /* @__PURE__ */ jsx(InvalidClinicalAnswer, { label, answer: response?.answer });
2625
- return /* @__PURE__ */ jsxs("section", { "aria-label": label, className: "min-w-0 space-y-2", children: [
2626
- /* @__PURE__ */ jsx(
2627
- Assessment,
2628
- {
2629
- concerns,
2630
- items,
2631
- orders: value.orders,
2632
- title: null,
2633
- className: clinicalAssessmentClasses,
2634
- readOnly,
2635
- rowActions: codeLookup || !requireCoding ? ["add-order"] : [],
2636
- addModes: ["order"],
2637
- addFormLabel: "Add plan order",
2638
- defaultAddMode: "order",
2639
- renderOrderSearch: renderSearch,
2640
- onAddOrder: codeLookup || !requireCoding ? (item, order) => {
2641
- if (requireCoding && !order.code) return;
2642
- commit((next) => ({
2643
- ...next,
2644
- orders: [
2645
- ...next.orders,
2646
- {
2647
- ...order,
2648
- orderId: globalThis.crypto.randomUUID(),
2649
- concernId: item?.concernId
2650
- }
2651
- ]
2652
- }));
2653
- } : void 0,
2654
- onEditOrder: (order, changes) => commit((next) => ({
2655
- ...next,
2656
- orders: next.orders.map(
2657
- (candidate) => candidate.orderId === order.orderId ? { ...candidate, ...changes } : candidate
2658
- )
2659
- })),
2660
- onRemoveOrder: (order) => commit((next) => ({
2661
- ...next,
2662
- orders: next.orders.filter(
2663
- (candidate) => candidate.orderId !== order.orderId
2664
- )
2665
- })),
2666
- onLinkOrder: (order, concernId) => commit((next) => ({
2667
- ...next,
2668
- orders: next.orders.map(
2669
- (candidate) => candidate.orderId === order.orderId ? { ...candidate, concernId } : candidate
2670
- )
2671
- })),
2672
- onReorderOrders: (ids) => commit((next) => ({
2673
- ...next,
2674
- orders: reorderClinicalEntries(
2675
- next.orders,
2676
- ids,
2677
- ({ orderId }) => orderId
2678
- )
2679
- }))
2680
- }
2681
- ),
2682
- !readOnly && requireCoding && !codeLookup && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: m("Coded order search is unavailable.") })
2683
- ] });
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
+ ) });
2684
1913
  }
2685
1914
 
2686
1915
  // src/components/EncounterVisit/fields.ts