275 lines
8.8 KiB
Svelte
Raw Normal View History

<script>
import { validateTestCode, validateTestName } from '$lib/api/tests.js';
import { AlertCircle } from 'lucide-svelte';
let { formData = $bindable(), isDirty = $bindable(false), mode = 'create' } = $props();
const isEditMode = $derived(mode === 'edit');
let validationErrors = $state({
TestSiteCode: '',
TestSiteName: '',
TestType: ''
});
const testTypes = [
{ value: 'TEST', label: 'Test', description: 'Single Test' },
{ value: 'PARAM', label: 'Parameter', description: 'Test Parameter' },
{ value: 'CALC', label: 'Calculated', description: 'Formula-based' },
{ value: 'GROUP', label: 'Panel', description: 'Test Group' },
{ value: 'TITLE', label: 'Header', description: 'Section Header' }
];
function validateField(field) {
validationErrors[field] = '';
switch (field) {
case 'TestSiteCode':
const codeResult = validateTestCode(formData.TestSiteCode);
if (!codeResult.valid) {
validationErrors[field] = codeResult.error;
return false;
}
break;
case 'TestSiteName':
const nameResult = validateTestName(formData.TestSiteName);
if (!nameResult.valid) {
validationErrors[field] = nameResult.error;
return false;
}
break;
case 'TestType':
if (!formData.TestType) {
validationErrors[field] = 'Test type is required';
return false;
}
break;
}
return true;
}
export function validateAll() {
const fields = ['TestSiteCode', 'TestSiteName', 'TestType'];
let isValid = true;
for (const field of fields) {
if (!validateField(field)) {
isValid = false;
}
}
return isValid;
}
function handleFieldChange() {
isDirty = true;
}
function handleCodeInput(event) {
const value = event.target.value.toUpperCase();
formData.TestSiteCode = value;
handleFieldChange();
validateField('TestSiteCode');
}
function handleNameInput(event) {
handleFieldChange();
validateField('TestSiteName');
}
</script>
<div class="space-y-5">
<!-- Test Type Header -->
<div class="bg-base-100 border border-base-200 rounded-lg p-4">
{#if isEditMode}
{@const selectedType = testTypes.find(t => t.value === formData.TestType)}
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center">
<span class="text-lg font-bold text-primary">{selectedType?.label?.[0] || '?'}</span>
</div>
<div class="flex-1">
<div class="flex items-center gap-2">
<span class="font-semibold text-base">{selectedType?.label || 'Unknown'}</span>
<span class="text-xs text-gray-500">({selectedType?.description || 'Unknown'})</span>
</div>
<span class="text-xs text-gray-500">Test type cannot be changed after creation</span>
</div>
</div>
{:else}
{@const selectedType = testTypes.find(t => t.value === formData.TestType)}
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center">
<span class="text-lg font-bold text-primary">{selectedType?.label?.[0] || '?'}</span>
</div>
<div class="flex-1">
<div class="flex items-center gap-2">
<span class="font-semibold text-base">{selectedType?.label || 'Unknown'}</span>
<span class="text-xs text-gray-500">({selectedType?.description || 'Unknown'})</span>
</div>
<span class="text-xs text-gray-500">{formData.TestType}</span>
</div>
</div>
{/if}
</div>
<!-- Test Identity -->
<div>
<h3 class="text-sm font-semibold text-gray-700 mb-3">Test Identity</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Test Code -->
<div class="space-y-1">
<label for="testCode" class="block text-sm font-medium text-gray-700">
Test Code <span class="text-error">*</span>
</label>
<input
id="testCode"
type="text"
class="input input-sm input-bordered w-full font-mono uppercase"
class:input-error={validationErrors.TestSiteCode}
bind:value={formData.TestSiteCode}
oninput={handleCodeInput}
placeholder="e.g., CBC, HGB, WBC"
required
/>
{#if validationErrors.TestSiteCode}
<span class="text-xs text-error flex items-center gap-1">
<AlertCircle class="w-3 h-3" />
{validationErrors.TestSiteCode}
</span>
{/if}
</div>
<!-- Test Name -->
<div class="space-y-1">
<label for="testName" class="block text-sm font-medium text-gray-700">
Test Name <span class="text-error">*</span>
</label>
<input
id="testName"
type="text"
class="input input-sm input-bordered w-full"
class:input-error={validationErrors.TestSiteName}
bind:value={formData.TestSiteName}
oninput={handleNameInput}
placeholder="e.g., Complete Blood Count"
maxlength="255"
required
/>
{#if validationErrors.TestSiteName}
<span class="text-xs text-error flex items-center gap-1">
<AlertCircle class="w-3 h-3" />
{validationErrors.TestSiteName}
</span>
{:else}
<span class="text-xs text-gray-500">3-255 characters</span>
{/if}
</div>
</div>
<!-- Description - moved under Test Identity -->
<div class="mt-4 space-y-1">
<label for="description" class="block text-sm font-medium text-gray-700">Description</label>
<input
id="description"
type="text"
class="input input-sm input-bordered w-full"
bind:value={formData.Description}
placeholder="Optional description..."
maxlength="500"
oninput={handleFieldChange}
/>
</div>
</div>
<!-- Display Settings -->
<div>
<h3 class="text-sm font-semibold text-gray-700 mb-3">Display Settings</h3>
<div class="grid grid-cols-6 gap-4">
<!-- Screen Sequence -->
<div class="space-y-1">
<label for="seqScr" class="block text-sm font-medium text-gray-700">Screen Seq</label>
<input
id="seqScr"
type="number"
class="input input-sm input-bordered w-full"
bind:value={formData.SeqScr}
min="0"
oninput={handleFieldChange}
/>
</div>
<!-- Report Sequence -->
<div class="space-y-1">
<label for="seqRpt" class="block text-sm font-medium text-gray-700">Report Seq</label>
<input
id="seqRpt"
type="number"
class="input input-sm input-bordered w-full"
bind:value={formData.SeqRpt}
min="0"
oninput={handleFieldChange}
/>
</div>
<!-- Visible Screen -->
<div class="space-y-1">
<span class="block text-sm font-medium text-gray-700">Screen</span>
<label class="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
class="checkbox checkbox-sm checkbox-primary"
bind:checked={formData.VisibleScr}
onchange={handleFieldChange}
/>
<span class="text-sm">Visible</span>
</label>
</div>
<!-- Visible Report -->
<div class="space-y-1">
<span class="block text-sm font-medium text-gray-700">Report</span>
<label class="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
class="checkbox checkbox-sm checkbox-primary"
bind:checked={formData.VisibleRpt}
onchange={handleFieldChange}
/>
<span class="text-sm">Visible</span>
</label>
</div>
<!-- Count Statistics -->
<div class="space-y-1">
<span class="block text-sm font-medium text-gray-700">Statistics</span>
<label class="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
class="checkbox checkbox-sm checkbox-primary"
bind:checked={formData.CountStat}
onchange={handleFieldChange}
/>
<span class="text-sm">Count</span>
</label>
</div>
<!-- Requestable -->
<div class="space-y-1">
<span class="block text-sm font-medium text-gray-700">Request</span>
<label class="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
class="checkbox checkbox-sm checkbox-primary"
bind:checked={formData.Requestable}
onchange={handleFieldChange}
/>
<span class="text-sm">Requestable</span>
</label>
</div>
</div>
</div>
</div>