formanitor 0.0.42 → 0.0.44
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 +1722 -276
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -9
- package/dist/index.d.ts +61 -9
- package/dist/index.mjs +1722 -277
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -102,12 +102,82 @@ function validateForm(values, fields) {
|
|
|
102
102
|
return errors;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
// src/core/suggestionTokens.ts
|
|
106
|
+
function getSuggestionTokensFromText(text) {
|
|
107
|
+
return text.split(/[,\n\r]+/).map((p) => p.trim()).filter(Boolean);
|
|
108
|
+
}
|
|
109
|
+
var DEFAULT_TOKEN_SEPARATOR = ", ";
|
|
110
|
+
function commaSplitLine(line) {
|
|
111
|
+
return line.split(",").map((p) => p.trim()).filter(Boolean);
|
|
112
|
+
}
|
|
113
|
+
function dedupeChipOrder(chips) {
|
|
114
|
+
const seen = /* @__PURE__ */ new Set();
|
|
115
|
+
const out = [];
|
|
116
|
+
for (const c of chips) {
|
|
117
|
+
if (seen.has(c)) continue;
|
|
118
|
+
seen.add(c);
|
|
119
|
+
out.push(c);
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
function parseProseAndChipTokens(text, allowed) {
|
|
124
|
+
const normalized = text.replace(/\r\n/g, "\n");
|
|
125
|
+
let lines = normalized.split("\n");
|
|
126
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
127
|
+
lines.pop();
|
|
128
|
+
}
|
|
129
|
+
if (lines.length === 0) return { prose: "", chips: [] };
|
|
130
|
+
let chipStart = lines.length;
|
|
131
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
132
|
+
const toks = commaSplitLine(lines[i]);
|
|
133
|
+
if (toks.length === 0) break;
|
|
134
|
+
if (!toks.every((t) => allowed.has(t))) break;
|
|
135
|
+
chipStart = i;
|
|
136
|
+
}
|
|
137
|
+
if (chipStart < lines.length) {
|
|
138
|
+
const chipLines = lines.slice(chipStart);
|
|
139
|
+
const prose = lines.slice(0, chipStart).join("\n");
|
|
140
|
+
const chips = dedupeChipOrder(chipLines.flatMap(commaSplitLine));
|
|
141
|
+
return { prose, chips };
|
|
142
|
+
}
|
|
143
|
+
const parts = getSuggestionTokensFromText(normalized);
|
|
144
|
+
const chipsFallback = dedupeChipOrder(parts.filter((t) => allowed.has(t)));
|
|
145
|
+
const proseParts = parts.filter((t) => !allowed.has(t));
|
|
146
|
+
return { prose: proseParts.join(DEFAULT_TOKEN_SEPARATOR), chips: chipsFallback };
|
|
147
|
+
}
|
|
148
|
+
function composeProseAndChips(prose, chips, separator = DEFAULT_TOKEN_SEPARATOR) {
|
|
149
|
+
const chipStr = dedupeChipOrder(chips).join(separator);
|
|
150
|
+
const p = prose.replace(/\s+$/, "");
|
|
151
|
+
if (p && chipStr) return `${p}
|
|
152
|
+
${chipStr}`;
|
|
153
|
+
if (chipStr) return chipStr;
|
|
154
|
+
return p;
|
|
155
|
+
}
|
|
156
|
+
function toggleSuggestionTokenInText(current, token, separator = DEFAULT_TOKEN_SEPARATOR, allowedChipValues) {
|
|
157
|
+
const t = String(token).trim();
|
|
158
|
+
if (!t) return current;
|
|
159
|
+
if (!allowedChipValues || allowedChipValues.size === 0) {
|
|
160
|
+
const parts = getSuggestionTokensFromText(current);
|
|
161
|
+
const i = parts.findIndex((p) => p === t);
|
|
162
|
+
if (i >= 0) parts.splice(i, 1);
|
|
163
|
+
else parts.push(t);
|
|
164
|
+
return parts.join(separator);
|
|
165
|
+
}
|
|
166
|
+
if (!allowedChipValues.has(t)) return current;
|
|
167
|
+
let { prose, chips } = parseProseAndChipTokens(current, allowedChipValues);
|
|
168
|
+
const idx = chips.indexOf(t);
|
|
169
|
+
if (idx >= 0) chips.splice(idx, 1);
|
|
170
|
+
else chips.push(t);
|
|
171
|
+
chips = dedupeChipOrder(chips);
|
|
172
|
+
return composeProseAndChips(prose, chips, separator);
|
|
173
|
+
}
|
|
174
|
+
|
|
105
175
|
// src/core/rules.ts
|
|
106
176
|
function tokenListContainsString(text, needle) {
|
|
107
177
|
if (typeof text !== "string") return false;
|
|
108
178
|
const want = String(needle).trim();
|
|
109
179
|
if (!want) return false;
|
|
110
|
-
const parts = text
|
|
180
|
+
const parts = getSuggestionTokensFromText(text);
|
|
111
181
|
return parts.includes(want);
|
|
112
182
|
}
|
|
113
183
|
function evaluateCondition(value, condition) {
|
|
@@ -318,7 +388,7 @@ function normalizeGeneralSurgerySmartHistory(raw) {
|
|
|
318
388
|
...painIn,
|
|
319
389
|
severity: Number.isFinite(sev) ? Math.min(10, Math.max(0, Math.round(sev))) : d.pain.severity
|
|
320
390
|
};
|
|
321
|
-
const
|
|
391
|
+
const str4 = (x) => typeof x === "string" ? x : "";
|
|
322
392
|
const strArr = (x) => Array.isArray(x) ? x.filter((i) => typeof i === "string") : [];
|
|
323
393
|
const swell = v.swelling && typeof v.swelling === "object" ? v.swelling : {};
|
|
324
394
|
const gi = v.gi && typeof v.gi === "object" ? v.gi : {};
|
|
@@ -328,44 +398,44 @@ function normalizeGeneralSurgerySmartHistory(raw) {
|
|
|
328
398
|
return {
|
|
329
399
|
pain,
|
|
330
400
|
swelling: {
|
|
331
|
-
duration:
|
|
332
|
-
sizeProgression:
|
|
333
|
-
painfulPainless:
|
|
401
|
+
duration: str4(swell.duration),
|
|
402
|
+
sizeProgression: str4(swell.sizeProgression),
|
|
403
|
+
painfulPainless: str4(swell.painfulPainless),
|
|
334
404
|
checks: strArr(swell.checks)
|
|
335
405
|
},
|
|
336
406
|
gi: {
|
|
337
|
-
appetite:
|
|
338
|
-
weightLoss:
|
|
339
|
-
bowelHabits:
|
|
407
|
+
appetite: str4(gi.appetite),
|
|
408
|
+
weightLoss: str4(gi.weightLoss),
|
|
409
|
+
bowelHabits: str4(gi.bowelHabits),
|
|
340
410
|
checks: strArr(gi.checks)
|
|
341
411
|
},
|
|
342
412
|
breast: {
|
|
343
|
-
lumpDuration:
|
|
344
|
-
nippleDischarge:
|
|
413
|
+
lumpDuration: str4(breast.lumpDuration),
|
|
414
|
+
nippleDischarge: str4(breast.nippleDischarge),
|
|
345
415
|
checks: strArr(breast.checks)
|
|
346
416
|
},
|
|
347
417
|
ano: { checks: strArr(ano.checks) },
|
|
348
418
|
thyroid: {
|
|
349
|
-
swellingDuration:
|
|
419
|
+
swellingDuration: str4(thy.swellingDuration),
|
|
350
420
|
checks: strArr(thy.checks)
|
|
351
421
|
}
|
|
352
422
|
};
|
|
353
423
|
}
|
|
354
424
|
function normalizeBreastExamSide(partial) {
|
|
355
425
|
const e = defaultBreastExamSide();
|
|
356
|
-
const
|
|
357
|
-
const
|
|
426
|
+
const str4 = (x) => typeof x === "string" ? x : "";
|
|
427
|
+
const bool2 = (x) => x === true;
|
|
358
428
|
if (!partial || typeof partial !== "object") return e;
|
|
359
429
|
const p = partial;
|
|
360
430
|
return {
|
|
361
|
-
size:
|
|
362
|
-
quadrant:
|
|
363
|
-
tenderness:
|
|
364
|
-
fixity:
|
|
365
|
-
skinInvolvement:
|
|
366
|
-
consistency:
|
|
367
|
-
axillaryNodes:
|
|
368
|
-
nippleAreolarComplex:
|
|
431
|
+
size: str4(p.size),
|
|
432
|
+
quadrant: str4(p.quadrant),
|
|
433
|
+
tenderness: str4(p.tenderness),
|
|
434
|
+
fixity: str4(p.fixity),
|
|
435
|
+
skinInvolvement: str4(p.skinInvolvement),
|
|
436
|
+
consistency: str4(p.consistency),
|
|
437
|
+
axillaryNodes: bool2(p.axillaryNodes),
|
|
438
|
+
nippleAreolarComplex: bool2(p.nippleAreolarComplex)
|
|
369
439
|
};
|
|
370
440
|
}
|
|
371
441
|
function normalizeBreastExaminationBlock(raw) {
|
|
@@ -389,7 +459,7 @@ function normalizeGeneralSurgeryExamination(raw) {
|
|
|
389
459
|
const th = v.thyroid && typeof v.thyroid === "object" ? v.thyroid : {};
|
|
390
460
|
const ar = v.anorectal && typeof v.anorectal === "object" ? v.anorectal : {};
|
|
391
461
|
const lm = v.limb && typeof v.limb === "object" ? v.limb : {};
|
|
392
|
-
const
|
|
462
|
+
const bool2 = (x) => x === true;
|
|
393
463
|
return {
|
|
394
464
|
general: strArr(v.general),
|
|
395
465
|
regions: strArr(v.regions),
|
|
@@ -402,27 +472,27 @@ function normalizeGeneralSurgeryExamination(raw) {
|
|
|
402
472
|
},
|
|
403
473
|
hernia: {
|
|
404
474
|
site: typeof hb.site === "string" ? hb.site : "",
|
|
405
|
-
reducible:
|
|
406
|
-
coughImpulse:
|
|
407
|
-
tenderness:
|
|
475
|
+
reducible: bool2(hb.reducible),
|
|
476
|
+
coughImpulse: bool2(hb.coughImpulse),
|
|
477
|
+
tenderness: bool2(hb.tenderness)
|
|
408
478
|
},
|
|
409
479
|
breast: normalizeBreastExaminationBlock(br),
|
|
410
480
|
thyroid: {
|
|
411
481
|
size: typeof th.size === "string" ? th.size : "",
|
|
412
|
-
mobileDeglutition:
|
|
413
|
-
nodules:
|
|
414
|
-
cervicalNodes:
|
|
482
|
+
mobileDeglutition: bool2(th.mobileDeglutition),
|
|
483
|
+
nodules: bool2(th.nodules),
|
|
484
|
+
cervicalNodes: bool2(th.cervicalNodes)
|
|
415
485
|
},
|
|
416
486
|
anorectal: {
|
|
417
487
|
inspection: typeof ar.inspection === "string" ? ar.inspection : "",
|
|
418
|
-
dreDone:
|
|
419
|
-
proctoscopyDone:
|
|
488
|
+
dreDone: bool2(ar.dreDone),
|
|
489
|
+
proctoscopyDone: bool2(ar.proctoscopyDone),
|
|
420
490
|
notes: typeof ar.notes === "string" ? ar.notes : ""
|
|
421
491
|
},
|
|
422
492
|
limb: {
|
|
423
|
-
dilatedVeins:
|
|
424
|
-
ulcer:
|
|
425
|
-
oedema:
|
|
493
|
+
dilatedVeins: bool2(lm.dilatedVeins),
|
|
494
|
+
ulcer: bool2(lm.ulcer),
|
|
495
|
+
oedema: bool2(lm.oedema)
|
|
426
496
|
}
|
|
427
497
|
};
|
|
428
498
|
}
|
|
@@ -698,6 +768,375 @@ function formatGeneralSurgeryExaminationToNarrative(v) {
|
|
|
698
768
|
return blocks.join("\n\n");
|
|
699
769
|
}
|
|
700
770
|
|
|
771
|
+
// src/core/urologyPathway.ts
|
|
772
|
+
function defaultUrologySmartHistory() {
|
|
773
|
+
return {
|
|
774
|
+
socrates: {
|
|
775
|
+
site: "",
|
|
776
|
+
onset: "",
|
|
777
|
+
character: "",
|
|
778
|
+
radiation: "",
|
|
779
|
+
associated: "",
|
|
780
|
+
timing: "",
|
|
781
|
+
exacerbating: "",
|
|
782
|
+
severity: 0
|
|
783
|
+
},
|
|
784
|
+
ipss: {
|
|
785
|
+
incomplete: 0,
|
|
786
|
+
frequency: 0,
|
|
787
|
+
intermittency: 0,
|
|
788
|
+
urgency: 0,
|
|
789
|
+
weakStream: 0,
|
|
790
|
+
straining: 0,
|
|
791
|
+
nocturia: 0
|
|
792
|
+
},
|
|
793
|
+
ipssQoL: 0,
|
|
794
|
+
haematuria: {
|
|
795
|
+
present: false,
|
|
796
|
+
timing: "none",
|
|
797
|
+
painful: false,
|
|
798
|
+
clots: false
|
|
799
|
+
},
|
|
800
|
+
sexual: {
|
|
801
|
+
erectile: "none",
|
|
802
|
+
libido: "none",
|
|
803
|
+
ejaculation: "none",
|
|
804
|
+
infertilityMonths: 0
|
|
805
|
+
},
|
|
806
|
+
pastNotes: ""
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
function defaultUrologyExamination() {
|
|
810
|
+
return {
|
|
811
|
+
regions: [],
|
|
812
|
+
general: {
|
|
813
|
+
pallor: false,
|
|
814
|
+
oedema: false,
|
|
815
|
+
icterus: false,
|
|
816
|
+
lymphadenopathy: false
|
|
817
|
+
},
|
|
818
|
+
abdomen: {
|
|
819
|
+
distension: false,
|
|
820
|
+
scars: false,
|
|
821
|
+
mass: false,
|
|
822
|
+
kidneyPalpable: false,
|
|
823
|
+
bladderPalpable: false,
|
|
824
|
+
renalAngleTender: false,
|
|
825
|
+
bladderDull: false
|
|
826
|
+
},
|
|
827
|
+
gu: {
|
|
828
|
+
meatusAbnormal: false,
|
|
829
|
+
phimosis: false,
|
|
830
|
+
lesions: false,
|
|
831
|
+
scrotalSwelling: false,
|
|
832
|
+
transilluminant: false,
|
|
833
|
+
tenderTestis: false,
|
|
834
|
+
hydrocele: false,
|
|
835
|
+
varicocele: false,
|
|
836
|
+
suspiciousMass: false
|
|
837
|
+
},
|
|
838
|
+
dre: {
|
|
839
|
+
done: false,
|
|
840
|
+
sizeGrams: 0,
|
|
841
|
+
consistency: "",
|
|
842
|
+
medianSulcus: "",
|
|
843
|
+
tenderness: false
|
|
844
|
+
},
|
|
845
|
+
examinationNotes: ""
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
function defaultUrologyPathway() {
|
|
849
|
+
return {
|
|
850
|
+
stoneSizeMm: 0,
|
|
851
|
+
hydronephrosis: false,
|
|
852
|
+
prostateVolumeCc: 0,
|
|
853
|
+
psaNgMl: 0,
|
|
854
|
+
uroflowQmaxMlSec: 0,
|
|
855
|
+
risk: {
|
|
856
|
+
ckd: false,
|
|
857
|
+
diabetes: false,
|
|
858
|
+
anticoagulants: false,
|
|
859
|
+
activeInfection: false
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
function ipssItem(v) {
|
|
864
|
+
const n = Number(v);
|
|
865
|
+
if (!Number.isFinite(n)) return 0;
|
|
866
|
+
return Math.min(5, Math.max(0, Math.round(n)));
|
|
867
|
+
}
|
|
868
|
+
function urologyIpssSevenTotal(ipss) {
|
|
869
|
+
return ipss.incomplete + ipss.frequency + ipss.intermittency + ipss.urgency + ipss.weakStream + ipss.straining + ipss.nocturia;
|
|
870
|
+
}
|
|
871
|
+
function urologyIpssBand(total) {
|
|
872
|
+
if (total <= 7) return "Mild";
|
|
873
|
+
if (total <= 19) return "Moderate";
|
|
874
|
+
return "Severe";
|
|
875
|
+
}
|
|
876
|
+
function hopcMeaningfulStr(x) {
|
|
877
|
+
if (typeof x !== "string") return "";
|
|
878
|
+
const t = x.trim();
|
|
879
|
+
if (!t || t.toLowerCase() === "none") return "";
|
|
880
|
+
return t;
|
|
881
|
+
}
|
|
882
|
+
function hopcNonEmpty(lines) {
|
|
883
|
+
return lines.map((s) => (s ?? "").trim()).filter(Boolean);
|
|
884
|
+
}
|
|
885
|
+
function formatUrologySmartHistoryToSymptomsNarrative(safe, flags) {
|
|
886
|
+
const { showPain, showLUTS, showHaem, showSex } = flags;
|
|
887
|
+
const parts = [];
|
|
888
|
+
if (showPain) {
|
|
889
|
+
const s = safe.socrates;
|
|
890
|
+
const painLines = hopcNonEmpty([
|
|
891
|
+
s.site ? `Site: ${s.site}` : "",
|
|
892
|
+
s.onset ? `Onset: ${s.onset}` : "",
|
|
893
|
+
s.character ? `Character: ${s.character}` : "",
|
|
894
|
+
s.radiation ? `Radiation: ${s.radiation}` : "",
|
|
895
|
+
s.associated ? `Associated: ${s.associated}` : "",
|
|
896
|
+
s.timing ? `Timing: ${s.timing}` : "",
|
|
897
|
+
s.exacerbating ? `Exacerbating/relieving: ${s.exacerbating}` : "",
|
|
898
|
+
`Severity: ${s.severity}/10`
|
|
899
|
+
]);
|
|
900
|
+
const hasNonDefaultPain = painLines.length > 1 || painLines.length === 1 && painLines[0] !== "Severity: 0/10";
|
|
901
|
+
if (hasNonDefaultPain) parts.push(`SOCRATES:
|
|
902
|
+
${painLines.join("\n")}`);
|
|
903
|
+
}
|
|
904
|
+
if (showLUTS) {
|
|
905
|
+
const ip = safe.ipss;
|
|
906
|
+
const any = ip.incomplete || ip.frequency || ip.intermittency || ip.urgency || ip.weakStream || ip.straining || ip.nocturia || safe.ipssQoL;
|
|
907
|
+
if (any) {
|
|
908
|
+
const ipssTotal = urologyIpssSevenTotal(safe.ipss);
|
|
909
|
+
const ipssBandLabel = urologyIpssBand(ipssTotal);
|
|
910
|
+
parts.push(`IPSS: ${ipssTotal}/35 (${ipssBandLabel}) \xB7 QoL ${safe.ipssQoL}/6`);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
if (showHaem) {
|
|
914
|
+
const h = safe.haematuria;
|
|
915
|
+
const timing = hopcMeaningfulStr(h.timing);
|
|
916
|
+
const haemLines = hopcNonEmpty([
|
|
917
|
+
h.present ? "Haematuria present" : "",
|
|
918
|
+
timing ? `Timing: ${timing}` : "",
|
|
919
|
+
h.painful ? "Painful" : "",
|
|
920
|
+
h.clots ? "Clots" : ""
|
|
921
|
+
]);
|
|
922
|
+
if (haemLines.length) parts.push(`Haematuria:
|
|
923
|
+
${haemLines.join("\n")}`);
|
|
924
|
+
}
|
|
925
|
+
if (showSex) {
|
|
926
|
+
const sx = safe.sexual;
|
|
927
|
+
const infertility = sx.infertilityMonths;
|
|
928
|
+
const sexLines = hopcNonEmpty([
|
|
929
|
+
hopcMeaningfulStr(sx.erectile) ? `Erectile function: ${hopcMeaningfulStr(sx.erectile)}` : "",
|
|
930
|
+
hopcMeaningfulStr(sx.libido) ? `Libido: ${hopcMeaningfulStr(sx.libido)}` : "",
|
|
931
|
+
hopcMeaningfulStr(sx.ejaculation) ? `Ejaculation: ${hopcMeaningfulStr(sx.ejaculation)}` : "",
|
|
932
|
+
Number.isFinite(infertility) && infertility > 0 ? `Infertility: ${infertility} months` : ""
|
|
933
|
+
]);
|
|
934
|
+
if (sexLines.length) parts.push(`Sexual/reproductive:
|
|
935
|
+
${sexLines.join("\n")}`);
|
|
936
|
+
}
|
|
937
|
+
return parts.join("\n\n").trim();
|
|
938
|
+
}
|
|
939
|
+
function formatUrologyExaminationToClinicalExamination(v) {
|
|
940
|
+
const sections = [];
|
|
941
|
+
const generalLabels = {
|
|
942
|
+
pallor: "Pallor (chronic dz)",
|
|
943
|
+
oedema: "Oedema (renal)",
|
|
944
|
+
icterus: "Icterus",
|
|
945
|
+
lymphadenopathy: "Lymphadenopathy"
|
|
946
|
+
};
|
|
947
|
+
const general = Object.keys(generalLabels).filter((k) => v.general[k]).map((k) => generalLabels[k]);
|
|
948
|
+
if (general.length) sections.push(`General: ${general.join(", ")}`);
|
|
949
|
+
const abdLabels = {
|
|
950
|
+
distension: "Distension",
|
|
951
|
+
scars: "Surgical scars",
|
|
952
|
+
mass: "Visible mass",
|
|
953
|
+
kidneyPalpable: "Kidney palpable",
|
|
954
|
+
bladderPalpable: "Bladder palpable",
|
|
955
|
+
renalAngleTender: "Renal angle tender",
|
|
956
|
+
bladderDull: "Bladder dullness (retention)"
|
|
957
|
+
};
|
|
958
|
+
const abd = Object.keys(abdLabels).filter((k) => v.abdomen[k]).map((k) => abdLabels[k]);
|
|
959
|
+
if (abd.length) sections.push(`Abdomen: ${abd.join(", ")}`);
|
|
960
|
+
const guLabels = {
|
|
961
|
+
meatusAbnormal: "Meatus abnormal (hypospadias?)",
|
|
962
|
+
phimosis: "Phimosis / paraphimosis",
|
|
963
|
+
lesions: "Penile lesions / ulcers",
|
|
964
|
+
scrotalSwelling: "Scrotal swelling",
|
|
965
|
+
transilluminant: "Transilluminant",
|
|
966
|
+
tenderTestis: "Tender testis",
|
|
967
|
+
hydrocele: "Hydrocele",
|
|
968
|
+
varicocele: "Varicocele",
|
|
969
|
+
suspiciousMass: "Suspicious mass"
|
|
970
|
+
};
|
|
971
|
+
const gu = Object.keys(guLabels).filter((k) => v.gu[k]).map((k) => guLabels[k]);
|
|
972
|
+
if (gu.length) sections.push(`GU: ${gu.join(", ")}`);
|
|
973
|
+
const dreBits = hopcNonEmpty([
|
|
974
|
+
v.dre.done ? "Performed" : "",
|
|
975
|
+
v.dre.sizeGrams > 0 ? `Size ~${v.dre.sizeGrams} g` : "",
|
|
976
|
+
v.dre.consistency ? `Consistency: ${v.dre.consistency}` : "",
|
|
977
|
+
v.dre.medianSulcus ? `Median sulcus: ${v.dre.medianSulcus}` : "",
|
|
978
|
+
v.dre.tenderness ? "Tenderness" : ""
|
|
979
|
+
]);
|
|
980
|
+
if (dreBits.length) sections.push(`DRE: ${dreBits.join(", ")}`);
|
|
981
|
+
return sections.join("\n");
|
|
982
|
+
}
|
|
983
|
+
function str(x) {
|
|
984
|
+
return typeof x === "string" ? x : "";
|
|
985
|
+
}
|
|
986
|
+
function bool(x) {
|
|
987
|
+
return x === true;
|
|
988
|
+
}
|
|
989
|
+
function nonNegInt(x) {
|
|
990
|
+
const n = Number(x);
|
|
991
|
+
if (!Number.isFinite(n)) return 0;
|
|
992
|
+
return Math.max(0, Math.round(n));
|
|
993
|
+
}
|
|
994
|
+
function normalizeUrologySmartHistory(raw) {
|
|
995
|
+
const base = defaultUrologySmartHistory();
|
|
996
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return base;
|
|
997
|
+
const r = raw;
|
|
998
|
+
const soc = r.socrates && typeof r.socrates === "object" && !Array.isArray(r.socrates) ? r.socrates : {};
|
|
999
|
+
const socObj = soc;
|
|
1000
|
+
const sev = Number(socObj.severity);
|
|
1001
|
+
const socrates = {
|
|
1002
|
+
site: str(socObj.site),
|
|
1003
|
+
onset: str(socObj.onset),
|
|
1004
|
+
character: str(socObj.character),
|
|
1005
|
+
radiation: str(socObj.radiation),
|
|
1006
|
+
associated: str(socObj.associated),
|
|
1007
|
+
timing: str(socObj.timing),
|
|
1008
|
+
exacerbating: str(socObj.exacerbating),
|
|
1009
|
+
severity: Number.isFinite(sev) ? Math.min(10, Math.max(0, Math.round(sev))) : 0
|
|
1010
|
+
};
|
|
1011
|
+
const ipRaw = r.ipss && typeof r.ipss === "object" && !Array.isArray(r.ipss) ? r.ipss : {};
|
|
1012
|
+
const ip = ipRaw;
|
|
1013
|
+
const ipss = {
|
|
1014
|
+
incomplete: ipssItem(ip.incomplete),
|
|
1015
|
+
frequency: ipssItem(ip.frequency),
|
|
1016
|
+
intermittency: ipssItem(ip.intermittency),
|
|
1017
|
+
urgency: ipssItem(ip.urgency),
|
|
1018
|
+
weakStream: ipssItem(ip.weakStream ?? ip.weak_stream),
|
|
1019
|
+
straining: ipssItem(ip.straining),
|
|
1020
|
+
nocturia: ipssItem(ip.nocturia)
|
|
1021
|
+
};
|
|
1022
|
+
const qolRaw = Number(r.ipssQoL);
|
|
1023
|
+
const ipssQoL = Number.isFinite(qolRaw) ? Math.min(6, Math.max(0, Math.round(qolRaw))) : 0;
|
|
1024
|
+
const haRaw = r.haematuria && typeof r.haematuria === "object" && !Array.isArray(r.haematuria) ? r.haematuria : {};
|
|
1025
|
+
const ha = haRaw;
|
|
1026
|
+
const haematuria = {
|
|
1027
|
+
present: bool(ha.present),
|
|
1028
|
+
timing: str(ha.timing) || "none",
|
|
1029
|
+
painful: bool(ha.painful),
|
|
1030
|
+
clots: bool(ha.clots)
|
|
1031
|
+
};
|
|
1032
|
+
const sxRaw = r.sexual && typeof r.sexual === "object" && !Array.isArray(r.sexual) ? r.sexual : {};
|
|
1033
|
+
const sx = sxRaw;
|
|
1034
|
+
const infertilityMonths = nonNegInt(sx.infertilityMonths);
|
|
1035
|
+
const sexual = {
|
|
1036
|
+
erectile: str(sx.erectile) || "none",
|
|
1037
|
+
libido: str(sx.libido) || "none",
|
|
1038
|
+
ejaculation: str(sx.ejaculation) || "none",
|
|
1039
|
+
infertilityMonths
|
|
1040
|
+
};
|
|
1041
|
+
const pastNotes = str(r.pastNotes);
|
|
1042
|
+
return {
|
|
1043
|
+
socrates,
|
|
1044
|
+
ipss,
|
|
1045
|
+
ipssQoL,
|
|
1046
|
+
haematuria,
|
|
1047
|
+
sexual,
|
|
1048
|
+
pastNotes
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
function dreSelectStr(x) {
|
|
1052
|
+
const t = str(x).trim();
|
|
1053
|
+
if (!t || t.toLowerCase() === "none") return "";
|
|
1054
|
+
return t;
|
|
1055
|
+
}
|
|
1056
|
+
function normalizeUrologyExamination(raw) {
|
|
1057
|
+
const base = defaultUrologyExamination();
|
|
1058
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return base;
|
|
1059
|
+
const r = raw;
|
|
1060
|
+
const regions = Array.isArray(r.regions) ? r.regions.filter((x) => typeof x === "string") : [];
|
|
1061
|
+
const g = r.general && typeof r.general === "object" && !Array.isArray(r.general) ? r.general : {};
|
|
1062
|
+
const gObj = g;
|
|
1063
|
+
const general = {
|
|
1064
|
+
pallor: bool(gObj.pallor),
|
|
1065
|
+
oedema: bool(gObj.oedema),
|
|
1066
|
+
icterus: bool(gObj.icterus),
|
|
1067
|
+
lymphadenopathy: bool(gObj.lymphadenopathy)
|
|
1068
|
+
};
|
|
1069
|
+
const ab = r.abdomen && typeof r.abdomen === "object" && !Array.isArray(r.abdomen) ? r.abdomen : {};
|
|
1070
|
+
const abObj = ab;
|
|
1071
|
+
const abdomen = {
|
|
1072
|
+
distension: bool(abObj.distension),
|
|
1073
|
+
scars: bool(abObj.scars),
|
|
1074
|
+
mass: bool(abObj.mass),
|
|
1075
|
+
kidneyPalpable: bool(abObj.kidneyPalpable),
|
|
1076
|
+
bladderPalpable: bool(abObj.bladderPalpable),
|
|
1077
|
+
renalAngleTender: bool(abObj.renalAngleTender),
|
|
1078
|
+
bladderDull: bool(abObj.bladderDull)
|
|
1079
|
+
};
|
|
1080
|
+
const guRaw = r.gu && typeof r.gu === "object" && !Array.isArray(r.gu) ? r.gu : {};
|
|
1081
|
+
const guObj = guRaw;
|
|
1082
|
+
const gu = {
|
|
1083
|
+
meatusAbnormal: bool(guObj.meatusAbnormal),
|
|
1084
|
+
phimosis: bool(guObj.phimosis),
|
|
1085
|
+
lesions: bool(guObj.lesions),
|
|
1086
|
+
scrotalSwelling: bool(guObj.scrotalSwelling),
|
|
1087
|
+
transilluminant: bool(guObj.transilluminant),
|
|
1088
|
+
tenderTestis: bool(guObj.tenderTestis),
|
|
1089
|
+
hydrocele: bool(guObj.hydrocele),
|
|
1090
|
+
varicocele: bool(guObj.varicocele),
|
|
1091
|
+
suspiciousMass: bool(guObj.suspiciousMass)
|
|
1092
|
+
};
|
|
1093
|
+
const dr = r.dre && typeof r.dre === "object" && !Array.isArray(r.dre) ? r.dre : {};
|
|
1094
|
+
const drObj = dr;
|
|
1095
|
+
const sizeG = Number(drObj.sizeGrams);
|
|
1096
|
+
const dre = {
|
|
1097
|
+
done: bool(drObj.done),
|
|
1098
|
+
sizeGrams: Number.isFinite(sizeG) && sizeG >= 0 ? Math.round(sizeG) : 0,
|
|
1099
|
+
consistency: dreSelectStr(drObj.consistency),
|
|
1100
|
+
medianSulcus: dreSelectStr(drObj.medianSulcus),
|
|
1101
|
+
tenderness: bool(drObj.tenderness)
|
|
1102
|
+
};
|
|
1103
|
+
const examinationNotes = str(r.examinationNotes);
|
|
1104
|
+
return {
|
|
1105
|
+
regions,
|
|
1106
|
+
general,
|
|
1107
|
+
abdomen,
|
|
1108
|
+
gu,
|
|
1109
|
+
dre,
|
|
1110
|
+
examinationNotes
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
function normalizeUrologyPathway(raw) {
|
|
1114
|
+
const base = defaultUrologyPathway();
|
|
1115
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return base;
|
|
1116
|
+
const r = raw;
|
|
1117
|
+
const stoneSizeMm = nonNegInt(r.stoneSizeMm);
|
|
1118
|
+
const hydronephrosis = bool(r.hydronephrosis);
|
|
1119
|
+
const prostateVolumeCc = Math.max(0, Number(r.prostateVolumeCc) || 0);
|
|
1120
|
+
const psaNgMl = Math.max(0, Number(r.psaNgMl) || 0);
|
|
1121
|
+
const uroflowQmaxMlSec = Math.max(0, Number(r.uroflowQmaxMlSec) || 0);
|
|
1122
|
+
const riskRaw = r.risk && typeof r.risk === "object" && !Array.isArray(r.risk) ? r.risk : {};
|
|
1123
|
+
const rk = riskRaw;
|
|
1124
|
+
const risk = {
|
|
1125
|
+
ckd: bool(rk.ckd),
|
|
1126
|
+
diabetes: bool(rk.diabetes),
|
|
1127
|
+
anticoagulants: bool(rk.anticoagulants),
|
|
1128
|
+
activeInfection: bool(rk.activeInfection)
|
|
1129
|
+
};
|
|
1130
|
+
return {
|
|
1131
|
+
stoneSizeMm,
|
|
1132
|
+
hydronephrosis,
|
|
1133
|
+
prostateVolumeCc,
|
|
1134
|
+
psaNgMl,
|
|
1135
|
+
uroflowQmaxMlSec,
|
|
1136
|
+
risk
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
|
|
701
1140
|
// src/core/painScaleFlags.ts
|
|
702
1141
|
function normalizePainScaleFlags(raw, bounds) {
|
|
703
1142
|
const min = bounds?.min ?? 0;
|
|
@@ -810,6 +1249,12 @@ var FormStore = class {
|
|
|
810
1249
|
values[field.id] = field.defaultValue ?? defaultGeneralSurgeryExamination();
|
|
811
1250
|
} else if (field.type === "general_surgery_grading") {
|
|
812
1251
|
values[field.id] = field.defaultValue ?? defaultGeneralSurgeryGrading();
|
|
1252
|
+
} else if (field.type === "urology_smart_history") {
|
|
1253
|
+
values[field.id] = field.defaultValue ?? defaultUrologySmartHistory();
|
|
1254
|
+
} else if (field.type === "urology_examination") {
|
|
1255
|
+
values[field.id] = field.defaultValue ?? defaultUrologyExamination();
|
|
1256
|
+
} else if (field.type === "urology_pathway") {
|
|
1257
|
+
values[field.id] = field.defaultValue ?? defaultUrologyPathway();
|
|
813
1258
|
} else if (field.type === "pain_scale_flags") {
|
|
814
1259
|
const f = field;
|
|
815
1260
|
values[field.id] = normalizePainScaleFlags(
|
|
@@ -8523,26 +8968,11 @@ var DischargeMedicationOrdersWidget = ({ fieldId }) => {
|
|
|
8523
8968
|
showError && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500", children: error })
|
|
8524
8969
|
] });
|
|
8525
8970
|
};
|
|
8526
|
-
var
|
|
8971
|
+
var DEFAULT_TOKEN_SEPARATOR2 = ", ";
|
|
8527
8972
|
function getAppendSeparator(def) {
|
|
8528
|
-
const raw = def.meta && typeof def.meta.appendSeparator === "string" ? def.meta.appendSeparator :
|
|
8973
|
+
const raw = def.meta && typeof def.meta.appendSeparator === "string" ? def.meta.appendSeparator : DEFAULT_TOKEN_SEPARATOR2;
|
|
8529
8974
|
return raw;
|
|
8530
8975
|
}
|
|
8531
|
-
function getSuggestionTokensFromText(text) {
|
|
8532
|
-
return text.split(",").map((p) => p.trim()).filter(Boolean);
|
|
8533
|
-
}
|
|
8534
|
-
function toggleSuggestionTokenInText(current, token, separator = DEFAULT_TOKEN_SEPARATOR) {
|
|
8535
|
-
const t = String(token).trim();
|
|
8536
|
-
if (!t) return current;
|
|
8537
|
-
const parts = getSuggestionTokensFromText(current);
|
|
8538
|
-
const i = parts.findIndex((p) => p === t);
|
|
8539
|
-
if (i >= 0) {
|
|
8540
|
-
parts.splice(i, 1);
|
|
8541
|
-
} else {
|
|
8542
|
-
parts.push(t);
|
|
8543
|
-
}
|
|
8544
|
-
return parts.join(separator);
|
|
8545
|
-
}
|
|
8546
8976
|
function isTokenSelected(text, optionValue) {
|
|
8547
8977
|
const t = String(optionValue).trim();
|
|
8548
8978
|
if (!t) return false;
|
|
@@ -8616,8 +9046,11 @@ var SuggestionTextareaWidget = ({ fieldId }) => {
|
|
|
8616
9046
|
setOptions([]);
|
|
8617
9047
|
}
|
|
8618
9048
|
}, [def]);
|
|
9049
|
+
const chipAllowedSet = useMemo(
|
|
9050
|
+
() => new Set(options.map((o) => String(o.value).trim()).filter(Boolean)),
|
|
9051
|
+
[options]
|
|
9052
|
+
);
|
|
8619
9053
|
useEffect(() => {
|
|
8620
|
-
if (!parallelChipFieldId || !def) return;
|
|
8621
9054
|
const parallelDef = store.getFieldDef(parallelChipFieldId);
|
|
8622
9055
|
if (!parallelDef || parallelDef.type !== "multiselect") return;
|
|
8623
9056
|
const next = deriveKnownSuggestionChipValues(textValue, options);
|
|
@@ -8643,7 +9076,7 @@ var SuggestionTextareaWidget = ({ fieldId }) => {
|
|
|
8643
9076
|
const labelEl = theme ? /* @__PURE__ */ jsx(Label, { className: labelClass, children: labelContent }) : /* @__PURE__ */ jsx(Label, { children: labelContent });
|
|
8644
9077
|
const onChipClick = (optValue) => {
|
|
8645
9078
|
if (disabled) return;
|
|
8646
|
-
setValue(toggleSuggestionTokenInText(textValue, String(optValue), appendSeparator));
|
|
9079
|
+
setValue(toggleSuggestionTokenInText(textValue, String(optValue), appendSeparator, chipAllowedSet));
|
|
8647
9080
|
};
|
|
8648
9081
|
const hasSuggestions = options.length > 0;
|
|
8649
9082
|
const hasEmbedded = embeddedFieldIds.length > 0;
|
|
@@ -9082,7 +9515,7 @@ var EMPTY = {
|
|
|
9082
9515
|
specularMicroscopyDone: false,
|
|
9083
9516
|
endothelialCount: ""
|
|
9084
9517
|
};
|
|
9085
|
-
function
|
|
9518
|
+
function str2(x) {
|
|
9086
9519
|
return typeof x === "string" ? x : "";
|
|
9087
9520
|
}
|
|
9088
9521
|
function parseValue2(raw) {
|
|
@@ -9090,24 +9523,24 @@ function parseValue2(raw) {
|
|
|
9090
9523
|
const o = raw;
|
|
9091
9524
|
const m = o.method;
|
|
9092
9525
|
const method = m === "ascan_immersion" || m === "ascan_contact" || m === "optical_biometry" ? m : "optical_biometry";
|
|
9093
|
-
const formula =
|
|
9526
|
+
const formula = str2(o.formula);
|
|
9094
9527
|
return {
|
|
9095
9528
|
...EMPTY,
|
|
9096
|
-
axialLengthOD:
|
|
9097
|
-
axialLengthOS:
|
|
9098
|
-
k1k2AxisOD:
|
|
9099
|
-
k1k2AxisOS:
|
|
9100
|
-
anteriorChamberDepthOD:
|
|
9101
|
-
anteriorChamberDepthOS:
|
|
9102
|
-
iolPowerOD:
|
|
9103
|
-
iolPowerOS:
|
|
9104
|
-
targetRefractionOD:
|
|
9105
|
-
targetRefractionOS:
|
|
9529
|
+
axialLengthOD: str2(o.axialLengthOD),
|
|
9530
|
+
axialLengthOS: str2(o.axialLengthOS),
|
|
9531
|
+
k1k2AxisOD: str2(o.k1k2AxisOD),
|
|
9532
|
+
k1k2AxisOS: str2(o.k1k2AxisOS),
|
|
9533
|
+
anteriorChamberDepthOD: str2(o.anteriorChamberDepthOD),
|
|
9534
|
+
anteriorChamberDepthOS: str2(o.anteriorChamberDepthOS),
|
|
9535
|
+
iolPowerOD: str2(o.iolPowerOD),
|
|
9536
|
+
iolPowerOS: str2(o.iolPowerOS),
|
|
9537
|
+
targetRefractionOD: str2(o.targetRefractionOD),
|
|
9538
|
+
targetRefractionOS: str2(o.targetRefractionOS),
|
|
9106
9539
|
formula: formula || "SRK/T",
|
|
9107
9540
|
method,
|
|
9108
9541
|
cornealTopoUploaded: o.cornealTopoUploaded === true,
|
|
9109
9542
|
specularMicroscopyDone: o.specularMicroscopyDone === true,
|
|
9110
|
-
endothelialCount:
|
|
9543
|
+
endothelialCount: str2(o.endothelialCount)
|
|
9111
9544
|
};
|
|
9112
9545
|
}
|
|
9113
9546
|
var paramCell = "emr-biometry-param text-[11px] font-semibold leading-snug text-foreground sm:text-xs py-2";
|
|
@@ -10578,172 +11011,1020 @@ var PainScaleFlagsWidget = ({ fieldId }) => {
|
|
|
10578
11011
|
showError && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500", children: error })
|
|
10579
11012
|
] });
|
|
10580
11013
|
};
|
|
10581
|
-
var
|
|
10582
|
-
|
|
10583
|
-
|
|
10584
|
-
|
|
10585
|
-
|
|
10586
|
-
|
|
11014
|
+
var CHIEF_CHIPS_FIELD3 = "chief_complaints_chips";
|
|
11015
|
+
var ACTIVE_SYNDROME_FIELD = "uro_active_syndrome";
|
|
11016
|
+
var CHIEF_TEXT_FIELD = "chief_complaints";
|
|
11017
|
+
var SYMPTOMS_FIELD = "symptoms";
|
|
11018
|
+
var DIAGNOSIS_FIELD = "diagnosis";
|
|
11019
|
+
var URO_DURATION_FIELD = "uro_chief_duration";
|
|
11020
|
+
var URO_ONSET_FIELD = "uro_chief_onset";
|
|
11021
|
+
var URO_PROGRESSION_FIELD = "uro_chief_progression";
|
|
11022
|
+
var SYNDROME_LABELS = {
|
|
11023
|
+
luts: "LUTS / BPH",
|
|
11024
|
+
stone: "Urolithiasis (stone)",
|
|
11025
|
+
haematuria: "Haematuria",
|
|
11026
|
+
scrotal: "Scrotal swelling",
|
|
11027
|
+
retention: "Acute retention",
|
|
11028
|
+
ed_infertility: "ED / Infertility",
|
|
11029
|
+
uti: "UTI / infection"
|
|
10587
11030
|
};
|
|
10588
|
-
function
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
case "number":
|
|
10592
|
-
return /* @__PURE__ */ jsx(TextWidget, { fieldId });
|
|
10593
|
-
case "slider":
|
|
10594
|
-
return /* @__PURE__ */ jsx(SliderWidget, { fieldId });
|
|
10595
|
-
case "textarea":
|
|
10596
|
-
if (fieldMetaRequestsMarMedicationOrdersButton(fieldDef.meta)) {
|
|
10597
|
-
return /* @__PURE__ */ jsx(MarMedicationOrdersWidget, { fieldId });
|
|
10598
|
-
}
|
|
10599
|
-
return /* @__PURE__ */ jsx(TextWidget, { fieldId });
|
|
10600
|
-
case "richtext":
|
|
10601
|
-
return /* @__PURE__ */ jsx(RichTextWidget, { fieldId });
|
|
10602
|
-
case "date":
|
|
10603
|
-
return /* @__PURE__ */ jsx(DateWidget, { fieldId });
|
|
10604
|
-
case "datetime":
|
|
10605
|
-
return /* @__PURE__ */ jsx(DateTimeWidget, { fieldId });
|
|
10606
|
-
case "select":
|
|
10607
|
-
return /* @__PURE__ */ jsx(SelectWidget, { fieldId });
|
|
10608
|
-
case "radio":
|
|
10609
|
-
return /* @__PURE__ */ jsx(RadioWidget, { fieldId });
|
|
10610
|
-
case "checkbox":
|
|
10611
|
-
return /* @__PURE__ */ jsx(CheckboxWidget, { fieldId });
|
|
10612
|
-
case "multiselect":
|
|
10613
|
-
return /* @__PURE__ */ jsx(MultiSelectWidget, { fieldId });
|
|
10614
|
-
case "repeatable":
|
|
10615
|
-
return /* @__PURE__ */ jsx(RepeatableWidget, { fieldId });
|
|
10616
|
-
case "image_upload":
|
|
10617
|
-
return /* @__PURE__ */ jsx(ImageUploadWidget, { fieldId });
|
|
10618
|
-
case "media_upload":
|
|
10619
|
-
return /* @__PURE__ */ jsx(MediaUploadWidget, { fieldId });
|
|
10620
|
-
case "signature":
|
|
10621
|
-
return /* @__PURE__ */ jsx(SignatureUploadWidget, { fieldId });
|
|
10622
|
-
case "editable_table":
|
|
10623
|
-
return /* @__PURE__ */ jsx(EditableTableWidget, { fieldId });
|
|
10624
|
-
case "medications":
|
|
10625
|
-
return /* @__PURE__ */ jsx(AddMedicationWidget, { fieldId });
|
|
10626
|
-
case "discharge_medication_orders":
|
|
10627
|
-
return /* @__PURE__ */ jsx(DischargeMedicationOrdersWidget, { fieldId });
|
|
10628
|
-
case "investigations":
|
|
10629
|
-
return /* @__PURE__ */ jsx(AddInvestigationWidget, { fieldId });
|
|
10630
|
-
case "procedures":
|
|
10631
|
-
return /* @__PURE__ */ jsx(AddProcedureWidget, { fieldId });
|
|
10632
|
-
case "differential_diagnosis":
|
|
10633
|
-
return /* @__PURE__ */ jsx(DifferentialDiagnosisWidget, { fieldId });
|
|
10634
|
-
case "vitals":
|
|
10635
|
-
return /* @__PURE__ */ jsx(VitalsWidget, { fieldId });
|
|
10636
|
-
case "hidden_vitals":
|
|
10637
|
-
return /* @__PURE__ */ jsx(HiddenVitalsWidget, { fieldId });
|
|
10638
|
-
case "referral":
|
|
10639
|
-
return /* @__PURE__ */ jsx(ReferralWidget, { fieldId });
|
|
10640
|
-
case "followup":
|
|
10641
|
-
return /* @__PURE__ */ jsx(FollowupWidget, { fieldId });
|
|
10642
|
-
case "smart_textarea":
|
|
10643
|
-
return /* @__PURE__ */ jsx(SmartTextareaWidget, { fieldId });
|
|
10644
|
-
case "diagnosis_textarea":
|
|
10645
|
-
return /* @__PURE__ */ jsx(DiagnosisTextareaWidget, { fieldId });
|
|
10646
|
-
case "obstetric_history":
|
|
10647
|
-
return /* @__PURE__ */ jsx(ObstetricHistoryWidget, { fieldId });
|
|
10648
|
-
case "eye_prescription":
|
|
10649
|
-
return /* @__PURE__ */ jsx(OphthalmologyWidget, { fieldId });
|
|
10650
|
-
case "ophthal_diagnosis":
|
|
10651
|
-
return /* @__PURE__ */ jsx(OphthalDiagnosisWidget, { fieldId });
|
|
10652
|
-
case "orthopedic_exam":
|
|
10653
|
-
return /* @__PURE__ */ jsx(OrthopedicExamWidget, { fieldId });
|
|
10654
|
-
case "general_surgery_smart_history":
|
|
10655
|
-
return /* @__PURE__ */ jsx(GsSmartHistoryWidget, { fieldId });
|
|
10656
|
-
case "general_surgery_examination":
|
|
10657
|
-
return /* @__PURE__ */ jsx(GsExaminationWidget, { fieldId });
|
|
10658
|
-
case "general_surgery_grading":
|
|
10659
|
-
return /* @__PURE__ */ jsx(GsGradingWidget, { fieldId });
|
|
10660
|
-
case "pain_scale_flags":
|
|
10661
|
-
return /* @__PURE__ */ jsx(PainScaleFlagsWidget, { fieldId });
|
|
10662
|
-
case "toggle": {
|
|
10663
|
-
const { value, setValue, setTouched, disabled } = useField(fieldId);
|
|
10664
|
-
const store = useFormStore();
|
|
10665
|
-
const excludes = fieldDef.excludes ?? [];
|
|
10666
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
10667
|
-
/* @__PURE__ */ jsx(
|
|
10668
|
-
Switch,
|
|
10669
|
-
{
|
|
10670
|
-
checked: value === true,
|
|
10671
|
-
disabled,
|
|
10672
|
-
onCheckedChange: (checked) => {
|
|
10673
|
-
setValue(checked);
|
|
10674
|
-
setTouched();
|
|
10675
|
-
if (checked) excludes.forEach((id) => store.setValue(id, false));
|
|
10676
|
-
}
|
|
10677
|
-
}
|
|
10678
|
-
),
|
|
10679
|
-
/* @__PURE__ */ jsx(Label, { className: "text-sm font-medium leading-none cursor-pointer select-none", children: fieldDef.label })
|
|
10680
|
-
] });
|
|
10681
|
-
}
|
|
10682
|
-
case "suggestion_textarea":
|
|
10683
|
-
case "complaint_chips":
|
|
10684
|
-
return /* @__PURE__ */ jsx(SuggestionTextareaWidget, { fieldId });
|
|
10685
|
-
case "lens_assessment":
|
|
10686
|
-
return /* @__PURE__ */ jsx(LensAssessmentWidget, { fieldId });
|
|
10687
|
-
case "functional_impairment_score":
|
|
10688
|
-
return /* @__PURE__ */ jsx(FunctionalImpairmentScoreWidget, { fieldId });
|
|
10689
|
-
case "biometry_iol_workup":
|
|
10690
|
-
return /* @__PURE__ */ jsx(BiometryIolWorkupWidget, { fieldId });
|
|
10691
|
-
case "surgical_risk_flags":
|
|
10692
|
-
return /* @__PURE__ */ jsx(SurgicalRiskFlagsWidget, { fieldId });
|
|
10693
|
-
case "static_text": {
|
|
10694
|
-
const def = fieldDef;
|
|
10695
|
-
const size = def.size ?? "regular";
|
|
10696
|
-
const labelClass = size === "large" ? "text-base" : "text-sm";
|
|
10697
|
-
const descClass = size === "large" ? "text-sm" : "text-xs";
|
|
10698
|
-
return /* @__PURE__ */ jsxs("div", { className: `${labelClass} text-muted-foreground`, children: [
|
|
10699
|
-
def.label,
|
|
10700
|
-
def.description && /* @__PURE__ */ jsx("p", { className: `mt-1 ${descClass} opacity-90`, children: def.description })
|
|
10701
|
-
] });
|
|
10702
|
-
}
|
|
10703
|
-
default:
|
|
10704
|
-
return /* @__PURE__ */ jsxs("div", { className: "text-red-500", children: [
|
|
10705
|
-
"Unknown field type: ",
|
|
10706
|
-
fieldDef.type
|
|
10707
|
-
] });
|
|
10708
|
-
}
|
|
11031
|
+
function chipList2(state) {
|
|
11032
|
+
const raw = state.values[CHIEF_CHIPS_FIELD3];
|
|
11033
|
+
return Array.isArray(raw) ? raw.filter((x) => typeof x === "string") : [];
|
|
10709
11034
|
}
|
|
10710
|
-
|
|
10711
|
-
|
|
10712
|
-
|
|
10713
|
-
|
|
10714
|
-
|
|
10715
|
-
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
|
|
10720
|
-
|
|
10721
|
-
|
|
10722
|
-
|
|
10723
|
-
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
|
|
10728
|
-
|
|
10729
|
-
),
|
|
10730
|
-
/* @__PURE__ */ jsx("div", { className: contentClassName, children: /* @__PURE__ */ jsx("div", { className: "p-4 pt-4 space-y-4 border-t-0", children }) })
|
|
10731
|
-
] });
|
|
11035
|
+
function str3(x) {
|
|
11036
|
+
return typeof x === "string" ? x : "";
|
|
11037
|
+
}
|
|
11038
|
+
function chiefOptionalFieldDisplay(x) {
|
|
11039
|
+
const s = str3(x).trim();
|
|
11040
|
+
if (!s || s.toLowerCase() === "none") return "";
|
|
11041
|
+
return s;
|
|
11042
|
+
}
|
|
11043
|
+
function nonEmptyLines(lines) {
|
|
11044
|
+
return lines.map((s) => (s ?? "").trim()).filter(Boolean);
|
|
11045
|
+
}
|
|
11046
|
+
var IPSS_LABELS = {
|
|
11047
|
+
incomplete: "Incomplete emptying",
|
|
11048
|
+
frequency: "Frequency (<2h)",
|
|
11049
|
+
intermittency: "Intermittency",
|
|
11050
|
+
urgency: "Urgency",
|
|
11051
|
+
weakStream: "Weak stream",
|
|
11052
|
+
straining: "Straining",
|
|
11053
|
+
nocturia: "Nocturia (\xD7/night)"
|
|
10732
11054
|
};
|
|
10733
|
-
var
|
|
10734
|
-
|
|
10735
|
-
|
|
11055
|
+
var SOCRATES_KEYS2 = [
|
|
11056
|
+
"site",
|
|
11057
|
+
"onset",
|
|
11058
|
+
"character",
|
|
11059
|
+
"radiation",
|
|
11060
|
+
"associated",
|
|
11061
|
+
"timing",
|
|
11062
|
+
"exacerbating"
|
|
11063
|
+
];
|
|
11064
|
+
function Panel2({
|
|
11065
|
+
title,
|
|
11066
|
+
right,
|
|
10736
11067
|
children
|
|
10737
|
-
})
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
|
|
10741
|
-
|
|
10742
|
-
|
|
10743
|
-
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
|
|
11068
|
+
}) {
|
|
11069
|
+
return /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-md border border-[#D0D0D0] bg-white", children: [
|
|
11070
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-[#D0D0D0] bg-[#FEE8EC]/50 px-3 py-1.5", children: [
|
|
11071
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-slate-700", children: title }),
|
|
11072
|
+
right
|
|
11073
|
+
] }),
|
|
11074
|
+
/* @__PURE__ */ jsx("div", { className: "p-3", children })
|
|
11075
|
+
] });
|
|
11076
|
+
}
|
|
11077
|
+
var UrologySmartHistoryWidget = ({ fieldId }) => {
|
|
11078
|
+
const { fieldDef, value, setValue, setTouched, error, touched, disabled } = useField(fieldId);
|
|
11079
|
+
const hopcField = useField(SYMPTOMS_FIELD);
|
|
11080
|
+
const syndromeField = useField(ACTIVE_SYNDROME_FIELD);
|
|
11081
|
+
const { state } = useForm();
|
|
11082
|
+
const store = useFormStore();
|
|
11083
|
+
const showError = !!error && (touched || state.submitAttempted);
|
|
11084
|
+
const safe = useMemo(() => normalizeUrologySmartHistory(value), [value]);
|
|
11085
|
+
const chips = useMemo(() => chipList2(state), [state]);
|
|
11086
|
+
const rawSyndrome = syndromeField.value ?? state.values[ACTIVE_SYNDROME_FIELD];
|
|
11087
|
+
const syndrome = typeof rawSyndrome === "string" && rawSyndrome !== "" && rawSyndrome !== "none" ? rawSyndrome : "none";
|
|
11088
|
+
const showPain = chips.includes("pain abdomen flank") || chips.includes("scrotal swelling pain") || syndrome === "stone" || syndrome === "scrotal";
|
|
11089
|
+
const showLUTS = syndrome === "luts" || syndrome === "retention" || chips.includes("frequency urgency") || chips.includes("poor stream") || chips.includes("acute retention");
|
|
11090
|
+
const showHaem = syndrome === "haematuria" || chips.includes("haematuria");
|
|
11091
|
+
const showSex = syndrome === "ed_infertility" || chips.includes("erectile dysfunction") || chips.includes("infertility concern");
|
|
11092
|
+
const update = (next) => {
|
|
11093
|
+
setValue(next);
|
|
11094
|
+
setTouched();
|
|
11095
|
+
};
|
|
11096
|
+
const ipssTotal = urologyIpssSevenTotal(safe.ipss);
|
|
11097
|
+
const ipssBandLabel = urologyIpssBand(ipssTotal);
|
|
11098
|
+
const bandClass = ipssBandLabel === "Severe" ? "bg-red-500/15 text-red-700" : ipssBandLabel === "Moderate" ? "bg-amber-500/15 text-amber-800" : "bg-emerald-500/15 text-emerald-800";
|
|
11099
|
+
const setSocrates = (key, v) => {
|
|
11100
|
+
if (key === "severity") {
|
|
11101
|
+
const n = typeof v === "number" ? v : Number(v);
|
|
11102
|
+
const sev = Number.isFinite(n) ? Math.min(10, Math.max(0, Math.round(n))) : 0;
|
|
11103
|
+
update({ ...safe, socrates: { ...safe.socrates, severity: sev } });
|
|
11104
|
+
return;
|
|
11105
|
+
}
|
|
11106
|
+
update({ ...safe, socrates: { ...safe.socrates, [key]: String(v) } });
|
|
11107
|
+
};
|
|
11108
|
+
const setIpss = (key, v) => {
|
|
11109
|
+
update({ ...safe, ipss: { ...safe.ipss, [key]: v } });
|
|
11110
|
+
};
|
|
11111
|
+
const [hopcNarrativeLocked, setHopcNarrativeLocked] = useState(false);
|
|
11112
|
+
useEffect(() => {
|
|
11113
|
+
if (hopcNarrativeLocked || disabled) return;
|
|
11114
|
+
const next = formatUrologySmartHistoryToSymptomsNarrative(normalizeUrologySmartHistory(value), {
|
|
11115
|
+
showPain,
|
|
11116
|
+
showLUTS,
|
|
11117
|
+
showHaem,
|
|
11118
|
+
showSex
|
|
11119
|
+
});
|
|
11120
|
+
hopcField.setValue(next);
|
|
11121
|
+
}, [value, hopcNarrativeLocked, showPain, showLUTS, showHaem, showSex, disabled]);
|
|
11122
|
+
useEffect(() => {
|
|
11123
|
+
if (disabled) return;
|
|
11124
|
+
const label = syndrome && syndrome !== "none" ? SYNDROME_LABELS[syndrome] ?? syndrome : "";
|
|
11125
|
+
const prev = store.getFieldState(DIAGNOSIS_FIELD)?.value;
|
|
11126
|
+
const prevText = typeof prev === "string" ? prev : "";
|
|
11127
|
+
const syndromeLabels = new Set(Object.values(SYNDROME_LABELS));
|
|
11128
|
+
const keptLines = prevText.split("\n").map((l) => l.trim()).filter(Boolean).filter((l) => !syndromeLabels.has(l));
|
|
11129
|
+
const nextLines = label ? [...keptLines, label] : keptLines;
|
|
11130
|
+
const next = nextLines.join("\n");
|
|
11131
|
+
if (prevText.trim() === next.trim()) return;
|
|
11132
|
+
store.setValues({ [DIAGNOSIS_FIELD]: next }, { touch: false });
|
|
11133
|
+
}, [disabled, store, syndrome]);
|
|
11134
|
+
useEffect(() => {
|
|
11135
|
+
if (disabled) return;
|
|
11136
|
+
const duration = chiefOptionalFieldDisplay(state.values[URO_DURATION_FIELD]);
|
|
11137
|
+
const onset = chiefOptionalFieldDisplay(state.values[URO_ONSET_FIELD]);
|
|
11138
|
+
const progression = chiefOptionalFieldDisplay(state.values[URO_PROGRESSION_FIELD]);
|
|
11139
|
+
const prev = store.getFieldState(CHIEF_TEXT_FIELD)?.value;
|
|
11140
|
+
const prevText = typeof prev === "string" ? prev : "";
|
|
11141
|
+
const kept = prevText.split(",").map((t) => t.trim()).filter(Boolean).filter((t) => {
|
|
11142
|
+
const lower = t.toLowerCase();
|
|
11143
|
+
return !lower.startsWith("duration:") && !lower.startsWith("onset:") && !lower.startsWith("progression:");
|
|
11144
|
+
});
|
|
11145
|
+
const extra = nonEmptyLines([
|
|
11146
|
+
duration ? `Duration: ${duration}` : "",
|
|
11147
|
+
onset ? `Onset: ${onset}` : "",
|
|
11148
|
+
progression ? `Progression: ${progression}` : ""
|
|
11149
|
+
]);
|
|
11150
|
+
const next = [...kept, ...extra].join(", ").trim();
|
|
11151
|
+
if (prevText.trim() === next.trim()) return;
|
|
11152
|
+
store.setValues({ [CHIEF_TEXT_FIELD]: next }, { touch: false });
|
|
11153
|
+
}, [
|
|
11154
|
+
disabled,
|
|
11155
|
+
state.values[URO_DURATION_FIELD],
|
|
11156
|
+
state.values[URO_ONSET_FIELD],
|
|
11157
|
+
state.values[URO_PROGRESSION_FIELD],
|
|
11158
|
+
store
|
|
11159
|
+
]);
|
|
11160
|
+
if (!fieldDef) return null;
|
|
11161
|
+
const theme = getThemeConfig("textarea", fieldDef.theme);
|
|
11162
|
+
const wrapperClass = theme?.wrapperClassName ?? "rounded-md overflow-hidden flex flex-col border border-[#D0D0D0]";
|
|
11163
|
+
const labelClass = theme?.labelClassName;
|
|
11164
|
+
const labelContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
11165
|
+
fieldDef.label,
|
|
11166
|
+
fieldDef.required ? /* @__PURE__ */ jsx("span", { className: "ml-1 text-red-500", children: "*" }) : null
|
|
11167
|
+
] });
|
|
11168
|
+
const labelEl = theme ? /* @__PURE__ */ jsx(Label, { className: labelClass, children: labelContent }) : /* @__PURE__ */ jsx(Label, { className: "text-sm font-medium", children: labelContent });
|
|
11169
|
+
const bodyClass = cn(
|
|
11170
|
+
"-mt-1 flex min-w-0 flex-col gap-3 border-t border-[#D0D0D0] bg-white px-3 py-3",
|
|
11171
|
+
disabled && "pointer-events-none opacity-60"
|
|
11172
|
+
);
|
|
11173
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("w-full min-w-0", wrapperClass), children: [
|
|
11174
|
+
labelEl,
|
|
11175
|
+
/* @__PURE__ */ jsxs("div", { className: bodyClass, children: [
|
|
11176
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: "Panels follow chief complaint chips and active syndrome (Clearsight SmartHistoryUro)." }),
|
|
11177
|
+
showPain ? /* @__PURE__ */ jsx(Panel2, { title: "SOCRATES \u2014 Pain (flank / suprapubic / testicular)", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2 md:grid-cols-4", children: [
|
|
11178
|
+
SOCRATES_KEYS2.map((k) => /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
11179
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: k }),
|
|
11180
|
+
/* @__PURE__ */ jsx(
|
|
11181
|
+
Input,
|
|
11182
|
+
{
|
|
11183
|
+
className: "h-8 text-xs",
|
|
11184
|
+
value: safe.socrates[k],
|
|
11185
|
+
onChange: (e) => setSocrates(k, e.target.value),
|
|
11186
|
+
placeholder: k === "radiation" ? "e.g. groin \u2192 ureteric" : k === "character" ? "colicky / dull" : "",
|
|
11187
|
+
disabled
|
|
11188
|
+
}
|
|
11189
|
+
)
|
|
11190
|
+
] }, k)),
|
|
11191
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
11192
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Severity 0\u201310" }),
|
|
11193
|
+
/* @__PURE__ */ jsx(
|
|
11194
|
+
Input,
|
|
11195
|
+
{
|
|
11196
|
+
type: "number",
|
|
11197
|
+
min: 0,
|
|
11198
|
+
max: 10,
|
|
11199
|
+
className: "h-8 text-xs",
|
|
11200
|
+
value: safe.socrates.severity,
|
|
11201
|
+
onChange: (e) => setSocrates("severity", e.target.value),
|
|
11202
|
+
disabled
|
|
11203
|
+
}
|
|
11204
|
+
)
|
|
11205
|
+
] })
|
|
11206
|
+
] }) }) : null,
|
|
11207
|
+
showLUTS ? /* @__PURE__ */ jsxs(
|
|
11208
|
+
Panel2,
|
|
11209
|
+
{
|
|
11210
|
+
title: "IPSS \u2014 International Prostate Symptom Score",
|
|
11211
|
+
right: /* @__PURE__ */ jsxs("span", { className: `rounded-full px-2 py-0.5 text-[10px] font-medium ${bandClass}`, children: [
|
|
11212
|
+
ipssTotal,
|
|
11213
|
+
"/35 \u2014 ",
|
|
11214
|
+
ipssBandLabel
|
|
11215
|
+
] }),
|
|
11216
|
+
children: [
|
|
11217
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2 md:grid-cols-4", children: [
|
|
11218
|
+
Object.keys(IPSS_LABELS).map((k) => /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
11219
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: IPSS_LABELS[k] }),
|
|
11220
|
+
/* @__PURE__ */ jsxs(
|
|
11221
|
+
Select,
|
|
11222
|
+
{
|
|
11223
|
+
value: String(safe.ipss[k]),
|
|
11224
|
+
onValueChange: (val) => setIpss(k, Number(val)),
|
|
11225
|
+
disabled,
|
|
11226
|
+
children: [
|
|
11227
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
11228
|
+
/* @__PURE__ */ jsx(SelectContent, { children: [0, 1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ jsx(SelectItem, { value: String(n), children: n }, n)) })
|
|
11229
|
+
]
|
|
11230
|
+
}
|
|
11231
|
+
)
|
|
11232
|
+
] }, k)),
|
|
11233
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
11234
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "QoL (0\u20136)" }),
|
|
11235
|
+
/* @__PURE__ */ jsxs(
|
|
11236
|
+
Select,
|
|
11237
|
+
{
|
|
11238
|
+
value: String(safe.ipssQoL),
|
|
11239
|
+
onValueChange: (val) => update({ ...safe, ipssQoL: Math.min(6, Math.max(0, Number(val))) }),
|
|
11240
|
+
disabled,
|
|
11241
|
+
children: [
|
|
11242
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
11243
|
+
/* @__PURE__ */ jsx(SelectContent, { children: [0, 1, 2, 3, 4, 5, 6].map((n) => /* @__PURE__ */ jsx(SelectItem, { value: String(n), children: n }, n)) })
|
|
11244
|
+
]
|
|
11245
|
+
}
|
|
11246
|
+
)
|
|
11247
|
+
] })
|
|
11248
|
+
] }),
|
|
11249
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-2 text-[10px] text-muted-foreground", children: [
|
|
11250
|
+
"Bands: 0\u20137 mild \xB7 8\u201319 moderate \xB7 20\u201335 severe.",
|
|
11251
|
+
" ",
|
|
11252
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: "IPSS \u2265 20 \u2192 consider surgical intervention." })
|
|
11253
|
+
] })
|
|
11254
|
+
]
|
|
11255
|
+
}
|
|
11256
|
+
) : null,
|
|
11257
|
+
showHaem ? /* @__PURE__ */ jsxs(Panel2, { title: "Haematuria profile", children: [
|
|
11258
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3 text-xs md:grid-cols-4", children: [
|
|
11259
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11260
|
+
/* @__PURE__ */ jsx(
|
|
11261
|
+
Checkbox,
|
|
11262
|
+
{
|
|
11263
|
+
checked: safe.haematuria.present,
|
|
11264
|
+
onCheckedChange: (c) => update({
|
|
11265
|
+
...safe,
|
|
11266
|
+
haematuria: { ...safe.haematuria, present: c === true }
|
|
11267
|
+
}),
|
|
11268
|
+
disabled
|
|
11269
|
+
}
|
|
11270
|
+
),
|
|
11271
|
+
"Present"
|
|
11272
|
+
] }),
|
|
11273
|
+
/* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1", children: [
|
|
11274
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Timing" }),
|
|
11275
|
+
/* @__PURE__ */ jsxs(
|
|
11276
|
+
Select,
|
|
11277
|
+
{
|
|
11278
|
+
value: safe.haematuria.timing || "none",
|
|
11279
|
+
onValueChange: (val) => update({
|
|
11280
|
+
...safe,
|
|
11281
|
+
haematuria: { ...safe.haematuria, timing: val === "none" ? "" : val }
|
|
11282
|
+
}),
|
|
11283
|
+
disabled,
|
|
11284
|
+
children: [
|
|
11285
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "\u2014" }) }),
|
|
11286
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
11287
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "none", children: "\u2014" }),
|
|
11288
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "Initial", children: "Initial" }),
|
|
11289
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "Terminal", children: "Terminal" }),
|
|
11290
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "Total", children: "Total" })
|
|
11291
|
+
] })
|
|
11292
|
+
]
|
|
11293
|
+
}
|
|
11294
|
+
)
|
|
11295
|
+
] }),
|
|
11296
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11297
|
+
/* @__PURE__ */ jsx(
|
|
11298
|
+
Checkbox,
|
|
11299
|
+
{
|
|
11300
|
+
checked: safe.haematuria.painful,
|
|
11301
|
+
onCheckedChange: (c) => update({ ...safe, haematuria: { ...safe.haematuria, painful: c === true } }),
|
|
11302
|
+
disabled
|
|
11303
|
+
}
|
|
11304
|
+
),
|
|
11305
|
+
"Painful"
|
|
11306
|
+
] }),
|
|
11307
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11308
|
+
/* @__PURE__ */ jsx(
|
|
11309
|
+
Checkbox,
|
|
11310
|
+
{
|
|
11311
|
+
checked: safe.haematuria.clots,
|
|
11312
|
+
onCheckedChange: (c) => update({ ...safe, haematuria: { ...safe.haematuria, clots: c === true } }),
|
|
11313
|
+
disabled
|
|
11314
|
+
}
|
|
11315
|
+
),
|
|
11316
|
+
"Clots"
|
|
11317
|
+
] })
|
|
11318
|
+
] }),
|
|
11319
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-[10px] text-red-600", children: "Painless haematuria = malignancy until proven otherwise." })
|
|
11320
|
+
] }) : null,
|
|
11321
|
+
showSex ? /* @__PURE__ */ jsx(Panel2, { title: "Sexual & reproductive history", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3 text-xs md:grid-cols-4", children: [
|
|
11322
|
+
/* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1", children: [
|
|
11323
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Erectile function" }),
|
|
11324
|
+
/* @__PURE__ */ jsxs(
|
|
11325
|
+
Select,
|
|
11326
|
+
{
|
|
11327
|
+
value: safe.sexual.erectile || "none",
|
|
11328
|
+
onValueChange: (val) => update({
|
|
11329
|
+
...safe,
|
|
11330
|
+
sexual: { ...safe.sexual, erectile: val === "none" ? "" : val }
|
|
11331
|
+
}),
|
|
11332
|
+
disabled,
|
|
11333
|
+
children: [
|
|
11334
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "\u2014" }) }),
|
|
11335
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
11336
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "none", children: "\u2014" }),
|
|
11337
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "Normal", children: "Normal" }),
|
|
11338
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "Mild", children: "Mild" }),
|
|
11339
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "Moderate", children: "Moderate" }),
|
|
11340
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "Severe", children: "Severe" })
|
|
11341
|
+
] })
|
|
11342
|
+
]
|
|
11343
|
+
}
|
|
11344
|
+
)
|
|
11345
|
+
] }),
|
|
11346
|
+
/* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1", children: [
|
|
11347
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Libido" }),
|
|
11348
|
+
/* @__PURE__ */ jsxs(
|
|
11349
|
+
Select,
|
|
11350
|
+
{
|
|
11351
|
+
value: safe.sexual.libido || "none",
|
|
11352
|
+
onValueChange: (val) => update({
|
|
11353
|
+
...safe,
|
|
11354
|
+
sexual: { ...safe.sexual, libido: val === "none" ? "" : val }
|
|
11355
|
+
}),
|
|
11356
|
+
disabled,
|
|
11357
|
+
children: [
|
|
11358
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "\u2014" }) }),
|
|
11359
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
11360
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "none", children: "\u2014" }),
|
|
11361
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "Normal", children: "Normal" }),
|
|
11362
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "Reduced", children: "Reduced" })
|
|
11363
|
+
] })
|
|
11364
|
+
]
|
|
11365
|
+
}
|
|
11366
|
+
)
|
|
11367
|
+
] }),
|
|
11368
|
+
/* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1", children: [
|
|
11369
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Ejaculation" }),
|
|
11370
|
+
/* @__PURE__ */ jsxs(
|
|
11371
|
+
Select,
|
|
11372
|
+
{
|
|
11373
|
+
value: safe.sexual.ejaculation || "none",
|
|
11374
|
+
onValueChange: (val) => update({
|
|
11375
|
+
...safe,
|
|
11376
|
+
sexual: { ...safe.sexual, ejaculation: val === "none" ? "" : val }
|
|
11377
|
+
}),
|
|
11378
|
+
disabled,
|
|
11379
|
+
children: [
|
|
11380
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "\u2014" }) }),
|
|
11381
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
11382
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "none", children: "\u2014" }),
|
|
11383
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "Normal", children: "Normal" }),
|
|
11384
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "Premature", children: "Premature" }),
|
|
11385
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "Retrograde", children: "Retrograde" }),
|
|
11386
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "Anejaculation", children: "Anejaculation" })
|
|
11387
|
+
] })
|
|
11388
|
+
]
|
|
11389
|
+
}
|
|
11390
|
+
)
|
|
11391
|
+
] }),
|
|
11392
|
+
/* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1", children: [
|
|
11393
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Infertility (months)" }),
|
|
11394
|
+
/* @__PURE__ */ jsx(
|
|
11395
|
+
Input,
|
|
11396
|
+
{
|
|
11397
|
+
type: "number",
|
|
11398
|
+
min: 0,
|
|
11399
|
+
className: "h-8 text-xs",
|
|
11400
|
+
value: safe.sexual.infertilityMonths,
|
|
11401
|
+
onChange: (e) => update({
|
|
11402
|
+
...safe,
|
|
11403
|
+
sexual: {
|
|
11404
|
+
...safe.sexual,
|
|
11405
|
+
infertilityMonths: Math.max(0, Math.round(Number(e.target.value)) || 0)
|
|
11406
|
+
}
|
|
11407
|
+
}),
|
|
11408
|
+
disabled
|
|
11409
|
+
}
|
|
11410
|
+
)
|
|
11411
|
+
] })
|
|
11412
|
+
] }) }) : null,
|
|
11413
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
11414
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
11415
|
+
/* @__PURE__ */ jsx(Label, { className: "text-xs font-semibold text-slate-700", children: hopcField.fieldDef?.label ?? "History of presenting complaint" }),
|
|
11416
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
11417
|
+
hopcNarrativeLocked ? /* @__PURE__ */ jsxs("span", { className: "rounded bg-amber-50 px-1.5 py-0.5 text-[10px] font-medium text-amber-900 ring-1 ring-amber-200/80", children: [
|
|
11418
|
+
"You edited this summary. Use",
|
|
11419
|
+
" ",
|
|
11420
|
+
/* @__PURE__ */ jsx("span", { className: "italic", children: "'Regenerate'" }),
|
|
11421
|
+
" to update it from the form."
|
|
11422
|
+
] }) : null,
|
|
11423
|
+
hopcNarrativeLocked ? /* @__PURE__ */ jsx(
|
|
11424
|
+
Button,
|
|
11425
|
+
{
|
|
11426
|
+
type: "button",
|
|
11427
|
+
variant: "outline",
|
|
11428
|
+
size: "sm",
|
|
11429
|
+
className: "h-7 shrink-0 px-2 text-xs",
|
|
11430
|
+
disabled: disabled || hopcField.disabled,
|
|
11431
|
+
onClick: () => {
|
|
11432
|
+
setHopcNarrativeLocked(false);
|
|
11433
|
+
hopcField.setValue(
|
|
11434
|
+
formatUrologySmartHistoryToSymptomsNarrative(
|
|
11435
|
+
normalizeUrologySmartHistory(value),
|
|
11436
|
+
{ showPain, showLUTS, showHaem, showSex }
|
|
11437
|
+
)
|
|
11438
|
+
);
|
|
11439
|
+
},
|
|
11440
|
+
children: "Regenerate"
|
|
11441
|
+
}
|
|
11442
|
+
) : null
|
|
11443
|
+
] })
|
|
11444
|
+
] }),
|
|
11445
|
+
hopcNarrativeLocked ? /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: "Edit this box anytime. Use Regenerate to replace with fresh text from the structured fields." }) : null,
|
|
11446
|
+
/* @__PURE__ */ jsx(
|
|
11447
|
+
Textarea,
|
|
11448
|
+
{
|
|
11449
|
+
className: "min-h-[88px] rounded-md border border-input bg-white text-sm",
|
|
11450
|
+
placeholder: "Auto-filled from chief complaints + smart history\u2026",
|
|
11451
|
+
value: typeof hopcField.value === "string" ? hopcField.value : hopcField.value == null ? "" : String(hopcField.value),
|
|
11452
|
+
onChange: (e) => {
|
|
11453
|
+
setHopcNarrativeLocked(true);
|
|
11454
|
+
hopcField.setValue(e.target.value);
|
|
11455
|
+
hopcField.setTouched();
|
|
11456
|
+
},
|
|
11457
|
+
disabled: disabled || hopcField.disabled
|
|
11458
|
+
}
|
|
11459
|
+
)
|
|
11460
|
+
] })
|
|
11461
|
+
] }),
|
|
11462
|
+
showError ? /* @__PURE__ */ jsx("p", { className: "px-3 pb-2 text-xs text-red-500", children: error }) : null
|
|
11463
|
+
] });
|
|
11464
|
+
};
|
|
11465
|
+
var URO_CLINICAL_EXAMINATION_FIELD = "clinical_examination";
|
|
11466
|
+
var GENERAL_ROWS = [
|
|
11467
|
+
{ key: "pallor", label: "Pallor (chronic dz)" },
|
|
11468
|
+
{ key: "oedema", label: "Oedema (renal)" },
|
|
11469
|
+
{ key: "icterus", label: "Icterus" },
|
|
11470
|
+
{ key: "lymphadenopathy", label: "Lymphadenopathy" }
|
|
11471
|
+
];
|
|
11472
|
+
var ABD_ROWS = [
|
|
11473
|
+
{ key: "distension", label: "Distension" },
|
|
11474
|
+
{ key: "scars", label: "Surgical scars" },
|
|
11475
|
+
{ key: "mass", label: "Visible mass" },
|
|
11476
|
+
{ key: "kidneyPalpable", label: "Kidney palpable" },
|
|
11477
|
+
{ key: "bladderPalpable", label: "Bladder palpable" },
|
|
11478
|
+
{ key: "renalAngleTender", label: "Renal angle tender" },
|
|
11479
|
+
{ key: "bladderDull", label: "Bladder dullness (retention)" }
|
|
11480
|
+
];
|
|
11481
|
+
var GU_ROWS = [
|
|
11482
|
+
{ key: "meatusAbnormal", label: "Meatus abnormal (hypospadias?)" },
|
|
11483
|
+
{ key: "phimosis", label: "Phimosis / paraphimosis" },
|
|
11484
|
+
{ key: "lesions", label: "Penile lesions / ulcers" },
|
|
11485
|
+
{ key: "scrotalSwelling", label: "Scrotal swelling" },
|
|
11486
|
+
{ key: "transilluminant", label: "Transilluminant" },
|
|
11487
|
+
{ key: "tenderTestis", label: "Tender testis" },
|
|
11488
|
+
{ key: "hydrocele", label: "Hydrocele" },
|
|
11489
|
+
{ key: "varicocele", label: "Varicocele" },
|
|
11490
|
+
{ key: "suspiciousMass", label: "Suspicious mass" }
|
|
11491
|
+
];
|
|
11492
|
+
var DRE_CONSISTENCY = ["none", "Soft (benign)", "Firm", "Hard / nodular (malignancy?)"];
|
|
11493
|
+
var DRE_SULCUS = ["none", "Preserved", "Obliterated"];
|
|
11494
|
+
var UrologyExaminationWidget = ({ fieldId }) => {
|
|
11495
|
+
const { fieldDef, value, setValue, setTouched, error, touched, disabled } = useField(fieldId);
|
|
11496
|
+
const clinicalField = useField(URO_CLINICAL_EXAMINATION_FIELD);
|
|
11497
|
+
const { state } = useForm();
|
|
11498
|
+
const showError = !!error && (touched || state.submitAttempted);
|
|
11499
|
+
const safe = useMemo(() => normalizeUrologyExamination(value), [value]);
|
|
11500
|
+
const [clinicalNarrativeLocked, setClinicalNarrativeLocked] = useState(false);
|
|
11501
|
+
useEffect(() => {
|
|
11502
|
+
if (clinicalNarrativeLocked || disabled) return;
|
|
11503
|
+
const norm = normalizeUrologyExamination(value);
|
|
11504
|
+
const next = formatUrologyExaminationToClinicalExamination(norm);
|
|
11505
|
+
clinicalField.setValue(next);
|
|
11506
|
+
if (norm.examinationNotes !== next) {
|
|
11507
|
+
setValue({ ...norm, examinationNotes: next });
|
|
11508
|
+
}
|
|
11509
|
+
}, [value, clinicalNarrativeLocked, disabled]);
|
|
11510
|
+
const bump = (next) => {
|
|
11511
|
+
setValue(next);
|
|
11512
|
+
setTouched();
|
|
11513
|
+
};
|
|
11514
|
+
const toggleGeneral = (key) => {
|
|
11515
|
+
bump({ ...safe, general: { ...safe.general, [key]: !safe.general[key] } });
|
|
11516
|
+
};
|
|
11517
|
+
const toggleAbdomen = (key) => {
|
|
11518
|
+
bump({ ...safe, abdomen: { ...safe.abdomen, [key]: !safe.abdomen[key] } });
|
|
11519
|
+
};
|
|
11520
|
+
const toggleGu = (key) => {
|
|
11521
|
+
bump({ ...safe, gu: { ...safe.gu, [key]: !safe.gu[key] } });
|
|
11522
|
+
};
|
|
11523
|
+
if (!fieldDef) return null;
|
|
11524
|
+
const theme = getThemeConfig("textarea", fieldDef.theme);
|
|
11525
|
+
const wrapperClass = theme?.wrapperClassName ?? "rounded-md overflow-hidden flex flex-col border border-[#D0D0D0]";
|
|
11526
|
+
const labelClass = theme?.labelClassName;
|
|
11527
|
+
const labelContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
11528
|
+
fieldDef.label,
|
|
11529
|
+
fieldDef.required ? /* @__PURE__ */ jsx("span", { className: "ml-1 text-red-500", children: "*" }) : null
|
|
11530
|
+
] });
|
|
11531
|
+
const labelEl = theme ? /* @__PURE__ */ jsx(Label, { className: labelClass, children: labelContent }) : /* @__PURE__ */ jsx(Label, { className: "text-sm font-medium", children: labelContent });
|
|
11532
|
+
const bodyClass = cn(
|
|
11533
|
+
"-mt-1 flex min-w-0 flex-col gap-3 border-t border-[#D0D0D0] bg-white px-3 py-3",
|
|
11534
|
+
disabled && "pointer-events-none opacity-60"
|
|
11535
|
+
);
|
|
11536
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("w-full min-w-0", wrapperClass), children: [
|
|
11537
|
+
labelEl,
|
|
11538
|
+
/* @__PURE__ */ jsxs("div", { className: bodyClass, children: [
|
|
11539
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 text-xs md:grid-cols-2 xl:grid-cols-4", children: [
|
|
11540
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
11541
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-slate-800", children: "General" }),
|
|
11542
|
+
GENERAL_ROWS.map((r) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11543
|
+
/* @__PURE__ */ jsx(
|
|
11544
|
+
Checkbox,
|
|
11545
|
+
{
|
|
11546
|
+
checked: safe.general[r.key],
|
|
11547
|
+
onCheckedChange: () => toggleGeneral(r.key),
|
|
11548
|
+
disabled
|
|
11549
|
+
}
|
|
11550
|
+
),
|
|
11551
|
+
r.label
|
|
11552
|
+
] }, r.key))
|
|
11553
|
+
] }),
|
|
11554
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
11555
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-slate-800", children: "Abdomen" }),
|
|
11556
|
+
ABD_ROWS.map((r) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11557
|
+
/* @__PURE__ */ jsx(
|
|
11558
|
+
Checkbox,
|
|
11559
|
+
{
|
|
11560
|
+
checked: safe.abdomen[r.key],
|
|
11561
|
+
onCheckedChange: () => toggleAbdomen(r.key),
|
|
11562
|
+
disabled
|
|
11563
|
+
}
|
|
11564
|
+
),
|
|
11565
|
+
r.label
|
|
11566
|
+
] }, r.key))
|
|
11567
|
+
] }),
|
|
11568
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
11569
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-slate-800", children: "External genitalia / scrotum" }),
|
|
11570
|
+
GU_ROWS.map((r) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11571
|
+
/* @__PURE__ */ jsx(
|
|
11572
|
+
Checkbox,
|
|
11573
|
+
{
|
|
11574
|
+
checked: safe.gu[r.key],
|
|
11575
|
+
onCheckedChange: () => toggleGu(r.key),
|
|
11576
|
+
disabled
|
|
11577
|
+
}
|
|
11578
|
+
),
|
|
11579
|
+
r.key === "suspiciousMass" ? /* @__PURE__ */ jsx("span", { className: "font-medium text-red-600", children: r.label }) : r.label
|
|
11580
|
+
] }, r.key))
|
|
11581
|
+
] }),
|
|
11582
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
11583
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-slate-800", children: "DRE \u2014 Prostate" }),
|
|
11584
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11585
|
+
/* @__PURE__ */ jsx(
|
|
11586
|
+
Checkbox,
|
|
11587
|
+
{
|
|
11588
|
+
checked: safe.dre.done,
|
|
11589
|
+
onCheckedChange: (c) => bump({ ...safe, dre: { ...safe.dre, done: c === true } }),
|
|
11590
|
+
disabled
|
|
11591
|
+
}
|
|
11592
|
+
),
|
|
11593
|
+
"DRE performed"
|
|
11594
|
+
] }),
|
|
11595
|
+
/* @__PURE__ */ jsxs("label", { className: "block", children: [
|
|
11596
|
+
/* @__PURE__ */ jsx("span", { className: "mb-1 block font-medium", children: "Estimated size (g)" }),
|
|
11597
|
+
/* @__PURE__ */ jsx(
|
|
11598
|
+
Input,
|
|
11599
|
+
{
|
|
11600
|
+
type: "number",
|
|
11601
|
+
min: 0,
|
|
11602
|
+
className: "h-8 text-xs",
|
|
11603
|
+
value: safe.dre.sizeGrams,
|
|
11604
|
+
onChange: (e) => {
|
|
11605
|
+
const n = Number(e.target.value);
|
|
11606
|
+
bump({
|
|
11607
|
+
...safe,
|
|
11608
|
+
dre: {
|
|
11609
|
+
...safe.dre,
|
|
11610
|
+
sizeGrams: Number.isFinite(n) && n >= 0 ? Math.round(n) : 0
|
|
11611
|
+
}
|
|
11612
|
+
});
|
|
11613
|
+
},
|
|
11614
|
+
disabled
|
|
11615
|
+
}
|
|
11616
|
+
)
|
|
11617
|
+
] }),
|
|
11618
|
+
/* @__PURE__ */ jsxs("label", { className: "block", children: [
|
|
11619
|
+
/* @__PURE__ */ jsx("span", { className: "mb-1 block font-medium", children: "Consistency" }),
|
|
11620
|
+
/* @__PURE__ */ jsxs(
|
|
11621
|
+
Select,
|
|
11622
|
+
{
|
|
11623
|
+
value: safe.dre.consistency || "none",
|
|
11624
|
+
onValueChange: (val) => bump({
|
|
11625
|
+
...safe,
|
|
11626
|
+
dre: { ...safe.dre, consistency: val === "none" ? "" : val }
|
|
11627
|
+
}),
|
|
11628
|
+
disabled,
|
|
11629
|
+
children: [
|
|
11630
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "\u2014" }) }),
|
|
11631
|
+
/* @__PURE__ */ jsx(SelectContent, { children: DRE_CONSISTENCY.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt, children: opt === "none" ? "\u2014" : opt }, opt)) })
|
|
11632
|
+
]
|
|
11633
|
+
}
|
|
11634
|
+
)
|
|
11635
|
+
] }),
|
|
11636
|
+
/* @__PURE__ */ jsxs("label", { className: "block", children: [
|
|
11637
|
+
/* @__PURE__ */ jsx("span", { className: "mb-1 block font-medium", children: "Median sulcus" }),
|
|
11638
|
+
/* @__PURE__ */ jsxs(
|
|
11639
|
+
Select,
|
|
11640
|
+
{
|
|
11641
|
+
value: safe.dre.medianSulcus || "none",
|
|
11642
|
+
onValueChange: (val) => bump({
|
|
11643
|
+
...safe,
|
|
11644
|
+
dre: { ...safe.dre, medianSulcus: val === "none" ? "" : val }
|
|
11645
|
+
}),
|
|
11646
|
+
disabled,
|
|
11647
|
+
children: [
|
|
11648
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "\u2014" }) }),
|
|
11649
|
+
/* @__PURE__ */ jsx(SelectContent, { children: DRE_SULCUS.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt, children: opt === "none" ? "\u2014" : opt }, opt)) })
|
|
11650
|
+
]
|
|
11651
|
+
}
|
|
11652
|
+
)
|
|
11653
|
+
] }),
|
|
11654
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11655
|
+
/* @__PURE__ */ jsx(
|
|
11656
|
+
Checkbox,
|
|
11657
|
+
{
|
|
11658
|
+
checked: safe.dre.tenderness,
|
|
11659
|
+
onCheckedChange: (c) => bump({ ...safe, dre: { ...safe.dre, tenderness: c === true } }),
|
|
11660
|
+
disabled
|
|
11661
|
+
}
|
|
11662
|
+
),
|
|
11663
|
+
"Tender (prostatitis?)"
|
|
11664
|
+
] })
|
|
11665
|
+
] })
|
|
11666
|
+
] }),
|
|
11667
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
11668
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
11669
|
+
/* @__PURE__ */ jsx(Label, { className: "text-xs font-semibold text-slate-700", children: clinicalField.fieldDef?.label ?? "Clinical examination" }),
|
|
11670
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
11671
|
+
clinicalNarrativeLocked ? /* @__PURE__ */ jsxs("span", { className: "rounded bg-amber-50 px-1.5 py-0.5 text-[10px] font-medium text-amber-900 ring-1 ring-amber-200/80", children: [
|
|
11672
|
+
"You edited this summary. Use",
|
|
11673
|
+
" ",
|
|
11674
|
+
/* @__PURE__ */ jsx("span", { className: "italic", children: "'Regenerate'" }),
|
|
11675
|
+
" to update it from the form."
|
|
11676
|
+
] }) : null,
|
|
11677
|
+
clinicalNarrativeLocked ? /* @__PURE__ */ jsx(
|
|
11678
|
+
Button,
|
|
11679
|
+
{
|
|
11680
|
+
type: "button",
|
|
11681
|
+
variant: "outline",
|
|
11682
|
+
size: "sm",
|
|
11683
|
+
className: "h-7 shrink-0 px-2 text-xs",
|
|
11684
|
+
disabled: disabled || clinicalField.disabled,
|
|
11685
|
+
onClick: () => {
|
|
11686
|
+
setClinicalNarrativeLocked(false);
|
|
11687
|
+
const norm = normalizeUrologyExamination(value);
|
|
11688
|
+
clinicalField.setValue(formatUrologyExaminationToClinicalExamination(norm));
|
|
11689
|
+
},
|
|
11690
|
+
children: "Regenerate"
|
|
11691
|
+
}
|
|
11692
|
+
) : null
|
|
11693
|
+
] })
|
|
11694
|
+
] }),
|
|
11695
|
+
clinicalNarrativeLocked ? /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: "Edit this box anytime. Use Regenerate to replace with fresh text from the structured examination." }) : null,
|
|
11696
|
+
/* @__PURE__ */ jsx(
|
|
11697
|
+
Textarea,
|
|
11698
|
+
{
|
|
11699
|
+
className: "min-h-[88px] rounded-md border border-input bg-white text-sm",
|
|
11700
|
+
placeholder: "Auto-filled from selections above\u2026",
|
|
11701
|
+
value: typeof clinicalField.value === "string" ? clinicalField.value : clinicalField.value == null ? "" : String(clinicalField.value),
|
|
11702
|
+
onChange: (e) => {
|
|
11703
|
+
setClinicalNarrativeLocked(true);
|
|
11704
|
+
const t = e.target.value;
|
|
11705
|
+
clinicalField.setValue(t);
|
|
11706
|
+
clinicalField.setTouched();
|
|
11707
|
+
setValue({ ...safe, examinationNotes: t });
|
|
11708
|
+
setTouched();
|
|
11709
|
+
},
|
|
11710
|
+
disabled: disabled || clinicalField.disabled
|
|
11711
|
+
}
|
|
11712
|
+
)
|
|
11713
|
+
] })
|
|
11714
|
+
] }),
|
|
11715
|
+
showError ? /* @__PURE__ */ jsx("p", { className: "px-3 pb-2 text-xs text-red-500", children: error }) : null
|
|
11716
|
+
] });
|
|
11717
|
+
};
|
|
11718
|
+
var RISK_ROWS = [
|
|
11719
|
+
{ key: "ckd", label: "Chronic kidney disease" },
|
|
11720
|
+
{ key: "diabetes", label: "Diabetes" },
|
|
11721
|
+
{ key: "anticoagulants", label: "Anticoagulants" },
|
|
11722
|
+
{ key: "activeInfection", label: "Active infection" }
|
|
11723
|
+
];
|
|
11724
|
+
var UrologyPathwayWidget = ({ fieldId }) => {
|
|
11725
|
+
const { fieldDef, value, setValue, setTouched, error, touched, disabled } = useField(fieldId);
|
|
11726
|
+
const { state } = useForm();
|
|
11727
|
+
const showError = !!error && (touched || state.submitAttempted);
|
|
11728
|
+
const safe = useMemo(() => normalizeUrologyPathway(value), [value]);
|
|
11729
|
+
const update = (next) => {
|
|
11730
|
+
setValue(next);
|
|
11731
|
+
setTouched();
|
|
11732
|
+
};
|
|
11733
|
+
const setNum = (key, raw) => {
|
|
11734
|
+
const n = Number(raw);
|
|
11735
|
+
const v = Number.isFinite(n) && n >= 0 ? n : 0;
|
|
11736
|
+
if (key === "stoneSizeMm") {
|
|
11737
|
+
update({ ...safe, stoneSizeMm: Math.round(v) });
|
|
11738
|
+
return;
|
|
11739
|
+
}
|
|
11740
|
+
update({ ...safe, [key]: v });
|
|
11741
|
+
};
|
|
11742
|
+
const toggleRisk = (key) => {
|
|
11743
|
+
update({
|
|
11744
|
+
...safe,
|
|
11745
|
+
risk: { ...safe.risk, [key]: !safe.risk[key] }
|
|
11746
|
+
});
|
|
11747
|
+
};
|
|
11748
|
+
if (!fieldDef) return null;
|
|
11749
|
+
const theme = getThemeConfig("textarea", fieldDef.theme);
|
|
11750
|
+
const wrapperClass = theme?.wrapperClassName ?? "rounded-md overflow-hidden flex flex-col border border-[#D0D0D0]";
|
|
11751
|
+
const labelClass = theme?.labelClassName;
|
|
11752
|
+
const subtitle = fieldDef.meta && typeof fieldDef.meta === "object" && fieldDef.meta !== null ? String(fieldDef.meta.subtitle ?? "") : "";
|
|
11753
|
+
const labelContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
11754
|
+
fieldDef.label,
|
|
11755
|
+
fieldDef.required ? /* @__PURE__ */ jsx("span", { className: "ml-1 text-red-500", children: "*" }) : null
|
|
11756
|
+
] });
|
|
11757
|
+
const labelEl = theme ? /* @__PURE__ */ jsx(Label, { className: labelClass, children: labelContent }) : /* @__PURE__ */ jsx(Label, { className: "text-sm font-medium", children: labelContent });
|
|
11758
|
+
const bodyClass = cn(
|
|
11759
|
+
"-mt-1 flex min-w-0 flex-col gap-4 border-t border-[#D0D0D0] bg-white px-3 py-3",
|
|
11760
|
+
disabled && "pointer-events-none opacity-60"
|
|
11761
|
+
);
|
|
11762
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("w-full min-w-0", wrapperClass), children: [
|
|
11763
|
+
labelEl,
|
|
11764
|
+
subtitle ? /* @__PURE__ */ jsx("p", { className: "px-3 pt-2 text-[11px] text-muted-foreground", children: subtitle }) : null,
|
|
11765
|
+
/* @__PURE__ */ jsxs("div", { className: bodyClass, children: [
|
|
11766
|
+
showError ? /* @__PURE__ */ jsx("p", { className: "text-xs text-red-600", children: error }) : null,
|
|
11767
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 md:grid-cols-2", children: [
|
|
11768
|
+
/* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-xs", children: [
|
|
11769
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Stone size (mm)" }),
|
|
11770
|
+
/* @__PURE__ */ jsx(
|
|
11771
|
+
Input,
|
|
11772
|
+
{
|
|
11773
|
+
type: "number",
|
|
11774
|
+
min: 0,
|
|
11775
|
+
className: "h-8 text-xs",
|
|
11776
|
+
value: safe.stoneSizeMm,
|
|
11777
|
+
onChange: (e) => setNum("stoneSizeMm", e.target.value),
|
|
11778
|
+
disabled
|
|
11779
|
+
}
|
|
11780
|
+
)
|
|
11781
|
+
] }),
|
|
11782
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 pt-6 text-xs", children: [
|
|
11783
|
+
/* @__PURE__ */ jsx(
|
|
11784
|
+
Checkbox,
|
|
11785
|
+
{
|
|
11786
|
+
checked: safe.hydronephrosis,
|
|
11787
|
+
onCheckedChange: (c) => update({ ...safe, hydronephrosis: c === true }),
|
|
11788
|
+
disabled
|
|
11789
|
+
}
|
|
11790
|
+
),
|
|
11791
|
+
"Hydronephrosis on imaging"
|
|
11792
|
+
] }),
|
|
11793
|
+
/* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-xs", children: [
|
|
11794
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Prostate volume (cc)" }),
|
|
11795
|
+
/* @__PURE__ */ jsx(
|
|
11796
|
+
Input,
|
|
11797
|
+
{
|
|
11798
|
+
type: "number",
|
|
11799
|
+
min: 0,
|
|
11800
|
+
step: 0.1,
|
|
11801
|
+
className: "h-8 text-xs",
|
|
11802
|
+
value: safe.prostateVolumeCc,
|
|
11803
|
+
onChange: (e) => setNum("prostateVolumeCc", e.target.value),
|
|
11804
|
+
disabled
|
|
11805
|
+
}
|
|
11806
|
+
)
|
|
11807
|
+
] }),
|
|
11808
|
+
/* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-xs", children: [
|
|
11809
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "PSA (ng/mL)" }),
|
|
11810
|
+
/* @__PURE__ */ jsx(
|
|
11811
|
+
Input,
|
|
11812
|
+
{
|
|
11813
|
+
type: "number",
|
|
11814
|
+
min: 0,
|
|
11815
|
+
step: 0.01,
|
|
11816
|
+
className: "h-8 text-xs",
|
|
11817
|
+
value: safe.psaNgMl,
|
|
11818
|
+
onChange: (e) => setNum("psaNgMl", e.target.value),
|
|
11819
|
+
disabled
|
|
11820
|
+
}
|
|
11821
|
+
)
|
|
11822
|
+
] }),
|
|
11823
|
+
/* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-xs md:col-span-2", children: [
|
|
11824
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Uroflow Qmax (mL/s)" }),
|
|
11825
|
+
/* @__PURE__ */ jsx(
|
|
11826
|
+
Input,
|
|
11827
|
+
{
|
|
11828
|
+
type: "number",
|
|
11829
|
+
min: 0,
|
|
11830
|
+
step: 0.1,
|
|
11831
|
+
className: "h-8 text-xs",
|
|
11832
|
+
value: safe.uroflowQmaxMlSec,
|
|
11833
|
+
onChange: (e) => setNum("uroflowQmaxMlSec", e.target.value),
|
|
11834
|
+
disabled
|
|
11835
|
+
}
|
|
11836
|
+
)
|
|
11837
|
+
] })
|
|
11838
|
+
] }),
|
|
11839
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2 border-t border-muted-foreground/20 pt-3", children: [
|
|
11840
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-slate-800", children: "Risk stratification" }),
|
|
11841
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: RISK_ROWS.map((r) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-xs", children: [
|
|
11842
|
+
/* @__PURE__ */ jsx(
|
|
11843
|
+
Checkbox,
|
|
11844
|
+
{
|
|
11845
|
+
checked: safe.risk[r.key],
|
|
11846
|
+
onCheckedChange: () => toggleRisk(r.key),
|
|
11847
|
+
disabled
|
|
11848
|
+
}
|
|
11849
|
+
),
|
|
11850
|
+
r.label
|
|
11851
|
+
] }, r.key)) })
|
|
11852
|
+
] })
|
|
11853
|
+
] })
|
|
11854
|
+
] });
|
|
11855
|
+
};
|
|
11856
|
+
var FieldRenderer = ({ fieldId }) => {
|
|
11857
|
+
const { fieldDef, visible } = useField(fieldId);
|
|
11858
|
+
if (!visible || !fieldDef) {
|
|
11859
|
+
return null;
|
|
11860
|
+
}
|
|
11861
|
+
return renderWidget(fieldId, fieldDef);
|
|
11862
|
+
};
|
|
11863
|
+
function renderWidget(fieldId, fieldDef) {
|
|
11864
|
+
switch (fieldDef.type) {
|
|
11865
|
+
case "text":
|
|
11866
|
+
case "number":
|
|
11867
|
+
return /* @__PURE__ */ jsx(TextWidget, { fieldId });
|
|
11868
|
+
case "slider":
|
|
11869
|
+
return /* @__PURE__ */ jsx(SliderWidget, { fieldId });
|
|
11870
|
+
case "textarea":
|
|
11871
|
+
if (fieldMetaRequestsMarMedicationOrdersButton(fieldDef.meta)) {
|
|
11872
|
+
return /* @__PURE__ */ jsx(MarMedicationOrdersWidget, { fieldId });
|
|
11873
|
+
}
|
|
11874
|
+
return /* @__PURE__ */ jsx(TextWidget, { fieldId });
|
|
11875
|
+
case "richtext":
|
|
11876
|
+
return /* @__PURE__ */ jsx(RichTextWidget, { fieldId });
|
|
11877
|
+
case "date":
|
|
11878
|
+
return /* @__PURE__ */ jsx(DateWidget, { fieldId });
|
|
11879
|
+
case "datetime":
|
|
11880
|
+
return /* @__PURE__ */ jsx(DateTimeWidget, { fieldId });
|
|
11881
|
+
case "select":
|
|
11882
|
+
return /* @__PURE__ */ jsx(SelectWidget, { fieldId });
|
|
11883
|
+
case "radio":
|
|
11884
|
+
return /* @__PURE__ */ jsx(RadioWidget, { fieldId });
|
|
11885
|
+
case "checkbox":
|
|
11886
|
+
return /* @__PURE__ */ jsx(CheckboxWidget, { fieldId });
|
|
11887
|
+
case "multiselect":
|
|
11888
|
+
return /* @__PURE__ */ jsx(MultiSelectWidget, { fieldId });
|
|
11889
|
+
case "repeatable":
|
|
11890
|
+
return /* @__PURE__ */ jsx(RepeatableWidget, { fieldId });
|
|
11891
|
+
case "image_upload":
|
|
11892
|
+
return /* @__PURE__ */ jsx(ImageUploadWidget, { fieldId });
|
|
11893
|
+
case "media_upload":
|
|
11894
|
+
return /* @__PURE__ */ jsx(MediaUploadWidget, { fieldId });
|
|
11895
|
+
case "signature":
|
|
11896
|
+
return /* @__PURE__ */ jsx(SignatureUploadWidget, { fieldId });
|
|
11897
|
+
case "editable_table":
|
|
11898
|
+
return /* @__PURE__ */ jsx(EditableTableWidget, { fieldId });
|
|
11899
|
+
case "medications":
|
|
11900
|
+
return /* @__PURE__ */ jsx(AddMedicationWidget, { fieldId });
|
|
11901
|
+
case "discharge_medication_orders":
|
|
11902
|
+
return /* @__PURE__ */ jsx(DischargeMedicationOrdersWidget, { fieldId });
|
|
11903
|
+
case "investigations":
|
|
11904
|
+
return /* @__PURE__ */ jsx(AddInvestigationWidget, { fieldId });
|
|
11905
|
+
case "procedures":
|
|
11906
|
+
return /* @__PURE__ */ jsx(AddProcedureWidget, { fieldId });
|
|
11907
|
+
case "differential_diagnosis":
|
|
11908
|
+
return /* @__PURE__ */ jsx(DifferentialDiagnosisWidget, { fieldId });
|
|
11909
|
+
case "vitals":
|
|
11910
|
+
return /* @__PURE__ */ jsx(VitalsWidget, { fieldId });
|
|
11911
|
+
case "hidden_vitals":
|
|
11912
|
+
return /* @__PURE__ */ jsx(HiddenVitalsWidget, { fieldId });
|
|
11913
|
+
case "referral":
|
|
11914
|
+
return /* @__PURE__ */ jsx(ReferralWidget, { fieldId });
|
|
11915
|
+
case "followup":
|
|
11916
|
+
return /* @__PURE__ */ jsx(FollowupWidget, { fieldId });
|
|
11917
|
+
case "smart_textarea":
|
|
11918
|
+
return /* @__PURE__ */ jsx(SmartTextareaWidget, { fieldId });
|
|
11919
|
+
case "diagnosis_textarea":
|
|
11920
|
+
return /* @__PURE__ */ jsx(DiagnosisTextareaWidget, { fieldId });
|
|
11921
|
+
case "obstetric_history":
|
|
11922
|
+
return /* @__PURE__ */ jsx(ObstetricHistoryWidget, { fieldId });
|
|
11923
|
+
case "eye_prescription":
|
|
11924
|
+
return /* @__PURE__ */ jsx(OphthalmologyWidget, { fieldId });
|
|
11925
|
+
case "ophthal_diagnosis":
|
|
11926
|
+
return /* @__PURE__ */ jsx(OphthalDiagnosisWidget, { fieldId });
|
|
11927
|
+
case "orthopedic_exam":
|
|
11928
|
+
return /* @__PURE__ */ jsx(OrthopedicExamWidget, { fieldId });
|
|
11929
|
+
case "general_surgery_smart_history":
|
|
11930
|
+
return /* @__PURE__ */ jsx(GsSmartHistoryWidget, { fieldId });
|
|
11931
|
+
case "general_surgery_examination":
|
|
11932
|
+
return /* @__PURE__ */ jsx(GsExaminationWidget, { fieldId });
|
|
11933
|
+
case "general_surgery_grading":
|
|
11934
|
+
return /* @__PURE__ */ jsx(GsGradingWidget, { fieldId });
|
|
11935
|
+
case "pain_scale_flags":
|
|
11936
|
+
return /* @__PURE__ */ jsx(PainScaleFlagsWidget, { fieldId });
|
|
11937
|
+
case "urology_smart_history":
|
|
11938
|
+
return /* @__PURE__ */ jsx(UrologySmartHistoryWidget, { fieldId });
|
|
11939
|
+
case "urology_examination":
|
|
11940
|
+
return /* @__PURE__ */ jsx(UrologyExaminationWidget, { fieldId });
|
|
11941
|
+
case "urology_pathway":
|
|
11942
|
+
return /* @__PURE__ */ jsx(UrologyPathwayWidget, { fieldId });
|
|
11943
|
+
case "toggle": {
|
|
11944
|
+
const { value, setValue, setTouched, disabled } = useField(fieldId);
|
|
11945
|
+
const store = useFormStore();
|
|
11946
|
+
const excludes = fieldDef.excludes ?? [];
|
|
11947
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
11948
|
+
/* @__PURE__ */ jsx(
|
|
11949
|
+
Switch,
|
|
11950
|
+
{
|
|
11951
|
+
checked: value === true,
|
|
11952
|
+
disabled,
|
|
11953
|
+
onCheckedChange: (checked) => {
|
|
11954
|
+
setValue(checked);
|
|
11955
|
+
setTouched();
|
|
11956
|
+
if (checked) excludes.forEach((id) => store.setValue(id, false));
|
|
11957
|
+
}
|
|
11958
|
+
}
|
|
11959
|
+
),
|
|
11960
|
+
/* @__PURE__ */ jsx(Label, { className: "text-sm font-medium leading-none cursor-pointer select-none", children: fieldDef.label })
|
|
11961
|
+
] });
|
|
11962
|
+
}
|
|
11963
|
+
case "suggestion_textarea":
|
|
11964
|
+
case "complaint_chips":
|
|
11965
|
+
return /* @__PURE__ */ jsx(SuggestionTextareaWidget, { fieldId });
|
|
11966
|
+
case "lens_assessment":
|
|
11967
|
+
return /* @__PURE__ */ jsx(LensAssessmentWidget, { fieldId });
|
|
11968
|
+
case "functional_impairment_score":
|
|
11969
|
+
return /* @__PURE__ */ jsx(FunctionalImpairmentScoreWidget, { fieldId });
|
|
11970
|
+
case "biometry_iol_workup":
|
|
11971
|
+
return /* @__PURE__ */ jsx(BiometryIolWorkupWidget, { fieldId });
|
|
11972
|
+
case "surgical_risk_flags":
|
|
11973
|
+
return /* @__PURE__ */ jsx(SurgicalRiskFlagsWidget, { fieldId });
|
|
11974
|
+
case "static_text": {
|
|
11975
|
+
const def = fieldDef;
|
|
11976
|
+
const size = def.size ?? "regular";
|
|
11977
|
+
const labelClass = size === "large" ? "text-base" : "text-sm";
|
|
11978
|
+
const descClass = size === "large" ? "text-sm" : "text-xs";
|
|
11979
|
+
return /* @__PURE__ */ jsxs("div", { className: `${labelClass} text-muted-foreground`, children: [
|
|
11980
|
+
def.label,
|
|
11981
|
+
def.description && /* @__PURE__ */ jsx("p", { className: `mt-1 ${descClass} opacity-90`, children: def.description })
|
|
11982
|
+
] });
|
|
11983
|
+
}
|
|
11984
|
+
default:
|
|
11985
|
+
return /* @__PURE__ */ jsxs("div", { className: "text-red-500", children: [
|
|
11986
|
+
"Unknown field type: ",
|
|
11987
|
+
fieldDef.type
|
|
11988
|
+
] });
|
|
11989
|
+
}
|
|
11990
|
+
}
|
|
11991
|
+
var Section = ({ title, children }) => {
|
|
11992
|
+
const [expanded, setExpanded] = useState(true);
|
|
11993
|
+
const handleToggle = () => setExpanded((prev) => !prev);
|
|
11994
|
+
const contentClassName = cn(
|
|
11995
|
+
"overflow-hidden transition-[height] duration-200 ease-in-out",
|
|
11996
|
+
!expanded && "hidden"
|
|
11997
|
+
);
|
|
11998
|
+
return /* @__PURE__ */ jsxs("div", { className: "mb-6 border rounded-lg bg-white shadow-sm overflow-hidden", children: [
|
|
11999
|
+
/* @__PURE__ */ jsxs(
|
|
12000
|
+
"button",
|
|
12001
|
+
{
|
|
12002
|
+
type: "button",
|
|
12003
|
+
onClick: handleToggle,
|
|
12004
|
+
className: "w-full flex items-center justify-between gap-2 p-4 text-left border-b hover:bg-gray-50/80 transition-colors",
|
|
12005
|
+
children: [
|
|
12006
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-medium", children: title }),
|
|
12007
|
+
/* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-muted-foreground", "aria-hidden": true, children: expanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "h-5 w-5" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5" }) })
|
|
12008
|
+
]
|
|
12009
|
+
}
|
|
12010
|
+
),
|
|
12011
|
+
/* @__PURE__ */ jsx("div", { className: contentClassName, children: /* @__PURE__ */ jsx("div", { className: "p-4 pt-4 space-y-4 border-t-0", children }) })
|
|
12012
|
+
] });
|
|
12013
|
+
};
|
|
12014
|
+
var ColumnLayout = ({
|
|
12015
|
+
columns,
|
|
12016
|
+
label,
|
|
12017
|
+
children
|
|
12018
|
+
}) => {
|
|
12019
|
+
const gridCols = Math.max(1, Math.min(columns, 12));
|
|
12020
|
+
const gridClass = `grid gap-4 w-full`;
|
|
12021
|
+
const style = {
|
|
12022
|
+
gridTemplateColumns: `repeat(${gridCols}, minmax(0, 1fr))`
|
|
12023
|
+
};
|
|
12024
|
+
return /* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
|
|
12025
|
+
label != null && label !== "" && /* @__PURE__ */ jsx("div", { className: "mb-2 text-sm font-medium text-gray-700", children: label }),
|
|
12026
|
+
/* @__PURE__ */ jsx("div", { className: gridClass, style, children })
|
|
12027
|
+
] });
|
|
10747
12028
|
};
|
|
10748
12029
|
var DynamicForm = () => {
|
|
10749
12030
|
const store = useFormStore();
|
|
@@ -10779,6 +12060,75 @@ var DynamicForm = () => {
|
|
|
10779
12060
|
})
|
|
10780
12061
|
] });
|
|
10781
12062
|
};
|
|
12063
|
+
function PlainSection({
|
|
12064
|
+
title,
|
|
12065
|
+
showHeading,
|
|
12066
|
+
children
|
|
12067
|
+
}) {
|
|
12068
|
+
return /* @__PURE__ */ jsxs("div", { className: "mb-6 space-y-4", children: [
|
|
12069
|
+
showHeading ? /* @__PURE__ */ jsx("h2", { className: "text-base font-medium text-foreground", children: title }) : null,
|
|
12070
|
+
children
|
|
12071
|
+
] });
|
|
12072
|
+
}
|
|
12073
|
+
function renderSectionChildren(children) {
|
|
12074
|
+
return children.map((child, index) => {
|
|
12075
|
+
if (typeof child === "string") {
|
|
12076
|
+
return /* @__PURE__ */ jsx(FieldRenderer, { fieldId: child }, child);
|
|
12077
|
+
}
|
|
12078
|
+
if (child.type === "column_layout") {
|
|
12079
|
+
return /* @__PURE__ */ jsx(
|
|
12080
|
+
ColumnLayout,
|
|
12081
|
+
{
|
|
12082
|
+
columns: child.columns,
|
|
12083
|
+
label: child.label,
|
|
12084
|
+
children: child.children.map((fieldId) => /* @__PURE__ */ jsx(FieldRenderer, { fieldId }, fieldId))
|
|
12085
|
+
},
|
|
12086
|
+
child.id
|
|
12087
|
+
);
|
|
12088
|
+
}
|
|
12089
|
+
return /* @__PURE__ */ jsx(React15__default.Fragment, {}, index);
|
|
12090
|
+
});
|
|
12091
|
+
}
|
|
12092
|
+
var DynamicFormV2 = ({
|
|
12093
|
+
showTitle = true,
|
|
12094
|
+
sectionMode = "plain",
|
|
12095
|
+
showSectionTitles = true,
|
|
12096
|
+
className
|
|
12097
|
+
}) => {
|
|
12098
|
+
const store = useFormStore();
|
|
12099
|
+
const schema = store.getSchema();
|
|
12100
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-6", className), children: [
|
|
12101
|
+
showTitle ? /* @__PURE__ */ jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: schema.title }) }) : null,
|
|
12102
|
+
schema.layout.map((node) => {
|
|
12103
|
+
if (node.type === "section") {
|
|
12104
|
+
if (sectionMode === "accordion") {
|
|
12105
|
+
return /* @__PURE__ */ jsx(Section, { title: node.title, children: renderSectionChildren(node.children) }, node.id);
|
|
12106
|
+
}
|
|
12107
|
+
return /* @__PURE__ */ jsx(
|
|
12108
|
+
PlainSection,
|
|
12109
|
+
{
|
|
12110
|
+
title: node.title,
|
|
12111
|
+
showHeading: showSectionTitles,
|
|
12112
|
+
children: renderSectionChildren(node.children)
|
|
12113
|
+
},
|
|
12114
|
+
node.id
|
|
12115
|
+
);
|
|
12116
|
+
}
|
|
12117
|
+
if (node.type === "column_layout") {
|
|
12118
|
+
return /* @__PURE__ */ jsx(
|
|
12119
|
+
ColumnLayout,
|
|
12120
|
+
{
|
|
12121
|
+
columns: node.columns,
|
|
12122
|
+
label: node.label,
|
|
12123
|
+
children: node.children.map((fieldId) => /* @__PURE__ */ jsx(FieldRenderer, { fieldId }, fieldId))
|
|
12124
|
+
},
|
|
12125
|
+
node.id
|
|
12126
|
+
);
|
|
12127
|
+
}
|
|
12128
|
+
return null;
|
|
12129
|
+
})
|
|
12130
|
+
] });
|
|
12131
|
+
};
|
|
10782
12132
|
var SUGGESTION_KEYS = /* @__PURE__ */ new Set(["medications", "investigations", "procedures"]);
|
|
10783
12133
|
function shallowEqualKeys(a, b) {
|
|
10784
12134
|
const keysA = Object.keys(a);
|
|
@@ -11522,6 +12872,40 @@ var ReadOnlyFieldRenderer = ({
|
|
|
11522
12872
|
].join("\n");
|
|
11523
12873
|
return /* @__PURE__ */ jsx(ReadOnlyText, { fieldDef, value: lines });
|
|
11524
12874
|
}
|
|
12875
|
+
case "urology_smart_history": {
|
|
12876
|
+
const structuredDisplay = value && typeof value === "object" ? JSON.stringify(normalizeUrologySmartHistory(value), null, 2) : value == null || value === "" ? "\u2014" : String(value);
|
|
12877
|
+
const hopcDef = resolveFieldDef?.("symptoms");
|
|
12878
|
+
const hopcRaw = allValues?.["symptoms"];
|
|
12879
|
+
const hopcStr = typeof hopcRaw === "string" ? hopcRaw : hopcRaw != null && hopcRaw !== "" ? String(hopcRaw) : "";
|
|
12880
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
12881
|
+
/* @__PURE__ */ jsx(ReadOnlyText, { fieldDef, value: structuredDisplay }),
|
|
12882
|
+
hopcDef ? /* @__PURE__ */ jsx(ReadOnlyText, { fieldDef: hopcDef, value: hopcStr }) : hopcStr ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
12883
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "History of presenting complaint" }),
|
|
12884
|
+
/* @__PURE__ */ jsx("div", { className: "text-gray-900 border border-gray-200 rounded-md p-3 bg-gray-50 min-h-[2.5rem] whitespace-pre-wrap", children: hopcStr })
|
|
12885
|
+
] }) : null
|
|
12886
|
+
] });
|
|
12887
|
+
}
|
|
12888
|
+
case "urology_examination": {
|
|
12889
|
+
const structuredDisplay = value && typeof value === "object" ? JSON.stringify(value, null, 2) : value == null || value === "" ? "\u2014" : String(value);
|
|
12890
|
+
const clinicalDef = resolveFieldDef?.("clinical_examination");
|
|
12891
|
+
const clinicalRaw = allValues?.["clinical_examination"];
|
|
12892
|
+
const clinicalStr = typeof clinicalRaw === "string" ? clinicalRaw : clinicalRaw != null && clinicalRaw !== "" ? String(clinicalRaw) : "";
|
|
12893
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
12894
|
+
/* @__PURE__ */ jsx(ReadOnlyText, { fieldDef, value: structuredDisplay }),
|
|
12895
|
+
clinicalDef ? /* @__PURE__ */ jsx(ReadOnlyText, { fieldDef: clinicalDef, value: clinicalStr }) : clinicalStr ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
12896
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "Clinical examination" }),
|
|
12897
|
+
/* @__PURE__ */ jsx("div", { className: "text-gray-900 border border-gray-200 rounded-md p-3 bg-gray-50 min-h-[2.5rem] whitespace-pre-wrap", children: clinicalStr })
|
|
12898
|
+
] }) : null
|
|
12899
|
+
] });
|
|
12900
|
+
}
|
|
12901
|
+
case "urology_pathway":
|
|
12902
|
+
return /* @__PURE__ */ jsx(
|
|
12903
|
+
ReadOnlyText,
|
|
12904
|
+
{
|
|
12905
|
+
fieldDef,
|
|
12906
|
+
value: value && typeof value === "object" ? JSON.stringify(value, null, 2) : value == null || value === "" ? "\u2014" : String(value)
|
|
12907
|
+
}
|
|
12908
|
+
);
|
|
11525
12909
|
case "general_surgery_smart_history": {
|
|
11526
12910
|
const structuredDisplay = value && typeof value === "object" ? JSON.stringify(normalizeGeneralSurgerySmartHistory(value), null, 2) : value == null || value === "" ? "\u2014" : String(value);
|
|
11527
12911
|
const hopcDef = resolveFieldDef?.("symptoms");
|
|
@@ -11672,65 +13056,126 @@ var ReadOnlyForm = ({ schema, submission }) => {
|
|
|
11672
13056
|
};
|
|
11673
13057
|
var FormControls = ({
|
|
11674
13058
|
className,
|
|
13059
|
+
containerStyle,
|
|
13060
|
+
showWorkflowStatus = true,
|
|
13061
|
+
statusClassName,
|
|
13062
|
+
statusStyle,
|
|
13063
|
+
actionsClassName,
|
|
13064
|
+
actionsStyle,
|
|
13065
|
+
actionsFullWidth = false,
|
|
13066
|
+
buttonClassName,
|
|
11675
13067
|
onSaveDraft,
|
|
11676
13068
|
onSubmit
|
|
11677
13069
|
}) => {
|
|
11678
|
-
const {
|
|
13070
|
+
const {
|
|
13071
|
+
state,
|
|
13072
|
+
availableTransitions,
|
|
13073
|
+
submit,
|
|
13074
|
+
transition,
|
|
13075
|
+
hasPersistence,
|
|
13076
|
+
markSubmitAttempted
|
|
13077
|
+
} = useForm();
|
|
11679
13078
|
const hasUploadsInFlight = (state.pendingUploads ?? 0) > 0;
|
|
11680
13079
|
const draftAvailable = onSaveDraft || hasPersistence && submit;
|
|
11681
13080
|
const submitAvailable = onSubmit || availableTransitions.length > 0 && transition;
|
|
11682
13081
|
if (!draftAvailable && !submitAvailable) {
|
|
11683
13082
|
return null;
|
|
11684
13083
|
}
|
|
11685
|
-
|
|
11686
|
-
|
|
11687
|
-
|
|
11688
|
-
|
|
11689
|
-
|
|
11690
|
-
|
|
11691
|
-
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
onClick: onSaveDraft || submit,
|
|
11698
|
-
variant: "outline",
|
|
11699
|
-
size: "sm",
|
|
11700
|
-
disabled: hasUploadsInFlight,
|
|
11701
|
-
children: hasUploadsInFlight ? "Upload in progress\u2026" : "Save Draft"
|
|
11702
|
-
}
|
|
13084
|
+
const useWideActions = !showWorkflowStatus && actionsFullWidth;
|
|
13085
|
+
const buttonCn = cn(
|
|
13086
|
+
buttonClassName,
|
|
13087
|
+
useWideActions && "w-full sm:flex-1 min-w-0"
|
|
13088
|
+
);
|
|
13089
|
+
return /* @__PURE__ */ jsxs(
|
|
13090
|
+
"div",
|
|
13091
|
+
{
|
|
13092
|
+
className: cn(
|
|
13093
|
+
"flex gap-4 border-t bg-white p-4 items-center",
|
|
13094
|
+
showWorkflowStatus ? "justify-between" : useWideActions ? "flex-col sm:flex-row sm:justify-stretch sm:gap-4" : "justify-end",
|
|
13095
|
+
className
|
|
11703
13096
|
),
|
|
11704
|
-
|
|
11705
|
-
|
|
11706
|
-
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
|
|
11724
|
-
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
|
|
11730
|
-
|
|
11731
|
-
|
|
11732
|
-
|
|
11733
|
-
|
|
13097
|
+
style: containerStyle,
|
|
13098
|
+
children: [
|
|
13099
|
+
showWorkflowStatus ? /* @__PURE__ */ jsxs(
|
|
13100
|
+
"div",
|
|
13101
|
+
{
|
|
13102
|
+
className: cn("flex items-center gap-4", statusClassName),
|
|
13103
|
+
style: statusStyle,
|
|
13104
|
+
children: [
|
|
13105
|
+
/* @__PURE__ */ jsxs("span", { className: "font-bold text-sm text-gray-700", children: [
|
|
13106
|
+
"State: ",
|
|
13107
|
+
state.workflowState
|
|
13108
|
+
] }),
|
|
13109
|
+
/* @__PURE__ */ jsx(
|
|
13110
|
+
"span",
|
|
13111
|
+
{
|
|
13112
|
+
className: cn(
|
|
13113
|
+
"text-sm",
|
|
13114
|
+
state.isValid ? "text-green-600" : "text-red-600"
|
|
13115
|
+
),
|
|
13116
|
+
children: state.isValid ? "Valid" : "Invalid"
|
|
13117
|
+
}
|
|
13118
|
+
)
|
|
13119
|
+
]
|
|
13120
|
+
}
|
|
13121
|
+
) : null,
|
|
13122
|
+
/* @__PURE__ */ jsxs(
|
|
13123
|
+
"div",
|
|
13124
|
+
{
|
|
13125
|
+
className: cn(
|
|
13126
|
+
"flex gap-2",
|
|
13127
|
+
useWideActions ? "w-full flex-col sm:flex-row sm:flex-1 sm:justify-end sm:max-w-none" : "flex-shrink-0",
|
|
13128
|
+
actionsClassName
|
|
13129
|
+
),
|
|
13130
|
+
style: actionsStyle,
|
|
13131
|
+
children: [
|
|
13132
|
+
draftAvailable && /* @__PURE__ */ jsx(
|
|
13133
|
+
Button,
|
|
13134
|
+
{
|
|
13135
|
+
onClick: onSaveDraft || submit,
|
|
13136
|
+
variant: "outline",
|
|
13137
|
+
size: "sm",
|
|
13138
|
+
disabled: hasUploadsInFlight,
|
|
13139
|
+
className: buttonCn,
|
|
13140
|
+
children: hasUploadsInFlight ? "Upload in progress\u2026" : "Save Draft"
|
|
13141
|
+
}
|
|
13142
|
+
),
|
|
13143
|
+
submitAvailable && /* @__PURE__ */ jsx(Fragment, { children: availableTransitions.length > 0 ? availableTransitions.map((t) => /* @__PURE__ */ jsx(
|
|
13144
|
+
Button,
|
|
13145
|
+
{
|
|
13146
|
+
onClick: () => {
|
|
13147
|
+
markSubmitAttempted();
|
|
13148
|
+
if (onSubmit) {
|
|
13149
|
+
onSubmit(t.to);
|
|
13150
|
+
} else {
|
|
13151
|
+
transition(t.to);
|
|
13152
|
+
}
|
|
13153
|
+
},
|
|
13154
|
+
disabled: !state.isValid || hasUploadsInFlight,
|
|
13155
|
+
size: "sm",
|
|
13156
|
+
className: buttonCn,
|
|
13157
|
+
children: hasUploadsInFlight ? "Upload in progress\u2026" : availableTransitions.length > 1 ? `Submit to ${t.to}` : "Submit"
|
|
13158
|
+
},
|
|
13159
|
+
t.to
|
|
13160
|
+
)) : onSubmit ? /* @__PURE__ */ jsx(
|
|
13161
|
+
Button,
|
|
13162
|
+
{
|
|
13163
|
+
onClick: () => {
|
|
13164
|
+
markSubmitAttempted();
|
|
13165
|
+
onSubmit(state.workflowState || "submit");
|
|
13166
|
+
},
|
|
13167
|
+
disabled: !state.isValid || hasUploadsInFlight,
|
|
13168
|
+
size: "sm",
|
|
13169
|
+
className: buttonCn,
|
|
13170
|
+
children: hasUploadsInFlight ? "Upload in progress\u2026" : "Submit"
|
|
13171
|
+
}
|
|
13172
|
+
) : null })
|
|
13173
|
+
]
|
|
13174
|
+
}
|
|
13175
|
+
)
|
|
13176
|
+
]
|
|
13177
|
+
}
|
|
13178
|
+
);
|
|
11734
13179
|
};
|
|
11735
13180
|
|
|
11736
13181
|
// src/utils/createUploadHandler.ts
|
|
@@ -11763,6 +13208,6 @@ function createUploadHandler(options) {
|
|
|
11763
13208
|
};
|
|
11764
13209
|
}
|
|
11765
13210
|
|
|
11766
|
-
export { AddInvestigationField, AddMedicationField, DifferentialDiagnosis, DynamicForm, EMAIL_REGEX, FieldRenderer, FormControls, FormProvider, FormStore, ImageUploadWidget, MAR_MEDICATION_ORDERS_META_KEY, MediaUploadWidget, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlyMediaUpload, ReadOnlySignature, ReadOnlyTable, RichTextWidget, SignatureUploadWidget, SmartForm, SmartTextareaWidget, createUploadHandler, evaluateRules, fieldMetaRequestsMarMedicationOrdersButton, useField, useFieldHandlers, useForm, useFormStore, useFrequentItems, usePackages, useSmartKeywords, validateField, validateForm };
|
|
13211
|
+
export { AddInvestigationField, AddMedicationField, DifferentialDiagnosis, DynamicForm, DynamicFormV2, EMAIL_REGEX, FieldRenderer, FormControls, FormProvider, FormStore, ImageUploadWidget, MAR_MEDICATION_ORDERS_META_KEY, MediaUploadWidget, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlyMediaUpload, ReadOnlySignature, ReadOnlyTable, RichTextWidget, SignatureUploadWidget, SmartForm, SmartTextareaWidget, createUploadHandler, evaluateRules, fieldMetaRequestsMarMedicationOrdersButton, useField, useFieldHandlers, useForm, useFormStore, useFrequentItems, usePackages, useSmartKeywords, validateField, validateForm };
|
|
11767
13212
|
//# sourceMappingURL=index.mjs.map
|
|
11768
13213
|
//# sourceMappingURL=index.mjs.map
|