formanitor 0.0.39 → 0.0.41
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 +1903 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -3
- package/dist/index.d.ts +34 -3
- package/dist/index.mjs +1901 -93
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -65,6 +65,326 @@ 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
|
+
|
|
68
388
|
// src/core/validate.ts
|
|
69
389
|
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
70
390
|
function validateField(value, field) {
|
|
@@ -260,8 +580,21 @@ var defaultGeneralSurgerySmartHistory = () => ({
|
|
|
260
580
|
gi: { appetite: "", weightLoss: "", bowelHabits: "", checks: [] },
|
|
261
581
|
breast: { lumpDuration: "", nippleDischarge: "", checks: [] },
|
|
262
582
|
ano: { checks: [] },
|
|
263
|
-
thyroid: { swellingDuration: "", checks: [] }
|
|
264
|
-
|
|
583
|
+
thyroid: { swellingDuration: "", checks: [] }
|
|
584
|
+
});
|
|
585
|
+
var defaultBreastExamSide = () => ({
|
|
586
|
+
size: "",
|
|
587
|
+
quadrant: "",
|
|
588
|
+
tenderness: "",
|
|
589
|
+
fixity: "",
|
|
590
|
+
skinInvolvement: "",
|
|
591
|
+
consistency: "",
|
|
592
|
+
axillaryNodes: false,
|
|
593
|
+
nippleAreolarComplex: false
|
|
594
|
+
});
|
|
595
|
+
var defaultBreastExaminationBlock = () => ({
|
|
596
|
+
left: defaultBreastExamSide(),
|
|
597
|
+
right: defaultBreastExamSide()
|
|
265
598
|
});
|
|
266
599
|
var defaultGeneralSurgeryExamination = () => ({
|
|
267
600
|
general: [],
|
|
@@ -274,7 +607,7 @@ var defaultGeneralSurgeryExamination = () => ({
|
|
|
274
607
|
bowelSounds: "none"
|
|
275
608
|
},
|
|
276
609
|
hernia: { site: "", reducible: false, coughImpulse: false, tenderness: false },
|
|
277
|
-
breast:
|
|
610
|
+
breast: defaultBreastExaminationBlock(),
|
|
278
611
|
thyroid: { size: "", mobileDeglutition: false, nodules: false, cervicalNodes: false },
|
|
279
612
|
anorectal: { inspection: "", dreDone: false, proctoscopyDone: false, notes: "" },
|
|
280
613
|
limb: { dilatedVeins: false, ulcer: false, oedema: false }
|
|
@@ -370,8 +703,32 @@ function normalizeGeneralSurgerySmartHistory(raw) {
|
|
|
370
703
|
thyroid: {
|
|
371
704
|
swellingDuration: str2(thy.swellingDuration),
|
|
372
705
|
checks: strArr(thy.checks)
|
|
373
|
-
}
|
|
374
|
-
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
function normalizeBreastExamSide(partial) {
|
|
710
|
+
const e = defaultBreastExamSide();
|
|
711
|
+
const str2 = (x) => typeof x === "string" ? x : "";
|
|
712
|
+
const bool = (x) => x === true;
|
|
713
|
+
if (!partial || typeof partial !== "object") return e;
|
|
714
|
+
const p = partial;
|
|
715
|
+
return {
|
|
716
|
+
size: str2(p.size),
|
|
717
|
+
quadrant: str2(p.quadrant),
|
|
718
|
+
tenderness: str2(p.tenderness),
|
|
719
|
+
fixity: str2(p.fixity),
|
|
720
|
+
skinInvolvement: str2(p.skinInvolvement),
|
|
721
|
+
consistency: str2(p.consistency),
|
|
722
|
+
axillaryNodes: bool(p.axillaryNodes),
|
|
723
|
+
nippleAreolarComplex: bool(p.nippleAreolarComplex)
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
function normalizeBreastExaminationBlock(raw) {
|
|
727
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return defaultBreastExaminationBlock();
|
|
728
|
+
const br = raw;
|
|
729
|
+
return {
|
|
730
|
+
left: normalizeBreastExamSide(br.left),
|
|
731
|
+
right: normalizeBreastExamSide(br.right)
|
|
375
732
|
};
|
|
376
733
|
}
|
|
377
734
|
function normalizeGeneralSurgeryExamination(raw) {
|
|
@@ -404,12 +761,7 @@ function normalizeGeneralSurgeryExamination(raw) {
|
|
|
404
761
|
coughImpulse: bool(hb.coughImpulse),
|
|
405
762
|
tenderness: bool(hb.tenderness)
|
|
406
763
|
},
|
|
407
|
-
breast:
|
|
408
|
-
lumpSizeCm: typeof br.lumpSizeCm === "string" ? br.lumpSizeCm : "",
|
|
409
|
-
mobile: bool(br.mobile),
|
|
410
|
-
skinInvolvement: bool(br.skinInvolvement),
|
|
411
|
-
axillaryNodes: bool(br.axillaryNodes)
|
|
412
|
-
},
|
|
764
|
+
breast: normalizeBreastExaminationBlock(br),
|
|
413
765
|
thyroid: {
|
|
414
766
|
size: typeof th.size === "string" ? th.size : "",
|
|
415
767
|
mobileDeglutition: bool(th.mobileDeglutition),
|
|
@@ -447,6 +799,486 @@ function gsGradingRowVisible(cond, chips, regions, grade) {
|
|
|
447
799
|
if (t.regions.some((r) => regions.includes(r))) return true;
|
|
448
800
|
return false;
|
|
449
801
|
}
|
|
802
|
+
var SWELLING_OPT_LABELS = {
|
|
803
|
+
reducible_hernia: "Reducible (hernia)",
|
|
804
|
+
cough_impulse: "Cough impulse",
|
|
805
|
+
skin_changes: "Skin changes"
|
|
806
|
+
};
|
|
807
|
+
var GI_OPT_LABELS = {
|
|
808
|
+
bleeding_pr: "Bleeding PR",
|
|
809
|
+
vomiting: "Vomiting",
|
|
810
|
+
haematemesis: "Haematemesis"
|
|
811
|
+
};
|
|
812
|
+
var BREAST_OPT_LABELS = {
|
|
813
|
+
pain: "Pain",
|
|
814
|
+
fh_ca_breast: "Family history of Ca breast"
|
|
815
|
+
};
|
|
816
|
+
var ANO_OPT_LABELS = {
|
|
817
|
+
pain_defecation: "Pain during defecation",
|
|
818
|
+
bleeding: "Bleeding",
|
|
819
|
+
constipation: "Constipation",
|
|
820
|
+
discharge: "Discharge"
|
|
821
|
+
};
|
|
822
|
+
var THYROID_OPT_LABELS = {
|
|
823
|
+
dysphagia: "Dysphagia",
|
|
824
|
+
voice_change: "Voice change",
|
|
825
|
+
thyrotoxicosis: "Thyrotoxicosis symptoms"
|
|
826
|
+
};
|
|
827
|
+
var SOCRATES_LABELS = {
|
|
828
|
+
site: "Site",
|
|
829
|
+
onset: "Onset",
|
|
830
|
+
character: "Character",
|
|
831
|
+
radiation: "Radiation",
|
|
832
|
+
associated: "Associated",
|
|
833
|
+
timing: "Timing",
|
|
834
|
+
exacerbating: "Exacerbating / relieving"
|
|
835
|
+
};
|
|
836
|
+
var GENERAL_EXAM_LABELS = {
|
|
837
|
+
pallor: "Pallor",
|
|
838
|
+
icterus: "Icterus",
|
|
839
|
+
cyanosis: "Cyanosis",
|
|
840
|
+
clubbing: "Clubbing",
|
|
841
|
+
lymphadenopathy: "Lymphadenopathy",
|
|
842
|
+
oedema: "Oedema"
|
|
843
|
+
};
|
|
844
|
+
var REGION_EXAM_LABELS = {
|
|
845
|
+
abdomen: "Abdomen",
|
|
846
|
+
hernia: "Hernia",
|
|
847
|
+
breast: "Breast",
|
|
848
|
+
thyroid: "Thyroid",
|
|
849
|
+
anorectal: "Anorectal",
|
|
850
|
+
limb: "Limb / varicose"
|
|
851
|
+
};
|
|
852
|
+
var ABD_INSP_LABELS = {
|
|
853
|
+
distension: "Distension",
|
|
854
|
+
scars: "Scars",
|
|
855
|
+
visible_peristalsis: "Visible peristalsis"
|
|
856
|
+
};
|
|
857
|
+
var ABD_PALP_LABELS = {
|
|
858
|
+
tenderness: "Tenderness",
|
|
859
|
+
guarding: "Guarding",
|
|
860
|
+
rigidity: "Rigidity",
|
|
861
|
+
mass: "Palpable mass"
|
|
862
|
+
};
|
|
863
|
+
var ABD_PERC_LABELS = {
|
|
864
|
+
tympany: "Tympany",
|
|
865
|
+
shifting_dullness: "Shifting dullness"
|
|
866
|
+
};
|
|
867
|
+
function trimLine(s) {
|
|
868
|
+
return s.replace(/\s+/g, " ").trim();
|
|
869
|
+
}
|
|
870
|
+
var SK_ORDER = ["site", "onset", "character", "radiation", "associated", "timing", "exacerbating"];
|
|
871
|
+
function formatGeneralSurgerySmartHistoryToNarrative(v, chips) {
|
|
872
|
+
const blocks = [];
|
|
873
|
+
const showPain = chips.includes("pain");
|
|
874
|
+
const showSwelling = chips.includes("swelling_lump");
|
|
875
|
+
const showGI = chips.includes("bleeding") || chips.includes("obstruction");
|
|
876
|
+
const showBreast = chips.includes("breast");
|
|
877
|
+
const showAno = chips.includes("anorectal");
|
|
878
|
+
const showThyroid = chips.includes("thyroid");
|
|
879
|
+
if (showPain) {
|
|
880
|
+
const painLines = [];
|
|
881
|
+
for (const key of SK_ORDER) {
|
|
882
|
+
const raw = v.pain[key];
|
|
883
|
+
if (typeof raw === "string" && raw.trim()) {
|
|
884
|
+
painLines.push(`${SOCRATES_LABELS[key]}: ${trimLine(raw)}`);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
if (v.pain.severity > 0) {
|
|
888
|
+
painLines.push(`Severity (0\u201310): ${v.pain.severity}`);
|
|
889
|
+
}
|
|
890
|
+
if (painLines.length > 0) {
|
|
891
|
+
blocks.push(["SOCRATES \u2014 pain", ...painLines.map((l) => `\u2022 ${l}`)].join("\n"));
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
if (showSwelling) {
|
|
895
|
+
const parts = [];
|
|
896
|
+
if (v.swelling.duration.trim()) parts.push(`Duration: ${trimLine(v.swelling.duration)}`);
|
|
897
|
+
if (v.swelling.sizeProgression.trim())
|
|
898
|
+
parts.push(`Size progression: ${trimLine(v.swelling.sizeProgression)}`);
|
|
899
|
+
if (v.swelling.painfulPainless.trim())
|
|
900
|
+
parts.push(`Painful / painless: ${trimLine(v.swelling.painfulPainless)}`);
|
|
901
|
+
for (const c of v.swelling.checks) {
|
|
902
|
+
const lb = SWELLING_OPT_LABELS[c] ?? c;
|
|
903
|
+
parts.push(lb);
|
|
904
|
+
}
|
|
905
|
+
if (parts.length > 0) {
|
|
906
|
+
blocks.push(["Swelling history", ...parts.map((p) => `\u2022 ${p}`)].join("\n"));
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
if (showGI) {
|
|
910
|
+
const parts = [];
|
|
911
|
+
if (v.gi.appetite.trim()) parts.push(`Appetite: ${trimLine(v.gi.appetite)}`);
|
|
912
|
+
if (v.gi.weightLoss.trim()) parts.push(`Weight loss: ${trimLine(v.gi.weightLoss)}`);
|
|
913
|
+
if (v.gi.bowelHabits.trim()) parts.push(`Bowel habits: ${trimLine(v.gi.bowelHabits)}`);
|
|
914
|
+
for (const c of v.gi.checks) {
|
|
915
|
+
parts.push(GI_OPT_LABELS[c] ?? c);
|
|
916
|
+
}
|
|
917
|
+
if (parts.length > 0) {
|
|
918
|
+
blocks.push(["GI history", ...parts.map((p) => `\u2022 ${p}`)].join("\n"));
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
if (showBreast) {
|
|
922
|
+
const parts = [];
|
|
923
|
+
if (v.breast.lumpDuration.trim()) parts.push(`Lump duration: ${trimLine(v.breast.lumpDuration)}`);
|
|
924
|
+
if (v.breast.nippleDischarge.trim())
|
|
925
|
+
parts.push(`Nipple discharge: ${trimLine(v.breast.nippleDischarge)}`);
|
|
926
|
+
for (const c of v.breast.checks) {
|
|
927
|
+
parts.push(BREAST_OPT_LABELS[c] ?? c);
|
|
928
|
+
}
|
|
929
|
+
if (parts.length > 0) {
|
|
930
|
+
blocks.push(["Breast history", ...parts.map((p) => `\u2022 ${p}`)].join("\n"));
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
if (showAno) {
|
|
934
|
+
const parts = [];
|
|
935
|
+
for (const c of v.ano.checks) {
|
|
936
|
+
parts.push(ANO_OPT_LABELS[c] ?? c);
|
|
937
|
+
}
|
|
938
|
+
if (parts.length > 0) {
|
|
939
|
+
blocks.push(["Anorectal history", ...parts.map((p) => `\u2022 ${p}`)].join("\n"));
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
if (showThyroid) {
|
|
943
|
+
const parts = [];
|
|
944
|
+
if (v.thyroid.swellingDuration.trim())
|
|
945
|
+
parts.push(`Swelling duration: ${trimLine(v.thyroid.swellingDuration)}`);
|
|
946
|
+
for (const c of v.thyroid.checks) {
|
|
947
|
+
parts.push(THYROID_OPT_LABELS[c] ?? c);
|
|
948
|
+
}
|
|
949
|
+
if (parts.length > 0) {
|
|
950
|
+
blocks.push(["Thyroid history", ...parts.map((p) => `\u2022 ${p}`)].join("\n"));
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
return blocks.join("\n\n");
|
|
954
|
+
}
|
|
955
|
+
var BOWEL_SOUND_LABELS = {
|
|
956
|
+
none: "\u2014",
|
|
957
|
+
normal: "Normal",
|
|
958
|
+
up: "Increased (\u2191)",
|
|
959
|
+
down: "Decreased (\u2193)",
|
|
960
|
+
absent: "Absent"
|
|
961
|
+
};
|
|
962
|
+
function formatGeneralSurgeryExaminationToNarrative(v) {
|
|
963
|
+
const blocks = [];
|
|
964
|
+
const generalPositive = v.general.map((k) => GENERAL_EXAM_LABELS[k] ?? k).filter(Boolean);
|
|
965
|
+
if (generalPositive.length > 0) {
|
|
966
|
+
blocks.push(
|
|
967
|
+
["General examination", ...generalPositive.map((g) => `\u2022 ${g}`)].join("\n")
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
if (v.regions.length > 0) {
|
|
971
|
+
const names = v.regions.map((r) => REGION_EXAM_LABELS[r] ?? r);
|
|
972
|
+
blocks.push(`Regional examination: ${names.join(", ")}`);
|
|
973
|
+
}
|
|
974
|
+
if (v.regions.includes("abdomen")) {
|
|
975
|
+
const sub = [];
|
|
976
|
+
const ins = v.abdomen.inspection.map((x) => ABD_INSP_LABELS[x] ?? x);
|
|
977
|
+
const pal = v.abdomen.palpation.map((x) => ABD_PALP_LABELS[x] ?? x);
|
|
978
|
+
const per = v.abdomen.percussion.map((x) => ABD_PERC_LABELS[x] ?? x);
|
|
979
|
+
if (ins.length) sub.push(`Inspection: ${ins.join(", ")}`);
|
|
980
|
+
if (pal.length) sub.push(`Palpation: ${pal.join(", ")}`);
|
|
981
|
+
if (v.abdomen.massNotes.trim()) sub.push(`Mass / mobility: ${trimLine(v.abdomen.massNotes)}`);
|
|
982
|
+
if (per.length) sub.push(`Percussion: ${per.join(", ")}`);
|
|
983
|
+
const bs = BOWEL_SOUND_LABELS[v.abdomen.bowelSounds];
|
|
984
|
+
if (v.abdomen.bowelSounds !== "none") {
|
|
985
|
+
sub.push(`Bowel sounds: ${bs}`);
|
|
986
|
+
}
|
|
987
|
+
if (sub.length > 0) {
|
|
988
|
+
blocks.push(["Abdomen", ...sub.map((s) => `\u2022 ${s}`)].join("\n"));
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (v.regions.includes("hernia")) {
|
|
992
|
+
const sub = [];
|
|
993
|
+
if (v.hernia.site.trim()) sub.push(`Site: ${trimLine(v.hernia.site)}`);
|
|
994
|
+
if (v.hernia.reducible) sub.push("Reducible");
|
|
995
|
+
if (v.hernia.coughImpulse) sub.push("Cough impulse positive");
|
|
996
|
+
if (v.hernia.tenderness) sub.push("Tenderness");
|
|
997
|
+
if (sub.length > 0) {
|
|
998
|
+
blocks.push(["Hernia", ...sub.map((s) => `\u2022 ${s}`)].join("\n"));
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
if (v.regions.includes("breast")) {
|
|
1002
|
+
const sub = [];
|
|
1003
|
+
const appendSide = (label, s) => {
|
|
1004
|
+
const lines = [];
|
|
1005
|
+
if (s.size.trim()) lines.push(`Size: ${trimLine(s.size)}`);
|
|
1006
|
+
if (s.quadrant.trim()) lines.push(`Quadrant: ${trimLine(s.quadrant)}`);
|
|
1007
|
+
if (s.tenderness.trim()) lines.push(`Tenderness: ${trimLine(s.tenderness)}`);
|
|
1008
|
+
if (s.fixity.trim()) lines.push(`Fixity: ${trimLine(s.fixity)}`);
|
|
1009
|
+
if (s.skinInvolvement.trim()) lines.push(`Skin involvement: ${trimLine(s.skinInvolvement)}`);
|
|
1010
|
+
if (s.consistency.trim()) lines.push(`Consistency: ${trimLine(s.consistency)}`);
|
|
1011
|
+
if (s.axillaryNodes) lines.push("Axillary nodes");
|
|
1012
|
+
if (s.nippleAreolarComplex) lines.push("Nipple-areolar complex");
|
|
1013
|
+
if (lines.length > 0) {
|
|
1014
|
+
sub.push(`${label}: ${lines.join("; ")}`);
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
appendSide("Left breast", v.breast.left);
|
|
1018
|
+
appendSide("Right breast", v.breast.right);
|
|
1019
|
+
if (sub.length > 0) {
|
|
1020
|
+
blocks.push(["Breast", ...sub.map((s) => `\u2022 ${s}`)].join("\n"));
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
if (v.regions.includes("thyroid")) {
|
|
1024
|
+
const sub = [];
|
|
1025
|
+
if (v.thyroid.size.trim()) sub.push(`Size: ${trimLine(v.thyroid.size)}`);
|
|
1026
|
+
if (v.thyroid.mobileDeglutition) sub.push("Mobile with deglutition");
|
|
1027
|
+
if (v.thyroid.nodules) sub.push("Nodules");
|
|
1028
|
+
if (v.thyroid.cervicalNodes) sub.push("Cervical lymph nodes");
|
|
1029
|
+
if (sub.length > 0) {
|
|
1030
|
+
blocks.push(["Thyroid", ...sub.map((s) => `\u2022 ${s}`)].join("\n"));
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
if (v.regions.includes("anorectal")) {
|
|
1034
|
+
const sub = [];
|
|
1035
|
+
if (v.anorectal.inspection.trim())
|
|
1036
|
+
sub.push(`Inspection: ${trimLine(v.anorectal.inspection)}`);
|
|
1037
|
+
if (v.anorectal.dreDone) sub.push("DRE done");
|
|
1038
|
+
if (v.anorectal.proctoscopyDone) sub.push("Proctoscopy done");
|
|
1039
|
+
if (v.anorectal.notes.trim()) sub.push(`DRE / proctoscopy notes: ${trimLine(v.anorectal.notes)}`);
|
|
1040
|
+
if (sub.length > 0) {
|
|
1041
|
+
blocks.push(["Anorectal", ...sub.map((s) => `\u2022 ${s}`)].join("\n"));
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
if (v.regions.includes("limb")) {
|
|
1045
|
+
const sub = [];
|
|
1046
|
+
if (v.limb.dilatedVeins) sub.push("Dilated veins");
|
|
1047
|
+
if (v.limb.ulcer) sub.push("Ulcer");
|
|
1048
|
+
if (v.limb.oedema) sub.push("Oedema");
|
|
1049
|
+
if (sub.length > 0) {
|
|
1050
|
+
blocks.push(["Limb / varicose", ...sub.map((s) => `\u2022 ${s}`)].join("\n"));
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
return blocks.join("\n\n");
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// src/core/urologyPathway.ts
|
|
1057
|
+
var defaultUrologySmartHistory = () => ({
|
|
1058
|
+
socrates: {
|
|
1059
|
+
site: "",
|
|
1060
|
+
onset: "",
|
|
1061
|
+
character: "",
|
|
1062
|
+
radiation: "",
|
|
1063
|
+
associated: "",
|
|
1064
|
+
timing: "",
|
|
1065
|
+
exacerbating: "",
|
|
1066
|
+
severity: 0
|
|
1067
|
+
},
|
|
1068
|
+
ipss: {
|
|
1069
|
+
incomplete: 0,
|
|
1070
|
+
frequency: 0,
|
|
1071
|
+
intermittency: 0,
|
|
1072
|
+
urgency: 0,
|
|
1073
|
+
weakStream: 0,
|
|
1074
|
+
straining: 0,
|
|
1075
|
+
nocturia: 0
|
|
1076
|
+
},
|
|
1077
|
+
ipssQoL: 0,
|
|
1078
|
+
haematuria: { present: false, timing: "", painful: false, clots: false },
|
|
1079
|
+
sexual: { erectile: "", libido: "", ejaculation: "", infertilityMonths: 0 },
|
|
1080
|
+
pastNotes: ""
|
|
1081
|
+
});
|
|
1082
|
+
var defaultUrologyExamination = () => ({
|
|
1083
|
+
regions: [],
|
|
1084
|
+
general: { pallor: false, oedema: false, icterus: false, lymphadenopathy: false },
|
|
1085
|
+
abdomen: {
|
|
1086
|
+
distension: false,
|
|
1087
|
+
scars: false,
|
|
1088
|
+
mass: false,
|
|
1089
|
+
kidneyPalpable: false,
|
|
1090
|
+
bladderPalpable: false,
|
|
1091
|
+
renalAngleTender: false,
|
|
1092
|
+
bladderDull: false
|
|
1093
|
+
},
|
|
1094
|
+
gu: {
|
|
1095
|
+
meatusAbnormal: false,
|
|
1096
|
+
phimosis: false,
|
|
1097
|
+
lesions: false,
|
|
1098
|
+
scrotalSwelling: false,
|
|
1099
|
+
transilluminant: false,
|
|
1100
|
+
tenderTestis: false,
|
|
1101
|
+
hydrocele: false,
|
|
1102
|
+
varicocele: false,
|
|
1103
|
+
suspiciousMass: false
|
|
1104
|
+
},
|
|
1105
|
+
dre: { done: false, sizeGrams: 0, consistency: "", medianSulcus: "", tenderness: false },
|
|
1106
|
+
examinationNotes: ""
|
|
1107
|
+
});
|
|
1108
|
+
function clampInt(n, lo, hi) {
|
|
1109
|
+
if (!Number.isFinite(n)) return lo;
|
|
1110
|
+
return Math.min(hi, Math.max(lo, Math.round(n)));
|
|
1111
|
+
}
|
|
1112
|
+
var SOCRATES_KEYS = [
|
|
1113
|
+
"site",
|
|
1114
|
+
"onset",
|
|
1115
|
+
"character",
|
|
1116
|
+
"radiation",
|
|
1117
|
+
"associated",
|
|
1118
|
+
"timing",
|
|
1119
|
+
"exacerbating"
|
|
1120
|
+
];
|
|
1121
|
+
function urologyIpssSevenTotal(ipss) {
|
|
1122
|
+
return ipss.incomplete + ipss.frequency + ipss.intermittency + ipss.urgency + ipss.weakStream + ipss.straining + ipss.nocturia;
|
|
1123
|
+
}
|
|
1124
|
+
function urologyIpssBand(total) {
|
|
1125
|
+
if (total <= 7) return "Mild";
|
|
1126
|
+
if (total <= 19) return "Moderate";
|
|
1127
|
+
return "Severe";
|
|
1128
|
+
}
|
|
1129
|
+
function normalizeUrologySmartHistory(raw) {
|
|
1130
|
+
const d = defaultUrologySmartHistory();
|
|
1131
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return d;
|
|
1132
|
+
const v = raw;
|
|
1133
|
+
const socIn = v.socrates && typeof v.socrates === "object" ? v.socrates : {};
|
|
1134
|
+
const socrates = { ...d.socrates };
|
|
1135
|
+
for (const k of SOCRATES_KEYS) {
|
|
1136
|
+
const x = socIn[k];
|
|
1137
|
+
socrates[k] = typeof x === "string" ? x : "";
|
|
1138
|
+
}
|
|
1139
|
+
const sev = Number(socIn.severity);
|
|
1140
|
+
socrates.severity = clampInt(Number.isFinite(sev) ? sev : d.socrates.severity, 0, 10);
|
|
1141
|
+
const ipIn = v.ipss && typeof v.ipss === "object" ? v.ipss : {};
|
|
1142
|
+
const ipss = { ...d.ipss };
|
|
1143
|
+
for (const k of [
|
|
1144
|
+
"incomplete",
|
|
1145
|
+
"frequency",
|
|
1146
|
+
"intermittency",
|
|
1147
|
+
"urgency",
|
|
1148
|
+
"weakStream",
|
|
1149
|
+
"straining",
|
|
1150
|
+
"nocturia"
|
|
1151
|
+
]) {
|
|
1152
|
+
const n = Number(ipIn[k]);
|
|
1153
|
+
ipss[k] = clampInt(Number.isFinite(n) ? n : 0, 0, 5);
|
|
1154
|
+
}
|
|
1155
|
+
const qol = Number(v.ipssQoL);
|
|
1156
|
+
const ipssQoL = clampInt(Number.isFinite(qol) ? qol : d.ipssQoL, 0, 6);
|
|
1157
|
+
const haIn = v.haematuria && typeof v.haematuria === "object" ? v.haematuria : {};
|
|
1158
|
+
const timingRaw = haIn.timing;
|
|
1159
|
+
const haematuria = {
|
|
1160
|
+
present: haIn.present === true,
|
|
1161
|
+
timing: typeof timingRaw === "string" && timingRaw !== "none" && timingRaw.length > 0 ? timingRaw : "",
|
|
1162
|
+
painful: haIn.painful === true,
|
|
1163
|
+
clots: haIn.clots === true
|
|
1164
|
+
};
|
|
1165
|
+
const sxIn = v.sexual && typeof v.sexual === "object" ? v.sexual : {};
|
|
1166
|
+
const str2 = (x) => typeof x === "string" && x.length > 0 && x !== "none" ? x : "";
|
|
1167
|
+
const inf = Number(sxIn.infertilityMonths);
|
|
1168
|
+
const sexual = {
|
|
1169
|
+
erectile: str2(sxIn.erectile),
|
|
1170
|
+
libido: str2(sxIn.libido),
|
|
1171
|
+
ejaculation: str2(sxIn.ejaculation),
|
|
1172
|
+
infertilityMonths: Number.isFinite(inf) && inf >= 0 ? Math.round(inf) : 0
|
|
1173
|
+
};
|
|
1174
|
+
const pastNotes = typeof v.pastNotes === "string" ? v.pastNotes : d.pastNotes;
|
|
1175
|
+
return { socrates, ipss, ipssQoL, haematuria, sexual, pastNotes };
|
|
1176
|
+
}
|
|
1177
|
+
function normalizeUrologyExamination(raw) {
|
|
1178
|
+
const d = defaultUrologyExamination();
|
|
1179
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return d;
|
|
1180
|
+
const v = raw;
|
|
1181
|
+
const strArr = (x) => Array.isArray(x) ? x.filter((i) => typeof i === "string") : [];
|
|
1182
|
+
const bool = (x) => x === true;
|
|
1183
|
+
const g = v.general && typeof v.general === "object" ? v.general : {};
|
|
1184
|
+
const ab = v.abdomen && typeof v.abdomen === "object" ? v.abdomen : {};
|
|
1185
|
+
const gu = v.gu && typeof v.gu === "object" ? v.gu : {};
|
|
1186
|
+
const dr = v.dre && typeof v.dre === "object" ? v.dre : {};
|
|
1187
|
+
const sg = Number(dr.sizeGrams);
|
|
1188
|
+
const consistencyRaw = dr.consistency;
|
|
1189
|
+
const medianRaw = dr.medianSulcus;
|
|
1190
|
+
const dreEmpty = (x) => !(typeof x === "string" && x.length > 0 && x !== "none");
|
|
1191
|
+
return {
|
|
1192
|
+
regions: strArr(v.regions),
|
|
1193
|
+
general: {
|
|
1194
|
+
pallor: bool(g.pallor),
|
|
1195
|
+
oedema: bool(g.oedema),
|
|
1196
|
+
icterus: bool(g.icterus),
|
|
1197
|
+
lymphadenopathy: bool(g.lymphadenopathy)
|
|
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
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
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
|
+
function normalizeUrologyPathway(raw) {
|
|
1248
|
+
const d = defaultUrologyPathway();
|
|
1249
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return d;
|
|
1250
|
+
const v = raw;
|
|
1251
|
+
const rIn = v.risk && typeof v.risk === "object" && !Array.isArray(v.risk) ? v.risk : {};
|
|
1252
|
+
return {
|
|
1253
|
+
stoneSizeMm: clampNonNegativeNumber(v.stoneSizeMm, d.stoneSizeMm),
|
|
1254
|
+
hydronephrosis: v.hydronephrosis === true,
|
|
1255
|
+
prostateVolumeCc: clampNonNegativeNumber(v.prostateVolumeCc, d.prostateVolumeCc),
|
|
1256
|
+
psaNgMl: clampNonNegativeNumber(v.psaNgMl, d.psaNgMl),
|
|
1257
|
+
uroflowQmaxMlSec: clampNonNegativeNumber(v.uroflowQmaxMlSec, d.uroflowQmaxMlSec),
|
|
1258
|
+
risk: {
|
|
1259
|
+
ckd: rIn.ckd === true,
|
|
1260
|
+
diabetes: rIn.diabetes === true,
|
|
1261
|
+
anticoagulants: rIn.anticoagulants === true,
|
|
1262
|
+
activeInfection: rIn.activeInfection === true
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
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
|
+
}
|
|
450
1282
|
|
|
451
1283
|
// src/core/painScaleFlags.ts
|
|
452
1284
|
function normalizePainScaleFlags(raw, bounds) {
|
|
@@ -566,6 +1398,12 @@ var FormStore = class {
|
|
|
566
1398
|
f.defaultValue ?? { pain: f.min ?? 0, flags: [] },
|
|
567
1399
|
{ min: f.min, max: f.max }
|
|
568
1400
|
);
|
|
1401
|
+
} else if (field.type === "urology_smart_history") {
|
|
1402
|
+
values[field.id] = field.defaultValue ?? defaultUrologySmartHistory();
|
|
1403
|
+
} else if (field.type === "urology_examination") {
|
|
1404
|
+
values[field.id] = field.defaultValue ?? defaultUrologyExamination();
|
|
1405
|
+
} else if (field.type === "urology_pathway") {
|
|
1406
|
+
values[field.id] = field.defaultValue ?? defaultUrologyPathway();
|
|
569
1407
|
} else values[field.id] = null;
|
|
570
1408
|
}
|
|
571
1409
|
});
|
|
@@ -775,6 +1613,7 @@ var FormStore = class {
|
|
|
775
1613
|
}
|
|
776
1614
|
}
|
|
777
1615
|
});
|
|
1616
|
+
attachUrologyClinicalPathwayMetadata(result, this.schema);
|
|
778
1617
|
return result;
|
|
779
1618
|
}
|
|
780
1619
|
// --- Core Logic ---
|
|
@@ -5747,7 +6586,7 @@ function safeJsonParse(val) {
|
|
|
5747
6586
|
return null;
|
|
5748
6587
|
}
|
|
5749
6588
|
}
|
|
5750
|
-
function
|
|
6589
|
+
function clampInt2(val, min, max) {
|
|
5751
6590
|
if (!Number.isFinite(val)) return min;
|
|
5752
6591
|
return Math.max(min, Math.min(max, Math.trunc(val)));
|
|
5753
6592
|
}
|
|
@@ -5757,10 +6596,10 @@ function parseLegacyString(input) {
|
|
|
5757
6596
|
if (gpalMatch) {
|
|
5758
6597
|
const [, g, p, a, l] = gpalMatch;
|
|
5759
6598
|
result.gpal = {
|
|
5760
|
-
G:
|
|
5761
|
-
P:
|
|
5762
|
-
A:
|
|
5763
|
-
L:
|
|
6599
|
+
G: clampInt2(Number(g), 0, 20),
|
|
6600
|
+
P: clampInt2(Number(p), 0, 20),
|
|
6601
|
+
A: clampInt2(Number(a), 0, 20),
|
|
6602
|
+
L: clampInt2(Number(l), 0, 20)
|
|
5764
6603
|
};
|
|
5765
6604
|
}
|
|
5766
6605
|
const lmpMatch = input.match(/LMP:\s*([^\n]+)/i);
|
|
@@ -5793,10 +6632,10 @@ function normalizeToDraft(value) {
|
|
|
5793
6632
|
isNewEntry: false,
|
|
5794
6633
|
draft: {
|
|
5795
6634
|
gpal: {
|
|
5796
|
-
G:
|
|
5797
|
-
P:
|
|
5798
|
-
A:
|
|
5799
|
-
L:
|
|
6635
|
+
G: clampInt2(Number(gpal.G ?? 0), 0, 20),
|
|
6636
|
+
P: clampInt2(Number(gpal.P ?? 0), 0, 20),
|
|
6637
|
+
A: clampInt2(Number(gpal.A ?? 0), 0, 20),
|
|
6638
|
+
L: clampInt2(Number(gpal.L ?? 0), 0, 20)
|
|
5800
6639
|
},
|
|
5801
6640
|
lmp: typeof lmp === "string" ? lmp : "",
|
|
5802
6641
|
is_pregnant,
|
|
@@ -5814,10 +6653,10 @@ function normalizeToDraft(value) {
|
|
|
5814
6653
|
}
|
|
5815
6654
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
5816
6655
|
const gpalFromFlat = {
|
|
5817
|
-
G:
|
|
5818
|
-
P:
|
|
5819
|
-
A:
|
|
5820
|
-
L:
|
|
6656
|
+
G: clampInt2(Number(parsed.G ?? parsed.g ?? 0), 0, 20),
|
|
6657
|
+
P: clampInt2(Number(parsed.P ?? parsed.p ?? 0), 0, 20),
|
|
6658
|
+
A: clampInt2(Number(parsed.A ?? parsed.a ?? 0), 0, 20),
|
|
6659
|
+
L: clampInt2(Number(parsed.L ?? parsed.l ?? 0), 0, 20)
|
|
5821
6660
|
};
|
|
5822
6661
|
return {
|
|
5823
6662
|
isNewEntry: false,
|
|
@@ -5969,7 +6808,7 @@ var ObstetricHistoryWidget = ({ fieldId }) => {
|
|
|
5969
6808
|
const sanitizeGpalInput = (raw) => {
|
|
5970
6809
|
const digits = raw.replace(/[^\d]/g, "").slice(0, 2);
|
|
5971
6810
|
if (!digits) return 0;
|
|
5972
|
-
return
|
|
6811
|
+
return clampInt2(Number(digits), 0, 20);
|
|
5973
6812
|
};
|
|
5974
6813
|
const handleGpalChange = (key, raw) => {
|
|
5975
6814
|
const prevWasEmpty = draft.gpal[key] === 0 && activeGpalKey === key;
|
|
@@ -6669,7 +7508,11 @@ var OphthalmologyWidget = ({ fieldId }) => {
|
|
|
6669
7508
|
] })
|
|
6670
7509
|
] }),
|
|
6671
7510
|
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
6672
|
-
/* @__PURE__ */ jsxRuntime.jsx("td", { className: refractionLabelCellClass
|
|
7511
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: `${refractionLabelCellClass} whitespace-normal`, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "block leading-tight", children: [
|
|
7512
|
+
"Current",
|
|
7513
|
+
/* @__PURE__ */ jsxRuntime.jsx("br", {}),
|
|
7514
|
+
"Glass"
|
|
7515
|
+
] }) }),
|
|
6673
7516
|
/* @__PURE__ */ jsxRuntime.jsx("td", { className: refractionDataCellClass, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6674
7517
|
Input,
|
|
6675
7518
|
{
|
|
@@ -6924,12 +7767,12 @@ var OphthalmologyWidget = ({ fieldId }) => {
|
|
|
6924
7767
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: refractionHeaderCellClass, children: "Sph" }),
|
|
6925
7768
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: refractionHeaderCellClass, children: "Cyl" }),
|
|
6926
7769
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: refractionHeaderCellClass, children: "Axis" }),
|
|
6927
|
-
/* @__PURE__ */ jsxRuntime.jsx("th", { className: refractionHeaderCellClass, children: "
|
|
7770
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: refractionHeaderCellClass, children: "BCVA" }),
|
|
6928
7771
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: refractionHeaderCellClass, children: "P/D" }),
|
|
6929
7772
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: refractionHeaderCellClass, children: "Sph" }),
|
|
6930
7773
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: refractionHeaderCellClass, children: "Cyl" }),
|
|
6931
7774
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: refractionHeaderCellClass, children: "Axis" }),
|
|
6932
|
-
/* @__PURE__ */ jsxRuntime.jsx("th", { className: refractionHeaderCellClass, children: "
|
|
7775
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: refractionHeaderCellClass, children: "BCVA" }),
|
|
6933
7776
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: refractionHeaderCellClass, children: "P/D" })
|
|
6934
7777
|
] })
|
|
6935
7778
|
] }),
|
|
@@ -9267,6 +10110,7 @@ var SliderWidget = ({ fieldId }) => {
|
|
|
9267
10110
|
] });
|
|
9268
10111
|
};
|
|
9269
10112
|
var CHIEF_CHIPS_FIELD = "chief_complaints_chips";
|
|
10113
|
+
var GS_SYMPTOMS_FIELD = "symptoms";
|
|
9270
10114
|
var SWELLING_OPTS = [
|
|
9271
10115
|
{ value: "reducible_hernia", label: "Reducible (hernia)" },
|
|
9272
10116
|
{ value: "cough_impulse", label: "Cough impulse" },
|
|
@@ -9292,7 +10136,7 @@ var THYROID_OPTS = [
|
|
|
9292
10136
|
{ value: "voice_change", label: "Voice change" },
|
|
9293
10137
|
{ value: "thyrotoxicosis", label: "Thyrotoxicosis symptoms" }
|
|
9294
10138
|
];
|
|
9295
|
-
var
|
|
10139
|
+
var SOCRATES_KEYS2 = [
|
|
9296
10140
|
"site",
|
|
9297
10141
|
"onset",
|
|
9298
10142
|
"character",
|
|
@@ -9307,16 +10151,26 @@ function Panel({ title, children }) {
|
|
|
9307
10151
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3", children })
|
|
9308
10152
|
] });
|
|
9309
10153
|
}
|
|
9310
|
-
function
|
|
9311
|
-
const raw = state.values[CHIEF_CHIPS_FIELD];
|
|
10154
|
+
function chipListFromRaw(raw) {
|
|
9312
10155
|
return Array.isArray(raw) ? raw.filter((x) => typeof x === "string") : [];
|
|
9313
10156
|
}
|
|
9314
10157
|
var GsSmartHistoryWidget = ({ fieldId }) => {
|
|
9315
10158
|
const { fieldDef, value, setValue, setTouched, error, touched, disabled } = useField(fieldId);
|
|
10159
|
+
const hopcField = useField(GS_SYMPTOMS_FIELD);
|
|
9316
10160
|
const { state } = useForm();
|
|
9317
10161
|
const showError = !!error && (touched || state.submitAttempted);
|
|
9318
10162
|
const safe = React15.useMemo(() => normalizeGeneralSurgerySmartHistory(value), [value]);
|
|
9319
|
-
const
|
|
10163
|
+
const chiefChipsRaw = state.values[CHIEF_CHIPS_FIELD];
|
|
10164
|
+
const chips = React15.useMemo(() => chipListFromRaw(chiefChipsRaw), [chiefChipsRaw]);
|
|
10165
|
+
const [hopcNarrativeLocked, setHopcNarrativeLocked] = React15.useState(false);
|
|
10166
|
+
React15.useEffect(() => {
|
|
10167
|
+
if (hopcNarrativeLocked) return;
|
|
10168
|
+
const next = formatGeneralSurgerySmartHistoryToNarrative(
|
|
10169
|
+
normalizeGeneralSurgerySmartHistory(value),
|
|
10170
|
+
chips
|
|
10171
|
+
);
|
|
10172
|
+
hopcField.setValue(next);
|
|
10173
|
+
}, [value, chips, hopcNarrativeLocked]);
|
|
9320
10174
|
const showPain = chips.includes("pain");
|
|
9321
10175
|
const showSwelling = chips.includes("swelling_lump");
|
|
9322
10176
|
const showGI = chips.includes("bleeding") || chips.includes("obstruction");
|
|
@@ -9376,7 +10230,7 @@ var GsSmartHistoryWidget = ({ fieldId }) => {
|
|
|
9376
10230
|
labelEl,
|
|
9377
10231
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: bodyClass, children: [
|
|
9378
10232
|
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: [
|
|
9379
|
-
|
|
10233
|
+
SOCRATES_KEYS2.map((k) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
9380
10234
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: k }),
|
|
9381
10235
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9382
10236
|
Input,
|
|
@@ -9582,15 +10436,51 @@ var GsSmartHistoryWidget = ({ fieldId }) => {
|
|
|
9582
10436
|
] }, o.value)) })
|
|
9583
10437
|
] }) : null,
|
|
9584
10438
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
9585
|
-
/* @__PURE__ */ jsxRuntime.
|
|
10439
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
10440
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-xs font-semibold text-slate-700", children: hopcField.fieldDef?.label ?? "History of presenting complaint" }),
|
|
10441
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
10442
|
+
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: [
|
|
10443
|
+
"You edited this summary. Use",
|
|
10444
|
+
" ",
|
|
10445
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic", children: "'Regenerate'" }),
|
|
10446
|
+
" ",
|
|
10447
|
+
"to update it from the form."
|
|
10448
|
+
] }) : null,
|
|
10449
|
+
hopcNarrativeLocked ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
10450
|
+
Button,
|
|
10451
|
+
{
|
|
10452
|
+
type: "button",
|
|
10453
|
+
variant: "outline",
|
|
10454
|
+
size: "sm",
|
|
10455
|
+
className: "h-7 shrink-0 px-2 text-xs",
|
|
10456
|
+
disabled: disabled || hopcField.disabled,
|
|
10457
|
+
onClick: () => {
|
|
10458
|
+
setHopcNarrativeLocked(false);
|
|
10459
|
+
hopcField.setValue(
|
|
10460
|
+
formatGeneralSurgerySmartHistoryToNarrative(
|
|
10461
|
+
normalizeGeneralSurgerySmartHistory(value),
|
|
10462
|
+
chips
|
|
10463
|
+
)
|
|
10464
|
+
);
|
|
10465
|
+
},
|
|
10466
|
+
children: "Regenerate"
|
|
10467
|
+
}
|
|
10468
|
+
) : null
|
|
10469
|
+
] })
|
|
10470
|
+
] }),
|
|
10471
|
+
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,
|
|
9586
10472
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9587
10473
|
Textarea,
|
|
9588
10474
|
{
|
|
9589
|
-
className: "min-h-[
|
|
9590
|
-
placeholder: "
|
|
9591
|
-
value:
|
|
9592
|
-
onChange: (e) =>
|
|
9593
|
-
|
|
10475
|
+
className: "min-h-[88px] rounded-md border border-input bg-white text-sm",
|
|
10476
|
+
placeholder: "Onset, progression, associated symptoms, relevant negatives\u2026",
|
|
10477
|
+
value: typeof hopcField.value === "string" ? hopcField.value : hopcField.value == null ? "" : String(hopcField.value),
|
|
10478
|
+
onChange: (e) => {
|
|
10479
|
+
setHopcNarrativeLocked(true);
|
|
10480
|
+
hopcField.setValue(e.target.value);
|
|
10481
|
+
hopcField.setTouched();
|
|
10482
|
+
},
|
|
10483
|
+
disabled: disabled || hopcField.disabled
|
|
9594
10484
|
}
|
|
9595
10485
|
)
|
|
9596
10486
|
] })
|
|
@@ -9642,12 +10532,72 @@ function RegionCard({ title, children }) {
|
|
|
9642
10532
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3", children })
|
|
9643
10533
|
] });
|
|
9644
10534
|
}
|
|
10535
|
+
var BREAST_TEXT_KEYS = [
|
|
10536
|
+
{ key: "size", label: "Size" },
|
|
10537
|
+
{ key: "quadrant", label: "Quadrant" },
|
|
10538
|
+
{ key: "tenderness", label: "Tenderness" },
|
|
10539
|
+
{ key: "fixity", label: "Fixity" },
|
|
10540
|
+
{ key: "skinInvolvement", label: "Skin involvement" },
|
|
10541
|
+
{ key: "consistency", label: "Consistency" }
|
|
10542
|
+
];
|
|
10543
|
+
function BreastLumpColumn({
|
|
10544
|
+
title,
|
|
10545
|
+
data,
|
|
10546
|
+
onChange,
|
|
10547
|
+
disabled
|
|
10548
|
+
}) {
|
|
10549
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 rounded-md border border-[#D0D0D0] bg-slate-50/50 p-3", children: [
|
|
10550
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: title }),
|
|
10551
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: BREAST_TEXT_KEYS.map(({ key, label }) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
|
|
10552
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium", children: label }),
|
|
10553
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10554
|
+
Input,
|
|
10555
|
+
{
|
|
10556
|
+
className: "h-8 text-xs",
|
|
10557
|
+
value: data[key],
|
|
10558
|
+
onChange: (e) => onChange({ [key]: e.target.value }),
|
|
10559
|
+
disabled
|
|
10560
|
+
}
|
|
10561
|
+
)
|
|
10562
|
+
] }, key)) }),
|
|
10563
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-x-4 gap-y-2 pt-1", children: [
|
|
10564
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
10565
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10566
|
+
Checkbox,
|
|
10567
|
+
{
|
|
10568
|
+
checked: data.axillaryNodes,
|
|
10569
|
+
onCheckedChange: (c) => onChange({ axillaryNodes: c === true }),
|
|
10570
|
+
disabled
|
|
10571
|
+
}
|
|
10572
|
+
),
|
|
10573
|
+
"Axillary nodes"
|
|
10574
|
+
] }),
|
|
10575
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
10576
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10577
|
+
Checkbox,
|
|
10578
|
+
{
|
|
10579
|
+
checked: data.nippleAreolarComplex,
|
|
10580
|
+
onCheckedChange: (c) => onChange({ nippleAreolarComplex: c === true }),
|
|
10581
|
+
disabled
|
|
10582
|
+
}
|
|
10583
|
+
),
|
|
10584
|
+
"Nipple-areolar complex"
|
|
10585
|
+
] })
|
|
10586
|
+
] })
|
|
10587
|
+
] });
|
|
10588
|
+
}
|
|
9645
10589
|
var GsExaminationWidget = ({ fieldId }) => {
|
|
9646
10590
|
const { fieldDef, value, setValue, setTouched, error, touched, disabled } = useField(fieldId);
|
|
9647
10591
|
const clinicalField = useField(GS_CLINICAL_EXAMINATION_FIELD);
|
|
9648
10592
|
const { state } = useForm();
|
|
9649
10593
|
const showError = !!error && (touched || state.submitAttempted);
|
|
9650
10594
|
const safe = React15.useMemo(() => normalizeGeneralSurgeryExamination(value), [value]);
|
|
10595
|
+
const [clinicalNarrativeLocked, setClinicalNarrativeLocked] = React15.useState(false);
|
|
10596
|
+
React15.useEffect(() => {
|
|
10597
|
+
if (clinicalNarrativeLocked) return;
|
|
10598
|
+
const next = formatGeneralSurgeryExaminationToNarrative(normalizeGeneralSurgeryExamination(value));
|
|
10599
|
+
clinicalField.setValue(next);
|
|
10600
|
+
}, [value, clinicalNarrativeLocked]);
|
|
9651
10601
|
const update = (next) => {
|
|
9652
10602
|
setValue(next);
|
|
9653
10603
|
setTouched();
|
|
@@ -9839,56 +10789,35 @@ var GsExaminationWidget = ({ fieldId }) => {
|
|
|
9839
10789
|
"Tenderness"
|
|
9840
10790
|
] })
|
|
9841
10791
|
] }) }) : null,
|
|
9842
|
-
has("breast") ? /* @__PURE__ */ jsxRuntime.jsx(RegionCard, { title: "Breast exam", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-
|
|
10792
|
+
has("breast") ? /* @__PURE__ */ jsxRuntime.jsx(RegionCard, { title: "Breast exam", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 text-xs md:grid-cols-2", children: [
|
|
10793
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10794
|
+
BreastLumpColumn,
|
|
10795
|
+
{
|
|
10796
|
+
title: "Left breast",
|
|
10797
|
+
data: safe.breast.left,
|
|
10798
|
+
onChange: (p) => update({
|
|
10799
|
+
...safe,
|
|
10800
|
+
breast: { ...safe.breast, left: { ...safe.breast.left, ...p } }
|
|
10801
|
+
}),
|
|
10802
|
+
disabled
|
|
10803
|
+
}
|
|
10804
|
+
),
|
|
10805
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10806
|
+
BreastLumpColumn,
|
|
10807
|
+
{
|
|
10808
|
+
title: "Right breast",
|
|
10809
|
+
data: safe.breast.right,
|
|
10810
|
+
onChange: (p) => update({
|
|
10811
|
+
...safe,
|
|
10812
|
+
breast: { ...safe.breast, right: { ...safe.breast.right, ...p } }
|
|
10813
|
+
}),
|
|
10814
|
+
disabled
|
|
10815
|
+
}
|
|
10816
|
+
)
|
|
10817
|
+
] }) }) : null,
|
|
10818
|
+
has("thyroid") ? /* @__PURE__ */ jsxRuntime.jsx(RegionCard, { title: "Thyroid exam", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-3 text-xs md:grid-cols-4", children: [
|
|
9843
10819
|
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
|
|
9844
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium", children: "
|
|
9845
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9846
|
-
Input,
|
|
9847
|
-
{
|
|
9848
|
-
className: "h-8 text-xs",
|
|
9849
|
-
value: safe.breast.lumpSizeCm,
|
|
9850
|
-
onChange: (e) => update({ ...safe, breast: { ...safe.breast, lumpSizeCm: e.target.value } }),
|
|
9851
|
-
disabled
|
|
9852
|
-
}
|
|
9853
|
-
)
|
|
9854
|
-
] }),
|
|
9855
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
9856
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9857
|
-
Checkbox,
|
|
9858
|
-
{
|
|
9859
|
-
checked: safe.breast.mobile,
|
|
9860
|
-
onCheckedChange: (c) => update({ ...safe, breast: { ...safe.breast, mobile: c === true } }),
|
|
9861
|
-
disabled
|
|
9862
|
-
}
|
|
9863
|
-
),
|
|
9864
|
-
"Mobile"
|
|
9865
|
-
] }),
|
|
9866
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
9867
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9868
|
-
Checkbox,
|
|
9869
|
-
{
|
|
9870
|
-
checked: safe.breast.skinInvolvement,
|
|
9871
|
-
onCheckedChange: (c) => update({ ...safe, breast: { ...safe.breast, skinInvolvement: c === true } }),
|
|
9872
|
-
disabled
|
|
9873
|
-
}
|
|
9874
|
-
),
|
|
9875
|
-
"Skin involvement"
|
|
9876
|
-
] }),
|
|
9877
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
9878
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9879
|
-
Checkbox,
|
|
9880
|
-
{
|
|
9881
|
-
checked: safe.breast.axillaryNodes,
|
|
9882
|
-
onCheckedChange: (c) => update({ ...safe, breast: { ...safe.breast, axillaryNodes: c === true } }),
|
|
9883
|
-
disabled
|
|
9884
|
-
}
|
|
9885
|
-
),
|
|
9886
|
-
"Axillary nodes"
|
|
9887
|
-
] })
|
|
9888
|
-
] }) }) : null,
|
|
9889
|
-
has("thyroid") ? /* @__PURE__ */ jsxRuntime.jsx(RegionCard, { title: "Thyroid exam", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-3 text-xs md:grid-cols-4", children: [
|
|
9890
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
|
|
9891
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium", children: "Size" }),
|
|
10820
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium", children: "Size" }),
|
|
9892
10821
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9893
10822
|
Input,
|
|
9894
10823
|
{
|
|
@@ -10019,13 +10948,43 @@ var GsExaminationWidget = ({ fieldId }) => {
|
|
|
10019
10948
|
] }) }) : null
|
|
10020
10949
|
] }),
|
|
10021
10950
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
10022
|
-
/* @__PURE__ */ jsxRuntime.
|
|
10951
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
10952
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-xs font-semibold text-slate-700", children: clinicalField.fieldDef?.label ?? "Clinical examination" }),
|
|
10953
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
10954
|
+
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: [
|
|
10955
|
+
"You edited this summary. Use",
|
|
10956
|
+
" ",
|
|
10957
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic", children: "'Regenerate'" }),
|
|
10958
|
+
" ",
|
|
10959
|
+
"to update it from the form."
|
|
10960
|
+
] }) : null,
|
|
10961
|
+
clinicalNarrativeLocked ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
10962
|
+
Button,
|
|
10963
|
+
{
|
|
10964
|
+
type: "button",
|
|
10965
|
+
variant: "outline",
|
|
10966
|
+
size: "sm",
|
|
10967
|
+
className: "h-7 shrink-0 px-2 text-xs",
|
|
10968
|
+
disabled: disabled || clinicalField.disabled,
|
|
10969
|
+
onClick: () => {
|
|
10970
|
+
setClinicalNarrativeLocked(false);
|
|
10971
|
+
clinicalField.setValue(
|
|
10972
|
+
formatGeneralSurgeryExaminationToNarrative(normalizeGeneralSurgeryExamination(value))
|
|
10973
|
+
);
|
|
10974
|
+
},
|
|
10975
|
+
children: "Regenerate"
|
|
10976
|
+
}
|
|
10977
|
+
) : null
|
|
10978
|
+
] })
|
|
10979
|
+
] }),
|
|
10980
|
+
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,
|
|
10023
10981
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10024
10982
|
Textarea,
|
|
10025
10983
|
{
|
|
10026
10984
|
className: "min-h-[88px] rounded-md border border-input bg-white text-sm",
|
|
10027
10985
|
value: typeof clinicalField.value === "string" ? clinicalField.value : clinicalField.value == null ? "" : String(clinicalField.value),
|
|
10028
10986
|
onChange: (e) => {
|
|
10987
|
+
setClinicalNarrativeLocked(true);
|
|
10029
10988
|
clinicalField.setValue(e.target.value);
|
|
10030
10989
|
clinicalField.setTouched();
|
|
10031
10990
|
},
|
|
@@ -10039,7 +10998,7 @@ var GsExaminationWidget = ({ fieldId }) => {
|
|
|
10039
10998
|
};
|
|
10040
10999
|
var CHIEF_CHIPS_FIELD2 = "chief_complaints_chips";
|
|
10041
11000
|
var GS_EXAMINATION_FIELD = "gs_examination";
|
|
10042
|
-
function
|
|
11001
|
+
function chipList(state) {
|
|
10043
11002
|
const raw = state.values[CHIEF_CHIPS_FIELD2];
|
|
10044
11003
|
return Array.isArray(raw) ? raw.filter((x) => typeof x === "string") : [];
|
|
10045
11004
|
}
|
|
@@ -10053,7 +11012,7 @@ var GsGradingWidget = ({ fieldId }) => {
|
|
|
10053
11012
|
const { state } = useForm();
|
|
10054
11013
|
const showError = !!error && (touched || state.submitAttempted);
|
|
10055
11014
|
const safe = React15.useMemo(() => normalizeGeneralSurgeryGrading(value), [value]);
|
|
10056
|
-
const chips = React15.useMemo(() =>
|
|
11015
|
+
const chips = React15.useMemo(() => chipList(state), [state]);
|
|
10057
11016
|
const regions = React15.useMemo(() => regionList(state), [state]);
|
|
10058
11017
|
const visibleRows = React15.useMemo(() => {
|
|
10059
11018
|
return ALL_GS_CONDITIONS.filter(
|
|
@@ -10208,6 +11167,813 @@ var PainScaleFlagsWidget = ({ fieldId }) => {
|
|
|
10208
11167
|
showError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500", children: error })
|
|
10209
11168
|
] });
|
|
10210
11169
|
};
|
|
11170
|
+
var CHIEF_CHIPS_FIELD3 = "chief_complaints_chips";
|
|
11171
|
+
var ACTIVE_SYNDROME_FIELD = "uro_active_syndrome";
|
|
11172
|
+
function chipList2(state) {
|
|
11173
|
+
const raw = state.values[CHIEF_CHIPS_FIELD3];
|
|
11174
|
+
return Array.isArray(raw) ? raw.filter((x) => typeof x === "string") : [];
|
|
11175
|
+
}
|
|
11176
|
+
var IPSS_LABELS = {
|
|
11177
|
+
incomplete: "Incomplete emptying",
|
|
11178
|
+
frequency: "Frequency (<2h)",
|
|
11179
|
+
intermittency: "Intermittency",
|
|
11180
|
+
urgency: "Urgency",
|
|
11181
|
+
weakStream: "Weak stream",
|
|
11182
|
+
straining: "Straining",
|
|
11183
|
+
nocturia: "Nocturia (\xD7/night)"
|
|
11184
|
+
};
|
|
11185
|
+
var SOCRATES_KEYS3 = [
|
|
11186
|
+
"site",
|
|
11187
|
+
"onset",
|
|
11188
|
+
"character",
|
|
11189
|
+
"radiation",
|
|
11190
|
+
"associated",
|
|
11191
|
+
"timing",
|
|
11192
|
+
"exacerbating"
|
|
11193
|
+
];
|
|
11194
|
+
function Panel2({
|
|
11195
|
+
title,
|
|
11196
|
+
right,
|
|
11197
|
+
children
|
|
11198
|
+
}) {
|
|
11199
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-hidden rounded-md border border-[#D0D0D0] bg-white", children: [
|
|
11200
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b border-[#D0D0D0] bg-[#FEE8EC]/50 px-3 py-1.5", children: [
|
|
11201
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-slate-700", children: title }),
|
|
11202
|
+
right
|
|
11203
|
+
] }),
|
|
11204
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3", children })
|
|
11205
|
+
] });
|
|
11206
|
+
}
|
|
11207
|
+
var UrologySmartHistoryWidget = ({ fieldId }) => {
|
|
11208
|
+
const { fieldDef, value, setValue, setTouched, error, touched, disabled } = useField(fieldId);
|
|
11209
|
+
const syndromeField = useField(ACTIVE_SYNDROME_FIELD);
|
|
11210
|
+
const { state } = useForm();
|
|
11211
|
+
const showError = !!error && (touched || state.submitAttempted);
|
|
11212
|
+
const safe = React15.useMemo(() => normalizeUrologySmartHistory(value), [value]);
|
|
11213
|
+
const chips = React15.useMemo(() => chipList2(state), [state]);
|
|
11214
|
+
const rawSyndrome = syndromeField.value ?? state.values[ACTIVE_SYNDROME_FIELD];
|
|
11215
|
+
const syndrome = typeof rawSyndrome === "string" && rawSyndrome !== "" && rawSyndrome !== "none" ? rawSyndrome : "none";
|
|
11216
|
+
const showPain = chips.includes("pain_abdomen_flank") || chips.includes("scrotal_swelling_pain") || syndrome === "stone" || syndrome === "scrotal";
|
|
11217
|
+
const showLUTS = syndrome === "luts" || syndrome === "retention" || chips.includes("frequency_urgency") || chips.includes("poor_stream") || chips.includes("acute_retention");
|
|
11218
|
+
const showHaem = syndrome === "haematuria" || chips.includes("haematuria");
|
|
11219
|
+
const showSex = syndrome === "ed_infertility" || chips.includes("erectile_dysfunction") || chips.includes("infertility_concern");
|
|
11220
|
+
const update = (next) => {
|
|
11221
|
+
setValue(next);
|
|
11222
|
+
setTouched();
|
|
11223
|
+
};
|
|
11224
|
+
const ipssTotal = urologyIpssSevenTotal(safe.ipss);
|
|
11225
|
+
const ipssBandLabel = urologyIpssBand(ipssTotal);
|
|
11226
|
+
const bandClass = ipssBandLabel === "Severe" ? "bg-red-500/15 text-red-700" : ipssBandLabel === "Moderate" ? "bg-amber-500/15 text-amber-800" : "bg-emerald-500/15 text-emerald-800";
|
|
11227
|
+
const setSocrates = (key, v) => {
|
|
11228
|
+
if (key === "severity") {
|
|
11229
|
+
const n = typeof v === "number" ? v : Number(v);
|
|
11230
|
+
const sev = Number.isFinite(n) ? Math.min(10, Math.max(0, Math.round(n))) : 0;
|
|
11231
|
+
update({ ...safe, socrates: { ...safe.socrates, severity: sev } });
|
|
11232
|
+
return;
|
|
11233
|
+
}
|
|
11234
|
+
update({ ...safe, socrates: { ...safe.socrates, [key]: String(v) } });
|
|
11235
|
+
};
|
|
11236
|
+
const setIpss = (key, v) => {
|
|
11237
|
+
update({ ...safe, ipss: { ...safe.ipss, [key]: v } });
|
|
11238
|
+
};
|
|
11239
|
+
if (!fieldDef) return null;
|
|
11240
|
+
const theme = getThemeConfig("textarea", fieldDef.theme);
|
|
11241
|
+
const wrapperClass = theme?.wrapperClassName ?? "rounded-md overflow-hidden flex flex-col border border-[#D0D0D0]";
|
|
11242
|
+
const labelClass = theme?.labelClassName;
|
|
11243
|
+
const labelContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11244
|
+
fieldDef.label,
|
|
11245
|
+
fieldDef.required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-red-500", children: "*" }) : null
|
|
11246
|
+
] });
|
|
11247
|
+
const labelEl = theme ? /* @__PURE__ */ jsxRuntime.jsx(Label, { className: labelClass, children: labelContent }) : /* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-sm font-medium", children: labelContent });
|
|
11248
|
+
const bodyClass = cn(
|
|
11249
|
+
"-mt-1 flex min-w-0 flex-col gap-3 border-t border-[#D0D0D0] bg-white px-3 py-3",
|
|
11250
|
+
disabled && "pointer-events-none opacity-60"
|
|
11251
|
+
);
|
|
11252
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("w-full min-w-0", wrapperClass), children: [
|
|
11253
|
+
labelEl,
|
|
11254
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: bodyClass, children: [
|
|
11255
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] text-muted-foreground", children: "Panels follow chief complaint chips and active syndrome (Clearsight SmartHistoryUro)." }),
|
|
11256
|
+
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
|
+
SOCRATES_KEYS3.map((k) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
11258
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: k }),
|
|
11259
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11260
|
+
Input,
|
|
11261
|
+
{
|
|
11262
|
+
className: "h-8 text-xs",
|
|
11263
|
+
value: safe.socrates[k],
|
|
11264
|
+
onChange: (e) => setSocrates(k, e.target.value),
|
|
11265
|
+
placeholder: k === "radiation" ? "e.g. groin \u2192 ureteric" : k === "character" ? "colicky / dull" : "",
|
|
11266
|
+
disabled
|
|
11267
|
+
}
|
|
11268
|
+
)
|
|
11269
|
+
] }, k)),
|
|
11270
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
11271
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Severity 0\u201310" }),
|
|
11272
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11273
|
+
Input,
|
|
11274
|
+
{
|
|
11275
|
+
type: "number",
|
|
11276
|
+
min: 0,
|
|
11277
|
+
max: 10,
|
|
11278
|
+
className: "h-8 text-xs",
|
|
11279
|
+
value: safe.socrates.severity,
|
|
11280
|
+
onChange: (e) => setSocrates("severity", e.target.value),
|
|
11281
|
+
disabled
|
|
11282
|
+
}
|
|
11283
|
+
)
|
|
11284
|
+
] })
|
|
11285
|
+
] }) }) : null,
|
|
11286
|
+
showLUTS ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11287
|
+
Panel2,
|
|
11288
|
+
{
|
|
11289
|
+
title: "IPSS \u2014 International Prostate Symptom Score",
|
|
11290
|
+
right: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `rounded-full px-2 py-0.5 text-[10px] font-medium ${bandClass}`, children: [
|
|
11291
|
+
ipssTotal,
|
|
11292
|
+
"/35 \u2014 ",
|
|
11293
|
+
ipssBandLabel
|
|
11294
|
+
] }),
|
|
11295
|
+
children: [
|
|
11296
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-2 md:grid-cols-4", children: [
|
|
11297
|
+
Object.keys(IPSS_LABELS).map((k) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
11298
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: IPSS_LABELS[k] }),
|
|
11299
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11300
|
+
Select,
|
|
11301
|
+
{
|
|
11302
|
+
value: String(safe.ipss[k]),
|
|
11303
|
+
onValueChange: (val) => setIpss(k, Number(val)),
|
|
11304
|
+
disabled,
|
|
11305
|
+
children: [
|
|
11306
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, {}) }),
|
|
11307
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: [0, 1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: String(n), children: n }, n)) })
|
|
11308
|
+
]
|
|
11309
|
+
}
|
|
11310
|
+
)
|
|
11311
|
+
] }, k)),
|
|
11312
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
11313
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "QoL (0\u20136)" }),
|
|
11314
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11315
|
+
Select,
|
|
11316
|
+
{
|
|
11317
|
+
value: String(safe.ipssQoL),
|
|
11318
|
+
onValueChange: (val) => update({ ...safe, ipssQoL: Math.min(6, Math.max(0, Number(val))) }),
|
|
11319
|
+
disabled,
|
|
11320
|
+
children: [
|
|
11321
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, {}) }),
|
|
11322
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: [0, 1, 2, 3, 4, 5, 6].map((n) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: String(n), children: n }, n)) })
|
|
11323
|
+
]
|
|
11324
|
+
}
|
|
11325
|
+
)
|
|
11326
|
+
] })
|
|
11327
|
+
] }),
|
|
11328
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2 text-[10px] text-muted-foreground", children: [
|
|
11329
|
+
"Bands: 0\u20137 mild \xB7 8\u201319 moderate \xB7 20\u201335 severe.",
|
|
11330
|
+
" ",
|
|
11331
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "IPSS \u2265 20 \u2192 consider surgical intervention." })
|
|
11332
|
+
] })
|
|
11333
|
+
]
|
|
11334
|
+
}
|
|
11335
|
+
) : null,
|
|
11336
|
+
showHaem ? /* @__PURE__ */ jsxRuntime.jsxs(Panel2, { title: "Haematuria profile", children: [
|
|
11337
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3 text-xs md:grid-cols-4", children: [
|
|
11338
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11339
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11340
|
+
Checkbox,
|
|
11341
|
+
{
|
|
11342
|
+
checked: safe.haematuria.present,
|
|
11343
|
+
onCheckedChange: (c) => update({
|
|
11344
|
+
...safe,
|
|
11345
|
+
haematuria: { ...safe.haematuria, present: c === true }
|
|
11346
|
+
}),
|
|
11347
|
+
disabled
|
|
11348
|
+
}
|
|
11349
|
+
),
|
|
11350
|
+
"Present"
|
|
11351
|
+
] }),
|
|
11352
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex flex-col gap-1", children: [
|
|
11353
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Timing" }),
|
|
11354
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11355
|
+
Select,
|
|
11356
|
+
{
|
|
11357
|
+
value: safe.haematuria.timing || "none",
|
|
11358
|
+
onValueChange: (val) => update({
|
|
11359
|
+
...safe,
|
|
11360
|
+
haematuria: { ...safe.haematuria, timing: val === "none" ? "" : val }
|
|
11361
|
+
}),
|
|
11362
|
+
disabled,
|
|
11363
|
+
children: [
|
|
11364
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "\u2014" }) }),
|
|
11365
|
+
/* @__PURE__ */ jsxRuntime.jsxs(SelectContent, { children: [
|
|
11366
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "none", children: "\u2014" }),
|
|
11367
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "Initial", children: "Initial" }),
|
|
11368
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "Terminal", children: "Terminal" }),
|
|
11369
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "Total", children: "Total" })
|
|
11370
|
+
] })
|
|
11371
|
+
]
|
|
11372
|
+
}
|
|
11373
|
+
)
|
|
11374
|
+
] }),
|
|
11375
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11376
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11377
|
+
Checkbox,
|
|
11378
|
+
{
|
|
11379
|
+
checked: safe.haematuria.painful,
|
|
11380
|
+
onCheckedChange: (c) => update({ ...safe, haematuria: { ...safe.haematuria, painful: c === true } }),
|
|
11381
|
+
disabled
|
|
11382
|
+
}
|
|
11383
|
+
),
|
|
11384
|
+
"Painful"
|
|
11385
|
+
] }),
|
|
11386
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11387
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11388
|
+
Checkbox,
|
|
11389
|
+
{
|
|
11390
|
+
checked: safe.haematuria.clots,
|
|
11391
|
+
onCheckedChange: (c) => update({ ...safe, haematuria: { ...safe.haematuria, clots: c === true } }),
|
|
11392
|
+
disabled
|
|
11393
|
+
}
|
|
11394
|
+
),
|
|
11395
|
+
"Clots"
|
|
11396
|
+
] })
|
|
11397
|
+
] }),
|
|
11398
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-[10px] text-red-600", children: "Painless haematuria = malignancy until proven otherwise." })
|
|
11399
|
+
] }) : null,
|
|
11400
|
+
showSex ? /* @__PURE__ */ jsxRuntime.jsx(Panel2, { title: "Sexual & reproductive history", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3 text-xs md:grid-cols-4", children: [
|
|
11401
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex flex-col gap-1", children: [
|
|
11402
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Erectile function" }),
|
|
11403
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11404
|
+
Select,
|
|
11405
|
+
{
|
|
11406
|
+
value: safe.sexual.erectile || "none",
|
|
11407
|
+
onValueChange: (val) => update({
|
|
11408
|
+
...safe,
|
|
11409
|
+
sexual: { ...safe.sexual, erectile: val === "none" ? "" : val }
|
|
11410
|
+
}),
|
|
11411
|
+
disabled,
|
|
11412
|
+
children: [
|
|
11413
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "\u2014" }) }),
|
|
11414
|
+
/* @__PURE__ */ jsxRuntime.jsxs(SelectContent, { children: [
|
|
11415
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "none", children: "\u2014" }),
|
|
11416
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "Normal", children: "Normal" }),
|
|
11417
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "Mild", children: "Mild" }),
|
|
11418
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "Moderate", children: "Moderate" }),
|
|
11419
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "Severe", children: "Severe" })
|
|
11420
|
+
] })
|
|
11421
|
+
]
|
|
11422
|
+
}
|
|
11423
|
+
)
|
|
11424
|
+
] }),
|
|
11425
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex flex-col gap-1", children: [
|
|
11426
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Libido" }),
|
|
11427
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11428
|
+
Select,
|
|
11429
|
+
{
|
|
11430
|
+
value: safe.sexual.libido || "none",
|
|
11431
|
+
onValueChange: (val) => update({
|
|
11432
|
+
...safe,
|
|
11433
|
+
sexual: { ...safe.sexual, libido: val === "none" ? "" : val }
|
|
11434
|
+
}),
|
|
11435
|
+
disabled,
|
|
11436
|
+
children: [
|
|
11437
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "\u2014" }) }),
|
|
11438
|
+
/* @__PURE__ */ jsxRuntime.jsxs(SelectContent, { children: [
|
|
11439
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "none", children: "\u2014" }),
|
|
11440
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "Normal", children: "Normal" }),
|
|
11441
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "Reduced", children: "Reduced" })
|
|
11442
|
+
] })
|
|
11443
|
+
]
|
|
11444
|
+
}
|
|
11445
|
+
)
|
|
11446
|
+
] }),
|
|
11447
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex flex-col gap-1", children: [
|
|
11448
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Ejaculation" }),
|
|
11449
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11450
|
+
Select,
|
|
11451
|
+
{
|
|
11452
|
+
value: safe.sexual.ejaculation || "none",
|
|
11453
|
+
onValueChange: (val) => update({
|
|
11454
|
+
...safe,
|
|
11455
|
+
sexual: { ...safe.sexual, ejaculation: val === "none" ? "" : val }
|
|
11456
|
+
}),
|
|
11457
|
+
disabled,
|
|
11458
|
+
children: [
|
|
11459
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "\u2014" }) }),
|
|
11460
|
+
/* @__PURE__ */ jsxRuntime.jsxs(SelectContent, { children: [
|
|
11461
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "none", children: "\u2014" }),
|
|
11462
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "Normal", children: "Normal" }),
|
|
11463
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "Premature", children: "Premature" }),
|
|
11464
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "Retrograde", children: "Retrograde" }),
|
|
11465
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "Anejaculation", children: "Anejaculation" })
|
|
11466
|
+
] })
|
|
11467
|
+
]
|
|
11468
|
+
}
|
|
11469
|
+
)
|
|
11470
|
+
] }),
|
|
11471
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex flex-col gap-1", children: [
|
|
11472
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Infertility (months)" }),
|
|
11473
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11474
|
+
Input,
|
|
11475
|
+
{
|
|
11476
|
+
type: "number",
|
|
11477
|
+
min: 0,
|
|
11478
|
+
className: "h-8 text-xs",
|
|
11479
|
+
value: safe.sexual.infertilityMonths,
|
|
11480
|
+
onChange: (e) => update({
|
|
11481
|
+
...safe,
|
|
11482
|
+
sexual: {
|
|
11483
|
+
...safe.sexual,
|
|
11484
|
+
infertilityMonths: Math.max(0, Math.round(Number(e.target.value)) || 0)
|
|
11485
|
+
}
|
|
11486
|
+
}),
|
|
11487
|
+
disabled
|
|
11488
|
+
}
|
|
11489
|
+
)
|
|
11490
|
+
] })
|
|
11491
|
+
] }) }) : null,
|
|
11492
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
11493
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-xs font-medium text-slate-700", children: "Past history (stones / surgeries / drugs)" }),
|
|
11494
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11495
|
+
Textarea,
|
|
11496
|
+
{
|
|
11497
|
+
className: "min-h-[72px] rounded-md border border-input bg-white text-sm",
|
|
11498
|
+
placeholder: "Prior calculi, urological surgeries, anticoagulants, DM/HTN\u2026",
|
|
11499
|
+
value: safe.pastNotes,
|
|
11500
|
+
onChange: (e) => update({ ...safe, pastNotes: e.target.value }),
|
|
11501
|
+
disabled
|
|
11502
|
+
}
|
|
11503
|
+
)
|
|
11504
|
+
] })
|
|
11505
|
+
] }),
|
|
11506
|
+
showError ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-2 text-xs text-red-500", children: error }) : null
|
|
11507
|
+
] });
|
|
11508
|
+
};
|
|
11509
|
+
var GENERAL_ROWS = [
|
|
11510
|
+
{ key: "pallor", label: "Pallor (chronic dz)" },
|
|
11511
|
+
{ key: "oedema", label: "Oedema (renal)" },
|
|
11512
|
+
{ key: "icterus", label: "Icterus" },
|
|
11513
|
+
{ key: "lymphadenopathy", label: "Lymphadenopathy" }
|
|
11514
|
+
];
|
|
11515
|
+
var ABD_ROWS = [
|
|
11516
|
+
{ key: "distension", label: "Distension" },
|
|
11517
|
+
{ key: "scars", label: "Surgical scars" },
|
|
11518
|
+
{ key: "mass", label: "Visible mass" },
|
|
11519
|
+
{ key: "kidneyPalpable", label: "Kidney palpable" },
|
|
11520
|
+
{ key: "bladderPalpable", label: "Bladder palpable" },
|
|
11521
|
+
{ key: "renalAngleTender", label: "Renal angle tender" },
|
|
11522
|
+
{ key: "bladderDull", label: "Bladder dullness (retention)" }
|
|
11523
|
+
];
|
|
11524
|
+
var GU_ROWS = [
|
|
11525
|
+
{ key: "meatusAbnormal", label: "Meatus abnormal (hypospadias?)" },
|
|
11526
|
+
{ key: "phimosis", label: "Phimosis / paraphimosis" },
|
|
11527
|
+
{ key: "lesions", label: "Penile lesions / ulcers" },
|
|
11528
|
+
{ key: "scrotalSwelling", label: "Scrotal swelling" },
|
|
11529
|
+
{ key: "transilluminant", label: "Transilluminant" },
|
|
11530
|
+
{ key: "tenderTestis", label: "Tender testis" },
|
|
11531
|
+
{ key: "hydrocele", label: "Hydrocele" },
|
|
11532
|
+
{ key: "varicocele", label: "Varicocele" },
|
|
11533
|
+
{ key: "suspiciousMass", label: "Suspicious mass" }
|
|
11534
|
+
];
|
|
11535
|
+
var DRE_CONSISTENCY = ["none", "Soft (benign)", "Firm", "Hard / nodular (malignancy?)"];
|
|
11536
|
+
var DRE_SULCUS = ["none", "Preserved", "Obliterated"];
|
|
11537
|
+
var UrologyExaminationWidget = ({ fieldId }) => {
|
|
11538
|
+
const { fieldDef, value, setValue, setTouched, error, touched, disabled } = useField(fieldId);
|
|
11539
|
+
const { state } = useForm();
|
|
11540
|
+
const showError = !!error && (touched || state.submitAttempted);
|
|
11541
|
+
const safe = React15.useMemo(() => normalizeUrologyExamination(value), [value]);
|
|
11542
|
+
const update = (next) => {
|
|
11543
|
+
setValue(next);
|
|
11544
|
+
setTouched();
|
|
11545
|
+
};
|
|
11546
|
+
const toggleGeneral = (key) => {
|
|
11547
|
+
update({ ...safe, general: { ...safe.general, [key]: !safe.general[key] } });
|
|
11548
|
+
};
|
|
11549
|
+
const toggleAbdomen = (key) => {
|
|
11550
|
+
update({ ...safe, abdomen: { ...safe.abdomen, [key]: !safe.abdomen[key] } });
|
|
11551
|
+
};
|
|
11552
|
+
const toggleGu = (key) => {
|
|
11553
|
+
update({ ...safe, gu: { ...safe.gu, [key]: !safe.gu[key] } });
|
|
11554
|
+
};
|
|
11555
|
+
if (!fieldDef) return null;
|
|
11556
|
+
const theme = getThemeConfig("textarea", fieldDef.theme);
|
|
11557
|
+
const wrapperClass = theme?.wrapperClassName ?? "rounded-md overflow-hidden flex flex-col border border-[#D0D0D0]";
|
|
11558
|
+
const labelClass = theme?.labelClassName;
|
|
11559
|
+
const labelContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11560
|
+
fieldDef.label,
|
|
11561
|
+
fieldDef.required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-red-500", children: "*" }) : null
|
|
11562
|
+
] });
|
|
11563
|
+
const labelEl = theme ? /* @__PURE__ */ jsxRuntime.jsx(Label, { className: labelClass, children: labelContent }) : /* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-sm font-medium", children: labelContent });
|
|
11564
|
+
const bodyClass = cn(
|
|
11565
|
+
"-mt-1 flex min-w-0 flex-col gap-3 border-t border-[#D0D0D0] bg-white px-3 py-3",
|
|
11566
|
+
disabled && "pointer-events-none opacity-60"
|
|
11567
|
+
);
|
|
11568
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("w-full min-w-0", wrapperClass), children: [
|
|
11569
|
+
labelEl,
|
|
11570
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: bodyClass, children: [
|
|
11571
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-3 text-xs md:grid-cols-2 xl:grid-cols-4", children: [
|
|
11572
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
11573
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-slate-800", children: "General" }),
|
|
11574
|
+
GENERAL_ROWS.map((r) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11575
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11576
|
+
Checkbox,
|
|
11577
|
+
{
|
|
11578
|
+
checked: safe.general[r.key],
|
|
11579
|
+
onCheckedChange: () => toggleGeneral(r.key),
|
|
11580
|
+
disabled
|
|
11581
|
+
}
|
|
11582
|
+
),
|
|
11583
|
+
r.label
|
|
11584
|
+
] }, r.key))
|
|
11585
|
+
] }),
|
|
11586
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
11587
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-slate-800", children: "Abdomen" }),
|
|
11588
|
+
ABD_ROWS.map((r) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11589
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11590
|
+
Checkbox,
|
|
11591
|
+
{
|
|
11592
|
+
checked: safe.abdomen[r.key],
|
|
11593
|
+
onCheckedChange: () => toggleAbdomen(r.key),
|
|
11594
|
+
disabled
|
|
11595
|
+
}
|
|
11596
|
+
),
|
|
11597
|
+
r.label
|
|
11598
|
+
] }, r.key))
|
|
11599
|
+
] }),
|
|
11600
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
11601
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-slate-800", children: "External genitalia / scrotum" }),
|
|
11602
|
+
GU_ROWS.map((r) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11603
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11604
|
+
Checkbox,
|
|
11605
|
+
{
|
|
11606
|
+
checked: safe.gu[r.key],
|
|
11607
|
+
onCheckedChange: () => toggleGu(r.key),
|
|
11608
|
+
disabled
|
|
11609
|
+
}
|
|
11610
|
+
),
|
|
11611
|
+
r.key === "suspiciousMass" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-red-600", children: r.label }) : r.label
|
|
11612
|
+
] }, r.key))
|
|
11613
|
+
] }),
|
|
11614
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
11615
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-slate-800", children: "DRE \u2014 Prostate" }),
|
|
11616
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11617
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11618
|
+
Checkbox,
|
|
11619
|
+
{
|
|
11620
|
+
checked: safe.dre.done,
|
|
11621
|
+
onCheckedChange: (c) => update({ ...safe, dre: { ...safe.dre, done: c === true } }),
|
|
11622
|
+
disabled
|
|
11623
|
+
}
|
|
11624
|
+
),
|
|
11625
|
+
"DRE performed"
|
|
11626
|
+
] }),
|
|
11627
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
|
|
11628
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium", children: "Estimated size (g)" }),
|
|
11629
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11630
|
+
Input,
|
|
11631
|
+
{
|
|
11632
|
+
type: "number",
|
|
11633
|
+
min: 0,
|
|
11634
|
+
className: "h-8 text-xs",
|
|
11635
|
+
value: safe.dre.sizeGrams,
|
|
11636
|
+
onChange: (e) => {
|
|
11637
|
+
const n = Number(e.target.value);
|
|
11638
|
+
update({
|
|
11639
|
+
...safe,
|
|
11640
|
+
dre: {
|
|
11641
|
+
...safe.dre,
|
|
11642
|
+
sizeGrams: Number.isFinite(n) && n >= 0 ? Math.round(n) : 0
|
|
11643
|
+
}
|
|
11644
|
+
});
|
|
11645
|
+
},
|
|
11646
|
+
disabled
|
|
11647
|
+
}
|
|
11648
|
+
)
|
|
11649
|
+
] }),
|
|
11650
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
|
|
11651
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium", children: "Consistency" }),
|
|
11652
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11653
|
+
Select,
|
|
11654
|
+
{
|
|
11655
|
+
value: safe.dre.consistency || "none",
|
|
11656
|
+
onValueChange: (val) => update({
|
|
11657
|
+
...safe,
|
|
11658
|
+
dre: { ...safe.dre, consistency: val === "none" ? "" : val }
|
|
11659
|
+
}),
|
|
11660
|
+
disabled,
|
|
11661
|
+
children: [
|
|
11662
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "\u2014" }) }),
|
|
11663
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: DRE_CONSISTENCY.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: opt, children: opt === "none" ? "\u2014" : opt }, opt)) })
|
|
11664
|
+
]
|
|
11665
|
+
}
|
|
11666
|
+
)
|
|
11667
|
+
] }),
|
|
11668
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
|
|
11669
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium", children: "Median sulcus" }),
|
|
11670
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11671
|
+
Select,
|
|
11672
|
+
{
|
|
11673
|
+
value: safe.dre.medianSulcus || "none",
|
|
11674
|
+
onValueChange: (val) => update({
|
|
11675
|
+
...safe,
|
|
11676
|
+
dre: { ...safe.dre, medianSulcus: val === "none" ? "" : val }
|
|
11677
|
+
}),
|
|
11678
|
+
disabled,
|
|
11679
|
+
children: [
|
|
11680
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "\u2014" }) }),
|
|
11681
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: DRE_SULCUS.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: opt, children: opt === "none" ? "\u2014" : opt }, opt)) })
|
|
11682
|
+
]
|
|
11683
|
+
}
|
|
11684
|
+
)
|
|
11685
|
+
] }),
|
|
11686
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11687
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11688
|
+
Checkbox,
|
|
11689
|
+
{
|
|
11690
|
+
checked: safe.dre.tenderness,
|
|
11691
|
+
onCheckedChange: (c) => update({ ...safe, dre: { ...safe.dre, tenderness: c === true } }),
|
|
11692
|
+
disabled
|
|
11693
|
+
}
|
|
11694
|
+
),
|
|
11695
|
+
"Tender (prostatitis?)"
|
|
11696
|
+
] })
|
|
11697
|
+
] })
|
|
11698
|
+
] }),
|
|
11699
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
11700
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-xs font-semibold text-slate-700", children: "Examination notes" }),
|
|
11701
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11702
|
+
Textarea,
|
|
11703
|
+
{
|
|
11704
|
+
className: "min-h-[88px] rounded-md border border-input bg-white text-sm",
|
|
11705
|
+
placeholder: "Free-text exam findings\u2026",
|
|
11706
|
+
value: safe.examinationNotes,
|
|
11707
|
+
onChange: (e) => update({ ...safe, examinationNotes: e.target.value }),
|
|
11708
|
+
disabled
|
|
11709
|
+
}
|
|
11710
|
+
)
|
|
11711
|
+
] })
|
|
11712
|
+
] }),
|
|
11713
|
+
showError ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-2 text-xs text-red-500", children: error }) : null
|
|
11714
|
+
] });
|
|
11715
|
+
};
|
|
11716
|
+
var SMART_HISTORY_FIELD = "uro_smart_history";
|
|
11717
|
+
var EXAMINATION_FIELD = "uro_examination";
|
|
11718
|
+
var DRE_HARD = "Hard / nodular (malignancy?)";
|
|
11719
|
+
function Panel3({
|
|
11720
|
+
title,
|
|
11721
|
+
className,
|
|
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
|
+
}
|
|
11729
|
+
var UrologyPathwayWidget = ({ fieldId }) => {
|
|
11730
|
+
const { fieldDef, value, setValue, setTouched, error, touched, disabled } = useField(fieldId);
|
|
11731
|
+
const { state } = useForm();
|
|
11732
|
+
const showError = !!error && (touched || state.submitAttempted);
|
|
11733
|
+
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
|
+
const update = (next) => {
|
|
11755
|
+
setValue(next);
|
|
11756
|
+
setTouched();
|
|
11757
|
+
};
|
|
11758
|
+
const setRisk = (key, next) => {
|
|
11759
|
+
update({ ...safe, risk: { ...safe.risk, [key]: next } });
|
|
11760
|
+
};
|
|
11761
|
+
if (!fieldDef) return null;
|
|
11762
|
+
const theme = getThemeConfig("textarea", fieldDef.theme);
|
|
11763
|
+
const wrapperClass = theme?.wrapperClassName ?? "rounded-md overflow-hidden flex flex-col border border-[#D0D0D0]";
|
|
11764
|
+
const labelClass = theme?.labelClassName;
|
|
11765
|
+
const labelContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11766
|
+
fieldDef.label,
|
|
11767
|
+
fieldDef.required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-red-500", children: "*" }) : null
|
|
11768
|
+
] });
|
|
11769
|
+
const labelEl = theme ? /* @__PURE__ */ jsxRuntime.jsx(Label, { className: labelClass, children: labelContent }) : /* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-sm font-medium", children: labelContent });
|
|
11770
|
+
const bodyClass = cn(
|
|
11771
|
+
"-mt-1 flex min-w-0 flex-col gap-3 border-t border-[#D0D0D0] bg-white px-3 py-3",
|
|
11772
|
+
disabled && "pointer-events-none opacity-60"
|
|
11773
|
+
);
|
|
11774
|
+
const subtitle = typeof fieldDef.meta?.subtitle === "string" && fieldDef.meta.subtitle.trim().length > 0 ? fieldDef.meta.subtitle.trim() : "";
|
|
11775
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("w-full min-w-0", wrapperClass), children: [
|
|
11776
|
+
labelEl,
|
|
11777
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: bodyClass, children: [
|
|
11778
|
+
subtitle ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "-mt-0.5 mb-0 text-xs font-semibold tracking-wide text-slate-700", children: subtitle }) : null,
|
|
11779
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-3 text-xs md:grid-cols-2 xl:grid-cols-4", children: [
|
|
11780
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Panel3, { title: "Stone disease", children: [
|
|
11781
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
|
|
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
|
+
] }),
|
|
11817
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11818
|
+
"div",
|
|
11819
|
+
{
|
|
11820
|
+
className: cn(
|
|
11821
|
+
"mt-2 rounded p-2 text-[11px]",
|
|
11822
|
+
stone.surgical && safe.stoneSizeMm > 0 ? "bg-red-500/10 text-red-800" : safe.stoneSizeMm > 0 ? "bg-emerald-500/10 text-emerald-900" : "bg-muted/40 text-muted-foreground"
|
|
11823
|
+
),
|
|
11824
|
+
children: stone.plan
|
|
11825
|
+
}
|
|
11826
|
+
),
|
|
11827
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-[10px] text-muted-foreground", children: "Trigger: >10 mm OR obstruction \u2192 surgical." })
|
|
11828
|
+
] }),
|
|
11829
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Panel3, { title: "Prostate / BPH", children: [
|
|
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
|
+
] }),
|
|
11909
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11910
|
+
"div",
|
|
11911
|
+
{
|
|
11912
|
+
className: cn(
|
|
11913
|
+
"mt-2 rounded p-2 text-[11px]",
|
|
11914
|
+
safe.prostateVolumeCc > 30 ? "bg-amber-500/10 text-amber-900" : safe.prostateVolumeCc > 0 ? "bg-emerald-500/10 text-emerald-900" : "bg-muted/40 text-muted-foreground"
|
|
11915
|
+
),
|
|
11916
|
+
children: prostateLine
|
|
11917
|
+
}
|
|
11918
|
+
),
|
|
11919
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-[10px] text-muted-foreground", children: "<30 cc medical \xB7 30\u201380 cc TURP \xB7 >80 cc HoLEP/Open" })
|
|
11920
|
+
] }),
|
|
11921
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Panel3, { title: "IPSS & cues", children: [
|
|
11922
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
11923
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-700", children: "IPSS total" }),
|
|
11924
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: `rounded-full px-2 py-0.5 text-[11px] font-semibold ${ipssBandCls}`, children: [
|
|
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",
|
|
11932
|
+
{
|
|
11933
|
+
className: cn(
|
|
11934
|
+
"h-full transition-[width]",
|
|
11935
|
+
ipssTotal >= 20 ? "bg-red-500" : ipssTotal >= 8 ? "bg-amber-500" : "bg-emerald-500"
|
|
11936
|
+
),
|
|
11937
|
+
style: { width: `${Math.min(100, ipssTotal / 35 * 100)}%` }
|
|
11938
|
+
}
|
|
11939
|
+
) }),
|
|
11940
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mb-3 text-[10px] text-muted-foreground", children: [
|
|
11941
|
+
"From smart history (",
|
|
11942
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-foreground", children: SMART_HISTORY_FIELD }),
|
|
11943
|
+
"). \u226520 \u2192 consider surgical intervention."
|
|
11944
|
+
] }),
|
|
11945
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5 text-[11px]", children: [
|
|
11946
|
+
smart.haematuria.present && !smart.haematuria.painful ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded bg-red-500/10 p-1.5 text-red-800", children: "Painless haematuria \u2014 mandatory cystoscopy" }) : null,
|
|
11947
|
+
psaHigh ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded bg-amber-500/10 p-1.5 text-amber-900", children: "PSA >4 \u2014 workup for Ca prostate" }) : null,
|
|
11948
|
+
exam.dre.consistency === DRE_HARD ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded bg-red-500/10 p-1.5 text-red-800", children: [
|
|
11949
|
+
"Hard / nodular prostate (",
|
|
11950
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", children: EXAMINATION_FIELD }),
|
|
11951
|
+
") \u2014 biopsy pathway"
|
|
11952
|
+
] }) : null,
|
|
11953
|
+
exam.gu.suspiciousMass ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded bg-red-500/10 p-1.5 text-red-800", children: "Suspicious scrotal mass \u2014 urgent surgical eval" }) : null
|
|
11954
|
+
] })
|
|
11955
|
+
] }),
|
|
11956
|
+
/* @__PURE__ */ jsxRuntime.jsx(Panel3, { title: "Risk stratification", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: [
|
|
11957
|
+
["ckd", "CKD"],
|
|
11958
|
+
["diabetes", "Diabetes"],
|
|
11959
|
+
["anticoagulants", "Anticoagulants"],
|
|
11960
|
+
["activeInfection", "Active infection"]
|
|
11961
|
+
].map(([key, lab]) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
11962
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11963
|
+
Checkbox,
|
|
11964
|
+
{
|
|
11965
|
+
checked: safe.risk[key],
|
|
11966
|
+
disabled,
|
|
11967
|
+
onCheckedChange: (c) => setRisk(key, c === true)
|
|
11968
|
+
}
|
|
11969
|
+
),
|
|
11970
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-700", children: lab })
|
|
11971
|
+
] }, key)) }) })
|
|
11972
|
+
] })
|
|
11973
|
+
] }),
|
|
11974
|
+
showError ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-2 text-xs text-red-500", children: error }) : null
|
|
11975
|
+
] });
|
|
11976
|
+
};
|
|
10211
11977
|
var FieldRenderer = ({ fieldId }) => {
|
|
10212
11978
|
const { fieldDef, visible } = useField(fieldId);
|
|
10213
11979
|
if (!visible || !fieldDef) {
|
|
@@ -10289,6 +12055,12 @@ function renderWidget(fieldId, fieldDef) {
|
|
|
10289
12055
|
return /* @__PURE__ */ jsxRuntime.jsx(GsGradingWidget, { fieldId });
|
|
10290
12056
|
case "pain_scale_flags":
|
|
10291
12057
|
return /* @__PURE__ */ jsxRuntime.jsx(PainScaleFlagsWidget, { fieldId });
|
|
12058
|
+
case "urology_smart_history":
|
|
12059
|
+
return /* @__PURE__ */ jsxRuntime.jsx(UrologySmartHistoryWidget, { fieldId });
|
|
12060
|
+
case "urology_examination":
|
|
12061
|
+
return /* @__PURE__ */ jsxRuntime.jsx(UrologyExaminationWidget, { fieldId });
|
|
12062
|
+
case "urology_pathway":
|
|
12063
|
+
return /* @__PURE__ */ jsxRuntime.jsx(UrologyPathwayWidget, { fieldId });
|
|
10292
12064
|
case "toggle": {
|
|
10293
12065
|
const { value, setValue, setTouched, disabled } = useField(fieldId);
|
|
10294
12066
|
const store = useFormStore();
|
|
@@ -11152,7 +12924,43 @@ var ReadOnlyFieldRenderer = ({
|
|
|
11152
12924
|
].join("\n");
|
|
11153
12925
|
return /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyText, { fieldDef, value: lines });
|
|
11154
12926
|
}
|
|
11155
|
-
case "
|
|
12927
|
+
case "urology_smart_history":
|
|
12928
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
12929
|
+
ReadOnlyText,
|
|
12930
|
+
{
|
|
12931
|
+
fieldDef,
|
|
12932
|
+
value: value && typeof value === "object" ? JSON.stringify(value, null, 2) : value == null || value === "" ? "\u2014" : String(value)
|
|
12933
|
+
}
|
|
12934
|
+
);
|
|
12935
|
+
case "urology_examination":
|
|
12936
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
12937
|
+
ReadOnlyText,
|
|
12938
|
+
{
|
|
12939
|
+
fieldDef,
|
|
12940
|
+
value: value && typeof value === "object" ? JSON.stringify(value, null, 2) : value == null || value === "" ? "\u2014" : String(value)
|
|
12941
|
+
}
|
|
12942
|
+
);
|
|
12943
|
+
case "urology_pathway":
|
|
12944
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
12945
|
+
ReadOnlyText,
|
|
12946
|
+
{
|
|
12947
|
+
fieldDef,
|
|
12948
|
+
value: value && typeof value === "object" ? JSON.stringify(value, null, 2) : value == null || value === "" ? "\u2014" : String(value)
|
|
12949
|
+
}
|
|
12950
|
+
);
|
|
12951
|
+
case "general_surgery_smart_history": {
|
|
12952
|
+
const structuredDisplay = value && typeof value === "object" ? JSON.stringify(normalizeGeneralSurgerySmartHistory(value), null, 2) : value == null || value === "" ? "\u2014" : String(value);
|
|
12953
|
+
const hopcDef = resolveFieldDef?.("symptoms");
|
|
12954
|
+
const hopcRaw = allValues?.["symptoms"];
|
|
12955
|
+
const hopcStr = typeof hopcRaw === "string" ? hopcRaw : hopcRaw != null && hopcRaw !== "" ? String(hopcRaw) : "";
|
|
12956
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
12957
|
+
/* @__PURE__ */ jsxRuntime.jsx(ReadOnlyText, { fieldDef, value: structuredDisplay }),
|
|
12958
|
+
hopcDef ? /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyText, { fieldDef: hopcDef, value: hopcStr }) : hopcStr ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
12959
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-700", children: "History of presenting complaint" }),
|
|
12960
|
+
/* @__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 })
|
|
12961
|
+
] }) : null
|
|
12962
|
+
] });
|
|
12963
|
+
}
|
|
11156
12964
|
case "general_surgery_grading":
|
|
11157
12965
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
11158
12966
|
ReadOnlyText,
|
|
@@ -11402,9 +13210,12 @@ exports.RichTextWidget = RichTextWidget;
|
|
|
11402
13210
|
exports.SignatureUploadWidget = SignatureUploadWidget;
|
|
11403
13211
|
exports.SmartForm = SmartForm;
|
|
11404
13212
|
exports.SmartTextareaWidget = SmartTextareaWidget;
|
|
13213
|
+
exports.attachUrologyClinicalPathwayMetadata = attachUrologyClinicalPathwayMetadata;
|
|
11405
13214
|
exports.createUploadHandler = createUploadHandler;
|
|
11406
13215
|
exports.evaluateRules = evaluateRules;
|
|
11407
13216
|
exports.fieldMetaRequestsMarMedicationOrdersButton = fieldMetaRequestsMarMedicationOrdersButton;
|
|
13217
|
+
exports.isUrologyTemplate = isUrologyTemplate;
|
|
13218
|
+
exports.shouldFoldUrologyClinicalPathway = shouldFoldUrologyClinicalPathway;
|
|
11408
13219
|
exports.useField = useField;
|
|
11409
13220
|
exports.useFieldHandlers = useFieldHandlers;
|
|
11410
13221
|
exports.useForm = useForm;
|