formanitor 0.0.32 → 0.0.33

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/index.cjs CHANGED
@@ -58,6 +58,13 @@ var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(DialogPrimitive)
58
58
  var nlp__default = /*#__PURE__*/_interopDefault(nlp);
59
59
  var SliderPrimitive__namespace = /*#__PURE__*/_interopNamespace(SliderPrimitive);
60
60
 
61
+ // src/core/marMedicationOrdersMeta.ts
62
+ var MAR_MEDICATION_ORDERS_META_KEY = "mar_medication_orders";
63
+ function fieldMetaRequestsMarMedicationOrdersButton(meta) {
64
+ if (!meta || typeof meta !== "object") return false;
65
+ return meta[MAR_MEDICATION_ORDERS_META_KEY] === true;
66
+ }
67
+
61
68
  // src/core/validate.ts
62
69
  var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
63
70
  function validateField(value, field) {
@@ -7574,6 +7581,206 @@ var OrthopedicExamWidget = ({ fieldId }) => {
7574
7581
  ) : null
7575
7582
  ] });
7576
7583
  };
7584
+ var MarMedicationOrdersWidget = ({ fieldId }) => {
7585
+ const { fieldDef, value, setTouched, error, disabled, touched } = useField(fieldId);
7586
+ const { state } = useForm();
7587
+ const handlers = useFieldHandlers(fieldId);
7588
+ const showError = !!error && (touched || state.submitAttempted);
7589
+ const onOpen = React15.useCallback(async () => {
7590
+ await handlers.onOpenMedicationOrders?.();
7591
+ setTouched();
7592
+ }, [handlers, setTouched]);
7593
+ if (!fieldDef || fieldDef.type !== "textarea") return null;
7594
+ const textDef = fieldDef;
7595
+ const height = textDef.height;
7596
+ const theme = getThemeConfig("textarea", fieldDef.theme);
7597
+ const wrapperClass = theme?.wrapperClassName ?? "space-y-2";
7598
+ const labelClass = theme?.labelClassName;
7599
+ const labelContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7600
+ fieldDef.label,
7601
+ " ",
7602
+ fieldDef.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500", children: "*" })
7603
+ ] });
7604
+ const labelEl = theme ? /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: fieldId, className: labelClass, children: labelContent }) : /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: fieldId, children: labelContent });
7605
+ const text = typeof value === "string" ? value : value == null ? "" : String(value);
7606
+ const hasHandler = typeof handlers.onOpenMedicationOrders === "function";
7607
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: wrapperClass, children: [
7608
+ labelEl,
7609
+ fieldDef.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: fieldDef.description }),
7610
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between sm:gap-3", children: [
7611
+ /* @__PURE__ */ jsxRuntime.jsx(
7612
+ "div",
7613
+ {
7614
+ id: fieldId,
7615
+ className: cn(
7616
+ "min-h-[80px] flex-1 rounded-md border border-input bg-background px-3 py-2 text-sm whitespace-pre-wrap",
7617
+ showError && "border-red-500",
7618
+ !text && "text-muted-foreground"
7619
+ ),
7620
+ style: height != null ? { minHeight: height } : void 0,
7621
+ "aria-readonly": "true",
7622
+ children: text || fieldDef.placeholder || "No medication orders added."
7623
+ }
7624
+ ),
7625
+ /* @__PURE__ */ jsxRuntime.jsx(
7626
+ Button,
7627
+ {
7628
+ type: "button",
7629
+ variant: "default",
7630
+ disabled: disabled || !hasHandler,
7631
+ onClick: onOpen,
7632
+ className: "shrink-0 sm:mt-0",
7633
+ children: "Add medication orders"
7634
+ }
7635
+ )
7636
+ ] }),
7637
+ showError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500", children: error })
7638
+ ] });
7639
+ };
7640
+ function parseFieldValue(value) {
7641
+ if (value == null) return { orders: [], display_lines: [] };
7642
+ if (Array.isArray(value)) {
7643
+ return {
7644
+ orders: value,
7645
+ display_lines: value.map(() => null)
7646
+ };
7647
+ }
7648
+ if (typeof value === "object") {
7649
+ const v = value;
7650
+ const orders = Array.isArray(v.orders) ? v.orders : [];
7651
+ const rawLines = Array.isArray(v.display_lines) ? v.display_lines : Array.isArray(v.displayLines) ? v.displayLines : [];
7652
+ const display_lines = orders.map((_, i) => rawLines[i] ?? null);
7653
+ return { orders, display_lines };
7654
+ }
7655
+ return { orders: [], display_lines: [] };
7656
+ }
7657
+ function normalizePrescription(res) {
7658
+ const pharmacyOrderId = res.pharmacy_order_id ?? void 0;
7659
+ const orders = Array.isArray(res.orders) ? res.orders : [];
7660
+ const linesRaw = Array.isArray(res.display_lines) ? res.display_lines : [];
7661
+ const mapped = orders.filter((o) => o && typeof o.id === "string").map((o) => ({
7662
+ id: o.id,
7663
+ item_id: o.item_id == null ? void 0 : String(o.item_id),
7664
+ item_name: o.item_name ?? void 0,
7665
+ generic_name: o.generic_name ?? void 0,
7666
+ dose: o.dose == null ? void 0 : String(o.dose),
7667
+ route: o.route ?? void 0,
7668
+ frequency: o.frequency ?? void 0,
7669
+ admin_times: Array.isArray(o.admin_times) ? o.admin_times : void 0,
7670
+ meal_sleep_timing: o.meal_sleep_timing ?? void 0,
7671
+ start_date: o.start_date ?? void 0,
7672
+ end_date: o.end_date ?? void 0,
7673
+ order_type: o.order_type ?? void 0,
7674
+ status: o.status ?? void 0,
7675
+ notes: o.notes ?? null,
7676
+ pharmacy_order_id: pharmacyOrderId
7677
+ }));
7678
+ const display_lines = mapped.map((_, i) => linesRaw[i] ?? null);
7679
+ return { orders: mapped, display_lines };
7680
+ }
7681
+ function formatDuration(start, end) {
7682
+ if (!start && !end) return null;
7683
+ if (start && end) return `${start} \u2192 ${end}`;
7684
+ return start ? `Start: ${start}` : `End: ${end}`;
7685
+ }
7686
+ var DischargeMedicationOrdersWidget = ({ fieldId }) => {
7687
+ const { fieldDef, value, setValue, setTouched, error, disabled, touched, visible } = useField(fieldId);
7688
+ const { state } = useForm();
7689
+ const handlers = useFieldHandlers(fieldId);
7690
+ const showError = !!error && (touched || state.submitAttempted);
7691
+ const { orders, display_lines } = React15.useMemo(() => parseFieldValue(value), [value]);
7692
+ const onAdd = React15.useCallback(async () => {
7693
+ setTouched();
7694
+ const fn = handlers.onOpenDischargeMedicationOrders;
7695
+ if (!fn) return;
7696
+ const res = await fn();
7697
+ if (!res || typeof res !== "object") return;
7698
+ const normalized = normalizePrescription(res);
7699
+ setValue({
7700
+ orders: normalized.orders,
7701
+ display_lines: normalized.display_lines
7702
+ });
7703
+ }, [handlers.onOpenDischargeMedicationOrders, setTouched, setValue]);
7704
+ const onDelete = React15.useCallback(
7705
+ (id) => {
7706
+ setTouched();
7707
+ const idx = orders.findIndex((o) => o.id === id);
7708
+ if (idx === -1) return;
7709
+ setValue({
7710
+ orders: orders.filter((o) => o.id !== id),
7711
+ display_lines: display_lines.filter((_, i) => i !== idx)
7712
+ });
7713
+ },
7714
+ [orders, display_lines, setTouched, setValue]
7715
+ );
7716
+ if (!visible || !fieldDef) return null;
7717
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
7718
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
7719
+ /* @__PURE__ */ jsxRuntime.jsxs(Label, { className: "text-sm font-medium", children: [
7720
+ fieldDef.label,
7721
+ " ",
7722
+ fieldDef.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500", children: "*" })
7723
+ ] }),
7724
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", onClick: onAdd, disabled: disabled || !handlers.onOpenDischargeMedicationOrders, children: "Add medicine" })
7725
+ ] }),
7726
+ fieldDef.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: fieldDef.description }),
7727
+ orders.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
7728
+ "div",
7729
+ {
7730
+ className: cn(
7731
+ "rounded-md border border-input bg-background px-3 py-3 text-sm text-muted-foreground",
7732
+ showError && "border-red-500"
7733
+ ),
7734
+ children: "No medicines added."
7735
+ }
7736
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: orders.map((o, rowIdx) => {
7737
+ const line = display_lines[rowIdx];
7738
+ const duration = formatDuration(o.start_date, o.end_date);
7739
+ const header = typeof line?.name === "string" && line.name.trim() || o.item_name || "Medicine";
7740
+ const sub = o.generic_name ? o.generic_name : null;
7741
+ const line1 = [o.dose && `Dose: ${o.dose}`, o.route && `Route: ${o.route}`].filter(Boolean).join(" \u2022 ");
7742
+ const line2 = [
7743
+ o.frequency && `Freq: ${o.frequency}`,
7744
+ o.meal_sleep_timing && `Meal/Sleep: ${o.meal_sleep_timing}`,
7745
+ o.admin_times?.length ? `Times: ${o.admin_times.join(", ")}` : null
7746
+ ].filter(Boolean).join(" \u2022 ");
7747
+ const line3 = [
7748
+ duration ? `Duration: ${duration}` : null,
7749
+ o.order_type ? `Type: ${o.order_type}` : null,
7750
+ o.status ? `Status: ${o.status}` : null
7751
+ ].filter(Boolean).join(" \u2022 ");
7752
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-md border border-input bg-background p-3", children: [
7753
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
7754
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
7755
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-sm truncate", children: header }),
7756
+ sub && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground mt-0.5 truncate", children: sub })
7757
+ ] }),
7758
+ /* @__PURE__ */ jsxRuntime.jsx(
7759
+ Button,
7760
+ {
7761
+ type: "button",
7762
+ variant: "ghost",
7763
+ className: "h-8 w-8 p-0 text-destructive",
7764
+ onClick: () => onDelete(o.id),
7765
+ "aria-label": "Delete medicine",
7766
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-4 w-4" })
7767
+ }
7768
+ )
7769
+ ] }),
7770
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 space-y-1 text-xs text-muted-foreground", children: [
7771
+ line1 && /* @__PURE__ */ jsxRuntime.jsx("div", { children: line1 }),
7772
+ line2 && /* @__PURE__ */ jsxRuntime.jsx("div", { children: line2 }),
7773
+ line3 && /* @__PURE__ */ jsxRuntime.jsx("div", { children: line3 }),
7774
+ o.notes && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-foreground/80", children: [
7775
+ "Notes: ",
7776
+ o.notes
7777
+ ] })
7778
+ ] })
7779
+ ] }, o.id);
7780
+ }) }),
7781
+ showError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500", children: error })
7782
+ ] });
7783
+ };
7577
7784
  var FieldRenderer = ({ fieldId }) => {
7578
7785
  const { fieldDef, visible } = useField(fieldId);
7579
7786
  if (!visible || !fieldDef) {
@@ -7587,6 +7794,9 @@ function renderWidget(fieldId, fieldDef) {
7587
7794
  case "number":
7588
7795
  return /* @__PURE__ */ jsxRuntime.jsx(TextWidget, { fieldId });
7589
7796
  case "textarea":
7797
+ if (fieldMetaRequestsMarMedicationOrdersButton(fieldDef.meta)) {
7798
+ return /* @__PURE__ */ jsxRuntime.jsx(MarMedicationOrdersWidget, { fieldId });
7799
+ }
7590
7800
  return /* @__PURE__ */ jsxRuntime.jsx(TextWidget, { fieldId });
7591
7801
  case "richtext":
7592
7802
  return /* @__PURE__ */ jsxRuntime.jsx(RichTextWidget, { fieldId });
@@ -7614,6 +7824,8 @@ function renderWidget(fieldId, fieldDef) {
7614
7824
  return /* @__PURE__ */ jsxRuntime.jsx(EditableTableWidget, { fieldId });
7615
7825
  case "medications":
7616
7826
  return /* @__PURE__ */ jsxRuntime.jsx(AddMedicationWidget, { fieldId });
7827
+ case "discharge_medication_orders":
7828
+ return /* @__PURE__ */ jsxRuntime.jsx(DischargeMedicationOrdersWidget, { fieldId });
7617
7829
  case "investigations":
7618
7830
  return /* @__PURE__ */ jsxRuntime.jsx(AddInvestigationWidget, { fieldId });
7619
7831
  case "procedures":
@@ -8241,6 +8453,53 @@ var ReadOnlyReferral = ({ fieldDef, value }) => {
8241
8453
  }) })
8242
8454
  ] });
