formanitor 0.0.40 → 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 +1900 -93
- 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 +1898 -94
- 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;
|
|
@@ -9271,6 +10110,7 @@ var SliderWidget = ({ fieldId }) => {
|
|
|
9271
10110
|
] });
|
|
9272
10111
|
};
|
|
9273
10112
|
var CHIEF_CHIPS_FIELD = "chief_complaints_chips";
|
|
10113
|
+
var GS_SYMPTOMS_FIELD = "symptoms";
|
|
9274
10114
|
var SWELLING_OPTS = [
|
|
9275
10115
|
{ value: "reducible_hernia", label: "Reducible (hernia)" },
|
|
9276
10116
|
{ value: "cough_impulse", label: "Cough impulse" },
|
|
@@ -9296,7 +10136,7 @@ var THYROID_OPTS = [
|
|
|
9296
10136
|
{ value: "voice_change", label: "Voice change" },
|
|
9297
10137
|
{ value: "thyrotoxicosis", label: "Thyrotoxicosis symptoms" }
|
|
9298
10138
|
];
|
|
9299
|
-
var
|
|
10139
|
+
var SOCRATES_KEYS2 = [
|
|
9300
10140
|
"site",
|
|
9301
10141
|
"onset",
|
|
9302
10142
|
"character",
|
|
@@ -9311,16 +10151,26 @@ function Panel({ title, children }) {
|
|
|
9311
10151
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3", children })
|
|
9312
10152
|
] });
|
|
9313
10153
|
}
|
|
9314
|
-
function
|
|
9315
|
-
const raw = state.values[CHIEF_CHIPS_FIELD];
|
|
10154
|
+
function chipListFromRaw(raw) {
|
|
9316
10155
|
return Array.isArray(raw) ? raw.filter((x) => typeof x === "string") : [];
|
|
9317
10156
|
}
|
|
9318
10157
|
var GsSmartHistoryWidget = ({ fieldId }) => {
|
|
9319
10158
|
const { fieldDef, value, setValue, setTouched, error, touched, disabled } = useField(fieldId);
|
|
10159
|
+
const hopcField = useField(GS_SYMPTOMS_FIELD);
|
|
9320
10160
|
const { state } = useForm();
|
|
9321
10161
|
const showError = !!error && (touched || state.submitAttempted);
|
|
9322
10162
|
const safe = React15.useMemo(() => normalizeGeneralSurgerySmartHistory(value), [value]);
|
|
9323
|
-
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]);
|
|
9324
10174
|
const showPain = chips.includes("pain");
|
|
9325
10175
|
const showSwelling = chips.includes("swelling_lump");
|
|
9326
10176
|
const showGI = chips.includes("bleeding") || chips.includes("obstruction");
|
|
@@ -9380,7 +10230,7 @@ var GsSmartHistoryWidget = ({ fieldId }) => {
|
|
|
9380
10230
|
labelEl,
|
|
9381
10231
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: bodyClass, children: [
|
|
9382
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: [
|
|
9383
|
-
|
|
10233
|
+
SOCRATES_KEYS2.map((k) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
9384
10234
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: k }),
|
|
9385
10235
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9386
10236
|
Input,
|
|
@@ -9586,15 +10436,51 @@ var GsSmartHistoryWidget = ({ fieldId }) => {
|
|
|
9586
10436
|
] }, o.value)) })
|
|
9587
10437
|
] }) : null,
|
|
9588
10438
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
9589
|
-
/* @__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,
|
|
9590
10472
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9591
10473
|
Textarea,
|
|
9592
10474
|
{
|
|
9593
|
-
className: "min-h-[
|
|
9594
|
-
placeholder: "
|
|
9595
|
-
value:
|
|
9596
|
-
onChange: (e) =>
|
|
9597
|
-
|
|
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
|
|
9598
10484
|
}
|
|
9599
10485
|
)
|
|
9600
10486
|
] })
|
|
@@ -9646,12 +10532,72 @@ function RegionCard({ title, children }) {
|
|
|
9646
10532
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3", children })
|
|
9647
10533
|
] });
|
|
9648
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
|
+
}
|
|
9649
10589
|
var GsExaminationWidget = ({ fieldId }) => {
|
|
9650
10590
|
const { fieldDef, value, setValue, setTouched, error, touched, disabled } = useField(fieldId);
|
|
9651
10591
|
const clinicalField = useField(GS_CLINICAL_EXAMINATION_FIELD);
|
|
9652
10592
|
const { state } = useForm();
|
|
9653
10593
|
const showError = !!error && (touched || state.submitAttempted);
|
|
9654
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]);
|
|
9655
10601
|
const update = (next) => {
|
|
9656
10602
|
setValue(next);
|
|
9657
10603
|
setTouched();
|
|
@@ -9843,15 +10789,41 @@ var GsExaminationWidget = ({ fieldId }) => {
|
|
|
9843
10789
|
"Tenderness"
|
|
9844
10790
|
] })
|
|
9845
10791
|
] }) }) : null,
|
|
9846
|
-
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: [
|
|
9847
10819
|
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
|
|
9848
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium", children: "
|
|
10820
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium", children: "Size" }),
|
|
9849
10821
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9850
10822
|
Input,
|
|
9851
10823
|
{
|
|
9852
10824
|
className: "h-8 text-xs",
|
|
9853
|
-
value: safe.
|
|
9854
|
-
onChange: (e) => update({ ...safe,
|
|
10825
|
+
value: safe.thyroid.size,
|
|
10826
|
+
onChange: (e) => update({ ...safe, thyroid: { ...safe.thyroid, size: e.target.value } }),
|
|
9855
10827
|
disabled
|
|
9856
10828
|
}
|
|
9857
10829
|
)
|
|
@@ -9860,55 +10832,8 @@ var GsExaminationWidget = ({ fieldId }) => {
|
|
|
9860
10832
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9861
10833
|
Checkbox,
|
|
9862
10834
|
{
|
|
9863
|
-
checked: safe.
|
|
9864
|
-
onCheckedChange: (c) => update({ ...safe,
|
|
9865
|
-
disabled
|
|
9866
|
-
}
|
|
9867
|
-
),
|
|
9868
|
-
"Mobile"
|
|
9869
|
-
] }),
|
|
9870
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
9871
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9872
|
-
Checkbox,
|
|
9873
|
-
{
|
|
9874
|
-
checked: safe.breast.skinInvolvement,
|
|
9875
|
-
onCheckedChange: (c) => update({ ...safe, breast: { ...safe.breast, skinInvolvement: c === true } }),
|
|
9876
|
-
disabled
|
|
9877
|
-
}
|
|
9878
|
-
),
|
|
9879
|
-
"Skin involvement"
|
|
9880
|
-
] }),
|
|
9881
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
9882
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9883
|
-
Checkbox,
|
|
9884
|
-
{
|
|
9885
|
-
checked: safe.breast.axillaryNodes,
|
|
9886
|
-
onCheckedChange: (c) => update({ ...safe, breast: { ...safe.breast, axillaryNodes: c === true } }),
|
|
9887
|
-
disabled
|
|
9888
|
-
}
|
|
9889
|
-
),
|
|
9890
|
-
"Axillary nodes"
|
|
9891
|
-
] })
|
|
9892
|
-
] }) }) : null,
|
|
9893
|
-
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: [
|
|
9894
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
|
|
9895
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block font-medium", children: "Size" }),
|
|
9896
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9897
|
-
Input,
|
|
9898
|
-
{
|
|
9899
|
-
className: "h-8 text-xs",
|
|
9900
|
-
value: safe.thyroid.size,
|
|
9901
|
-
onChange: (e) => update({ ...safe, thyroid: { ...safe.thyroid, size: e.target.value } }),
|
|
9902
|
-
disabled
|
|
9903
|
-
}
|
|
9904
|
-
)
|
|
9905
|
-
] }),
|
|
9906
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
|
|
9907
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9908
|
-
Checkbox,
|
|
9909
|
-
{
|
|
9910
|
-
checked: safe.thyroid.mobileDeglutition,
|
|
9911
|
-
onCheckedChange: (c) => update({ ...safe, thyroid: { ...safe.thyroid, mobileDeglutition: c === true } }),
|
|
10835
|
+
checked: safe.thyroid.mobileDeglutition,
|
|
10836
|
+
onCheckedChange: (c) => update({ ...safe, thyroid: { ...safe.thyroid, mobileDeglutition: c === true } }),
|
|
9912
10837
|
disabled
|
|
9913
10838
|
}
|
|
9914
10839
|
),
|
|
@@ -10023,13 +10948,43 @@ var GsExaminationWidget = ({ fieldId }) => {
|
|
|
10023
10948
|
] }) }) : null
|
|
10024
10949
|
] }),
|
|
10025
10950
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
10026
|
-
/* @__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,
|
|
10027
10981
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10028
10982
|
Textarea,
|
|
10029
10983
|
{
|
|
10030
10984
|
className: "min-h-[88px] rounded-md border border-input bg-white text-sm",
|
|
10031
10985
|
value: typeof clinicalField.value === "string" ? clinicalField.value : clinicalField.value == null ? "" : String(clinicalField.value),
|
|
10032
10986
|
onChange: (e) => {
|
|
10987
|
+
setClinicalNarrativeLocked(true);
|
|
10033
10988
|
clinicalField.setValue(e.target.value);
|
|
10034
10989
|
clinicalField.setTouched();
|
|
10035
10990
|
},
|
|
@@ -10043,7 +10998,7 @@ var GsExaminationWidget = ({ fieldId }) => {
|
|
|
10043
10998
|
};
|
|
10044
10999
|
var CHIEF_CHIPS_FIELD2 = "chief_complaints_chips";
|
|
10045
11000
|
var GS_EXAMINATION_FIELD = "gs_examination";
|
|
10046
|
-
function
|
|
11001
|
+
function chipList(state) {
|
|
10047
11002
|
const raw = state.values[CHIEF_CHIPS_FIELD2];
|
|
10048
11003
|
return Array.isArray(raw) ? raw.filter((x) => typeof x === "string") : [];
|
|
10049
11004
|
}
|
|
@@ -10057,7 +11012,7 @@ var GsGradingWidget = ({ fieldId }) => {
|
|
|
10057
11012
|
const { state } = useForm();
|
|
10058
11013
|
const showError = !!error && (touched || state.submitAttempted);
|
|
10059
11014
|
const safe = React15.useMemo(() => normalizeGeneralSurgeryGrading(value), [value]);
|
|
10060
|
-
const chips = React15.useMemo(() =>
|
|
11015
|
+
const chips = React15.useMemo(() => chipList(state), [state]);
|
|
10061
11016
|
const regions = React15.useMemo(() => regionList(state), [state]);
|
|
10062
11017
|
const visibleRows = React15.useMemo(() => {
|
|
10063
11018
|
return ALL_GS_CONDITIONS.filter(
|
|
@@ -10212,6 +11167,813 @@ var PainScaleFlagsWidget = ({ fieldId }) => {
|
|
|
10212
11167
|
showError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500", children: error })
|
|
10213
11168
|
] });
|
|
10214
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
|
+
};
|
|
10215
11977
|
var FieldRenderer = ({ fieldId }) => {
|
|
10216
11978
|
const { fieldDef, visible } = useField(fieldId);
|
|
10217
11979
|
if (!visible || !fieldDef) {
|
|
@@ -10293,6 +12055,12 @@ function renderWidget(fieldId, fieldDef) {
|
|
|
10293
12055
|
return /* @__PURE__ */ jsxRuntime.jsx(GsGradingWidget, { fieldId });
|
|
10294
12056
|
case "pain_scale_flags":
|
|
10295
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 });
|
|
10296
12064
|
case "toggle": {
|
|
10297
12065
|
const { value, setValue, setTouched, disabled } = useField(fieldId);
|
|
10298
12066
|
const store = useFormStore();
|
|
@@ -11156,7 +12924,43 @@ var ReadOnlyFieldRenderer = ({
|
|
|
11156
12924
|
].join("\n");
|
|
11157
12925
|
return /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyText, { fieldDef, value: lines });
|
|
11158
12926
|
}
|
|
11159
|
-
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
|
+
}
|
|
11160
12964
|
case "general_surgery_grading":
|
|
11161
12965
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
11162
12966
|
ReadOnlyText,
|
|
@@ -11406,9 +13210,12 @@ exports.RichTextWidget = RichTextWidget;
|
|
|
11406
13210
|
exports.SignatureUploadWidget = SignatureUploadWidget;
|
|
11407
13211
|
exports.SmartForm = SmartForm;
|
|
11408
13212
|
exports.SmartTextareaWidget = SmartTextareaWidget;
|
|
13213
|
+
exports.attachUrologyClinicalPathwayMetadata = attachUrologyClinicalPathwayMetadata;
|
|
11409
13214
|
exports.createUploadHandler = createUploadHandler;
|
|
11410
13215
|
exports.evaluateRules = evaluateRules;
|
|
11411
13216
|
exports.fieldMetaRequestsMarMedicationOrdersButton = fieldMetaRequestsMarMedicationOrdersButton;
|
|
13217
|
+
exports.isUrologyTemplate = isUrologyTemplate;
|
|
13218
|
+
exports.shouldFoldUrologyClinicalPathway = shouldFoldUrologyClinicalPathway;
|
|
11412
13219
|
exports.useField = useField;
|
|
11413
13220
|
exports.useFieldHandlers = useFieldHandlers;
|
|
11414
13221
|
exports.useForm = useForm;
|