clqms-fe1/src/routes/(app)/master-data/tests/test-modal/ReferenceRangeSection.svelte

203 lines
6.4 KiB
Svelte
Raw Normal View History

<script>
import { Ruler, Info } from 'lucide-svelte';
import HelpTooltip from '$lib/components/HelpTooltip.svelte';
import NumericRefRange from './NumericRefRange.svelte';
import TextRefRange from './TextRefRange.svelte';
import { createNumRef, createTholdRef, createTextRef, createVsetRef } from '../referenceRange.js';
/**
* @typedef {Object} Props
* @property {Object} formData - Form data object
* @property {(formData: Object) => void} onupdateFormData - Update handler
*/
/** @type {Props} */
let {
formData = $bindable({}),
onupdateFormData = () => {}
} = $props();
// Map refRangeType to RefType labels for display
const refTypeLabels = {
'none': 'None',
'num': 'RANGE - Range',
'thold': 'Threshold (THOLD)',
'text': 'Text (TEXT)',
'vset': 'Value Set (VSET)'
};
const refTypeOptions = [
{ value: 'none', label: 'None - No reference range' },
{ value: 'num', label: 'RANGE - Range' },
{ value: 'thold', label: 'Threshold (THOLD) - Limit values' },
{ value: 'text', label: 'Text (TEXT) - Descriptive' },
{ value: 'vset', label: 'Value Set (VSET) - Predefined values' }
];
// Ensure all reference range items have defined values, never undefined
function normalizeRefNum(ref) {
return {
RefType: ref.RefType ?? 'REF',
Sex: ref.Sex ?? '0',
LowSign: ref.LowSign ?? 'GE',
HighSign: ref.HighSign ?? 'LE',
Low: ref.Low ?? null,
High: ref.High ?? null,
AgeStart: ref.AgeStart ?? 0,
AgeEnd: ref.AgeEnd ?? 120,
Flag: ref.Flag ?? 'N',
Interpretation: ref.Interpretation ?? '',
SpcType: ref.SpcType ?? '',
Criteria: ref.Criteria ?? ''
};
}
function normalizeRefTxt(ref) {
return {
RefType: ref.RefType ?? 'TEXT',
Sex: ref.Sex ?? '0',
AgeStart: ref.AgeStart ?? 0,
AgeEnd: ref.AgeEnd ?? 120,
RefTxt: ref.RefTxt ?? '',
Flag: ref.Flag ?? 'N',
SpcType: ref.SpcType ?? '',
Criteria: ref.Criteria ?? ''
};
}
// Reactive normalized data - filter by RefType
let allRefnum = $derived((formData.refnum || []).map(normalizeRefNum));
let allReftxt = $derived((formData.reftxt || []).map(normalizeRefTxt));
// Filtered arrays for display
let normalizedRefnum = $derived(allRefnum.filter(ref => ref.RefType !== 'THOLD'));
let normalizedRefthold = $derived(allRefnum.filter(ref => ref.RefType === 'THOLD'));
let normalizedReftxt = $derived(allReftxt.filter(ref => ref.RefType !== 'VSET'));
let normalizedRefvset = $derived(allReftxt.filter(ref => ref.RefType === 'VSET'));
function updateRefRangeType(type) {
// Initialize arrays if they don't exist
const currentRefnum = formData.refnum || [];
const currentReftxt = formData.reftxt || [];
if (type === 'num') {
// Add numeric range to refnum
onupdateFormData({
...formData,
refRangeType: type,
RefType: 'NMRC',
refnum: [...currentRefnum, createNumRef()]
});
} else if (type === 'thold') {
// Add threshold range to refnum
onupdateFormData({
...formData,
refRangeType: type,
RefType: 'THOLD',
refnum: [...currentRefnum, createTholdRef()]
});
} else if (type === 'text') {
// Add text range to reftxt
onupdateFormData({
...formData,
refRangeType: type,
RefType: 'TEXT',
reftxt: [...currentReftxt, createTextRef()]
});
} else if (type === 'vset') {
// Add value set range to reftxt
onupdateFormData({
...formData,
refRangeType: type,
RefType: 'VSET',
reftxt: [...currentReftxt, createVsetRef()]
});
} else {
// None selected
onupdateFormData({
...formData,
refRangeType: type,
RefType: ''
});
}
}
function updateRefnum(refnum) {
onupdateFormData({ ...formData, refnum });
}
function updateRefthold(refthold) {
// Merge thold items back into refnum
const nonThold = (formData.refnum || []).filter(ref => ref.RefType !== 'THOLD');
onupdateFormData({ ...formData, refnum: [...nonThold, ...refthold] });
}
function updateReftxt(reftxt) {
onupdateFormData({ ...formData, reftxt });
}
function updateRefvset(refvset) {
// Merge vset items back into reftxt
const nonVset = (formData.reftxt || []).filter(ref => ref.RefType !== 'VSET');
onupdateFormData({ ...formData, reftxt: [...nonVset, ...refvset] });
}
</script>
<div class="space-y-3">
<!-- Reference Range Type Selection -->
<div class="bg-base-100 rounded-lg border border-base-200 p-4">
<div class="flex items-center gap-2 mb-3">
<Ruler class="w-5 h-5 text-primary" />
<h3 class="font-semibold">Reference Range Type</h3>
<HelpTooltip
text="Choose how to define normal/abnormal ranges for this test."
title="Reference Range Help"
/>
</div>
<!-- Dropdown Select -->
<div class="form-control">
<select
class="select select-bordered w-full"
value={formData.refRangeType || 'none'}
onchange={(e) => updateRefRangeType(e.target.value)}
>
{#each refTypeOptions as option (option.value)}
<option value={option.value}>{option.label}</option>
{/each}
</select>
</div>
<!-- Show selected RefType info -->
{#if formData.refRangeType && formData.refRangeType !== 'none'}
<div class="mt-3 flex items-center gap-2 p-2 bg-info/10 rounded-lg border border-info/20">
<Info class="w-4 h-4 text-info" />
<span class="text-sm">
<span class="font-medium">Selected:</span>
{refTypeLabels[formData.refRangeType]}
</span>
</div>
{/if}
</div>
<!-- Numeric Reference Ranges -->
{#if formData.refRangeType === 'num'}
<NumericRefRange refnum={normalizedRefnum} onupdateRefnum={updateRefnum} />
{/if}
<!-- Threshold Reference Ranges (uses same component as numeric) -->
{#if formData.refRangeType === 'thold'}
<NumericRefRange refnum={normalizedRefthold} onupdateRefnum={updateRefthold} />
{/if}
<!-- Text Reference Ranges -->
{#if formData.refRangeType === 'text'}
<TextRefRange reftxt={normalizedReftxt} onupdateReftxt={updateReftxt} />
{/if}
<!-- Value Set Reference Ranges (uses same component as text) -->
{#if formData.refRangeType === 'vset'}
<TextRefRange reftxt={normalizedRefvset} onupdateReftxt={updateRefvset} />
{/if}
</div>