8243
8455
  };
8456
+ function isOrderArray(value) {
8457
+ return Array.isArray(value) && value.every(
8458
+ (v) => v != null && typeof v === "object" && typeof v.id === "string"
8459
+ );
8460
+ }
8461
+ function formatDuration2(start, end) {
8462
+ if (!start && !end) return null;
8463
+ if (start && end) return `${start} \u2192 ${end}`;
8464
+ return start ? `Start: ${start}` : `End: ${end}`;
8465
+ }
8466
+ var ReadOnlyDischargeMedicationOrders = ({ fieldDef, value }) => {
8467
+ const orders = React15.useMemo(() => isOrderArray(value) ? value : [], [value]);
8468
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
8469
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-sm font-medium text-gray-700", children: fieldDef.label }),
8470
+ orders.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-900 border border-gray-200 rounded-md p-3 bg-gray-50 min-h-[2.5rem]", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400 italic", children: "No medicines added" }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: orders.map((o) => {
8471
+ const duration = formatDuration2(o.start_date, o.end_date);
8472
+ const header = o.item_name ?? "Medicine";
8473
+ const sub = o.generic_name ? o.generic_name : null;
8474
+ const line1 = [o.dose && `Dose: ${o.dose}`, o.route && `Route: ${o.route}`].filter(Boolean).join(" \u2022 ");
8475
+ const line2 = [
8476
+ o.frequency && `Freq: ${o.frequency}`,
8477
+ o.meal_sleep_timing && `Meal/Sleep: ${o.meal_sleep_timing}`,
8478
+ o.admin_times?.length ? `Times: ${o.admin_times.join(", ")}` : null
8479
+ ].filter(Boolean).join(" \u2022 ");
8480
+ const line3 = [
8481
+ duration ? `Duration: ${duration}` : null,
8482
+ o.order_type ? `Type: ${o.order_type}` : null,
8483
+ o.status ? `Status: ${o.status}` : null
8484
+ ].filter(Boolean).join(" \u2022 ");
8485
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-md border border-gray-200 bg-gray-50 p-3", children: [
8486
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
8487
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-sm truncate text-gray-900", children: header }),
8488
+ sub && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500 mt-0.5 truncate", children: sub })
8489
+ ] }),
8490
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 space-y-1 text-xs text-gray-600", children: [
8491
+ line1 && /* @__PURE__ */ jsxRuntime.jsx("div", { children: line1 }),
8492
+ line2 && /* @__PURE__ */ jsxRuntime.jsx("div", { children: line2 }),
8493
+ line3 && /* @__PURE__ */ jsxRuntime.jsx("div", { children: line3 }),
8494
+ o.notes && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-gray-800/90", children: [
8495
+ "Notes: ",
8496
+ o.notes
8497
+ ] })
8498
+ ] })
8499
+ ] }, o.id);
8500
+ }) })
8501
+ ] });
8502
+ };
8244
8503
  var ReadOnlyFieldRenderer = ({ fieldDef, value }) => {
8245
8504
  if (!fieldDef) return null;
8246
8505
  switch (fieldDef.type) {
@@ -8273,6 +8532,8 @@ var ReadOnlyFieldRenderer = ({ fieldDef, value }) => {
8273
8532
  return /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyTable, { fieldDef, value });
8274
8533
  case "referral":
8275
8534
  return /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyReferral, { fieldDef, value });
8535
+ case "discharge_medication_orders":
8536
+ return /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyDischargeMedicationOrders, { fieldDef, value });
8276
8537
  case "static_text": {
8277
8538
  const def = fieldDef;
8278
8539
  const size = def.size ?? "regular";
@@ -8484,6 +8745,7 @@ exports.FormControls = FormControls;
8484
8745
  exports.FormProvider = FormProvider;
8485
8746
  exports.FormStore = FormStore;
8486
8747
  exports.ImageUploadWidget = ImageUploadWidget;
8748
+ exports.MAR_MEDICATION_ORDERS_META_KEY = MAR_MEDICATION_ORDERS_META_KEY;
8487
8749
  exports.MediaUploadWidget = MediaUploadWidget;
8488
8750
  exports.ReadOnlyForm = ReadOnlyForm;
8489
8751
  exports.ReadOnlyImageUpload = ReadOnlyImageUpload;
@@ -8496,6 +8758,7 @@ exports.SmartForm = SmartForm;
8496
8758
  exports.SmartTextareaWidget = SmartTextareaWidget;
8497
8759
  exports.createUploadHandler = createUploadHandler;
8498
8760
  exports.evaluateRules = evaluateRules;
8761
+ exports.fieldMetaRequestsMarMedicationOrdersButton = fieldMetaRequestsMarMedicationOrdersButton;
8499
8762
  exports.useField = useField;
8500
8763
  exports.useFieldHandlers = useFieldHandlers;
8501
8764
  exports.useForm = useForm;