307 lines
10 KiB
Svelte
Raw Normal View History

<script>
import { getResultTypeOptions, getRefTypeOptions, validateTypeCombination } from '$lib/api/tests.js';
import { AlertCircle } from 'lucide-svelte';
let { formData = $bindable(), disciplines = [], departments = [], isDirty = $bindable(false) } = $props();
let validationErrors = $state({
typeCombination: ''
});
const disciplineOptions = $derived(disciplines.map(d => ({ value: d.DisciplineID, label: d.DisciplineName })));
const departmentOptions = $derived(departments.map(d => ({ value: d.DepartmentID, label: d.DepartmentName })));
const resultTypeOptions = $derived(getResultTypeOptions(formData.TestType));
const refTypeOptions = $derived(getRefTypeOptions(formData.details?.ResultType));
const typeValidation = $derived.by(() => {
const result = validateTypeCombination(
formData.TestType,
formData.details?.ResultType,
formData.details?.RefType
);
return result;
});
function handleFieldChange() {
isDirty = true;
validationErrors.typeCombination = '';
}
function handleResultTypeChange() {
const resultType = formData.details.ResultType;
if (resultType === 'VSET') {
formData.details.RefType = 'VSET';
} else if (resultType === 'TEXT') {
formData.details.RefType = 'TEXT';
} else if (resultType === 'NORES') {
formData.details.RefType = 'NOREF';
} else if (resultType === 'NMRIC' || resultType === 'RANGE') {
formData.details.RefType = 'RANGE';
}
if (resultType !== 'VSET') {
formData.details.VSet = '';
}
handleFieldChange();
}
export function validateAll() {
validationErrors.typeCombination = '';
if (!typeValidation.valid) {
validationErrors.typeCombination = typeValidation.error;
return false;
}
if (formData.details.Decimal !== null && formData.details.Decimal !== undefined) {
if (formData.details.Decimal < 0 || formData.details.Decimal > 6) {
return false;
}
}
return true;
}
</script>
<div class="space-y-5">
<h2 class="text-lg font-semibold text-gray-800">Technical Details</h2>
{#if !typeValidation.valid}
<div class="alert alert-warning text-sm">
<AlertCircle class="w-4 h-4" />
<span>{typeValidation.error}</span>
</div>
{/if}
<!-- Categorization -->
<div>
<h3 class="text-sm font-semibold text-gray-700 mb-3">Categorization</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="space-y-1">
<label for="discipline" class="block text-sm font-medium text-gray-700">Discipline</label>
<select
id="discipline"
class="select select-sm select-bordered w-full"
bind:value={formData.details.DisciplineID}
onchange={handleFieldChange}
>
<option value={null}>Select discipline...</option>
{#each disciplineOptions as opt (opt.value)}
<option value={opt.value}>{opt.label}</option>
{/each}
</select>
</div>
<div class="space-y-1">
<label for="department" class="block text-sm font-medium text-gray-700">Department</label>
<select
id="department"
class="select select-sm select-bordered w-full"
bind:value={formData.details.DepartmentID}
onchange={handleFieldChange}
>
<option value={null}>Select department...</option>
{#each departmentOptions as opt (opt.value)}
<option value={opt.value}>{opt.label}</option>
{/each}
</select>
</div>
</div>
</div>
<!-- Result Configuration -->
<div>
<h3 class="text-sm font-semibold text-gray-700 mb-3">Result Configuration</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="space-y-1">
<label for="resultType" class="block text-sm font-medium text-gray-700">Result Type</label>
<select
id="resultType"
class="select select-sm select-bordered w-full"
bind:value={formData.details.ResultType}
onchange={handleResultTypeChange}
>
<option value="">Select result type...</option>
{#each resultTypeOptions as opt (opt.value)}
<option value={opt.value}>{opt.label}</option>
{/each}
</select>
{#if formData.TestType === 'CALC'}
<span class="text-xs text-gray-500">CALC type always uses Numeric Reference</span>
{/if}
</div>
<div class="space-y-1">
<label for="refType" class="block text-sm font-medium text-gray-700">Reference Type</label>
<select
id="refType"
class="select select-sm select-bordered w-full"
bind:value={formData.details.RefType}
onchange={handleFieldChange}
disabled={refTypeOptions.length === 0 || refTypeOptions.length === 1}
>
<option value="">Select reference type...</option>
{#each refTypeOptions as opt (opt.value)}
<option value={opt.value}>{opt.label}</option>
{/each}
</select>
{#if refTypeOptions.length === 1}
<span class="text-xs text-gray-500">Automatically set based on Result Type</span>
{/if}
</div>
</div>
{#if formData.details.ResultType === 'VSET'}
<div class="mt-4 space-y-1">
<label for="vset" class="block text-sm font-medium text-gray-700">
Value Set <span class="text-error">*</span>
</label>
<input
id="vset"
type="text"
class="input input-sm input-bordered w-full md:w-1/2"
bind:value={formData.details.VSet}
placeholder="Enter value set key..."
oninput={handleFieldChange}
required={formData.details.ResultType === 'VSET'}
/>
<span class="text-xs text-gray-500">Required when Result Type is 'Value Set'</span>
</div>
{/if}
</div>
<!-- Units & Conversion -->
<div>
<h3 class="text-sm font-semibold text-gray-700 mb-3">Units & Conversion</h3>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="space-y-1">
<label for="unit1" class="block text-sm font-medium text-gray-700">Unit 1</label>
<input
id="unit1"
type="text"
class="input input-sm input-bordered w-full"
bind:value={formData.details.Unit1}
placeholder="e.g., g/dL"
oninput={handleFieldChange}
/>
</div>
<div class="space-y-1">
<label for="factor" class="block text-sm font-medium text-gray-700">Factor</label>
<input
id="factor"
type="number"
step="0.01"
class="input input-sm input-bordered w-full"
bind:value={formData.details.Factor}
placeholder="1.0"
oninput={handleFieldChange}
/>
</div>
<div class="space-y-1">
<label for="unit2" class="block text-sm font-medium text-gray-700">Unit 2</label>
<input
id="unit2"
type="text"
class="input input-sm input-bordered w-full"
bind:value={formData.details.Unit2}
placeholder="e.g., g/L"
oninput={handleFieldChange}
/>
</div>
<div class="space-y-1">
<label for="decimal" class="block text-sm font-medium text-gray-700">Decimal</label>
<input
id="decimal"
type="number"
class="input input-sm input-bordered w-full"
bind:value={formData.details.Decimal}
min="0"
max="6"
oninput={handleFieldChange}
/>
<span class="text-xs text-gray-500">Max 6</span>
</div>
</div>
</div>
<!-- Sample Requirements -->
<div>
<h3 class="text-sm font-semibold text-gray-700 mb-3">Sample Requirements</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="space-y-1">
<label for="reqQty" class="block text-sm font-medium text-gray-700">Required Quantity</label>
<input
id="reqQty"
type="number"
step="0.1"
min="0"
class="input input-sm input-bordered w-full"
bind:value={formData.details.ReqQty}
placeholder="e.g., 5.0"
oninput={handleFieldChange}
/>
</div>
<div class="space-y-1">
<label for="reqQtyUnit" class="block text-sm font-medium text-gray-700">Quantity Unit</label>
<input
id="reqQtyUnit"
type="text"
class="input input-sm input-bordered w-full"
bind:value={formData.details.ReqQtyUnit}
placeholder="e.g., mL"
oninput={handleFieldChange}
/>
</div>
</div>
<div class="mt-4 space-y-1">
<label for="collReq" class="block text-sm font-medium text-gray-700">Collection Requirements</label>
<textarea
id="collReq"
class="textarea textarea-sm textarea-bordered w-full"
bind:value={formData.details.CollReq}
placeholder="e.g., Fasting required, Collect in lavender tube..."
rows="2"
oninput={handleFieldChange}
></textarea>
</div>
</div>
<!-- Method & TAT -->
<div>
<h3 class="text-sm font-semibold text-gray-700 mb-3">Method & TAT</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="space-y-1">
<label for="method" class="block text-sm font-medium text-gray-700">Method</label>
<input
id="method"
type="text"
class="input input-sm input-bordered w-full"
bind:value={formData.details.Method}
placeholder="e.g., Automated Analyzer"
oninput={handleFieldChange}
/>
</div>
<div class="space-y-1">
<label for="expectedTAT" class="block text-sm font-medium text-gray-700">Expected TAT (minutes)</label>
<input
id="expectedTAT"
type="number"
class="input input-sm input-bordered w-full"
bind:value={formData.details.ExpectedTAT}
min="0"
placeholder="e.g., 60"
oninput={handleFieldChange}
/>
</div>
</div>
</div>
</div>