mahdahar 99d622ad05 refactor(tests): consolidate test management modal components
- Consolidate fragmented test modal components into unified TestFormModal.svelte
- Reorganize reference range components into organized tabs (RefNumTab, RefTxtTab)
- Add new tab components: BasicInfoTab, TechDetailsTab, CalcDetailsTab, MappingsTab, GroupMembersTab
- Move test-related type definitions to src/lib/types/test.types.ts for better type organization
- Delete old component files: TestModal.svelte and 10+ sub-components
- Add backup directory for old component preservation
- Update AGENTS.md with coding guidelines and project conventions
- Update tests.js API client with improved structure
- Add documentation for frontend test management architecture
2026-02-20 13:51:54 +07:00

251 lines
8.9 KiB
Svelte

<script>
import { Plus, Edit2, Trash2, Link } from 'lucide-svelte';
import Modal from '$lib/components/Modal.svelte';
let { formData = $bindable(), isDirty = $bindable(false) } = $props();
let modalOpen = $state(false);
let modalMode = $state('create');
let selectedMapping = $state(null);
let editingMapping = $state({
HostType: '',
HostID: '',
HostDataSource: '',
HostTestCode: '',
HostTestName: '',
ClientType: '',
ClientID: '',
ClientDataSource: '',
ConDefID: '',
ClientTestCode: '',
ClientTestName: ''
});
const hostTypes = ['HIS', 'SITE', 'WST', 'INST'];
const clientTypes = ['HIS', 'SITE', 'WST', 'INST'];
function handleFieldChange() {
isDirty = true;
}
function openAddMapping() {
modalMode = 'create';
editingMapping = {
HostType: '',
HostID: '',
HostDataSource: '',
HostTestCode: '',
HostTestName: '',
ClientType: '',
ClientID: '',
ClientDataSource: '',
ConDefID: '',
ClientTestCode: '',
ClientTestName: ''
};
modalOpen = true;
}
function openEditMapping(mapping) {
modalMode = 'edit';
selectedMapping = mapping;
editingMapping = { ...mapping };
modalOpen = true;
}
function removeMapping(index) {
const newMappings = formData.testmap?.filter((_, i) => i !== index) || [];
formData.testmap = newMappings;
handleFieldChange();
}
function saveMapping() {
if (modalMode === 'create') {
formData.testmap = [...(formData.testmap || []), { ...editingMapping }];
} else {
const newMappings = formData.testmap?.map(m =>
m === selectedMapping ? { ...editingMapping } : m
) || [];
formData.testmap = newMappings;
}
modalOpen = false;
handleFieldChange();
}
</script>
<div class="space-y-6">
<h2 class="text-lg font-semibold text-gray-800">System Mappings</h2>
<div class="alert alert-info text-sm">
<Link class="w-4 h-4" />
<div>
<strong>Test Mappings:</strong> Configure how this test maps to external systems (HIS, Lab Information Systems, etc.)
</div>
</div>
<div class="space-y-4">
<h3 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Current Mappings ({formData.testmap?.length || 0})</h3>
{#if !formData.testmap || formData.testmap.length === 0}
<div class="text-center py-8 bg-base-200 rounded-lg">
<Link class="w-12 h-12 mx-auto text-gray-400 mb-2" />
<p class="text-sm text-gray-500">No mappings configured</p>
<p class="text-xs text-gray-400">Add mappings to external systems</p>
</div>
{:else}
<div class="overflow-x-auto border border-base-200 rounded-lg">
<table class="table table-sm table-compact">
<thead>
<tr class="bg-base-200">
<th>Host System</th>
<th>Host Code</th>
<th>Client System</th>
<th>Client Code</th>
<th class="w-24 text-center">Actions</th>
</tr>
</thead>
<tbody>
{#each formData.testmap as mapping, idx (idx)}
<tr class="hover:bg-base-100">
<td>
<div class="text-sm">
<div class="font-medium">{mapping.HostType}</div>
<div class="text-xs text-gray-500">ID: {mapping.HostID || '-'}</div>
</div>
</td>
<td>
<div class="text-sm">
<div class="font-mono">{mapping.HostTestCode || '-'}</div>
<div class="text-xs text-gray-500 truncate max-w-[150px]">{mapping.HostTestName || '-'}</div>
</div>
</td>
<td>
<div class="text-sm">
<div class="font-medium">{mapping.ClientType}</div>
<div class="text-xs text-gray-500">ID: {mapping.ClientID || '-'}</div>
</div>
</td>
<td>
<div class="text-sm">
<div class="font-mono">{mapping.ClientTestCode || '-'}</div>
<div class="text-xs text-gray-500 truncate max-w-[150px]">{mapping.ClientTestName || '-'}</div>
</div>
</td>
<td>
<div class="flex justify-center gap-1">
<button
class="btn btn-ghost btn-xs"
onclick={() => openEditMapping(mapping)}
title="Edit Mapping"
>
<Edit2 class="w-3 h-3" />
</button>
<button
class="btn btn-ghost btn-xs text-error"
onclick={() => removeMapping(idx)}
title="Remove Mapping"
>
<Trash2 class="w-3 h-3" />
</button>
</div>
</td>
</tr>
{/each}
</tbody>
</table>
</div>
{/if}
</div>
<div class="flex justify-end">
<button class="btn btn-sm btn-primary" onclick={openAddMapping}>
<Plus class="w-4 h-4 mr-2" />
Add Mapping
</button>
</div>
</div>
<Modal bind:open={modalOpen} title={modalMode === 'create' ? 'Add Mapping' : 'Edit Mapping'} size="lg">
<div class="space-y-6 max-h-[500px] overflow-y-auto">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-3">
<h3 class="text-sm font-medium text-gray-600 uppercase tracking-wide border-b pb-2">Host System</h3>
<div class="form-control">
<label class="label text-sm">Type</label>
<select class="select select-sm select-bordered" bind:value={editingMapping.HostType}>
<option value="">Select type...</option>
{#each hostTypes as type (type)}
<option value={type}>{type}</option>
{/each}
</select>
</div>
<div class="form-control">
<label class="label text-sm">ID</label>
<input type="text" class="input input-sm input-bordered" bind:value={editingMapping.HostID} placeholder="System ID" />
</div>
<div class="form-control">
<label class="label text-sm">Data Source</label>
<input type="text" class="input input-sm input-bordered" bind:value={editingMapping.HostDataSource} placeholder="e.g., DB, API" />
</div>
<div class="form-control">
<label class="label text-sm">Test Code</label>
<input type="text" class="input input-sm input-bordered" bind:value={editingMapping.HostTestCode} placeholder="Test code in host system" />
</div>
<div class="form-control">
<label class="label text-sm">Test Name</label>
<input type="text" class="input input-sm input-bordered" bind:value={editingMapping.HostTestName} placeholder="Test name in host system" />
</div>
</div>
<div class="space-y-3">
<h3 class="text-sm font-medium text-gray-600 uppercase tracking-wide border-b pb-2">Client System</h3>
<div class="form-control">
<label class="label text-sm">Type</label>
<select class="select select-sm select-bordered" bind:value={editingMapping.ClientType}>
<option value="">Select type...</option>
{#each clientTypes as type (type)}
<option value={type}>{type}</option>
{/each}
</select>
</div>
<div class="form-control">
<label class="label text-sm">ID</label>
<input type="text" class="input input-sm input-bordered" bind:value={editingMapping.ClientID} placeholder="System ID" />
</div>
<div class="form-control">
<label class="label text-sm">Data Source</label>
<input type="text" class="input input-sm input-bordered" bind:value={editingMapping.ClientDataSource} placeholder="e.g., DB, API" />
</div>
<div class="form-control">
<label class="label text-sm">Container</label>
<input type="text" class="input input-sm input-bordered" bind:value={editingMapping.ConDefID} placeholder="Container definition" />
</div>
<div class="form-control">
<label class="label text-sm">Test Code</label>
<input type="text" class="input input-sm input-bordered" bind:value={editingMapping.ClientTestCode} placeholder="Test code in client system" />
</div>
<div class="form-control">
<label class="label text-sm">Test Name</label>
<input type="text" class="input input-sm input-bordered" bind:value={editingMapping.ClientTestName} placeholder="Test name in client system" />
</div>
</div>
</div>
</div>
{#snippet footer()}
<button class="btn btn-ghost" onclick={() => modalOpen = false}>Cancel</button>
<button class="btn btn-primary" onclick={saveMapping}>Save</button>
{/snippet}
</Modal>