formanitor 0.0.41 → 0.0.43
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 +792 -871
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -40
- package/dist/index.d.ts +24 -40
- package/dist/index.mjs +793 -869
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -65,326 +65,6 @@ function fieldMetaRequestsMarMedicationOrdersButton(meta) {
|
|
|
65
65
|
return meta[MAR_MEDICATION_ORDERS_META_KEY] === true;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
// src/core/urologyClinicalPathwayMetadata.ts
|
|
69
|
-
function isUrologyTemplate(template) {
|
|
70
|
-
if (!template || typeof template !== "object") return false;
|
|
71
|
-
const t = template;
|
|
72
|
-
return t.meta?.specialization === "urology" || t.specialization === "urology" || t.id === "uro_prescription";
|
|
73
|
-
}
|
|
74
|
-
function shouldFoldUrologyClinicalPathway(template, payload) {
|
|
75
|
-
if (isUrologyTemplate(template)) return true;
|
|
76
|
-
return Object.keys(payload).some(
|
|
77
|
-
(k) => k.startsWith("uro_") || k === "chief_complaints" || k === "chief_complaints_chips"
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
var UROLOGY_EXTRA_METADATA_KEYS = ["symptoms", "notes", "vitals"];
|
|
81
|
-
function isUroPlainObject(v) {
|
|
82
|
-
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
83
|
-
}
|
|
84
|
-
function coerceUrologyBool(value) {
|
|
85
|
-
if (value === true || value === false) return value;
|
|
86
|
-
if (value === "true") return true;
|
|
87
|
-
if (value === "false") return false;
|
|
88
|
-
if (Array.isArray(value)) {
|
|
89
|
-
if (value.includes("yes") || value.includes(true)) return true;
|
|
90
|
-
if (value.length === 0) return false;
|
|
91
|
-
}
|
|
92
|
-
return value;
|
|
93
|
-
}
|
|
94
|
-
function urologyIpssItem(v) {
|
|
95
|
-
const n = Number(v);
|
|
96
|
-
if (!Number.isFinite(n)) return 0;
|
|
97
|
-
return Math.min(5, Math.max(0, Math.round(n)));
|
|
98
|
-
}
|
|
99
|
-
function urologyIpssSevenTotalFromParts(ipss) {
|
|
100
|
-
return ipss.incomplete + ipss.frequency + ipss.intermittency + ipss.urgency + ipss.weakStream + ipss.straining + ipss.nocturia;
|
|
101
|
-
}
|
|
102
|
-
function buildUrologySmartHistoryFromLegacyFlat(flat) {
|
|
103
|
-
return {
|
|
104
|
-
socrates_pain_flank_suprapubic: {
|
|
105
|
-
site: flat.uro_sh_socrates_site,
|
|
106
|
-
onset: flat.uro_sh_socrates_onset,
|
|
107
|
-
character: flat.uro_sh_socrates_character,
|
|
108
|
-
radiation: flat.uro_sh_socrates_radiation,
|
|
109
|
-
associated: flat.uro_sh_socrates_associated,
|
|
110
|
-
timing: flat.uro_sh_socrates_timing,
|
|
111
|
-
exacerbating: flat.uro_sh_socrates_exacerbating,
|
|
112
|
-
severity_0_to_10: flat.uro_sh_socrates_severity
|
|
113
|
-
},
|
|
114
|
-
ipss_international_symptom_score: {
|
|
115
|
-
incomplete_emptying: flat.uro_sh_ipss_incomplete,
|
|
116
|
-
frequency_lt_2h: flat.uro_sh_ipss_frequency,
|
|
117
|
-
intermittency: flat.uro_sh_ipss_intermittency,
|
|
118
|
-
urgency: flat.uro_sh_ipss_urgency,
|
|
119
|
-
weak_stream: flat.uro_sh_ipss_weak_stream,
|
|
120
|
-
straining: flat.uro_sh_ipss_straining,
|
|
121
|
-
nocturia: flat.uro_sh_ipss_nocturia,
|
|
122
|
-
qol_0_to_6: flat.uro_sh_ipss_qol,
|
|
123
|
-
seven_item_total_computed_max_35: flat.uro_sh_ipss_total_display
|
|
124
|
-
},
|
|
125
|
-
haematuria_profile: {
|
|
126
|
-
present: flat.uro_sh_haem_present,
|
|
127
|
-
timing: flat.uro_sh_haem_timing,
|
|
128
|
-
painful: flat.uro_sh_haem_painful,
|
|
129
|
-
clots: flat.uro_sh_haem_clots
|
|
130
|
-
},
|
|
131
|
-
sexual_and_reproductive: {
|
|
132
|
-
erectile_function: flat.uro_sh_sex_erectile,
|
|
133
|
-
libido: flat.uro_sh_sex_libido,
|
|
134
|
-
ejaculation: flat.uro_sh_sex_ejaculation,
|
|
135
|
-
infertility_months: flat.uro_sh_sex_infertility_months
|
|
136
|
-
},
|
|
137
|
-
context_prior_stones_surgery_comorbidity: flat.uro_smart_notes_free ?? ""
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
function buildUrologySmartHistoryFromComposite(raw, flat) {
|
|
141
|
-
const soc = isUroPlainObject(raw.socrates) ? raw.socrates : {};
|
|
142
|
-
const ipssIn = isUroPlainObject(raw.ipss) ? raw.ipss : {};
|
|
143
|
-
const haem = isUroPlainObject(raw.haematuria) ? raw.haematuria : {};
|
|
144
|
-
const sex = isUroPlainObject(raw.sexual) ? raw.sexual : {};
|
|
145
|
-
const str2 = (x) => typeof x === "string" ? x : "";
|
|
146
|
-
const sev = Number(soc.severity);
|
|
147
|
-
const severity0to10 = Number.isFinite(sev) ? Math.min(10, Math.max(0, Math.round(sev))) : 0;
|
|
148
|
-
const ipss = {
|
|
149
|
-
incomplete: urologyIpssItem(ipssIn.incomplete),
|
|
150
|
-
frequency: urologyIpssItem(ipssIn.frequency),
|
|
151
|
-
intermittency: urologyIpssItem(ipssIn.intermittency),
|
|
152
|
-
urgency: urologyIpssItem(ipssIn.urgency),
|
|
153
|
-
weakStream: urologyIpssItem(
|
|
154
|
-
ipssIn.weakStream ?? ipssIn.weak_stream
|
|
155
|
-
),
|
|
156
|
-
straining: urologyIpssItem(ipssIn.straining),
|
|
157
|
-
nocturia: urologyIpssItem(ipssIn.nocturia)
|
|
158
|
-
};
|
|
159
|
-
const sevenTotal = urologyIpssSevenTotalFromParts(ipss);
|
|
160
|
-
const qolRaw = Number(raw.ipssQoL);
|
|
161
|
-
const qol = Number.isFinite(qolRaw) ? Math.min(6, Math.max(0, Math.round(qolRaw))) : 0;
|
|
162
|
-
const timingRaw = str2(haem.timing);
|
|
163
|
-
const pastNotes = str2(raw.pastNotes);
|
|
164
|
-
const legacyNotes = flat.uro_smart_notes_free;
|
|
165
|
-
const contextPrior = pastNotes !== "" ? pastNotes : typeof legacyNotes === "string" ? legacyNotes : legacyNotes ?? "";
|
|
166
|
-
const infertilityRaw = Number(sex.infertilityMonths);
|
|
167
|
-
const infertilityMonths = Number.isFinite(infertilityRaw) ? Math.max(0, Math.round(infertilityRaw)) : 0;
|
|
168
|
-
return {
|
|
169
|
-
socrates_pain_flank_suprapubic: {
|
|
170
|
-
site: str2(soc.site),
|
|
171
|
-
onset: str2(soc.onset),
|
|
172
|
-
character: str2(soc.character),
|
|
173
|
-
radiation: str2(soc.radiation),
|
|
174
|
-
associated: str2(soc.associated),
|
|
175
|
-
timing: str2(soc.timing),
|
|
176
|
-
exacerbating: str2(soc.exacerbating),
|
|
177
|
-
severity_0_to_10: severity0to10
|
|
178
|
-
},
|
|
179
|
-
ipss_international_symptom_score: {
|
|
180
|
-
incomplete_emptying: ipss.incomplete,
|
|
181
|
-
frequency_lt_2h: ipss.frequency,
|
|
182
|
-
intermittency: ipss.intermittency,
|
|
183
|
-
urgency: ipss.urgency,
|
|
184
|
-
weak_stream: ipss.weakStream,
|
|
185
|
-
straining: ipss.straining,
|
|
186
|
-
nocturia: ipss.nocturia,
|
|
187
|
-
qol_0_to_6: qol,
|
|
188
|
-
seven_item_total_computed_max_35: sevenTotal
|
|
189
|
-
},
|
|
190
|
-
haematuria_profile: {
|
|
191
|
-
present: haem.present === true,
|
|
192
|
-
timing: timingRaw,
|
|
193
|
-
painful: haem.painful === true,
|
|
194
|
-
clots: haem.clots === true
|
|
195
|
-
},
|
|
196
|
-
sexual_and_reproductive: {
|
|
197
|
-
erectile_function: str2(sex.erectile),
|
|
198
|
-
libido: str2(sex.libido),
|
|
199
|
-
ejaculation: str2(sex.ejaculation),
|
|
200
|
-
infertility_months: infertilityMonths
|
|
201
|
-
},
|
|
202
|
-
context_prior_stones_surgery_comorbidity: contextPrior
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
function buildUrologyExaminationFromLegacyFlat(flat) {
|
|
206
|
-
return {
|
|
207
|
-
general: {
|
|
208
|
-
pallor: flat.uro_ex_general_pallor,
|
|
209
|
-
oedema: flat.uro_ex_general_oedema,
|
|
210
|
-
icterus: flat.uro_ex_general_icterus,
|
|
211
|
-
lymphadenopathy: flat.uro_ex_general_lymph
|
|
212
|
-
},
|
|
213
|
-
abdomen: {
|
|
214
|
-
distension: flat.uro_ex_ab_distension,
|
|
215
|
-
surgical_scars: flat.uro_ex_ab_scars,
|
|
216
|
-
visible_mass: flat.uro_ex_ab_mass,
|
|
217
|
-
kidney_palpable: flat.uro_ex_ab_kidney_palpable,
|
|
218
|
-
bladder_palpable: flat.uro_ex_ab_bladder_palpable,
|
|
219
|
-
renal_angle_tender: flat.uro_ex_ab_renal_angle_tender,
|
|
220
|
-
bladder_dullness_retention: flat.uro_ex_ab_bladder_dull
|
|
221
|
-
},
|
|
222
|
-
external_genitalia_scrotum: {
|
|
223
|
-
meatus_abnormal_hypospadias_query: flat.uro_ex_gu_meatus,
|
|
224
|
-
phimosis_paraphimosis: flat.uro_ex_gu_phimosis,
|
|
225
|
-
penile_lesions_ulcers: flat.uro_ex_gu_lesions,
|
|
226
|
-
scrotal_swelling: flat.uro_ex_gu_scrotal_swelling,
|
|
227
|
-
transilluminant: flat.uro_ex_gu_transilluminant,
|
|
228
|
-
tender_testis: flat.uro_ex_gu_tender_testis,
|
|
229
|
-
hydrocele: flat.uro_ex_gu_hydrocele,
|
|
230
|
-
varicocele: flat.uro_ex_gu_varicocele,
|
|
231
|
-
suspicious_mass_urgent: flat.uro_ex_gu_suspicious_mass
|
|
232
|
-
},
|
|
233
|
-
dre_prostate: {
|
|
234
|
-
dre_performed: flat.uro_ex_dre_done,
|
|
235
|
-
estimated_size_grams: flat.uro_ex_dre_size_g,
|
|
236
|
-
consistency: flat.uro_ex_dre_consistency,
|
|
237
|
-
median_sulcus: flat.uro_ex_dre_median_sulcus,
|
|
238
|
-
tenderness_prostatitis_query: flat.uro_ex_dre_tender
|
|
239
|
-
},
|
|
240
|
-
examination_notes_free_text: flat.uro_examination_notes ?? ""
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
function buildUrologyExaminationFromComposite(raw) {
|
|
244
|
-
const regions = Array.isArray(raw.regions) ? raw.regions.filter((x) => typeof x === "string") : [];
|
|
245
|
-
const g = isUroPlainObject(raw.general) ? raw.general : {};
|
|
246
|
-
const ab = isUroPlainObject(raw.abdomen) ? raw.abdomen : {};
|
|
247
|
-
const gu = isUroPlainObject(raw.gu) ? raw.gu : {};
|
|
248
|
-
const dr = isUroPlainObject(raw.dre) ? raw.dre : {};
|
|
249
|
-
const dreConsistency = typeof dr.consistency === "string" ? dr.consistency : "";
|
|
250
|
-
const dreMedian = typeof dr.medianSulcus === "string" ? dr.medianSulcus : typeof dr.median_sulcus === "string" ? dr.median_sulcus : "";
|
|
251
|
-
const sizeG = Number(dr.sizeGrams);
|
|
252
|
-
const sizeGrams = Number.isFinite(sizeG) && sizeG >= 0 ? Math.round(sizeG) : 0;
|
|
253
|
-
const notesRaw = raw.examinationNotes;
|
|
254
|
-
const notes = typeof notesRaw === "string" ? notesRaw : "";
|
|
255
|
-
const bool = (x) => x === true;
|
|
256
|
-
return {
|
|
257
|
-
regions_reviewed: regions,
|
|
258
|
-
general: {
|
|
259
|
-
pallor: bool(g.pallor),
|
|
260
|
-
oedema: bool(g.oedema),
|
|
261
|
-
icterus: bool(g.icterus),
|
|
262
|
-
lymphadenopathy: bool(g.lymphadenopathy)
|
|
263
|
-
},
|
|
264
|
-
abdomen: {
|
|
265
|
-
distension: bool(ab.distension),
|
|
266
|
-
surgical_scars: bool(ab.scars),
|
|
267
|
-
visible_mass: bool(ab.mass),
|
|
268
|
-
kidney_palpable: bool(ab.kidneyPalpable),
|
|
269
|
-
bladder_palpable: bool(ab.bladderPalpable),
|
|
270
|
-
renal_angle_tender: bool(ab.renalAngleTender),
|
|
271
|
-
bladder_dullness_retention: bool(ab.bladderDull)
|
|
272
|
-
},
|
|
273
|
-
external_genitalia_scrotum: {
|
|
274
|
-
meatus_abnormal_hypospadias_query: bool(gu.meatusAbnormal),
|
|
275
|
-
phimosis_paraphimosis: bool(gu.phimosis),
|
|
276
|
-
penile_lesions_ulcers: bool(gu.lesions),
|
|
277
|
-
scrotal_swelling: bool(gu.scrotalSwelling),
|
|
278
|
-
transilluminant: bool(gu.transilluminant),
|
|
279
|
-
tender_testis: bool(gu.tenderTestis),
|
|
280
|
-
hydrocele: bool(gu.hydrocele),
|
|
281
|
-
varicocele: bool(gu.varicocele),
|
|
282
|
-
suspicious_mass_urgent: bool(gu.suspiciousMass)
|
|
283
|
-
},
|
|
284
|
-
dre_prostate: {
|
|
285
|
-
dre_performed: bool(dr.done),
|
|
286
|
-
estimated_size_grams: sizeGrams,
|
|
287
|
-
consistency: dreConsistency === "none" ? "" : dreConsistency,
|
|
288
|
-
median_sulcus: dreMedian === "none" ? "" : dreMedian,
|
|
289
|
-
tenderness_prostatitis_query: bool(dr.tenderness)
|
|
290
|
-
},
|
|
291
|
-
examination_notes_free_text: notes
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
function buildUrologyDecisionEngineAndRisk(flat) {
|
|
295
|
-
const comp = flat.uro_pathway;
|
|
296
|
-
if (isUroPlainObject(comp)) {
|
|
297
|
-
const riskIn = isUroPlainObject(comp.risk) ? comp.risk : {};
|
|
298
|
-
return {
|
|
299
|
-
decision_engine: {
|
|
300
|
-
stone_disease: {
|
|
301
|
-
stone_size_mm: comp.stoneSizeMm ?? 0,
|
|
302
|
-
hydronephrosis_on_imaging: coerceUrologyBool(comp.hydronephrosis)
|
|
303
|
-
},
|
|
304
|
-
prostate_bph_flow_lab: {
|
|
305
|
-
prostate_volume_cc: comp.prostateVolumeCc ?? 0,
|
|
306
|
-
psa_ng_ml: comp.psaNgMl ?? 0,
|
|
307
|
-
uroflow_qmax_ml_s: comp.uroflowQmaxMlSec ?? 0
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
|
-
risk_stratification: {
|
|
311
|
-
ckd: coerceUrologyBool(riskIn.ckd),
|
|
312
|
-
diabetes: coerceUrologyBool(riskIn.diabetes),
|
|
313
|
-
anticoagulants: coerceUrologyBool(riskIn.anticoagulants),
|
|
314
|
-
active_infection: coerceUrologyBool(riskIn.activeInfection)
|
|
315
|
-
}
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
return {
|
|
319
|
-
decision_engine: {
|
|
320
|
-
stone_disease: {
|
|
321
|
-
stone_size_mm: flat.uro_stone_size_mm,
|
|
322
|
-
hydronephrosis_on_imaging: coerceUrologyBool(flat.uro_stone_hydronephrosis)
|
|
323
|
-
},
|
|
324
|
-
prostate_bph_flow_lab: {
|
|
325
|
-
prostate_volume_cc: flat.uro_prostate_vol_cc,
|
|
326
|
-
psa_ng_ml: flat.uro_psa_ng_ml,
|
|
327
|
-
uroflow_qmax_ml_s: flat.uro_flow_qmax
|
|
328
|
-
}
|
|
329
|
-
},
|
|
330
|
-
risk_stratification: {
|
|
331
|
-
ckd: coerceUrologyBool(flat.uro_risk_ckd),
|
|
332
|
-
diabetes: coerceUrologyBool(flat.uro_risk_diabetes),
|
|
333
|
-
anticoagulants: coerceUrologyBool(flat.uro_risk_anticoag),
|
|
334
|
-
active_infection: coerceUrologyBool(flat.uro_risk_active_infection)
|
|
335
|
-
}
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
function attachUrologyClinicalPathwayMetadata(payload, template) {
|
|
339
|
-
if (!shouldFoldUrologyClinicalPathway(template, payload)) return;
|
|
340
|
-
const extra = UROLOGY_EXTRA_METADATA_KEYS;
|
|
341
|
-
const keys = Object.keys(payload).filter(
|
|
342
|
-
(k) => k.startsWith("uro_") || k === "chief_complaints" || k === "chief_complaints_chips" || extra.includes(k)
|
|
343
|
-
);
|
|
344
|
-
if (keys.length === 0) return;
|
|
345
|
-
const flat = {};
|
|
346
|
-
for (const k of keys) {
|
|
347
|
-
flat[k] = payload[k];
|
|
348
|
-
delete payload[k];
|
|
349
|
-
}
|
|
350
|
-
const triage = flat.uro_triage;
|
|
351
|
-
const chiefComplaints = {
|
|
352
|
-
narrative: flat.chief_complaints ?? "",
|
|
353
|
-
chips: flat.chief_complaints_chips ?? [],
|
|
354
|
-
duration: flat.uro_chief_duration ?? "",
|
|
355
|
-
onset: flat.uro_chief_onset ?? "",
|
|
356
|
-
progression: flat.uro_chief_progression ?? "",
|
|
357
|
-
active_syndrome: flat.uro_active_syndrome ?? ""
|
|
358
|
-
};
|
|
359
|
-
const compositeSh = flat.uro_smart_history;
|
|
360
|
-
const compositeEx = flat.uro_examination;
|
|
361
|
-
const smart_history = isUroPlainObject(compositeSh) ? buildUrologySmartHistoryFromComposite(compositeSh, flat) : buildUrologySmartHistoryFromLegacyFlat(flat);
|
|
362
|
-
const examination = isUroPlainObject(compositeEx) ? buildUrologyExaminationFromComposite(compositeEx) : buildUrologyExaminationFromLegacyFlat(flat);
|
|
363
|
-
const { decision_engine, risk_stratification } = buildUrologyDecisionEngineAndRisk(flat);
|
|
364
|
-
const structured = {
|
|
365
|
-
triage_vas_and_flags: triage ?? null,
|
|
366
|
-
chief_complaint_engine: chiefComplaints,
|
|
367
|
-
smart_history,
|
|
368
|
-
examination,
|
|
369
|
-
pathway_triggers_objective_scores: decision_engine,
|
|
370
|
-
risk_stratification,
|
|
371
|
-
encounter_narrative: {
|
|
372
|
-
symptoms: flat.symptoms ?? "",
|
|
373
|
-
notes: flat.notes ?? "",
|
|
374
|
-
vitals: flat.vitals ?? ""
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
|
-
const existingMetadata = payload.metadata ?? {};
|
|
378
|
-
const existingPathways = existingMetadata.clinical_pathways ?? {};
|
|
379
|
-
payload.metadata = {
|
|
380
|
-
...existingMetadata,
|
|
381
|
-
clinical_pathways: {
|
|
382
|
-
...existingPathways,
|
|
383
|
-
urology: structured
|
|
384
|
-
}
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
|
|
388
68
|
// src/core/validate.ts
|
|
389
69
|
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
390
70
|
function validateField(value, field) {
|
|
@@ -457,12 +137,82 @@ function validateForm(values, fields) {
|
|
|
457
137
|
return errors;
|
|
458
138
|
}
|
|
459
139
|
|
|
140
|
+
// src/core/suggestionTokens.ts
|
|
141
|
+
function getSuggestionTokensFromText(text) {
|
|
142
|
+
return text.split(/[,\n\r]+/).map((p) => p.trim()).filter(Boolean);
|
|
143
|
+
}
|
|
144
|
+
var DEFAULT_TOKEN_SEPARATOR = ", ";
|
|
145
|
+
function commaSplitLine(line) {
|
|
146
|
+
return line.split(",").map((p) => p.trim()).filter(Boolean);
|
|
147
|
+
}
|
|
148
|
+
function dedupeChipOrder(chips) {
|
|
149
|
+
const seen = /* @__PURE__ */ new Set();
|
|
150
|
+
const out = [];
|
|
151
|
+
for (const c of chips) {
|
|
152
|
+
if (seen.has(c)) continue;
|
|
153
|
+
seen.add(c);
|
|
154
|
+
out.push(c);
|
|
155
|
+
}
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
function parseProseAndChipTokens(text, allowed) {
|
|
159
|
+
const normalized = text.replace(/\r\n/g, "\n");
|
|
160
|
+
let lines = normalized.split("\n");
|
|
161
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
162
|
+
lines.pop();
|
|
163
|
+
}
|
|
164
|
+
if (lines.length === 0) return { prose: "", chips: [] };
|
|
165
|
+
let chipStart = lines.length;
|
|
166
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
167
|
+
const toks = commaSplitLine(lines[i]);
|
|
168
|
+
if (toks.length === 0) break;
|
|
169
|
+
if (!toks.every((t) => allowed.has(t))) break;
|
|
170
|
+
chipStart = i;
|
|
171
|
+
}
|
|
172
|
+
if (chipStart < lines.length) {
|
|
173
|
+
const chipLines = lines.slice(chipStart);
|
|
174
|
+
const prose = lines.slice(0, chipStart).join("\n");
|
|
175
|
+
const chips = dedupeChipOrder(chipLines.flatMap(commaSplitLine));
|
|
176
|
+
return { prose, chips };
|
|
177
|
+
}
|
|
178
|
+
const parts = getSuggestionTokensFromText(normalized);
|
|
179
|
+
const chipsFallback = dedupeChipOrder(parts.filter((t) => allowed.has(t)));
|
|
180
|
+
const proseParts = parts.filter((t) => !allowed.has(t));
|
|
181
|
+
return { prose: proseParts.join(DEFAULT_TOKEN_SEPARATOR), chips: chipsFallback };
|
|
182
|
+
}
|
|
183
|
+
function composeProseAndChips(prose, chips, separator = DEFAULT_TOKEN_SEPARATOR) {
|
|
184
|
+
const chipStr = dedupeChipOrder(chips).join(separator);
|
|
185
|
+
const p = prose.replace(/\s+$/, "");
|
|
186
|
+
if (p && chipStr) return `${p}
|
|
187
|
+
${chipStr}`;
|
|
188
|
+
if (chipStr) return chipStr;
|
|
189
|
+
return p;
|
|
190
|
+
}
|
|
191
|
+
function toggleSuggestionTokenInText(current, token, separator = DEFAULT_TOKEN_SEPARATOR, allowedChipValues) {
|
|
192
|
+
const t = String(token).trim();
|
|
193
|
+
if (!t) return current;
|
|
194
|
+
if (!allowedChipValues || allowedChipValues.size === 0) {
|
|
195
|
+
const parts = getSuggestionTokensFromText(current);
|
|
196
|
+
const i = parts.findIndex((p) => p === t);
|
|
197
|
+
if (i >= 0) parts.splice(i, 1);
|
|
198
|
+
else parts.push(t);
|
|
199
|
+
return parts.join(separator);
|
|
200
|
+
}
|
|
201
|
+
if (!allowedChipValues.has(t)) return current;
|
|
202
|
+
let { prose, chips } = parseProseAndChipTokens(current, allowedChipValues);
|
|
203
|
+
const idx = chips.indexOf(t);
|
|
204
|
+
if (idx >= 0) chips.splice(idx, 1);
|
|
205
|
+
else chips.push(t);
|
|
206
|
+
chips = dedupeChipOrder(chips);
|
|
207
|
+
return composeProseAndChips(prose, chips, separator);
|
|
208
|
+
}
|
|
209
|
+
|
|
460
210
|
// src/core/rules.ts
|
|
461
211
|
function tokenListContainsString(text, needle) {
|
|
462
212
|
if (typeof text !== "string") return false;
|
|
463
213
|
const want = String(needle).trim();
|
|
464
214
|
if (!want) return false;
|
|
465
|
-
const parts = text
|
|
215
|
+
const parts = getSuggestionTokensFromText(text);
|
|
466
216
|
return parts.includes(want);
|
|
467
217
|
}
|
|
468
218
|
function evaluateCondition(value, condition) {
|
|
@@ -673,7 +423,7 @@ function normalizeGeneralSurgerySmartHistory(raw) {
|
|
|
673
423
|
...painIn,
|
|
674
424
|
severity: Number.isFinite(sev) ? Math.min(10, Math.max(0, Math.round(sev))) : d.pain.severity
|
|
675
425
|
};
|
|
676
|
-
const
|
|
426
|
+
const str4 = (x) => typeof x === "string" ? x : "";
|
|
677
427
|
const strArr = (x) => Array.isArray(x) ? x.filter((i) => typeof i === "string") : [];
|
|
678
428
|
const swell = v.swelling && typeof v.swelling === "object" ? v.swelling : {};
|
|
679
429
|
const gi = v.gi && typeof v.gi === "object" ? v.gi : {};
|
|
@@ -683,44 +433,44 @@ function normalizeGeneralSurgerySmartHistory(raw) {
|
|
|
683
433
|
return {
|
|
684
434
|
pain,
|
|
685
435
|
swelling: {
|
|
686
|
-
duration:
|
|
687
|
-
sizeProgression:
|
|
688
|
-
painfulPainless:
|
|
436
|
+
duration: str4(swell.duration),
|
|
437
|
+
sizeProgression: str4(swell.sizeProgression),
|
|
438
|
+
painfulPainless: str4(swell.painfulPainless),
|
|
689
439
|
checks: strArr(swell.checks)
|
|
690
440
|
},
|
|
691
441
|
gi: {
|
|
692
|
-
appetite:
|
|
693
|
-
weightLoss:
|
|
694
|
-
bowelHabits:
|
|
442
|
+
appetite: str4(gi.appetite),
|
|
443
|
+
weightLoss: str4(gi.weightLoss),
|
|
444
|
+
bowelHabits: str4(gi.bowelHabits),
|
|
695
445
|
checks: strArr(gi.checks)
|
|
696
446
|
},
|
|
697
447
|
breast: {
|
|
698
|
-
lumpDuration:
|
|
699
|
-
nippleDischarge:
|
|
448
|
+
lumpDuration: str4(breast.lumpDuration),
|
|
449
|
+
nippleDischarge: str4(breast.nippleDischarge),
|
|
700
450
|
checks: strArr(breast.checks)
|
|
701
451
|
},
|
|
702
452
|
ano: { checks: strArr(ano.checks) },
|
|
703
453
|
thyroid: {
|
|
704
|
-
swellingDuration:
|
|
454
|
+
swellingDuration: str4(thy.swellingDuration),
|
|
705
455
|
checks: strArr(thy.checks)
|
|
706
456
|
}
|
|
707
457
|
};
|
|
708
458
|
}
|
|
709
459
|
function normalizeBreastExamSide(partial) {
|
|
710
460
|
const e = defaultBreastExamSide();
|
|
711
|
-
const
|
|
712
|
-
const
|
|
461
|
+
const str4 = (x) => typeof x === "string" ? x : "";
|
|
462
|
+
const bool2 = (x) => x === true;
|
|
713
463
|
if (!partial || typeof partial !== "object") return e;
|
|
714
464
|
const p = partial;
|
|
715
465
|
return {
|
|
716
|
-
size:
|
|
717
|
-
quadrant:
|
|
718
|
-
tenderness:
|
|
719
|
-
fixity:
|
|
720
|
-
skinInvolvement:
|
|
721
|
-
consistency:
|
|
722
|
-
axillaryNodes:
|
|
723
|
-
nippleAreolarComplex:
|
|
466
|
+
size: str4(p.size),
|
|
467
|
+
quadrant: str4(p.quadrant),
|
|
468
|
+
tenderness: str4(p.tenderness),
|
|
469
|
+
fixity: str4(p.fixity),
|
|
470
|
+
skinInvolvement: str4(p.skinInvolvement),
|
|
471
|
+
consistency: str4(p.consistency),
|
|
472
|
+
axillaryNodes: bool2(p.axillaryNodes),
|
|
473
|
+
nippleAreolarComplex: bool2(p.nippleAreolarComplex)
|
|
724
474
|
};
|
|
725
475
|
}
|
|
726
476
|
function normalizeBreastExaminationBlock(raw) {
|
|
@@ -744,7 +494,7 @@ function normalizeGeneralSurgeryExamination(raw) {
|
|
|
744
494
|
const th = v.thyroid && typeof v.thyroid === "object" ? v.thyroid : {};
|
|
745
495
|
const ar = v.anorectal && typeof v.anorectal === "object" ? v.anorectal : {};
|
|
746
496
|
const lm = v.limb && typeof v.limb === "object" ? v.limb : {};
|
|
747
|
-
const
|
|
497
|
+
const bool2 = (x) => x === true;
|
|
748
498
|
return {
|
|
749
499
|
general: strArr(v.general),
|
|
750
500
|
regions: strArr(v.regions),
|
|
@@ -757,27 +507,27 @@ function normalizeGeneralSurgeryExamination(raw) {
|
|
|
757
507
|
},
|
|
758
508
|
hernia: {
|
|
759
509
|
site: typeof hb.site === "string" ? hb.site : "",
|
|
760
|
-
reducible:
|
|
761
|
-
coughImpulse:
|
|
762
|
-
tenderness:
|
|
510
|
+
reducible: bool2(hb.reducible),
|
|
511
|
+
coughImpulse: bool2(hb.coughImpulse),
|
|
512
|
+
tenderness: bool2(hb.tenderness)
|
|
763
513
|
},
|
|
764
514
|
breast: normalizeBreastExaminationBlock(br),
|
|
765
515
|
thyroid: {
|
|
766
516
|
size: typeof th.size === "string" ? th.size : "",
|
|
767
|
-
mobileDeglutition:
|
|
768
|
-
nodules:
|
|
769
|
-
cervicalNodes:
|
|
517
|
+
mobileDeglutition: bool2(th.mobileDeglutition),
|
|
518
|
+
nodules: bool2(th.nodules),
|
|
519
|
+
cervicalNodes: bool2(th.cervicalNodes)
|
|
770
520
|
},
|
|
771
521
|
anorectal: {
|
|
772
522
|
inspection: typeof ar.inspection === "string" ? ar.inspection : "",
|
|
773
|
-
dreDone:
|
|
774
|
-
proctoscopyDone:
|
|
523
|
+
dreDone: bool2(ar.dreDone),
|
|
524
|
+
proctoscopyDone: bool2(ar.proctoscopyDone),
|
|
775
525
|
notes: typeof ar.notes === "string" ? ar.notes : ""
|
|
776
526
|
},
|
|
777
527
|
limb: {
|
|
778
|
-
dilatedVeins:
|
|
779
|
-
ulcer:
|
|
780
|
-
oedema:
|
|
528
|
+
dilatedVeins: bool2(lm.dilatedVeins),
|
|
529
|
+
ulcer: bool2(lm.ulcer),
|
|
530
|
+
oedema: bool2(lm.oedema)
|
|
781
531
|
}
|
|
782
532
|
};
|
|
783
533
|
}
|
|
@@ -1054,70 +804,102 @@ function formatGeneralSurgeryExaminationToNarrative(v) {
|
|
|
1054
804
|
}
|
|
1055
805
|
|
|
1056
806
|
// src/core/urologyPathway.ts
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
807
|
+
function defaultUrologySmartHistory() {
|
|
808
|
+
return {
|
|
809
|
+
socrates: {
|
|
810
|
+
site: "",
|
|
811
|
+
onset: "",
|
|
812
|
+
character: "",
|
|
813
|
+
radiation: "",
|
|
814
|
+
associated: "",
|
|
815
|
+
timing: "",
|
|
816
|
+
exacerbating: "",
|
|
817
|
+
severity: 0
|
|
818
|
+
},
|
|
819
|
+
ipss: {
|
|
820
|
+
incomplete: 0,
|
|
821
|
+
frequency: 0,
|
|
822
|
+
intermittency: 0,
|
|
823
|
+
urgency: 0,
|
|
824
|
+
weakStream: 0,
|
|
825
|
+
straining: 0,
|
|
826
|
+
nocturia: 0
|
|
827
|
+
},
|
|
828
|
+
ipssQoL: 0,
|
|
829
|
+
haematuria: {
|
|
830
|
+
present: false,
|
|
831
|
+
timing: "none",
|
|
832
|
+
painful: false,
|
|
833
|
+
clots: false
|
|
834
|
+
},
|
|
835
|
+
sexual: {
|
|
836
|
+
erectile: "none",
|
|
837
|
+
libido: "none",
|
|
838
|
+
ejaculation: "none",
|
|
839
|
+
infertilityMonths: 0
|
|
840
|
+
},
|
|
841
|
+
pastNotes: ""
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
function defaultUrologyExamination() {
|
|
845
|
+
return {
|
|
846
|
+
regions: [],
|
|
847
|
+
general: {
|
|
848
|
+
pallor: false,
|
|
849
|
+
oedema: false,
|
|
850
|
+
icterus: false,
|
|
851
|
+
lymphadenopathy: false
|
|
852
|
+
},
|
|
853
|
+
abdomen: {
|
|
854
|
+
distension: false,
|
|
855
|
+
scars: false,
|
|
856
|
+
mass: false,
|
|
857
|
+
kidneyPalpable: false,
|
|
858
|
+
bladderPalpable: false,
|
|
859
|
+
renalAngleTender: false,
|
|
860
|
+
bladderDull: false
|
|
861
|
+
},
|
|
862
|
+
gu: {
|
|
863
|
+
meatusAbnormal: false,
|
|
864
|
+
phimosis: false,
|
|
865
|
+
lesions: false,
|
|
866
|
+
scrotalSwelling: false,
|
|
867
|
+
transilluminant: false,
|
|
868
|
+
tenderTestis: false,
|
|
869
|
+
hydrocele: false,
|
|
870
|
+
varicocele: false,
|
|
871
|
+
suspiciousMass: false
|
|
872
|
+
},
|
|
873
|
+
dre: {
|
|
874
|
+
done: false,
|
|
875
|
+
sizeGrams: 0,
|
|
876
|
+
consistency: "",
|
|
877
|
+
medianSulcus: "",
|
|
878
|
+
tenderness: false
|
|
879
|
+
},
|
|
880
|
+
examinationNotes: ""
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
function defaultUrologyPathway() {
|
|
884
|
+
return {
|
|
885
|
+
stoneSizeMm: 0,
|
|
886
|
+
hydronephrosis: false,
|
|
887
|
+
prostateVolumeCc: 0,
|
|
888
|
+
psaNgMl: 0,
|
|
889
|
+
uroflowQmaxMlSec: 0,
|
|
890
|
+
risk: {
|
|
891
|
+
ckd: false,
|
|
892
|
+
diabetes: false,
|
|
893
|
+
anticoagulants: false,
|
|
894
|
+
activeInfection: false
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
function ipssItem(v) {
|
|
899
|
+
const n = Number(v);
|
|
900
|
+
if (!Number.isFinite(n)) return 0;
|
|
901
|
+
return Math.min(5, Math.max(0, Math.round(n)));
|
|
1111
902
|
}
|
|
1112
|
-
var SOCRATES_KEYS = [
|
|
1113
|
-
"site",
|
|
1114
|
-
"onset",
|
|
1115
|
-
"character",
|
|
1116
|
-
"radiation",
|
|
1117
|
-
"associated",
|
|
1118
|
-
"timing",
|
|
1119
|
-
"exacerbating"
|
|
1120
|
-
];
|
|
1121
903
|
function urologyIpssSevenTotal(ipss) {
|
|
1122
904
|
return ipss.incomplete + ipss.frequency + ipss.intermittency + ipss.urgency + ipss.weakStream + ipss.straining + ipss.nocturia;
|
|
1123
905
|
}
|
|
@@ -1126,159 +908,269 @@ function urologyIpssBand(total) {
|
|
|
1126
908
|
if (total <= 19) return "Moderate";
|
|
1127
909
|
return "Severe";
|
|
1128
910
|
}
|
|
911
|
+
function hopcMeaningfulStr(x) {
|
|
912
|
+
if (typeof x !== "string") return "";
|
|
913
|
+
const t = x.trim();
|
|
914
|
+
if (!t || t.toLowerCase() === "none") return "";
|
|
915
|
+
return t;
|
|
916
|
+
}
|
|
917
|
+
function hopcNonEmpty(lines) {
|
|
918
|
+
return lines.map((s) => (s ?? "").trim()).filter(Boolean);
|
|
919
|
+
}
|
|
920
|
+
function formatUrologySmartHistoryToSymptomsNarrative(safe, flags) {
|
|
921
|
+
const { showPain, showLUTS, showHaem, showSex } = flags;
|
|
922
|
+
const parts = [];
|
|
923
|
+
if (showPain) {
|
|
924
|
+
const s = safe.socrates;
|
|
925
|
+
const painLines = hopcNonEmpty([
|
|
926
|
+
s.site ? `Site: ${s.site}` : "",
|
|
927
|
+
s.onset ? `Onset: ${s.onset}` : "",
|
|
928
|
+
s.character ? `Character: ${s.character}` : "",
|
|
929
|
+
s.radiation ? `Radiation: ${s.radiation}` : "",
|
|
930
|
+
s.associated ? `Associated: ${s.associated}` : "",
|
|
931
|
+
s.timing ? `Timing: ${s.timing}` : "",
|
|
932
|
+
s.exacerbating ? `Exacerbating/relieving: ${s.exacerbating}` : "",
|
|
933
|
+
`Severity: ${s.severity}/10`
|
|
934
|
+
]);
|
|
935
|
+
const hasNonDefaultPain = painLines.length > 1 || painLines.length === 1 && painLines[0] !== "Severity: 0/10";
|
|
936
|
+
if (hasNonDefaultPain) parts.push(`SOCRATES:
|
|
937
|
+
${painLines.join("\n")}`);
|
|
938
|
+
}
|
|
939
|
+
if (showLUTS) {
|
|
940
|
+
const ip = safe.ipss;
|
|
941
|
+
const any = ip.incomplete || ip.frequency || ip.intermittency || ip.urgency || ip.weakStream || ip.straining || ip.nocturia || safe.ipssQoL;
|
|
942
|
+
if (any) {
|
|
943
|
+
const ipssTotal = urologyIpssSevenTotal(safe.ipss);
|
|
944
|
+
const ipssBandLabel = urologyIpssBand(ipssTotal);
|
|
945
|
+
parts.push(`IPSS: ${ipssTotal}/35 (${ipssBandLabel}) \xB7 QoL ${safe.ipssQoL}/6`);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
if (showHaem) {
|
|
949
|
+
const h = safe.haematuria;
|
|
950
|
+
const timing = hopcMeaningfulStr(h.timing);
|
|
951
|
+
const haemLines = hopcNonEmpty([
|
|
952
|
+
h.present ? "Haematuria present" : "",
|
|
953
|
+
timing ? `Timing: ${timing}` : "",
|
|
954
|
+
h.painful ? "Painful" : "",
|
|
955
|
+
h.clots ? "Clots" : ""
|
|
956
|
+
]);
|
|
957
|
+
if (haemLines.length) parts.push(`Haematuria:
|
|
958
|
+
${haemLines.join("\n")}`);
|
|
959
|
+
}
|
|
960
|
+
if (showSex) {
|
|
961
|
+
const sx = safe.sexual;
|
|
962
|
+
const infertility = sx.infertilityMonths;
|
|
963
|
+
const sexLines = hopcNonEmpty([
|
|
964
|
+
hopcMeaningfulStr(sx.erectile) ? `Erectile function: ${hopcMeaningfulStr(sx.erectile)}` : "",
|
|
965
|
+
hopcMeaningfulStr(sx.libido) ? `Libido: ${hopcMeaningfulStr(sx.libido)}` : "",
|
|
966
|
+
hopcMeaningfulStr(sx.ejaculation) ? `Ejaculation: ${hopcMeaningfulStr(sx.ejaculation)}` : "",
|
|
967
|
+
Number.isFinite(infertility) && infertility > 0 ? `Infertility: ${infertility} months` : ""
|
|
968
|
+
]);
|
|
969
|
+
if (sexLines.length) parts.push(`Sexual/reproductive:
|
|
970
|
+
${sexLines.join("\n")}`);
|
|
971
|
+
}
|
|
972
|
+
return parts.join("\n\n").trim();
|
|
973
|
+
}
|
|
974
|
+
function formatUrologyExaminationToClinicalExamination(v) {
|
|
975
|
+
const sections = [];
|
|
976
|
+
const generalLabels = {
|
|
977
|
+
pallor: "Pallor (chronic dz)",
|
|
978
|
+
oedema: "Oedema (renal)",
|
|
979
|
+
icterus: "Icterus",
|
|
980
|
+
lymphadenopathy: "Lymphadenopathy"
|
|
981
|
+
};
|
|
982
|
+
const general = Object.keys(generalLabels).filter((k) => v.general[k]).map((k) => generalLabels[k]);
|
|
983
|
+
if (general.length) sections.push(`General: ${general.join(", ")}`);
|
|
984
|
+
const abdLabels = {
|
|
985
|
+
distension: "Distension",
|
|
986
|
+
scars: "Surgical scars",
|
|
987
|
+
mass: "Visible mass",
|
|
988
|
+
kidneyPalpable: "Kidney palpable",
|
|
989
|
+
bladderPalpable: "Bladder palpable",
|
|
990
|
+
renalAngleTender: "Renal angle tender",
|
|
991
|
+
bladderDull: "Bladder dullness (retention)"
|
|
992
|
+
};
|
|
993
|
+
const abd = Object.keys(abdLabels).filter((k) => v.abdomen[k]).map((k) => abdLabels[k]);
|
|
994
|
+
if (abd.length) sections.push(`Abdomen: ${abd.join(", ")}`);
|
|
995
|
+
const guLabels = {
|
|
996
|
+
meatusAbnormal: "Meatus abnormal (hypospadias?)",
|
|
997
|
+
phimosis: "Phimosis / paraphimosis",
|
|
998
|
+
lesions: "Penile lesions / ulcers",
|
|
999
|
+
scrotalSwelling: "Scrotal swelling",
|
|
1000
|
+
transilluminant: "Transilluminant",
|
|
1001
|
+
tenderTestis: "Tender testis",
|
|
1002
|
+
hydrocele: "Hydrocele",
|
|
1003
|
+
varicocele: "Varicocele",
|
|
1004
|
+
suspiciousMass: "Suspicious mass"
|
|
1005
|
+
};
|
|
1006
|
+
const gu = Object.keys(guLabels).filter((k) => v.gu[k]).map((k) => guLabels[k]);
|
|
1007
|
+
if (gu.length) sections.push(`GU: ${gu.join(", ")}`);
|
|
1008
|
+
const dreBits = hopcNonEmpty([
|
|
1009
|
+
v.dre.done ? "Performed" : "",
|
|
1010
|
+
v.dre.sizeGrams > 0 ? `Size ~${v.dre.sizeGrams} g` : "",
|
|
1011
|
+
v.dre.consistency ? `Consistency: ${v.dre.consistency}` : "",
|
|
1012
|
+
v.dre.medianSulcus ? `Median sulcus: ${v.dre.medianSulcus}` : "",
|
|
1013
|
+
v.dre.tenderness ? "Tenderness" : ""
|
|
1014
|
+
]);
|
|
1015
|
+
if (dreBits.length) sections.push(`DRE: ${dreBits.join(", ")}`);
|
|
1016
|
+
return sections.join("\n");
|
|
1017
|
+
}
|
|
1018
|
+
function str(x) {
|
|
1019
|
+
return typeof x === "string" ? x : "";
|
|
1020
|
+
}
|
|
1021
|
+
function bool(x) {
|
|
1022
|
+
return x === true;
|
|
1023
|
+
}
|
|
1024
|
+
function nonNegInt(x) {
|
|
1025
|
+
const n = Number(x);
|
|
1026
|
+
if (!Number.isFinite(n)) return 0;
|
|
1027
|
+
return Math.max(0, Math.round(n));
|
|
1028
|
+
}
|
|
1129
1029
|
function normalizeUrologySmartHistory(raw) {
|
|
1130
|
-
const
|
|
1131
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return
|
|
1132
|
-
const
|
|
1133
|
-
const
|
|
1134
|
-
const
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
const
|
|
1158
|
-
const
|
|
1030
|
+
const base = defaultUrologySmartHistory();
|
|
1031
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return base;
|
|
1032
|
+
const r = raw;
|
|
1033
|
+
const soc = r.socrates && typeof r.socrates === "object" && !Array.isArray(r.socrates) ? r.socrates : {};
|
|
1034
|
+
const socObj = soc;
|
|
1035
|
+
const sev = Number(socObj.severity);
|
|
1036
|
+
const socrates = {
|
|
1037
|
+
site: str(socObj.site),
|
|
1038
|
+
onset: str(socObj.onset),
|
|
1039
|
+
character: str(socObj.character),
|
|
1040
|
+
radiation: str(socObj.radiation),
|
|
1041
|
+
associated: str(socObj.associated),
|
|
1042
|
+
timing: str(socObj.timing),
|
|
1043
|
+
exacerbating: str(socObj.exacerbating),
|
|
1044
|
+
severity: Number.isFinite(sev) ? Math.min(10, Math.max(0, Math.round(sev))) : 0
|
|
1045
|
+
};
|
|
1046
|
+
const ipRaw = r.ipss && typeof r.ipss === "object" && !Array.isArray(r.ipss) ? r.ipss : {};
|
|
1047
|
+
const ip = ipRaw;
|
|
1048
|
+
const ipss = {
|
|
1049
|
+
incomplete: ipssItem(ip.incomplete),
|
|
1050
|
+
frequency: ipssItem(ip.frequency),
|
|
1051
|
+
intermittency: ipssItem(ip.intermittency),
|
|
1052
|
+
urgency: ipssItem(ip.urgency),
|
|
1053
|
+
weakStream: ipssItem(ip.weakStream ?? ip.weak_stream),
|
|
1054
|
+
straining: ipssItem(ip.straining),
|
|
1055
|
+
nocturia: ipssItem(ip.nocturia)
|
|
1056
|
+
};
|
|
1057
|
+
const qolRaw = Number(r.ipssQoL);
|
|
1058
|
+
const ipssQoL = Number.isFinite(qolRaw) ? Math.min(6, Math.max(0, Math.round(qolRaw))) : 0;
|
|
1059
|
+
const haRaw = r.haematuria && typeof r.haematuria === "object" && !Array.isArray(r.haematuria) ? r.haematuria : {};
|
|
1060
|
+
const ha = haRaw;
|
|
1159
1061
|
const haematuria = {
|
|
1160
|
-
present:
|
|
1161
|
-
timing:
|
|
1162
|
-
painful:
|
|
1163
|
-
clots:
|
|
1164
|
-
};
|
|
1165
|
-
const
|
|
1166
|
-
const
|
|
1167
|
-
const
|
|
1062
|
+
present: bool(ha.present),
|
|
1063
|
+
timing: str(ha.timing) || "none",
|
|
1064
|
+
painful: bool(ha.painful),
|
|
1065
|
+
clots: bool(ha.clots)
|
|
1066
|
+
};
|
|
1067
|
+
const sxRaw = r.sexual && typeof r.sexual === "object" && !Array.isArray(r.sexual) ? r.sexual : {};
|
|
1068
|
+
const sx = sxRaw;
|
|
1069
|
+
const infertilityMonths = nonNegInt(sx.infertilityMonths);
|
|
1168
1070
|
const sexual = {
|
|
1169
|
-
erectile:
|
|
1170
|
-
libido:
|
|
1171
|
-
ejaculation:
|
|
1172
|
-
infertilityMonths
|
|
1071
|
+
erectile: str(sx.erectile) || "none",
|
|
1072
|
+
libido: str(sx.libido) || "none",
|
|
1073
|
+
ejaculation: str(sx.ejaculation) || "none",
|
|
1074
|
+
infertilityMonths
|
|
1173
1075
|
};
|
|
1174
|
-
const pastNotes =
|
|
1175
|
-
return {
|
|
1076
|
+
const pastNotes = str(r.pastNotes);
|
|
1077
|
+
return {
|
|
1078
|
+
socrates,
|
|
1079
|
+
ipss,
|
|
1080
|
+
ipssQoL,
|
|
1081
|
+
haematuria,
|
|
1082
|
+
sexual,
|
|
1083
|
+
pastNotes
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
function dreSelectStr(x) {
|
|
1087
|
+
const t = str(x).trim();
|
|
1088
|
+
if (!t || t.toLowerCase() === "none") return "";
|
|
1089
|
+
return t;
|
|
1176
1090
|
}
|
|
1177
1091
|
function normalizeUrologyExamination(raw) {
|
|
1178
|
-
const
|
|
1179
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return
|
|
1180
|
-
const
|
|
1181
|
-
const
|
|
1182
|
-
const
|
|
1183
|
-
const
|
|
1184
|
-
const
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
const
|
|
1092
|
+
const base = defaultUrologyExamination();
|
|
1093
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return base;
|
|
1094
|
+
const r = raw;
|
|
1095
|
+
const regions = Array.isArray(r.regions) ? r.regions.filter((x) => typeof x === "string") : [];
|
|
1096
|
+
const g = r.general && typeof r.general === "object" && !Array.isArray(r.general) ? r.general : {};
|
|
1097
|
+
const gObj = g;
|
|
1098
|
+
const general = {
|
|
1099
|
+
pallor: bool(gObj.pallor),
|
|
1100
|
+
oedema: bool(gObj.oedema),
|
|
1101
|
+
icterus: bool(gObj.icterus),
|
|
1102
|
+
lymphadenopathy: bool(gObj.lymphadenopathy)
|
|
1103
|
+
};
|
|
1104
|
+
const ab = r.abdomen && typeof r.abdomen === "object" && !Array.isArray(r.abdomen) ? r.abdomen : {};
|
|
1105
|
+
const abObj = ab;
|
|
1106
|
+
const abdomen = {
|
|
1107
|
+
distension: bool(abObj.distension),
|
|
1108
|
+
scars: bool(abObj.scars),
|
|
1109
|
+
mass: bool(abObj.mass),
|
|
1110
|
+
kidneyPalpable: bool(abObj.kidneyPalpable),
|
|
1111
|
+
bladderPalpable: bool(abObj.bladderPalpable),
|
|
1112
|
+
renalAngleTender: bool(abObj.renalAngleTender),
|
|
1113
|
+
bladderDull: bool(abObj.bladderDull)
|
|
1114
|
+
};
|
|
1115
|
+
const guRaw = r.gu && typeof r.gu === "object" && !Array.isArray(r.gu) ? r.gu : {};
|
|
1116
|
+
const guObj = guRaw;
|
|
1117
|
+
const gu = {
|
|
1118
|
+
meatusAbnormal: bool(guObj.meatusAbnormal),
|
|
1119
|
+
phimosis: bool(guObj.phimosis),
|
|
1120
|
+
lesions: bool(guObj.lesions),
|
|
1121
|
+
scrotalSwelling: bool(guObj.scrotalSwelling),
|
|
1122
|
+
transilluminant: bool(guObj.transilluminant),
|
|
1123
|
+
tenderTestis: bool(guObj.tenderTestis),
|
|
1124
|
+
hydrocele: bool(guObj.hydrocele),
|
|
1125
|
+
varicocele: bool(guObj.varicocele),
|
|
1126
|
+
suspiciousMass: bool(guObj.suspiciousMass)
|
|
1127
|
+
};
|
|
1128
|
+
const dr = r.dre && typeof r.dre === "object" && !Array.isArray(r.dre) ? r.dre : {};
|
|
1129
|
+
const drObj = dr;
|
|
1130
|
+
const sizeG = Number(drObj.sizeGrams);
|
|
1131
|
+
const dre = {
|
|
1132
|
+
done: bool(drObj.done),
|
|
1133
|
+
sizeGrams: Number.isFinite(sizeG) && sizeG >= 0 ? Math.round(sizeG) : 0,
|
|
1134
|
+
consistency: dreSelectStr(drObj.consistency),
|
|
1135
|
+
medianSulcus: dreSelectStr(drObj.medianSulcus),
|
|
1136
|
+
tenderness: bool(drObj.tenderness)
|
|
1137
|
+
};
|
|
1138
|
+
const examinationNotes = str(r.examinationNotes);
|
|
1191
1139
|
return {
|
|
1192
|
-
regions
|
|
1193
|
-
general
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
},
|
|
1199
|
-
abdomen: {
|
|
1200
|
-
distension: bool(ab.distension),
|
|
1201
|
-
scars: bool(ab.scars),
|
|
1202
|
-
mass: bool(ab.mass),
|
|
1203
|
-
kidneyPalpable: bool(ab.kidneyPalpable),
|
|
1204
|
-
bladderPalpable: bool(ab.bladderPalpable),
|
|
1205
|
-
renalAngleTender: bool(ab.renalAngleTender),
|
|
1206
|
-
bladderDull: bool(ab.bladderDull)
|
|
1207
|
-
},
|
|
1208
|
-
gu: {
|
|
1209
|
-
meatusAbnormal: bool(gu.meatusAbnormal),
|
|
1210
|
-
phimosis: bool(gu.phimosis),
|
|
1211
|
-
lesions: bool(gu.lesions),
|
|
1212
|
-
scrotalSwelling: bool(gu.scrotalSwelling),
|
|
1213
|
-
transilluminant: bool(gu.transilluminant),
|
|
1214
|
-
tenderTestis: bool(gu.tenderTestis),
|
|
1215
|
-
hydrocele: bool(gu.hydrocele),
|
|
1216
|
-
varicocele: bool(gu.varicocele),
|
|
1217
|
-
suspiciousMass: bool(gu.suspiciousMass)
|
|
1218
|
-
},
|
|
1219
|
-
dre: {
|
|
1220
|
-
done: bool(dr.done),
|
|
1221
|
-
sizeGrams: Number.isFinite(sg) && sg >= 0 ? Math.round(sg) : 0,
|
|
1222
|
-
consistency: dreEmpty(consistencyRaw) ? "" : String(consistencyRaw),
|
|
1223
|
-
medianSulcus: dreEmpty(medianRaw) ? "" : String(medianRaw),
|
|
1224
|
-
tenderness: bool(dr.tenderness)
|
|
1225
|
-
},
|
|
1226
|
-
examinationNotes: typeof v.examinationNotes === "string" ? v.examinationNotes : d.examinationNotes
|
|
1140
|
+
regions,
|
|
1141
|
+
general,
|
|
1142
|
+
abdomen,
|
|
1143
|
+
gu,
|
|
1144
|
+
dre,
|
|
1145
|
+
examinationNotes
|
|
1227
1146
|
};
|
|
1228
1147
|
}
|
|
1229
|
-
var defaultUrologyPathway = () => ({
|
|
1230
|
-
stoneSizeMm: 0,
|
|
1231
|
-
hydronephrosis: false,
|
|
1232
|
-
prostateVolumeCc: 0,
|
|
1233
|
-
psaNgMl: 0,
|
|
1234
|
-
uroflowQmaxMlSec: 0,
|
|
1235
|
-
risk: {
|
|
1236
|
-
ckd: false,
|
|
1237
|
-
diabetes: false,
|
|
1238
|
-
anticoagulants: false,
|
|
1239
|
-
activeInfection: false
|
|
1240
|
-
}
|
|
1241
|
-
});
|
|
1242
|
-
function clampNonNegativeNumber(x, fallback) {
|
|
1243
|
-
const n = Number(x);
|
|
1244
|
-
if (!Number.isFinite(n) || n < 0) return fallback;
|
|
1245
|
-
return n;
|
|
1246
|
-
}
|
|
1247
1148
|
function normalizeUrologyPathway(raw) {
|
|
1248
|
-
const
|
|
1249
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return
|
|
1250
|
-
const
|
|
1251
|
-
const
|
|
1149
|
+
const base = defaultUrologyPathway();
|
|
1150
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return base;
|
|
1151
|
+
const r = raw;
|
|
1152
|
+
const stoneSizeMm = nonNegInt(r.stoneSizeMm);
|
|
1153
|
+
const hydronephrosis = bool(r.hydronephrosis);
|
|
1154
|
+
const prostateVolumeCc = Math.max(0, Number(r.prostateVolumeCc) || 0);
|
|
1155
|
+
const psaNgMl = Math.max(0, Number(r.psaNgMl) || 0);
|
|
1156
|
+
const uroflowQmaxMlSec = Math.max(0, Number(r.uroflowQmaxMlSec) || 0);
|
|
1157
|
+
const riskRaw = r.risk && typeof r.risk === "object" && !Array.isArray(r.risk) ? r.risk : {};
|
|
1158
|
+
const rk = riskRaw;
|
|
1159
|
+
const risk = {
|
|
1160
|
+
ckd: bool(rk.ckd),
|
|
1161
|
+
diabetes: bool(rk.diabetes),
|
|
1162
|
+
anticoagulants: bool(rk.anticoagulants),
|
|
1163
|
+
activeInfection: bool(rk.activeInfection)
|
|
1164
|
+
};
|
|
1252
1165
|
return {
|
|
1253
|
-
stoneSizeMm
|
|
1254
|
-
hydronephrosis
|
|
1255
|
-
prostateVolumeCc
|
|
1256
|
-
psaNgMl
|
|
1257
|
-
uroflowQmaxMlSec
|
|
1258
|
-
risk
|
|
1259
|
-
ckd: rIn.ckd === true,
|
|
1260
|
-
diabetes: rIn.diabetes === true,
|
|
1261
|
-
anticoagulants: rIn.anticoagulants === true,
|
|
1262
|
-
activeInfection: rIn.activeInfection === true
|
|
1263
|
-
}
|
|
1166
|
+
stoneSizeMm,
|
|
1167
|
+
hydronephrosis,
|
|
1168
|
+
prostateVolumeCc,
|
|
1169
|
+
psaNgMl,
|
|
1170
|
+
uroflowQmaxMlSec,
|
|
1171
|
+
risk
|
|
1264
1172
|
};
|
|
1265
1173
|
}
|
|
1266
|
-
function urologyStoneRecommendation(sizeMm, hydronephrosis) {
|
|
1267
|
-
if (sizeMm <= 0) return { plan: "Enter stone size to auto-trigger plan.", surgical: false };
|
|
1268
|
-
if (sizeMm < 5) return { plan: "Conservative + MET (\u03B1-blocker), hydration", surgical: false };
|
|
1269
|
-
if (sizeMm <= 10)
|
|
1270
|
-
return {
|
|
1271
|
-
plan: hydronephrosis ? "URS / SWL \u2014 obstruction present" : "Medical expulsive therapy / URS",
|
|
1272
|
-
surgical: hydronephrosis
|
|
1273
|
-
};
|
|
1274
|
-
return { plan: "URS / PCNL \u2014 surgical clearance indicated", surgical: true };
|
|
1275
|
-
}
|
|
1276
|
-
function urologyProstateProcedure(volumeCc) {
|
|
1277
|
-
if (volumeCc <= 0) return "Enter prostate volume.";
|
|
1278
|
-
if (volumeCc < 30) return "Medical (\u03B1-blocker \xB1 5-ARI)";
|
|
1279
|
-
if (volumeCc <= 80) return "TURP";
|
|
1280
|
-
return "HoLEP / Open simple prostatectomy";
|
|
1281
|
-
}
|
|
1282
1174
|
|
|
1283
1175
|
// src/core/painScaleFlags.ts
|
|
1284
1176
|
function normalizePainScaleFlags(raw, bounds) {
|
|
@@ -1392,18 +1284,18 @@ var FormStore = class {
|
|
|
1392
1284
|
values[field.id] = field.defaultValue ?? defaultGeneralSurgeryExamination();
|
|
1393
1285
|
} else if (field.type === "general_surgery_grading") {
|
|
1394
1286
|
values[field.id] = field.defaultValue ?? defaultGeneralSurgeryGrading();
|
|
1395
|
-
} else if (field.type === "pain_scale_flags") {
|
|
1396
|
-
const f = field;
|
|
1397
|
-
values[field.id] = normalizePainScaleFlags(
|
|
1398
|
-
f.defaultValue ?? { pain: f.min ?? 0, flags: [] },
|
|
1399
|
-
{ min: f.min, max: f.max }
|
|
1400
|
-
);
|
|
1401
1287
|
} else if (field.type === "urology_smart_history") {
|
|
1402
1288
|
values[field.id] = field.defaultValue ?? defaultUrologySmartHistory();
|
|
1403
1289
|
} else if (field.type === "urology_examination") {
|
|
1404
1290
|
values[field.id] = field.defaultValue ?? defaultUrologyExamination();
|
|
1405
1291
|
} else if (field.type === "urology_pathway") {
|
|
1406
1292
|
values[field.id] = field.defaultValue ?? defaultUrologyPathway();
|
|
1293
|
+
} else if (field.type === "pain_scale_flags") {
|
|
1294
|
+
const f = field;
|
|
1295
|
+
values[field.id] = normalizePainScaleFlags(
|
|
1296
|
+
f.defaultValue ?? { pain: f.min ?? 0, flags: [] },
|
|
1297
|
+
{ min: f.min, max: f.max }
|
|
1298
|
+
);
|
|
1407
1299
|
} else values[field.id] = null;
|
|
1408
1300
|
}
|
|
1409
1301
|
});
|
|
@@ -1613,7 +1505,6 @@ var FormStore = class {
|
|
|
1613
1505
|
}
|
|
1614
1506
|
}
|
|
1615
1507
|
});
|
|
1616
|
-
attachUrologyClinicalPathwayMetadata(result, this.schema);
|
|
1617
1508
|
return result;
|
|
1618
1509
|
}
|
|
1619
1510
|
// --- Core Logic ---
|
|
@@ -6586,7 +6477,7 @@ function safeJsonParse(val) {
|
|
|
6586
6477
|
return null;
|
|
6587
6478
|
}
|
|
6588
6479
|
}
|
|
6589
|
-
function
|
|
6480
|
+
function clampInt(val, min, max) {
|
|
6590
6481
|
if (!Number.isFinite(val)) return min;
|
|
6591
6482
|
return Math.max(min, Math.min(max, Math.trunc(val)));
|
|
6592
6483
|
}
|
|
@@ -6596,10 +6487,10 @@ function parseLegacyString(input) {
|
|
|
6596
6487
|
if (gpalMatch) {
|
|
6597
6488
|
const [, g, p, a, l] = gpalMatch;
|
|
6598
6489
|
result.gpal = {
|
|
6599
|
-
G:
|
|
6600
|
-
P:
|
|
6601
|
-
A:
|
|
6602
|
-
L:
|
|
6490
|
+
G: clampInt(Number(g), 0, 20),
|
|
6491
|
+
P: clampInt(Number(p), 0, 20),
|
|
6492
|
+
A: clampInt(Number(a), 0, 20),
|
|
6493
|
+
L: clampInt(Number(l), 0, 20)
|
|
6603
6494
|
};
|
|
6604
6495
|
}
|
|
6605
6496
|
const lmpMatch = input.match(/LMP:\s*([^\n]+)/i);
|
|
@@ -6632,10 +6523,10 @@ function normalizeToDraft(value) {
|
|
|
6632
6523
|
isNewEntry: false,
|
|
6633
6524
|
draft: {
|
|
6634
6525
|
gpal: {
|
|
6635
|
-
G:
|
|
6636
|
-
P:
|
|
6637
|
-
A:
|
|
6638
|
-
L:
|
|
6526
|
+
G: clampInt(Number(gpal.G ?? 0), 0, 20),
|
|
6527
|
+
P: clampInt(Number(gpal.P ?? 0), 0, 20),
|
|
6528
|
+
A: clampInt(Number(gpal.A ?? 0), 0, 20),
|
|
6529
|
+
L: clampInt(Number(gpal.L ?? 0), 0, 20)
|
|
6639
6530
|
},
|
|
6640
6531
|
lmp: typeof lmp === "string" ? lmp : "",
|
|
6641
6532
|
is_pregnant,
|
|
@@ -6653,10 +6544,10 @@ function normalizeToDraft(value) {
|
|
|
6653
6544
|
}
|
|
6654
6545
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
6655
6546
|
const gpalFromFlat = {
|
|
6656
|
-
G:
|
|
6657
|
-
P:
|
|
6658
|
-
A:
|
|
6659
|
-
L:
|
|
6547
|
+
G: clampInt(Number(parsed.G ?? parsed.g ?? 0), 0, 20),
|
|
6548
|
+
P: clampInt(Number(parsed.P ?? parsed.p ?? 0), 0, 20),
|
|
6549
|
+
A: clampInt(Number(parsed.A ?? parsed.a ?? 0), 0, 20),
|
|
6550
|
+
L: clampInt(Number(parsed.L ?? parsed.l ?? 0), 0, 20)
|
|
6660
6551
|
};
|
|
6661
6552
|
return {
|
|
6662
6553
|
isNewEntry: false,
|
|
@@ -6808,7 +6699,7 @@ var ObstetricHistoryWidget = ({ fieldId }) => {
|
|
|
6808
6699
|
const sanitizeGpalInput = (raw) => {
|
|
6809
6700
|
const digits = raw.replace(/[^\d]/g, "").slice(0, 2);
|
|
6810
6701
|
if (!digits) return 0;
|
|
6811
|
-
return
|
|
6702
|
+
return clampInt(Number(digits), 0, 20);
|
|
6812
6703
|
};
|
|
6813
6704
|
const handleGpalChange = (key, raw) => {
|
|
6814
6705
|
const prevWasEmpty = draft.gpal[key] === 0 && activeGpalKey === key;
|
|
@@ -9112,26 +9003,11 @@ var DischargeMedicationOrdersWidget = ({ fieldId }) => {
|
|
|
9112
9003
|
showError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500", children: error })
|
|
9113
9004
|
] });
|
|
9114
9005
|
};
|
|
9115
|
-
var
|
|
9006
|
+
var DEFAULT_TOKEN_SEPARATOR2 = ", ";
|
|
9116
9007
|
function getAppendSeparator(def) {
|
|
9117
|
-
const raw = def.meta && typeof def.meta.appendSeparator === "string" ? def.meta.appendSeparator :
|
|
9008
|
+
const raw = def.meta && typeof def.meta.appendSeparator === "string" ? def.meta.appendSeparator : DEFAULT_TOKEN_SEPARATOR2;
|
|
9118
9009
|
return raw;
|
|
9119
9010
|
}
|
|
9120
|
-
function getSuggestionTokensFromText(text) {
|
|
9121
|
-
return text.split(",").map((p) => p.trim()).filter(Boolean);
|
|
9122
|
-
}
|
|
9123
|
-
function toggleSuggestionTokenInText(current, token, separator = DEFAULT_TOKEN_SEPARATOR) {
|
|
9124
|
-
const t = String(token).trim();
|
|
9125
|
-
if (!t) return current;
|
|
9126
|
-
const parts = getSuggestionTokensFromText(current);
|
|
9127
|
-
const i = parts.findIndex((p) => p === t);
|
|
9128
|
-
if (i >= 0) {
|
|
9129
|
-
parts.splice(i, 1);
|
|
9130
|
-
} else {
|
|
9131
|
-
parts.push(t);
|
|
9132
|
-
}
|
|
9133
|
-
return parts.join(separator);
|
|
9134
|
-
}
|
|
9135
9011
|
function isTokenSelected(text, optionValue) {
|
|
9136
9012
|
const t = String(optionValue).trim();
|
|
9137
9013
|
if (!t) return false;
|
|
@@ -9205,8 +9081,11 @@ var SuggestionTextareaWidget = ({ fieldId }) => {
|
|
|
9205
9081
|
setOptions([]);
|
|
9206
9082
|
}
|
|
9207
9083
|
}, [def]);
|
|
9084
|
+
const chipAllowedSet = React15.useMemo(
|
|
9085
|
+
() => new Set(options.map((o) => String(o.value).trim()).filter(Boolean)),
|
|
9086
|
+
[options]
|
|
9087
|
+
);
|
|
9208
9088
|
React15.useEffect(() => {
|
|
9209
|
-
if (!parallelChipFieldId || !def) return;
|
|
9210
9089
|
const parallelDef = store.getFieldDef(parallelChipFieldId);
|
|
9211
9090
|
if (!parallelDef || parallelDef.type !== "multiselect") return;
|
|
9212
9091
|
const next = deriveKnownSuggestionChipValues(textValue, options);
|
|
@@ -9232,7 +9111,7 @@ var SuggestionTextareaWidget = ({ fieldId }) => {
|
|
|
9232
9111
|
const labelEl = theme ? /* @__PURE__ */ jsxRuntime.jsx(Label, { className: labelClass, children: labelContent }) : /* @__PURE__ */ jsxRuntime.jsx(Label, { children: labelContent });
|
|
9233
9112
|
const onChipClick = (optValue) => {
|
|
9234
9113
|
if (disabled) return;
|
|
9235
|
-
setValue(toggleSuggestionTokenInText(textValue, String(optValue), appendSeparator));
|
|
9114
|
+
setValue(toggleSuggestionTokenInText(textValue, String(optValue), appendSeparator, chipAllowedSet));
|
|
9236
9115
|
};
|
|
9237
9116
|
const hasSuggestions = options.length > 0;
|
|
9238
9117
|
const hasEmbedded = embeddedFieldIds.length > 0;
|
|
@@ -9671,7 +9550,7 @@ var EMPTY = {
|
|
|
9671
9550
|
specularMicroscopyDone: false,
|
|
9672
9551
|
endothelialCount: ""
|
|
9673
9552
|
};
|
|
9674
|
-
function
|
|
9553
|
+
function str2(x) {
|
|
9675
9554
|
return typeof x === "string" ? x : "";
|
|
9676
9555
|
}
|
|
9677
9556
|
function parseValue2(raw) {
|
|
@@ -9679,24 +9558,24 @@ function parseValue2(raw) {
|
|
|
9679
9558
|
const o = raw;
|
|
9680
9559
|
const m = o.method;
|
|
9681
9560
|
const method = m === "ascan_immersion" || m === "ascan_contact" || m === "optical_biometry" ? m : "optical_biometry";
|
|
9682
|
-
const formula =
|
|
9561
|
+
const formula = str2(o.formula);
|
|
9683
9562
|
return {
|
|
9684
9563
|
...EMPTY,
|
|
9685
|
-
axialLengthOD:
|
|
9686
|
-
axialLengthOS:
|
|
9687
|
-
k1k2AxisOD:
|
|
9688
|
-
k1k2AxisOS:
|
|
9689
|
-
anteriorChamberDepthOD:
|
|
9690
|
-
anteriorChamberDepthOS:
|
|
9691
|
-
iolPowerOD:
|
|
9692
|
-
iolPowerOS:
|
|
9693
|
-
targetRefractionOD:
|
|
9694
|
-
targetRefractionOS:
|
|
9564
|
+
axialLengthOD: str2(o.axialLengthOD),
|
|
9565
|
+
axialLengthOS: str2(o.axialLengthOS),
|
|
9566
|
+
k1k2AxisOD: str2(o.k1k2AxisOD),
|
|
9567
|
+
k1k2AxisOS: str2(o.k1k2AxisOS),
|
|
9568
|
+
anteriorChamberDepthOD: str2(o.anteriorChamberDepthOD),
|
|
9569
|
+
anteriorChamberDepthOS: str2(o.anteriorChamberDepthOS),
|
|
9570
|
+
iolPowerOD: str2(o.iolPowerOD),
|
|
9571
|
+
iolPowerOS: str2(o.iolPowerOS),
|
|
9572
|
+
targetRefractionOD: str2(o.targetRefractionOD),
|
|
9573
|
+
targetRefractionOS: str2(o.targetRefractionOS),
|
|
9695
9574
|
formula: formula || "SRK/T",
|
|
9696
9575
|
method,
|
|
9697
9576
|
cornealTopoUploaded: o.cornealTopoUploaded === true,
|
|
9698
9577
|
specularMicroscopyDone: o.specularMicroscopyDone === true,
|
|
9699
|
-
endothelialCount:
|
|
9578
|
+
endothelialCount: str2(o.endothelialCount)
|
|
9700
9579
|
};
|
|
9701
9580
|
}
|
|
9702
9581
|
var paramCell = "emr-biometry-param text-[11px] font-semibold leading-snug text-foreground sm:text-xs py-2";
|
|
@@ -10136,7 +10015,7 @@ var THYROID_OPTS = [
|
|
|
10136
10015
|
{ value: "voice_change", label: "Voice change" },
|
|
10137
10016
|
{ value: "thyrotoxicosis", label: "Thyrotoxicosis symptoms" }
|
|
10138
10017
|
];
|
|
10139
|
-
var
|
|
10018
|
+
var SOCRATES_KEYS = [
|
|
10140
10019
|
"site",
|
|
10141
10020
|
"onset",
|
|
10142
10021
|
"character",
|
|
@@ -10230,7 +10109,7 @@ var GsSmartHistoryWidget = ({ fieldId }) => {
|
|
|
10230
10109
|
labelEl,
|
|
10231
10110
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: bodyClass, children: [
|
|
10232
10111
|
showPain ? /* @__PURE__ */ jsxRuntime.jsx(Panel, { title: "SOCRATES \u2014 pain", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-2 md:grid-cols-4", children: [
|
|
10233
|
-
|
|
10112
|
+
SOCRATES_KEYS.map((k) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
10234
10113
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: k }),
|
|
10235
10114
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10236
10115
|
Input,
|
|
@@ -11169,10 +11048,36 @@ var PainScaleFlagsWidget = ({ fieldId }) => {
|
|
|
11169
11048
|
};
|
|
11170
11049
|
var CHIEF_CHIPS_FIELD3 = "chief_complaints_chips";
|
|
11171
11050
|
var ACTIVE_SYNDROME_FIELD = "uro_active_syndrome";
|
|
11051
|
+
var CHIEF_TEXT_FIELD = "chief_complaints";
|
|
11052
|
+
var SYMPTOMS_FIELD = "symptoms";
|
|
11053
|
+
var DIAGNOSIS_FIELD = "diagnosis";
|
|
11054
|
+
var URO_DURATION_FIELD = "uro_chief_duration";
|
|
11055
|
+
var URO_ONSET_FIELD = "uro_chief_onset";
|
|
11056
|
+
var URO_PROGRESSION_FIELD = "uro_chief_progression";
|
|
11057
|
+
var SYNDROME_LABELS = {
|
|
11058
|
+
luts: "LUTS / BPH",
|
|
11059
|
+
stone: "Urolithiasis (stone)",
|
|
11060
|
+
haematuria: "Haematuria",
|
|
11061
|
+
scrotal: "Scrotal swelling",
|
|
11062
|
+
retention: "Acute retention",
|
|
11063
|
+
ed_infertility: "ED / Infertility",
|
|
11064
|
+
uti: "UTI / infection"
|
|
11065
|
+
};
|
|
11172
11066
|
function chipList2(state) {
|
|
11173
11067
|
const raw = state.values[CHIEF_CHIPS_FIELD3];
|
|
11174
11068
|
return Array.isArray(raw) ? raw.filter((x) => typeof x === "string") : [];
|
|
11175
11069
|
}
|
|
11070
|
+
function str3(x) {
|
|
11071
|
+
return typeof x === "string" ? x : "";
|
|
11072
|
+
}
|
|
11073
|
+
function chiefOptionalFieldDisplay(x) {
|
|
11074
|
+
const s = str3(x).trim();
|
|
11075
|
+
if (!s || s.toLowerCase() === "none") return "";
|
|
11076
|
+
return s;
|
|
11077
|
+
}
|
|
11078
|
+
function nonEmptyLines(lines) {
|
|
11079
|
+
return lines.map((s) => (s ?? "").trim()).filter(Boolean);
|
|
11080
|
+
}
|
|
11176
11081
|
var IPSS_LABELS = {
|
|
11177
11082
|
incomplete: "Incomplete emptying",
|
|
11178
11083
|
frequency: "Frequency (<2h)",
|
|
@@ -11182,7 +11087,7 @@ var IPSS_LABELS = {
|
|
|
11182
11087
|
straining: "Straining",
|
|
11183
11088
|
nocturia: "Nocturia (\xD7/night)"
|
|
11184
11089
|
};
|
|
11185
|
-
var
|
|
11090
|
+
var SOCRATES_KEYS2 = [
|
|
11186
11091
|
"site",
|
|
11187
11092
|
"onset",
|
|
11188
11093
|
"character",
|
|
@@ -11206,17 +11111,19 @@ function Panel2({
|
|
|
11206
11111
|
}
|
|
11207
11112
|
var UrologySmartHistoryWidget = ({ fieldId }) => {
|
|
11208
11113
|
const { fieldDef, value, setValue, setTouched, error, touched, disabled } = useField(fieldId);
|
|
11114
|
+
const hopcField = useField(SYMPTOMS_FIELD);
|
|
11209
11115
|
const syndromeField = useField(ACTIVE_SYNDROME_FIELD);
|
|
11210
11116
|
const { state } = useForm();
|
|
11117
|
+
const store = useFormStore();
|
|
11211
11118
|
const showError = !!error && (touched || state.submitAttempted);
|
|
11212
11119
|
const safe = React15.useMemo(() => normalizeUrologySmartHistory(value), [value]);
|
|
11213
11120
|
const chips = React15.useMemo(() => chipList2(state), [state]);
|
|
11214
11121
|
const rawSyndrome = syndromeField.value ?? state.values[ACTIVE_SYNDROME_FIELD];
|
|
11215
11122
|
const syndrome = typeof rawSyndrome === "string" && rawSyndrome !== "" && rawSyndrome !== "none" ? rawSyndrome : "none";
|
|
11216
|
-
const showPain = chips.includes("
|
|
11217
|
-
const showLUTS = syndrome === "luts" || syndrome === "retention" || chips.includes("
|
|
11123
|
+
const showPain = chips.includes("pain abdomen flank") || chips.includes("scrotal swelling pain") || syndrome === "stone" || syndrome === "scrotal";
|
|
11124
|
+
const showLUTS = syndrome === "luts" || syndrome === "retention" || chips.includes("frequency urgency") || chips.includes("poor stream") || chips.includes("acute retention");
|
|
11218
11125
|
const showHaem = syndrome === "haematuria" || chips.includes("haematuria");
|
|
11219
|
-
const showSex = syndrome === "ed_infertility" || chips.includes("
|
|
11126
|
+
const showSex = syndrome === "ed_infertility" || chips.includes("erectile dysfunction") || chips.includes("infertility concern");
|
|
11220
11127
|
const update = (next) => {
|
|
11221
11128
|
setValue(next);
|
|
11222
11129
|
setTouched();
|
|
@@ -11236,6 +11143,55 @@ var UrologySmartHistoryWidget = ({ fieldId }) => {
|
|
|
11236
11143
|
const setIpss = (key, v) => {
|
|
11237
11144
|
update({ ...safe, ipss: { ...safe.ipss, [key]: v } });
|
|
11238
11145
|
};
|
|
11146
|
+
const [hopcNarrativeLocked, setHopcNarrativeLocked] = React15.useState(false);
|
|
11147
|
+
React15.useEffect(() => {
|
|
11148
|
+
if (hopcNarrativeLocked || disabled) return;
|
|
11149
|
+
const next = formatUrologySmartHistoryToSymptomsNarrative(normalizeUrologySmartHistory(value), {
|
|
11150
|
+
showPain,
|
|
11151
|
+
showLUTS,
|
|
11152
|
+
showHaem,
|
|
11153
|
+
showSex
|
|
11154
|
+
});
|
|
11155
|
+
hopcField.setValue(next);
|
|
11156
|
+
}, [value, hopcNarrativeLocked, showPain, showLUTS, showHaem, showSex, disabled]);
|
|
11157
|
+
React15.useEffect(() => {
|
|
11158
|
+
if (disabled) return;
|
|
11159
|
+
const label = syndrome && syndrome !== "none" ? SYNDROME_LABELS[syndrome] ?? syndrome : "";
|
|
11160
|
+
const prev = store.getFieldState(DIAGNOSIS_FIELD)?.value;
|
|
11161
|
+
const prevText = typeof prev === "string" ? prev : "";
|
|
11162
|
+
const syndromeLabels = new Set(Object.values(SYNDROME_LABELS));
|
|
11163
|
+
const keptLines = prevText.split("\n").map((l) => l.trim()).filter(Boolean).filter((l) => !syndromeLabels.has(l));
|
|
11164
|
+
const nextLines = label ? [...keptLines, label] : keptLines;
|
|
11165
|
+
const next = nextLines.join("\n");
|
|
11166
|
+
if (prevText.trim() === next.trim()) return;
|
|
11167
|
+
store.setValues({ [DIAGNOSIS_FIELD]: next }, { touch: false });
|
|
11168
|
+
}, [disabled, store, syndrome]);
|
|
11169
|
+
React15.useEffect(() => {
|
|
11170
|
+
if (disabled) return;
|
|
11171
|
+
const duration = chiefOptionalFieldDisplay(state.values[URO_DURATION_FIELD]);
|
|
11172
|
+
const onset = chiefOptionalFieldDisplay(state.values[URO_ONSET_FIELD]);
|
|
11173
|
+
const progression = chiefOptionalFieldDisplay(state.values[URO_PROGRESSION_FIELD]);
|
|
11174
|
+
const prev = store.getFieldState(CHIEF_TEXT_FIELD)?.value;
|
|
11175
|
+
const prevText = typeof prev === "string" ? prev : "";
|
|
11176
|
+
const kept = prevText.split(",").map((t) => t.trim()).filter(Boolean).filter((t) => {
|
|
11177
|
+
const lower = t.toLowerCase();
|
|
11178
|
+
return !lower.startsWith("duration:") && !lower.startsWith("onset:") && !lower.startsWith("progression:");
|
|
11179
|
+
});
|
|
11180
|
+
const extra = nonEmptyLines([
|
|
11181
|
+
duration ? `Duration: ${duration}` : "",
|
|
11182
|
+
onset ? `Onset: ${onset}` : "",
|
|
11183
|
+
progression ? `Progression: ${progression}` : ""
|
|
11184
|
+
]);
|
|
11185
|
+
const next = [...kept, ...extra].join(", ").trim();
|
|
11186
|
+
if (prevText.trim() === next.trim()) return;
|
|
11187
|
+
store.setValues({ [CHIEF_TEXT_FIELD]: next }, { touch: false });
|
|
11188
|
+
}, [
|
|
11189
|
+
disabled,
|
|
11190
|
+
state.values[URO_DURATION_FIELD],
|
|
11191
|
+
state.values[URO_ONSET_FIELD],
|
|
11192
|
+
state.values[URO_PROGRESSION_FIELD],
|
|
11193
|
+
store
|
|
11194
|
+
]);
|
|
11239
11195
|
if (!fieldDef) return null;
|
|
11240
11196
|
const theme = getThemeConfig("textarea", fieldDef.theme);
|
|
11241
11197
|
const wrapperClass = theme?.wrapperClassName ?? "rounded-md overflow-hidden flex flex-col border border-[#D0D0D0]";
|
|
@@ -11254,7 +11210,7 @@ var UrologySmartHistoryWidget = ({ fieldId }) => {
|
|
|
11254
11210
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: bodyClass, children: [
|
|
11255
11211
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] text-muted-foreground", children: "Panels follow chief complaint chips and active syndrome (Clearsight SmartHistoryUro)." }),
|
|
11256
11212
|
showPain ? /* @__PURE__ */ jsxRuntime.jsx(Panel2, { title: "SOCRATES \u2014 Pain (flank / suprapubic / testicular)", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-2 md:grid-cols-4", children: [
|
|
11257
|
-
|
|
11213
|
+
SOCRATES_KEYS2.map((k) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
11258
11214
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: k }),
|
|
11259
11215
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11260
11216
|
Input,
|
|
@@ -11490,15 +11446,50 @@ var UrologySmartHistoryWidget = ({ fieldId }) => {
|
|
|
11490
11446
|
] })
|
|
11491
11447
|
] }) }) : null,
|
|
11492
11448
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
11493
|
-
/* @__PURE__ */ jsxRuntime.
|
|
11449
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
11450
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-xs font-semibold text-slate-700", children: hopcField.fieldDef?.label ?? "History of presenting complaint" }),
|
|
11451
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
11452
|
+
hopcNarrativeLocked ? /* @__PURE__ */ jsxRuntime.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: [
|
|
11453
|
+
"You edited this summary. Use",
|
|
11454
|
+
" ",
|
|
11455
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic", children: "'Regenerate'" }),
|
|
11456
|
+
" to update it from the form."
|
|
11457
|
+
] }) : null,
|
|
11458
|
+
hopcNarrativeLocked ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11459
|
+
Button,
|
|
11460
|
+
{
|
|
11461
|
+
type: "button",
|
|
11462
|
+
variant: "outline",
|
|
11463
|
+
size: "sm",
|
|
11464
|
+
className: "h-7 shrink-0 px-2 text-xs",
|
|
11465
|
+
disabled: disabled || hopcField.disabled,
|
|
11466
|
+
onClick: () => {
|
|
11467
|
+
setHopcNarrativeLocked(false);
|
|
11468
|
+
hopcField.setValue(
|
|
11469
|
+
formatUrologySmartHistoryToSymptomsNarrative(
|
|
11470
|
+
normalizeUrologySmartHistory(value),
|
|
11471
|
+
{ showPain, showLUTS, showHaem, showSex }
|
|
11472
|
+
)
|
|
11473
|
+
);
|
|
11474
|
+
},
|
|
11475
|
+
children: "Regenerate"
|
|
11476
|
+
}
|
|
11477
|
+
) : null
|
|
11478
|
+
] })
|
|
11479
|
+
] }),
|
|
11480
|
+
hopcNarrativeLocked ? /* @__PURE__ */ jsxRuntime.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,
|
|
11494
11481
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11495
11482
|
Textarea,
|
|
11496
11483
|
{
|
|
11497
|
-
className: "min-h-[
|
|
11498
|
-
placeholder: "
|
|
11499
|
-
value:
|
|
11500
|
-
onChange: (e) =>
|
|
11501
|
-
|
|
11484
|
+
className: "min-h-[88px] rounded-md border border-input bg-white text-sm",
|
|
11485
|
+
placeholder: "Auto-filled from chief complaints + smart history\u2026",
|
|
11486
|
+
value: typeof hopcField.value === "string" ? hopcField.value : hopcField.value == null ? "" : String(hopcField.value),
|
|
11487
|
+
onChange: (e) => {
|
|
11488
|
+
setHopcNarrativeLocked(true);
|
|
11489
|
+
hopcField.setValue(e.target.value);
|
|
11490
|
+
hopcField.setTouched();
|
|
11491
|
+
},
|
|
11492
|
+
disabled: disabled || hopcField.disabled
|
|
11502
11493
|
}
|
|
11503
11494
|
)
|
|
11504
11495
|
] })
|
|
@@ -11506,6 +11497,7 @@ var UrologySmartHistoryWidget = ({ fieldId }) => {
|
|
|
11506
11497
|
showError ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-2 text-xs text-red-500", children: error }) : null
|
|
11507
11498
|
] });
|
|
11508
11499
|
};
|
|
11500
|
+
var URO_CLINICAL_EXAMINATION_FIELD = "clinical_examination";
|
|
11509
11501
|
var GENERAL_ROWS = [
|
|
11510
11502
|
{ key: "pallor", label: "Pallor (chronic dz)" },
|
|
11511
11503
|
{ key: "oedema", label: "Oedema (renal)" },
|
|
@@ -11536,21 +11528,32 @@ var DRE_CONSISTENCY = ["none", "Soft (benign)", "Firm", "Hard / nodular (maligna
|
|
|
11536
11528
|
var DRE_SULCUS = ["none", "Preserved", "Obliterated"];
|
|
11537
11529
|
var UrologyExaminationWidget = ({ fieldId }) => {
|
|
11538
11530
|
const { fieldDef, value, setValue, setTouched, error, touched, disabled } = useField(fieldId);
|
|
11531
|
+
const clinicalField = useField(URO_CLINICAL_EXAMINATION_FIELD);
|
|
11539
11532
|
const { state } = useForm();
|
|
11540
11533
|
const showError = !!error && (touched || state.submitAttempted);
|
|
11541
11534
|
const safe = React15.useMemo(() => normalizeUrologyExamination(value), [value]);
|
|
11542
|
-
const
|
|
11535
|
+
const [clinicalNarrativeLocked, setClinicalNarrativeLocked] = React15.useState(false);
|
|
11536
|
+
React15.useEffect(() => {
|
|
11537
|
+
if (clinicalNarrativeLocked || disabled) return;
|
|
11538
|
+
const norm = normalizeUrologyExamination(value);
|
|
11539
|
+
const next = formatUrologyExaminationToClinicalExamination(norm);
|
|
11540
|
+
clinicalField.setValue(next);
|
|
11541
|
+
if (norm.examinationNotes !== next) {
|
|
11542
|
+
setValue({ ...norm, examinationNotes: next });
|
|
11543
|
+
}
|
|
11544
|
+
}, [value, clinicalNarrativeLocked, disabled]);
|
|
11545
|
+
const bump = (next) => {
|
|
11543
11546
|
setValue(next);
|
|
11544
11547
|
setTouched();
|
|
11545
11548
|
};
|
|
11546
11549
|
const toggleGeneral = (key) => {
|
|
11547
|
-
|
|
11550
|
+
bump({ ...safe, general: { ...safe.general, [key]: !safe.general[key] } });
|
|
11548
11551
|
};
|
|
11549
11552
|
const toggleAbdomen = (key) => {
|
|
11550
|
-
|
|
11553
|
+
bump({ ...safe, abdomen: { ...safe.abdomen, [key]: !safe.abdomen[key] } });
|
|
11551
11554
|
};
|
|
11552
11555
|
const toggleGu = (key) => {
|
|
11553
|
-
|
|
11556
|
+
bump({ ...safe, gu: { ...safe.gu, [key]: !safe.gu[key] } });
|
|
11554
11557
|
};
|
|
11555
11558
|
if (!fieldDef) return null;
|
|
11556
11559
|
const theme = getThemeConfig("textarea", fieldDef.theme);
|
|
@@ -11618,7 +11621,7 @@ var UrologyExaminationWidget = ({ fieldId }) => {
|
|
|
11618
11621
|
Checkbox,
|
|
11619
11622
|
{
|
|
11620
11623
|
checked: safe.dre.done,
|
|
11621
|
-
onCheckedChange: (c) =>
|
|
11624
|
+
onCheckedChange: (c) => bump({ ...safe, dre: { ...safe.dre, done: c === true } }),
|
|
11622
11625
|
disabled
|
|
11623
11626
|
}
|
|
11624
11627
|
),
|
|
@@ -11635,7 +11638,7 @@ var UrologyExaminationWidget = ({ fieldId }) => {
|
|
|
11635
11638
|
value: safe.dre.sizeGrams,
|
|
11636
11639
|
onChange: (e) => {
|
|
11637
11640
|
const n = Number(e.target.value);
|
|
11638
|
-
|
|
11641
|
+
bump({
|
|
11639
11642
|
...safe,
|
|
11640
11643
|
dre: {
|
|
11641
11644
|
...safe.dre,
|
|
@@ -11653,7 +11656,7 @@ var UrologyExaminationWidget = ({ fieldId }) => {
|
|
|
11653
11656
|
Select,
|
|
11654
11657
|
{
|
|
11655
11658
|
value: safe.dre.consistency || "none",
|
|
11656
|
-
onValueChange: (val) =>
|
|
11659
|
+
onValueChange: (val) => bump({
|
|
11657
11660
|
...safe,
|
|
11658
11661
|
dre: { ...safe.dre, consistency: val === "none" ? "" : val }
|
|
11659
11662
|
}),
|
|
@@ -11671,7 +11674,7 @@ var UrologyExaminationWidget = ({ fieldId }) => {
|
|
|
11671
11674
|
Select,
|
|
11672
11675
|
{
|
|
11673
11676
|
value: safe.dre.medianSulcus || "none",
|
|
11674
|
-
onValueChange: (val) =>
|
|
11677
|
+
onValueChange: (val) => bump({
|
|
11675
11678
|
...safe,
|
|
11676
11679
|
dre: { ...safe.dre, medianSulcus: val === "none" ? "" : val }
|
|
11677
11680
|
}),
|
|
@@ -11688,7 +11691,7 @@ var UrologyExaminationWidget = ({ fieldId }) => {
|
|
|
11688
11691
|
Checkbox,
|
|
11689
11692
|
{
|
|
11690
11693
|
checked: safe.dre.tenderness,
|
|
11691
|
-
onCheckedChange: (c) =>
|
|
11694
|
+
onCheckedChange: (c) => bump({ ...safe, dre: { ...safe.dre, tenderness: c === true } }),
|
|
11692
11695
|
disabled
|
|
11693
11696
|
}
|
|
11694
11697
|
),
|
|
@@ -11697,15 +11700,49 @@ var UrologyExaminationWidget = ({ fieldId }) => {
|
|
|
11697
11700
|
] })
|
|
11698
11701
|
] }),
|
|
11699
11702
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
11700
|
-
/* @__PURE__ */ jsxRuntime.
|
|
11703
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
11704
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-xs font-semibold text-slate-700", children: clinicalField.fieldDef?.label ?? "Clinical examination" }),
|
|
11705
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
11706
|
+
clinicalNarrativeLocked ? /* @__PURE__ */ jsxRuntime.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: [
|
|
11707
|
+
"You edited this summary. Use",
|
|
11708
|
+
" ",
|
|
11709
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic", children: "'Regenerate'" }),
|
|
11710
|
+
" to update it from the form."
|
|
11711
|
+
] }) : null,
|
|
11712
|
+
clinicalNarrativeLocked ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11713
|
+
Button,
|
|
11714
|
+
{
|
|
11715
|
+
type: "button",
|
|
11716
|
+
variant: "outline",
|
|
11717
|
+
size: "sm",
|
|
11718
|
+
className: "h-7 shrink-0 px-2 text-xs",
|
|
11719
|
+
disabled: disabled || clinicalField.disabled,
|
|
11720
|
+
onClick: () => {
|
|
11721
|
+
setClinicalNarrativeLocked(false);
|
|
11722
|
+
const norm = normalizeUrologyExamination(value);
|
|
11723
|
+
clinicalField.setValue(formatUrologyExaminationToClinicalExamination(norm));
|
|
11724
|
+
},
|
|
11725
|
+
children: "Regenerate"
|
|
11726
|
+
}
|
|
11727
|
+
) : null
|
|
11728
|
+
] })
|
|
11729
|
+
] }),
|
|
11730
|
+
clinicalNarrativeLocked ? /* @__PURE__ */ jsxRuntime.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,
|
|
11701
11731
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11702
11732
|
Textarea,
|
|
11703
11733
|
{
|
|
11704
11734
|
className: "min-h-[88px] rounded-md border border-input bg-white text-sm",
|
|
11705
|
-
placeholder: "
|
|
11706
|
-
value:
|
|
11707
|
-
onChange: (e) =>
|
|
11708
|
-
|
|
11735
|
+
placeholder: "Auto-filled from selections above\u2026",
|
|
11736
|
+
value: typeof clinicalField.value === "string" ? clinicalField.value : clinicalField.value == null ? "" : String(clinicalField.value),
|
|
11737
|
+
onChange: (e) => {
|
|
11738
|
+
setClinicalNarrativeLocked(true);
|
|
11739
|
+
const t = e.target.value;
|
|
11740
|
+
clinicalField.setValue(t);
|
|
11741
|
+
clinicalField.setTouched();
|
|
11742
|
+
setValue({ ...safe, examinationNotes: t });
|
|
11743
|
+
setTouched();
|
|
11744
|
+
},
|
|
11745
|
+
disabled: disabled || clinicalField.disabled
|
|
11709
11746
|
}
|
|
11710
11747
|
)
|
|
11711
11748
|
] })
|
|
@@ -11713,265 +11750,142 @@ var UrologyExaminationWidget = ({ fieldId }) => {
|
|
|
11713
11750
|
showError ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-2 text-xs text-red-500", children: error }) : null
|
|
11714
11751
|
] });
|
|
11715
11752
|
};
|
|
11716
|
-
var
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
children
|
|
11723
|
-
}) {
|
|
11724
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("overflow-hidden rounded-md border border-[#D0D0D0] bg-white", className), children: [
|
|
11725
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-[#D0D0D0] bg-[#FEE8EC]/50 px-3 py-1.5", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-slate-700", children: title }) }),
|
|
11726
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3", children })
|
|
11727
|
-
] });
|
|
11728
|
-
}
|
|
11753
|
+
var RISK_ROWS = [
|
|
11754
|
+
{ key: "ckd", label: "Chronic kidney disease" },
|
|
11755
|
+
{ key: "diabetes", label: "Diabetes" },
|
|
11756
|
+
{ key: "anticoagulants", label: "Anticoagulants" },
|
|
11757
|
+
{ key: "activeInfection", label: "Active infection" }
|
|
11758
|
+
];
|
|
11729
11759
|
var UrologyPathwayWidget = ({ fieldId }) => {
|
|
11730
11760
|
const { fieldDef, value, setValue, setTouched, error, touched, disabled } = useField(fieldId);
|
|
11731
11761
|
const { state } = useForm();
|
|
11732
11762
|
const showError = !!error && (touched || state.submitAttempted);
|
|
11733
11763
|
const safe = React15.useMemo(() => normalizeUrologyPathway(value), [value]);
|
|
11734
|
-
const smart = React15.useMemo(
|
|
11735
|
-
() => normalizeUrologySmartHistory(state.values[SMART_HISTORY_FIELD]),
|
|
11736
|
-
[state.values]
|
|
11737
|
-
);
|
|
11738
|
-
const exam = React15.useMemo(
|
|
11739
|
-
() => normalizeUrologyExamination(state.values[EXAMINATION_FIELD]),
|
|
11740
|
-
[state.values]
|
|
11741
|
-
);
|
|
11742
|
-
const ipssTotal = urologyIpssSevenTotal(smart.ipss);
|
|
11743
|
-
const ipssBandCls = ipssTotal >= 20 ? "bg-red-500/15 text-red-700" : ipssTotal >= 8 ? "bg-amber-500/15 text-amber-900" : "bg-emerald-500/15 text-emerald-800";
|
|
11744
|
-
const stone = React15.useMemo(
|
|
11745
|
-
() => urologyStoneRecommendation(safe.stoneSizeMm, safe.hydronephrosis),
|
|
11746
|
-
[safe.stoneSizeMm, safe.hydronephrosis]
|
|
11747
|
-
);
|
|
11748
|
-
const prostateLine = React15.useMemo(
|
|
11749
|
-
() => urologyProstateProcedure(safe.prostateVolumeCc),
|
|
11750
|
-
[safe.prostateVolumeCc]
|
|
11751
|
-
);
|
|
11752
|
-
const psaHigh = safe.psaNgMl > 4;
|
|
11753
|
-
const qmaxLow = safe.uroflowQmaxMlSec > 0 && Number.isFinite(safe.uroflowQmaxMlSec) && safe.uroflowQmaxMlSec < 10;
|
|
11754
11764
|
const update = (next) => {
|
|
11755
11765
|
setValue(next);
|
|
11756
11766
|
setTouched();
|
|
11757
11767
|
};
|
|
11758
|
-
const
|
|
11759
|
-
|
|
11768
|
+
const setNum = (key, raw) => {
|
|
11769
|
+
const n = Number(raw);
|
|
11770
|
+
const v = Number.isFinite(n) && n >= 0 ? n : 0;
|
|
11771
|
+
if (key === "stoneSizeMm") {
|
|
11772
|
+
update({ ...safe, stoneSizeMm: Math.round(v) });
|
|
11773
|
+
return;
|
|
11774
|
+
}
|
|
11775
|
+
update({ ...safe, [key]: v });
|
|
11776
|
+
};
|
|
11777
|
+
const toggleRisk = (key) => {
|
|
11778
|
+
update({
|
|
11779
|
+
...safe,
|
|
11780
|
+
risk: { ...safe.risk, [key]: !safe.risk[key] }
|
|
11781
|
+
});
|
|
11760
11782
|
};
|
|
11761
11783
|
if (!fieldDef) return null;
|
|
11762
11784
|
const theme = getThemeConfig("textarea", fieldDef.theme);
|
|
11763
11785
|
const wrapperClass = theme?.wrapperClassName ?? "rounded-md overflow-hidden flex flex-col border border-[#D0D0D0]";
|
|
11764
11786
|
const labelClass = theme?.labelClassName;
|
|
11787
|
+
const subtitle = fieldDef.meta && typeof fieldDef.meta === "object" && fieldDef.meta !== null ? String(fieldDef.meta.subtitle ?? "") : "";
|
|
11765
11788
|
const labelContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11766
11789
|
fieldDef.label,
|
|
11767
11790
|
fieldDef.required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-red-500", children: "*" }) : null
|
|
11768
11791
|
] });
|
|
11769
11792
|
const labelEl = theme ? /* @__PURE__ */ jsxRuntime.jsx(Label, { className: labelClass, children: labelContent }) : /* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-sm font-medium", children: labelContent });
|
|
11770
11793
|
const bodyClass = cn(
|
|
11771
|
-
"-mt-1 flex min-w-0 flex-col gap-
|
|
11794
|
+
"-mt-1 flex min-w-0 flex-col gap-4 border-t border-[#D0D0D0] bg-white px-3 py-3",
|
|
11772
11795
|
disabled && "pointer-events-none opacity-60"
|
|
11773
11796
|
);
|
|
11774
|
-
const subtitle = typeof fieldDef.meta?.subtitle === "string" && fieldDef.meta.subtitle.trim().length > 0 ? fieldDef.meta.subtitle.trim() : "";
|
|
11775
11797
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("w-full min-w-0", wrapperClass), children: [
|
|
11776
11798
|
labelEl,
|
|
11799
|
+
subtitle ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pt-2 text-[11px] text-muted-foreground", children: subtitle }) : null,
|
|
11777
11800
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: bodyClass, children: [
|
|
11778
|
-
|
|
11779
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-3
|
|
11780
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11781
|
-
/* @__PURE__ */ jsxRuntime.
|
|
11782
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium text-slate-700", children: "Stone size (mm)" }),
|
|
11783
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11784
|
-
Input,
|
|
11785
|
-
{
|
|
11786
|
-
type: "number",
|
|
11787
|
-
min: 0,
|
|
11788
|
-
step: "any",
|
|
11789
|
-
className: cn(
|
|
11790
|
-
"h-8 text-xs",
|
|
11791
|
-
disabled && "pointer-events-none"
|
|
11792
|
-
),
|
|
11793
|
-
value: safe.stoneSizeMm,
|
|
11794
|
-
onChange: (e) => {
|
|
11795
|
-
const raw = e.target.value;
|
|
11796
|
-
const n = Number(raw);
|
|
11797
|
-
update({
|
|
11798
|
-
...safe,
|
|
11799
|
-
stoneSizeMm: raw === "" || raw === "-" ? 0 : !Number.isFinite(n) ? 0 : Math.max(0, n)
|
|
11800
|
-
});
|
|
11801
|
-
},
|
|
11802
|
-
disabled
|
|
11803
|
-
}
|
|
11804
|
-
)
|
|
11805
|
-
] }),
|
|
11806
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mt-2 flex items-center gap-2", children: [
|
|
11807
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11808
|
-
Checkbox,
|
|
11809
|
-
{
|
|
11810
|
-
checked: safe.hydronephrosis,
|
|
11811
|
-
disabled,
|
|
11812
|
-
onCheckedChange: (checked) => update({ ...safe, hydronephrosis: checked === true })
|
|
11813
|
-
}
|
|
11814
|
-
),
|
|
11815
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-700", children: "Hydronephrosis on imaging" })
|
|
11816
|
-
] }),
|
|
11801
|
+
showError ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-600", children: error }) : null,
|
|
11802
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-3 md:grid-cols-2", children: [
|
|
11803
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block space-y-1 text-xs", children: [
|
|
11804
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Stone size (mm)" }),
|
|
11817
11805
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11818
|
-
|
|
11806
|
+
Input,
|
|
11819
11807
|
{
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11808
|
+
type: "number",
|
|
11809
|
+
min: 0,
|
|
11810
|
+
className: "h-8 text-xs",
|
|
11811
|
+
value: safe.stoneSizeMm,
|
|
11812
|
+
onChange: (e) => setNum("stoneSizeMm", e.target.value),
|
|
11813
|
+
disabled
|
|
11825
11814
|
}
|
|
11826
|
-
)
|
|
11827
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-[10px] text-muted-foreground", children: "Trigger: >10 mm OR obstruction \u2192 surgical." })
|
|
11815
|
+
)
|
|
11828
11816
|
] }),
|
|
11829
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11830
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
11831
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
|
|
11832
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium text-slate-700", children: "Volume (cc)" }),
|
|
11833
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11834
|
-
Input,
|
|
11835
|
-
{
|
|
11836
|
-
type: "number",
|
|
11837
|
-
min: 0,
|
|
11838
|
-
step: "any",
|
|
11839
|
-
className: cn(
|
|
11840
|
-
"h-8 text-xs",
|
|
11841
|
-
disabled && "pointer-events-none"
|
|
11842
|
-
),
|
|
11843
|
-
value: safe.prostateVolumeCc,
|
|
11844
|
-
onChange: (e) => {
|
|
11845
|
-
const raw = e.target.value;
|
|
11846
|
-
const n = Number(raw);
|
|
11847
|
-
update({
|
|
11848
|
-
...safe,
|
|
11849
|
-
prostateVolumeCc: raw === "" || raw === "-" ? 0 : !Number.isFinite(n) ? 0 : Math.max(0, n)
|
|
11850
|
-
});
|
|
11851
|
-
},
|
|
11852
|
-
disabled
|
|
11853
|
-
}
|
|
11854
|
-
)
|
|
11855
|
-
] }),
|
|
11856
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
|
|
11857
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium text-slate-700", children: "PSA (ng/ml)" }),
|
|
11858
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11859
|
-
Input,
|
|
11860
|
-
{
|
|
11861
|
-
type: "number",
|
|
11862
|
-
min: 0,
|
|
11863
|
-
step: 0.1,
|
|
11864
|
-
className: cn(
|
|
11865
|
-
"h-8 text-xs",
|
|
11866
|
-
psaHigh && "border-red-400 text-red-800",
|
|
11867
|
-
disabled && "pointer-events-none"
|
|
11868
|
-
),
|
|
11869
|
-
value: safe.psaNgMl,
|
|
11870
|
-
onChange: (e) => {
|
|
11871
|
-
const raw = e.target.value;
|
|
11872
|
-
const n = Number(raw);
|
|
11873
|
-
update({
|
|
11874
|
-
...safe,
|
|
11875
|
-
psaNgMl: raw === "" || raw === "-" ? 0 : !Number.isFinite(n) ? 0 : Math.max(0, n)
|
|
11876
|
-
});
|
|
11877
|
-
},
|
|
11878
|
-
disabled
|
|
11879
|
-
}
|
|
11880
|
-
)
|
|
11881
|
-
] }),
|
|
11882
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "col-span-2 block", children: [
|
|
11883
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium text-slate-700", children: "Uroflow Qmax (ml/s)" }),
|
|
11884
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11885
|
-
Input,
|
|
11886
|
-
{
|
|
11887
|
-
type: "number",
|
|
11888
|
-
min: 0,
|
|
11889
|
-
step: 0.1,
|
|
11890
|
-
className: cn(
|
|
11891
|
-
"h-8 text-xs",
|
|
11892
|
-
qmaxLow && "border-amber-500 text-amber-900",
|
|
11893
|
-
disabled && "pointer-events-none"
|
|
11894
|
-
),
|
|
11895
|
-
value: safe.uroflowQmaxMlSec,
|
|
11896
|
-
onChange: (e) => {
|
|
11897
|
-
const raw = e.target.value;
|
|
11898
|
-
const n = Number(raw);
|
|
11899
|
-
update({
|
|
11900
|
-
...safe,
|
|
11901
|
-
uroflowQmaxMlSec: raw === "" || raw === "-" ? 0 : !Number.isFinite(n) ? 0 : Math.max(0, n)
|
|
11902
|
-
});
|
|
11903
|
-
},
|
|
11904
|
-
disabled
|
|
11905
|
-
}
|
|
11906
|
-
)
|
|
11907
|
-
] })
|
|
11908
|
-
] }),
|
|
11817
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2 pt-6 text-xs", children: [
|
|
11909
11818
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11910
|
-
|
|
11819
|
+
Checkbox,
|
|
11911
11820
|
{
|
|
11912
|
-
|
|
11913
|
-
|
|
11914
|
-
|
|
11915
|
-
),
|
|
11916
|
-
children: prostateLine
|
|
11821
|
+
checked: safe.hydronephrosis,
|
|
11822
|
+
onCheckedChange: (c) => update({ ...safe, hydronephrosis: c === true }),
|
|
11823
|
+
disabled
|
|
11917
11824
|
}
|
|
11918
11825
|
),
|
|
11919
|
-
|
|
11826
|
+
"Hydronephrosis on imaging"
|
|
11920
11827
|
] }),
|
|
11921
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11922
|
-
/* @__PURE__ */ jsxRuntime.
|
|
11923
|
-
|
|
11924
|
-
|
|
11925
|
-
ipssTotal,
|
|
11926
|
-
"/35 \xB7 ",
|
|
11927
|
-
urologyIpssBand(ipssTotal)
|
|
11928
|
-
] })
|
|
11929
|
-
] }),
|
|
11930
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-3 h-1.5 overflow-hidden rounded-full bg-slate-200", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
11931
|
-
"div",
|
|
11828
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block space-y-1 text-xs", children: [
|
|
11829
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Prostate volume (cc)" }),
|
|
11830
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11831
|
+
Input,
|
|
11932
11832
|
{
|
|
11933
|
-
|
|
11934
|
-
|
|
11935
|
-
|
|
11936
|
-
|
|
11937
|
-
|
|
11833
|
+
type: "number",
|
|
11834
|
+
min: 0,
|
|
11835
|
+
step: 0.1,
|
|
11836
|
+
className: "h-8 text-xs",
|
|
11837
|
+
value: safe.prostateVolumeCc,
|
|
11838
|
+
onChange: (e) => setNum("prostateVolumeCc", e.target.value),
|
|
11839
|
+
disabled
|
|
11938
11840
|
}
|
|
11939
|
-
)
|
|
11940
|
-
|
|
11941
|
-
|
|
11942
|
-
|
|
11943
|
-
|
|
11944
|
-
|
|
11945
|
-
|
|
11946
|
-
|
|
11947
|
-
|
|
11948
|
-
|
|
11949
|
-
"
|
|
11950
|
-
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
|
|
11841
|
+
)
|
|
11842
|
+
] }),
|
|
11843
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block space-y-1 text-xs", children: [
|
|
11844
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "PSA (ng/mL)" }),
|
|
11845
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11846
|
+
Input,
|
|
11847
|
+
{
|
|
11848
|
+
type: "number",
|
|
11849
|
+
min: 0,
|
|
11850
|
+
step: 0.01,
|
|
11851
|
+
className: "h-8 text-xs",
|
|
11852
|
+
value: safe.psaNgMl,
|
|
11853
|
+
onChange: (e) => setNum("psaNgMl", e.target.value),
|
|
11854
|
+
disabled
|
|
11855
|
+
}
|
|
11856
|
+
)
|
|
11955
11857
|
] }),
|
|
11956
|
-
/* @__PURE__ */ jsxRuntime.
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11858
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block space-y-1 text-xs md:col-span-2", children: [
|
|
11859
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Uroflow Qmax (mL/s)" }),
|
|
11860
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11861
|
+
Input,
|
|
11862
|
+
{
|
|
11863
|
+
type: "number",
|
|
11864
|
+
min: 0,
|
|
11865
|
+
step: 0.1,
|
|
11866
|
+
className: "h-8 text-xs",
|
|
11867
|
+
value: safe.uroflowQmaxMlSec,
|
|
11868
|
+
onChange: (e) => setNum("uroflowQmaxMlSec", e.target.value),
|
|
11869
|
+
disabled
|
|
11870
|
+
}
|
|
11871
|
+
)
|
|
11872
|
+
] })
|
|
11873
|
+
] }),
|
|
11874
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 border-t border-muted-foreground/20 pt-3", children: [
|
|
11875
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-semibold text-slate-800", children: "Risk stratification" }),
|
|
11876
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: RISK_ROWS.map((r) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2 text-xs", children: [
|
|
11962
11877
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11963
11878
|
Checkbox,
|
|
11964
11879
|
{
|
|
11965
|
-
checked: safe.risk[key],
|
|
11966
|
-
|
|
11967
|
-
|
|
11880
|
+
checked: safe.risk[r.key],
|
|
11881
|
+
onCheckedChange: () => toggleRisk(r.key),
|
|
11882
|
+
disabled
|
|
11968
11883
|
}
|
|
11969
11884
|
),
|
|
11970
|
-
|
|
11971
|
-
] }, key)) })
|
|
11885
|
+
r.label
|
|
11886
|
+
] }, r.key)) })
|
|
11972
11887
|
] })
|
|
11973
|
-
] })
|
|
11974
|
-
showError ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-2 text-xs text-red-500", children: error }) : null
|
|
11888
|
+
] })
|
|
11975
11889
|
] });
|
|
11976
11890
|
};
|
|
11977
11891
|
var FieldRenderer = ({ fieldId }) => {
|
|
@@ -12924,22 +12838,32 @@ var ReadOnlyFieldRenderer = ({
|
|
|
12924
12838
|
].join("\n");
|
|
12925
12839
|
return /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyText, { fieldDef, value: lines });
|
|
12926
12840
|
}
|
|
12927
|
-
case "urology_smart_history":
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
12933
|
-
}
|
|
12934
|
-
|
|
12935
|
-
|
|
12936
|
-
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
12940
|
-
|
|
12941
|
-
|
|
12942
|
-
);
|
|
12841
|
+
case "urology_smart_history": {
|
|
12842
|
+
const structuredDisplay = value && typeof value === "object" ? JSON.stringify(normalizeUrologySmartHistory(value), null, 2) : value == null || value === "" ? "\u2014" : String(value);
|
|
12843
|
+
const hopcDef = resolveFieldDef?.("symptoms");
|
|
12844
|
+
const hopcRaw = allValues?.["symptoms"];
|
|
12845
|
+
const hopcStr = typeof hopcRaw === "string" ? hopcRaw : hopcRaw != null && hopcRaw !== "" ? String(hopcRaw) : "";
|
|
12846
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
12847
|
+
/* @__PURE__ */ jsxRuntime.jsx(ReadOnlyText, { fieldDef, value: structuredDisplay }),
|
|
12848
|
+
hopcDef ? /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyText, { fieldDef: hopcDef, value: hopcStr }) : hopcStr ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
12849
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-700", children: "History of presenting complaint" }),
|
|
12850
|
+
/* @__PURE__ */ jsxRuntime.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 })
|
|
12851
|
+
] }) : null
|
|
12852
|
+
] });
|
|
12853
|
+
}
|
|
12854
|
+
case "urology_examination": {
|
|
12855
|
+
const structuredDisplay = value && typeof value === "object" ? JSON.stringify(value, null, 2) : value == null || value === "" ? "\u2014" : String(value);
|
|
12856
|
+
const clinicalDef = resolveFieldDef?.("clinical_examination");
|
|
12857
|
+
const clinicalRaw = allValues?.["clinical_examination"];
|
|
12858
|
+
const clinicalStr = typeof clinicalRaw === "string" ? clinicalRaw : clinicalRaw != null && clinicalRaw !== "" ? String(clinicalRaw) : "";
|
|
12859
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
12860
|
+
/* @__PURE__ */ jsxRuntime.jsx(ReadOnlyText, { fieldDef, value: structuredDisplay }),
|
|
12861
|
+
clinicalDef ? /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyText, { fieldDef: clinicalDef, value: clinicalStr }) : clinicalStr ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
12862
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-700", children: "Clinical examination" }),
|
|
12863
|
+
/* @__PURE__ */ jsxRuntime.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 })
|
|
12864
|
+
] }) : null
|
|
12865
|
+
] });
|
|
12866
|
+
}
|
|
12943
12867
|
case "urology_pathway":
|
|
12944
12868
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
12945
12869
|
ReadOnlyText,
|
|
@@ -13210,12 +13134,9 @@ exports.RichTextWidget = RichTextWidget;
|
|
|
13210
13134
|
exports.SignatureUploadWidget = SignatureUploadWidget;
|
|
13211
13135
|
exports.SmartForm = SmartForm;
|
|
13212
13136
|
exports.SmartTextareaWidget = SmartTextareaWidget;
|
|
13213
|
-
exports.attachUrologyClinicalPathwayMetadata = attachUrologyClinicalPathwayMetadata;
|
|
13214
13137
|
exports.createUploadHandler = createUploadHandler;
|
|
13215
13138
|
exports.evaluateRules = evaluateRules;
|
|
13216
13139
|
exports.fieldMetaRequestsMarMedicationOrdersButton = fieldMetaRequestsMarMedicationOrdersButton;
|
|
13217
|
-
exports.isUrologyTemplate = isUrologyTemplate;
|
|
13218
|
-
exports.shouldFoldUrologyClinicalPathway = shouldFoldUrologyClinicalPathway;
|
|
13219
13140
|
exports.useField = useField;
|
|
13220
13141
|
exports.useFieldHandlers = useFieldHandlers;
|
|
13221
13142
|
exports.useForm = useForm;
|