Update test modal components with improved UX and add TestTypeSelector
This commit is contained in:
parent
8d77370357
commit
995cdd3fec
@ -6,11 +6,12 @@
|
|||||||
import DataTable from '$lib/components/DataTable.svelte';
|
import DataTable from '$lib/components/DataTable.svelte';
|
||||||
import Modal from '$lib/components/Modal.svelte';
|
import Modal from '$lib/components/Modal.svelte';
|
||||||
import TestModal from './TestModal.svelte';
|
import TestModal from './TestModal.svelte';
|
||||||
|
import TestTypeSelector from './test-modal/TestTypeSelector.svelte';
|
||||||
import { validateNumericRange, validateTholdRange, validateTextRange, validateVsetRange } from './referenceRange.js';
|
import { validateNumericRange, validateTholdRange, validateTextRange, validateVsetRange } from './referenceRange.js';
|
||||||
import { Plus, Edit2, Trash2, ArrowLeft, Filter, Search, ChevronDown, ChevronRight, Microscope, Variable, Calculator, Box, Layers } from 'lucide-svelte';
|
import { Plus, Edit2, Trash2, ArrowLeft, Filter, Search, ChevronDown, ChevronRight, Microscope, Variable, Calculator, Box, Layers } from 'lucide-svelte';
|
||||||
|
|
||||||
let loading = $state(false), tests = $state([]), disciplines = $state([]), departments = $state([]);
|
let loading = $state(false), tests = $state([]), disciplines = $state([]), departments = $state([]);
|
||||||
let modalOpen = $state(false), selectedRowIndex = $state(-1), expandedGroups = $state(new Set());
|
let modalOpen = $state(false), selectedRowIndex = $state(-1), expandedGroups = $state(new Set()), typeSelectorOpen = $state(false);
|
||||||
let currentPage = $state(1), perPage = $state(20), totalItems = $state(0), totalPages = $state(1);
|
let currentPage = $state(1), perPage = $state(20), totalItems = $state(0), totalPages = $state(1);
|
||||||
let modalMode = $state('create'), saving = $state(false), selectedType = $state(''), searchQuery = $state(''), searchInputRef = $state(null);
|
let modalMode = $state('create'), saving = $state(false), selectedType = $state(''), searchQuery = $state(''), searchInputRef = $state(null);
|
||||||
let deleteModalOpen = $state(false), testToDelete = $state(null), deleting = $state(false);
|
let deleteModalOpen = $state(false), testToDelete = $state(null), deleting = $state(false);
|
||||||
@ -70,14 +71,23 @@ const canHaveRefRange = $derived(formData.TestType === 'TEST' || formData.TestTy
|
|||||||
function getVisibleTests() { return tests.filter(t => t.IsActive !== '0' && t.IsActive !== 0); }
|
function getVisibleTests() { return tests.filter(t => t.IsActive !== '0' && t.IsActive !== 0); }
|
||||||
function getTestTypeConfig(type) { return testTypeConfig[type] || testTypeConfig.TEST; }
|
function getTestTypeConfig(type) { return testTypeConfig[type] || testTypeConfig.TEST; }
|
||||||
function formatReferenceRange(test) { return '-'; }
|
function formatReferenceRange(test) { return '-'; }
|
||||||
function openCreateModal() {
|
function openTypeSelector() {
|
||||||
|
typeSelectorOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTypeSelect(type) {
|
||||||
|
typeSelectorOpen = false;
|
||||||
|
openCreateModal(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCreateModal(type = 'TEST') {
|
||||||
modalMode = 'create';
|
modalMode = 'create';
|
||||||
formData = {
|
formData = {
|
||||||
// Basic Info
|
// Basic Info
|
||||||
TestSiteID: null,
|
TestSiteID: null,
|
||||||
TestSiteCode: '',
|
TestSiteCode: '',
|
||||||
TestSiteName: '',
|
TestSiteName: '',
|
||||||
TestType: 'TEST',
|
TestType: type,
|
||||||
DisciplineID: null,
|
DisciplineID: null,
|
||||||
DepartmentID: null,
|
DepartmentID: null,
|
||||||
SeqScr: '0',
|
SeqScr: '0',
|
||||||
@ -284,7 +294,7 @@ const canHaveRefRange = $derived(formData.TestType === 'TEST' || formData.TestTy
|
|||||||
<h1 class="text-3xl font-bold text-gray-800">Test Definitions</h1>
|
<h1 class="text-3xl font-bold text-gray-800">Test Definitions</h1>
|
||||||
<p class="text-gray-600">Manage laboratory tests, panels, and calculated values</p>
|
<p class="text-gray-600">Manage laboratory tests, panels, and calculated values</p>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-primary" onclick={openCreateModal}><Plus class="w-4 h-4 mr-2" />Add Test</button>
|
<button class="btn btn-primary" onclick={openTypeSelector}><Plus class="w-4 h-4 mr-2" />Add Test</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-base-100 rounded-lg shadow border border-base-200 p-4 mb-4">
|
<div class="bg-base-100 rounded-lg shadow border border-base-200 p-4 mb-4">
|
||||||
@ -296,7 +306,7 @@ const canHaveRefRange = $derived(formData.TestType === 'TEST' || formData.TestTy
|
|||||||
<div class="w-full sm:w-48">
|
<div class="w-full sm:w-48">
|
||||||
<select class="select select-sm select-bordered w-full" bind:value={selectedType} onchange={handleFilter}>
|
<select class="select select-sm select-bordered w-full" bind:value={selectedType} onchange={handleFilter}>
|
||||||
<option value="">All Types</option>
|
<option value="">All Types</option>
|
||||||
<option value="TEST">Technical Test</option>
|
<option value="TEST">Single Test</option>
|
||||||
<option value="PARAM">Parameter</option>
|
<option value="PARAM">Parameter</option>
|
||||||
<option value="CALC">Calculated</option>
|
<option value="CALC">Calculated</option>
|
||||||
<option value="GROUP">Panel/Profile</option>
|
<option value="GROUP">Panel/Profile</option>
|
||||||
@ -323,6 +333,13 @@ const canHaveRefRange = $derived(formData.TestType === 'TEST' || formData.TestTy
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Modal bind:open={typeSelectorOpen} title="Add Test" size="md">
|
||||||
|
<TestTypeSelector
|
||||||
|
onselect={handleTypeSelect}
|
||||||
|
oncancel={() => typeSelectorOpen = false}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<TestModal
|
<TestModal
|
||||||
bind:open={modalOpen}
|
bind:open={modalOpen}
|
||||||
mode={modalMode}
|
mode={modalMode}
|
||||||
|
|||||||
@ -20,6 +20,13 @@
|
|||||||
onsave = () => {}
|
onsave = () => {}
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
|
const typeLabels = {
|
||||||
|
TEST: 'Single Test',
|
||||||
|
PARAM: 'Parameter',
|
||||||
|
CALC: 'Calculated',
|
||||||
|
GROUP: 'Panel'
|
||||||
|
};
|
||||||
|
|
||||||
function handleSubmit(e) {
|
function handleSubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onsave();
|
onsave();
|
||||||
@ -27,6 +34,12 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form class="space-y-3" onsubmit={handleSubmit}>
|
<form class="space-y-3" onsubmit={handleSubmit}>
|
||||||
|
<!-- Test Type Header -->
|
||||||
|
<div class="bg-base-200 rounded-lg px-4 py-3 mb-4">
|
||||||
|
<span class="text-sm text-gray-500">Test Type</span>
|
||||||
|
<div class="font-semibold text-lg">{typeLabels[formData.TestType]}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Basic Info -->
|
<!-- Basic Info -->
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
@ -59,38 +72,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Type and Sequence -->
|
<!-- Sequence -->
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="form-control max-w-xs">
|
||||||
<div class="form-control">
|
<label class="label" for="seqScr">
|
||||||
<label class="label" for="testType">
|
<span class="label-text font-medium">Screen Sequence</span>
|
||||||
<span class="label-text font-medium">Test Type</span>
|
</label>
|
||||||
<span class="label-text-alt text-error">*</span>
|
<input
|
||||||
</label>
|
id="seqScr"
|
||||||
<select
|
type="number"
|
||||||
id="testType"
|
class="input input-sm input-bordered w-full"
|
||||||
class="select select-sm select-bordered w-full"
|
bind:value={formData.SeqScr}
|
||||||
bind:value={formData.TestType}
|
placeholder="0"
|
||||||
required
|
/>
|
||||||
>
|
|
||||||
<option value="TEST">Technical Test</option>
|
|
||||||
<option value="PARAM">Parameter</option>
|
|
||||||
<option value="CALC">Calculated</option>
|
|
||||||
<option value="GROUP">Panel/Profile</option>
|
|
||||||
<option value="TITLE">Section Header</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label" for="seqScr">
|
|
||||||
<span class="label-text font-medium">Screen Sequence</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="seqScr"
|
|
||||||
type="number"
|
|
||||||
class="input input-sm input-bordered w-full"
|
|
||||||
bind:value={formData.SeqScr}
|
|
||||||
placeholder="0"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Discipline and Department -->
|
<!-- Discipline and Department -->
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import { Search, Plus, X, GripVertical, Microscope } from 'lucide-svelte';
|
import { Search, Plus, X, Microscope } from 'lucide-svelte';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Props
|
* @typedef {Object} Props
|
||||||
@ -16,7 +16,6 @@
|
|||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
let searchQuery = $state('');
|
let searchQuery = $state('');
|
||||||
let draggedIndex = $state(-1);
|
|
||||||
|
|
||||||
// Filter out the current test and already selected tests
|
// Filter out the current test and already selected tests
|
||||||
let filteredTests = $derived(
|
let filteredTests = $derived(
|
||||||
@ -35,8 +34,7 @@
|
|||||||
TestSiteID: test.TestSiteID,
|
TestSiteID: test.TestSiteID,
|
||||||
TestSiteCode: test.TestSiteCode,
|
TestSiteCode: test.TestSiteCode,
|
||||||
TestSiteName: test.TestSiteName,
|
TestSiteName: test.TestSiteName,
|
||||||
TestType: test.TestType,
|
TestType: test.TestType
|
||||||
Sequence: (formData.groupMembers?.length || 0) + 1
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
onupdateFormData({ ...formData, groupMembers: newMembers });
|
onupdateFormData({ ...formData, groupMembers: newMembers });
|
||||||
@ -45,43 +43,9 @@
|
|||||||
|
|
||||||
function removeMember(index) {
|
function removeMember(index) {
|
||||||
const newMembers = formData.groupMembers.filter((_, i) => i !== index);
|
const newMembers = formData.groupMembers.filter((_, i) => i !== index);
|
||||||
// Reorder sequences
|
|
||||||
newMembers.forEach((member, i) => {
|
|
||||||
member.Sequence = i + 1;
|
|
||||||
});
|
|
||||||
onupdateFormData({ ...formData, groupMembers: newMembers });
|
onupdateFormData({ ...formData, groupMembers: newMembers });
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveMember(fromIndex, toIndex) {
|
|
||||||
if (toIndex < 0 || toIndex >= formData.groupMembers.length) return;
|
|
||||||
|
|
||||||
const members = [...formData.groupMembers];
|
|
||||||
const [moved] = members.splice(fromIndex, 1);
|
|
||||||
members.splice(toIndex, 0, moved);
|
|
||||||
|
|
||||||
// Reorder sequences
|
|
||||||
members.forEach((member, i) => {
|
|
||||||
member.Sequence = i + 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
onupdateFormData({ ...formData, groupMembers: members });
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragStart(index) {
|
|
||||||
draggedIndex = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragOver(e, index) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (draggedIndex === -1 || draggedIndex === index) return;
|
|
||||||
moveMember(draggedIndex, index);
|
|
||||||
draggedIndex = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragEnd() {
|
|
||||||
draggedIndex = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTestTypeBadge(testType) {
|
function getTestTypeBadge(testType) {
|
||||||
const badges = {
|
const badges = {
|
||||||
'TEST': 'badge-primary',
|
'TEST': 'badge-primary',
|
||||||
@ -94,15 +58,15 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="space-y-3">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 h-[500px]">
|
||||||
<!-- Add Member Section -->
|
<!-- Left: Search for Tests -->
|
||||||
<div class="bg-base-100 rounded-lg border border-base-200 p-4">
|
<div class="bg-base-100 rounded-lg border border-base-200 p-4 flex flex-col">
|
||||||
<div class="flex items-center gap-2 mb-4">
|
<div class="flex items-center gap-2 mb-4">
|
||||||
<Microscope class="w-5 h-5 text-primary" />
|
<Microscope class="w-5 h-5 text-primary" />
|
||||||
<h3 class="font-semibold">Add Group Members</h3>
|
<h3 class="font-semibold">Add Group Members</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-control">
|
<div class="form-control mb-3">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<Search class="w-5 h-5 absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
|
<Search class="w-5 h-5 absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
|
||||||
<input
|
<input
|
||||||
@ -114,8 +78,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if searchQuery}
|
<div class="flex-1 overflow-y-auto border border-base-200 rounded-lg">
|
||||||
<div class="mt-2 max-h-60 overflow-y-auto border border-base-200 rounded-lg">
|
{#if searchQuery}
|
||||||
{#if filteredTests.length === 0}
|
{#if filteredTests.length === 0}
|
||||||
<div class="p-4 text-center text-gray-500">
|
<div class="p-4 text-center text-gray-500">
|
||||||
No tests found matching "{searchQuery}"
|
No tests found matching "{searchQuery}"
|
||||||
@ -138,70 +102,57 @@
|
|||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
{:else}
|
||||||
{/if}
|
<div class="p-8 text-center text-gray-400">
|
||||||
</div>
|
<Search class="w-12 h-12 mx-auto mb-2 opacity-50" />
|
||||||
|
<p>Type to search for tests</p>
|
||||||
<!-- Selected Members -->
|
</div>
|
||||||
<div class="bg-base-100 rounded-lg border border-base-200 p-4">
|
|
||||||
<div class="flex items-center justify-between mb-4">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<span class="font-semibold">Group Members</span>
|
|
||||||
<span class="badge badge-sm badge-primary">{formData.groupMembers?.length || 0}</span>
|
|
||||||
</div>
|
|
||||||
{#if formData.groupMembers?.length > 0}
|
|
||||||
<span class="text-sm text-gray-500">Drag to reorder</span>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if !formData.groupMembers || formData.groupMembers.length === 0}
|
<!-- Right: Selected Members -->
|
||||||
<div class="text-center py-8 bg-base-200 rounded-lg border-2 border-dashed border-base-300">
|
<div class="bg-base-100 rounded-lg border border-base-200 p-2 flex flex-col">
|
||||||
<Microscope class="w-12 h-12 mx-auto text-gray-400 mb-2" />
|
<div class="flex items-center justify-between mb-2 px-1">
|
||||||
<p class="text-gray-500">No members added yet</p>
|
<div class="flex items-center gap-2">
|
||||||
<p class="text-sm text-gray-400 mt-1">Search and add tests above</p>
|
<span class="font-semibold text-sm">Group Members</span>
|
||||||
|
<span class="badge badge-xs badge-primary">{formData.groupMembers?.length || 0}</span>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
</div>
|
||||||
<div class="space-y-2">
|
|
||||||
{#each formData.groupMembers as member, index (member.TestSiteID)}
|
|
||||||
<div
|
|
||||||
class="card bg-base-100 border border-base-200 hover:border-primary/50 transition-colors"
|
|
||||||
draggable="true"
|
|
||||||
ondragstart={() => handleDragStart(index)}
|
|
||||||
ondragover={(e) => handleDragOver(e, index)}
|
|
||||||
ondragend={handleDragEnd}
|
|
||||||
class:opacity-50={draggedIndex === index}
|
|
||||||
>
|
|
||||||
<div class="card-body p-3 flex flex-row items-center gap-3">
|
|
||||||
<div class="cursor-move text-gray-400 hover:text-gray-600">
|
|
||||||
<GripVertical class="w-5 h-5" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center text-primary font-bold text-sm">
|
<div class="flex-1 overflow-y-auto">
|
||||||
{member.Sequence}
|
{#if !formData.groupMembers || formData.groupMembers.length === 0}
|
||||||
</div>
|
<div class="h-full flex flex-col items-center justify-center text-center py-6 bg-base-200 rounded-lg border-2 border-dashed border-base-300">
|
||||||
|
<Microscope class="w-8 h-8 text-gray-400 mb-1" />
|
||||||
<div class="flex-1">
|
<p class="text-gray-500 text-sm">No members added yet</p>
|
||||||
<div class="flex items-center gap-2">
|
<p class="text-xs text-gray-400 mt-0.5">Search and add tests from the left</p>
|
||||||
<span class="font-mono text-sm text-gray-600">{member.TestSiteCode}</span>
|
</div>
|
||||||
<span class="font-medium">{member.TestSiteName}</span>
|
{:else}
|
||||||
|
<div class="space-y-1">
|
||||||
|
{#each formData.groupMembers as member, index (`${member.TestSiteID}-${index}`)}
|
||||||
|
<div class="flex items-center gap-2 px-2 py-1.5 bg-base-100 border border-base-200 rounded hover:border-primary/50 transition-colors">
|
||||||
|
<div class="flex-1 min-w-0">
|
||||||
|
<div class="flex items-center gap-1.5">
|
||||||
|
<span class="font-mono text-xs text-gray-600">{member.TestSiteCode}</span>
|
||||||
|
<span class="text-sm truncate">{member.TestSiteName}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="badge badge-sm {getTestTypeBadge(member.TestType)}">
|
<span class="badge badge-xs {getTestTypeBadge(member.TestType)}">
|
||||||
{member.TestType}
|
{member.TestType}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm btn-ghost text-error"
|
class="btn btn-xs btn-ghost text-error p-0 min-h-0 h-6 w-6"
|
||||||
onclick={() => removeMember(index)}
|
onclick={() => removeMember(index)}
|
||||||
>
|
>
|
||||||
<X class="w-4 h-4" />
|
<X class="w-3 h-3" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/each}
|
||||||
{/each}
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
{/if}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -89,9 +89,9 @@
|
|||||||
if (ref.Low !== null && ref.High !== null) {
|
if (ref.Low !== null && ref.High !== null) {
|
||||||
rangeText = `${ref.Low} - ${ref.High}`;
|
rangeText = `${ref.Low} - ${ref.High}`;
|
||||||
} else if (ref.Low !== null) {
|
} else if (ref.Low !== null) {
|
||||||
rangeText = `${ref.Low}`;
|
rangeText = `> ${ref.Low}`;
|
||||||
} else if (ref.High !== null) {
|
} else if (ref.High !== null) {
|
||||||
rangeText = `${ref.High}`;
|
rangeText = `< ${ref.High}`;
|
||||||
} else {
|
} else {
|
||||||
rangeText = 'Not set';
|
rangeText = 'Not set';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,43 @@
|
|||||||
|
<script>
|
||||||
|
import { Microscope, Variable, Calculator, Box } from 'lucide-svelte';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Props
|
||||||
|
* @property {(type: string) => void} onselect - Selection handler
|
||||||
|
* @property {() => void} oncancel - Cancel handler
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {Props} */
|
||||||
|
let {
|
||||||
|
onselect = () => {},
|
||||||
|
oncancel = () => {}
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
const types = [
|
||||||
|
{ value: 'TEST', label: 'Single Test', icon: Microscope, color: 'text-primary', bg: 'bg-primary/10' },
|
||||||
|
{ value: 'PARAM', label: 'Parameter', icon: Variable, color: 'text-secondary', bg: 'bg-secondary/10' },
|
||||||
|
{ value: 'CALC', label: 'Calculated', icon: Calculator, color: 'text-accent', bg: 'bg-accent/10' },
|
||||||
|
{ value: 'GROUP', label: 'Panel', icon: Box, color: 'text-info', bg: 'bg-info/10' }
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<p class="text-center text-gray-600">Select test type to create</p>
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
{#each types as type}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="card bg-base-100 border border-base-200 hover:border-primary hover:shadow-md transition-all p-6 flex flex-col items-center gap-3"
|
||||||
|
onclick={() => onselect(type.value)}
|
||||||
|
>
|
||||||
|
<div class="w-12 h-12 rounded-full {type.bg} flex items-center justify-center">
|
||||||
|
<svelte:component this={type.icon} class="w-6 h-6 {type.color}" />
|
||||||
|
</div>
|
||||||
|
<span class="font-medium">{type.label}</span>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-center pt-2">
|
||||||
|
<button class="btn btn-ghost btn-sm" onclick={oncancel} type="button">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
Loading…
x
Reference in New Issue
Block a user