- 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
251 lines
8.9 KiB
Svelte
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>
|