fix: Improve SelectDropdown handling and valueset caching
This commit is contained in:
parent
4accf7b6d6
commit
4641668f78
@ -8,7 +8,7 @@ export async function fetchProvinces() {
|
||||
return get('/api/areageo/provinces');
|
||||
}
|
||||
|
||||
export async function fetchCities(provinceId = null) {
|
||||
const query = provinceId ? `?province_id=${provinceId}` : '';
|
||||
export async function fetchCities(provinceId = null) {
|
||||
const query = provinceId ? `?province_id=${encodeURIComponent(provinceId)}` : '';
|
||||
return get(`/api/areageo/cities${query}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +29,16 @@
|
||||
lg: 'modal-lg',
|
||||
xl: 'modal-xl',
|
||||
full: 'modal-full',
|
||||
wide: 'modal-wide',
|
||||
};
|
||||
|
||||
const widthStyles = {
|
||||
sm: 'max-width: 400px;',
|
||||
md: 'max-width: 500px;',
|
||||
lg: 'max-width: 800px;',
|
||||
xl: 'max-width: 1200px;',
|
||||
full: 'max-width: 100%; width: 100%; height: 100%;',
|
||||
wide: 'max-width: 90vw; width: 1200px;',
|
||||
};
|
||||
|
||||
/**
|
||||
@ -67,7 +77,7 @@
|
||||
<svelte:window onkeydown={handleKeydown} />
|
||||
|
||||
<dialog class="modal {sizeClasses[size] || ''}" class:modal-open={open}>
|
||||
<div class="modal-box" role="dialog" aria-modal="true" aria-labelledby={title ? 'modal-title' : undefined}>
|
||||
<div class="modal-box" style={widthStyles[size] || ''} role="dialog" aria-modal="true" aria-labelledby={title ? 'modal-title' : undefined}>
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
{#if title}
|
||||
|
||||
@ -38,12 +38,20 @@
|
||||
onMount(async () => {
|
||||
if (valueSetKey) {
|
||||
loading = true;
|
||||
try {
|
||||
const items = await valueSets.load(valueSetKey);
|
||||
if (!items || items.length === 0) {
|
||||
console.warn('SelectDropdown: No items loaded for valueset:', valueSetKey);
|
||||
}
|
||||
dropdownOptions = items.map((item) => ({
|
||||
value: item.Value,
|
||||
label: item.Label,
|
||||
value: item.value || item.Value || item.code || item.Code || item.ItemCode || item.itemCode || '',
|
||||
label: item.label || item.Label || item.description || item.Description || item.name || item.Name || item.value || item.Value || '',
|
||||
}));
|
||||
} catch (err) {
|
||||
console.error('SelectDropdown error loading valueset:', err);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
} else if (options.length > 0) {
|
||||
dropdownOptions = options;
|
||||
}
|
||||
@ -51,7 +59,7 @@
|
||||
|
||||
// Watch for changes in manual options
|
||||
$effect(() => {
|
||||
if (!valueSetKey && options.length > 0) {
|
||||
if (!valueSetKey) {
|
||||
dropdownOptions = options;
|
||||
}
|
||||
});
|
||||
|
||||
@ -11,6 +11,9 @@ import { error as toastError } from '$lib/utils/toast.js';
|
||||
function createValueSetsStore() {
|
||||
const { subscribe, set, update } = writable({});
|
||||
|
||||
// Keep track of in-flight requests to prevent duplicates
|
||||
const inflightRequests = new Map();
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
|
||||
@ -20,44 +23,42 @@ function createValueSetsStore() {
|
||||
* @returns {Promise<Array>} - Array of value set items
|
||||
*/
|
||||
async load(key) {
|
||||
let result = [];
|
||||
console.log('valuesets.load() called for key:', key);
|
||||
|
||||
update((cache) => {
|
||||
// If already loading, return current state
|
||||
if (cache[key]?.loading) {
|
||||
return cache;
|
||||
// If there's already an in-flight request, return its promise
|
||||
if (inflightRequests.has(key)) {
|
||||
console.log('valuesets: returning existing in-flight request for:', key);
|
||||
return inflightRequests.get(key);
|
||||
}
|
||||
|
||||
// If already loaded, return cached items
|
||||
if (cache[key]?.loaded) {
|
||||
result = cache[key].items;
|
||||
return cache;
|
||||
// Check if already loaded in cache
|
||||
let cacheState = null;
|
||||
subscribe((state) => { cacheState = state; })();
|
||||
|
||||
if (cacheState?.[key]?.loaded) {
|
||||
console.log('valuesets: returning cached items for:', key);
|
||||
return cacheState[key].items;
|
||||
}
|
||||
|
||||
// Create the fetch promise
|
||||
const fetchPromise = (async () => {
|
||||
console.log('valuesets: starting fetch for:', key);
|
||||
|
||||
// Mark as loading
|
||||
return {
|
||||
update((cache) => ({
|
||||
...cache,
|
||||
[key]: { items: [], loaded: false, loading: true, error: null },
|
||||
};
|
||||
});
|
||||
|
||||
// If already loaded, return immediately
|
||||
if (result.length > 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// If already loading, wait a bit and retry
|
||||
const currentState = getCurrentState();
|
||||
if (currentState[key]?.loading) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
return this.load(key);
|
||||
}
|
||||
}));
|
||||
|
||||
try {
|
||||
console.log('valuesets: calling API for key:', key);
|
||||
const response = await fetchValueSetByKey(key);
|
||||
console.log('valuesets: API response for', key, ':', response);
|
||||
|
||||
if (response.status === 'success' && response.data) {
|
||||
const items = response.data.Items || [];
|
||||
// Handle both response.data being an array or having an Items property
|
||||
let items = Array.isArray(response.data) ? response.data : (response.data.Items || []);
|
||||
console.log('valuesets: items for', key, ':', items);
|
||||
|
||||
// Sort by Sequence if available
|
||||
items.sort((a, b) => (a.Sequence || 0) - (b.Sequence || 0));
|
||||
@ -67,12 +68,14 @@ function createValueSetsStore() {
|
||||
[key]: { items, loaded: true, loading: false, error: null },
|
||||
}));
|
||||
|
||||
console.log('valuesets: returning items for', key);
|
||||
return items;
|
||||
} else {
|
||||
throw new Error(response.message || 'Failed to load value set');
|
||||
}
|
||||
} catch (err) {
|
||||
const errorMessage = err.message || `Failed to load value set: ${key}`;
|
||||
console.error('valuesets: error for', key, ':', err);
|
||||
|
||||
update((cache) => ({
|
||||
...cache,
|
||||
@ -81,7 +84,13 @@ function createValueSetsStore() {
|
||||
|
||||
toastError(errorMessage);
|
||||
return [];
|
||||
} finally {
|
||||
inflightRequests.delete(key);
|
||||
}
|
||||
})();
|
||||
|
||||
inflightRequests.set(key, fetchPromise);
|
||||
return fetchPromise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -163,15 +172,6 @@ function createValueSetsStore() {
|
||||
};
|
||||
}
|
||||
|
||||
// Helper to get current state synchronously
|
||||
function getCurrentState() {
|
||||
let state = {};
|
||||
valueSets.subscribe((s) => {
|
||||
state = s;
|
||||
})();
|
||||
return state;
|
||||
}
|
||||
|
||||
export const valueSets = createValueSetsStore();
|
||||
|
||||
/**
|
||||
|
||||
@ -2,10 +2,10 @@
|
||||
import { onMount } from 'svelte';
|
||||
import {
|
||||
fetchPatients,
|
||||
fetchPatient,
|
||||
createPatient,
|
||||
updatePatient,
|
||||
deletePatient,
|
||||
checkPatientExists,
|
||||
} from '$lib/api/patients.js';
|
||||
import { fetchProvinces, fetchCities } from '$lib/api/geography.js';
|
||||
import { success as toastSuccess, error as toastError } from '$lib/utils/toast.js';
|
||||
@ -16,6 +16,7 @@
|
||||
|
||||
// State
|
||||
let loading = $state(false);
|
||||
let modalLoading = $state(false);
|
||||
let patients = $state([]);
|
||||
let provinces = $state([]);
|
||||
let cities = $state([]);
|
||||
@ -26,6 +27,7 @@
|
||||
let deleteItem = $state(null);
|
||||
let currentStep = $state(1);
|
||||
let formErrors = $state({});
|
||||
let previousProvince = $state('');
|
||||
|
||||
// Search and pagination
|
||||
let searchQuery = $state('');
|
||||
@ -69,7 +71,9 @@
|
||||
PatCom: '',
|
||||
});
|
||||
|
||||
// Dropdown options
|
||||
|
||||
|
||||
// Dropdown options (prefix - valueset not available yet)
|
||||
const prefixOptions = [
|
||||
{ value: 'Mr', label: 'Mr' },
|
||||
{ value: 'Mrs', label: 'Mrs' },
|
||||
@ -78,35 +82,13 @@
|
||||
{ value: 'Prof', label: 'Prof' },
|
||||
];
|
||||
|
||||
const sexOptions = [
|
||||
{ value: '1', label: 'Female' },
|
||||
{ value: '2', label: 'Male' },
|
||||
];
|
||||
|
||||
const maritalStatusOptions = [
|
||||
{ value: 'A', label: 'Annulled' },
|
||||
{ value: 'B', label: 'Separated' },
|
||||
{ value: 'D', label: 'Divorced' },
|
||||
{ value: 'M', label: 'Married' },
|
||||
{ value: 'S', label: 'Single' },
|
||||
{ value: 'W', label: 'Widowed' },
|
||||
];
|
||||
|
||||
const identifierTypeOptions = [
|
||||
{ value: 'KTP', label: 'KTP (National ID)' },
|
||||
{ value: 'PASS', label: 'Passport' },
|
||||
{ value: 'SSN', label: 'SSN' },
|
||||
{ value: 'SIM', label: 'Driver License' },
|
||||
{ value: 'KTAS', label: 'KTAS' },
|
||||
];
|
||||
|
||||
// Derived values
|
||||
const provinceOptions = $derived(
|
||||
provinces.map((p) => ({ value: p.AreaCode, label: p.AreaName }))
|
||||
provinces.map((p) => ({ value: p.value, label: p.label }))
|
||||
);
|
||||
|
||||
const cityOptions = $derived(
|
||||
cities.map((c) => ({ value: c.AreaCode, label: c.AreaName }))
|
||||
cities.map((c) => ({ value: c.value, label: c.label }))
|
||||
);
|
||||
|
||||
const columns = [
|
||||
@ -118,7 +100,10 @@
|
||||
];
|
||||
|
||||
onMount(async () => {
|
||||
await Promise.all([loadPatients(), loadProvinces()]);
|
||||
await Promise.all([
|
||||
loadPatients(),
|
||||
loadProvinces(),
|
||||
]);
|
||||
});
|
||||
|
||||
async function loadPatients() {
|
||||
@ -164,7 +149,7 @@
|
||||
}
|
||||
try {
|
||||
const response = await fetchCities(provinceCode);
|
||||
cities = Array.isArray(response.data) ? response.data : [];
|
||||
cities = Array.isArray(response.data) ? response.data : (Array.isArray(response) ? response : []);
|
||||
} catch (err) {
|
||||
console.error('Failed to load cities:', err);
|
||||
cities = [];
|
||||
@ -224,50 +209,62 @@
|
||||
modalOpen = true;
|
||||
}
|
||||
|
||||
function openEditModal(row) {
|
||||
async function openEditModal(row) {
|
||||
modalMode = 'edit';
|
||||
currentStep = 1;
|
||||
formErrors = {};
|
||||
modalLoading = true;
|
||||
|
||||
try {
|
||||
// Fetch full patient details including individual name fields
|
||||
const response = await fetchPatient(row.InternalPID);
|
||||
const patient = response.data || response;
|
||||
|
||||
formData = {
|
||||
InternalPID: row.InternalPID,
|
||||
PatientID: row.PatientID || '',
|
||||
Prefix: row.Prefix || '',
|
||||
NameFirst: row.NameFirst || '',
|
||||
NameMiddle: row.NameMiddle || '',
|
||||
NameLast: row.NameLast || '',
|
||||
NameMaiden: row.NameMaiden || '',
|
||||
Suffix: row.Suffix || '',
|
||||
Sex: row.Sex || '',
|
||||
Birthdate: row.Birthdate ? row.Birthdate.split('T')[0] : '',
|
||||
PlaceOfBirth: row.PlaceOfBirth || '',
|
||||
Citizenship: row.Citizenship || '',
|
||||
Street_1: row.Street_1 || '',
|
||||
Street_2: row.Street_2 || '',
|
||||
Street_3: row.Street_3 || '',
|
||||
ZIP: row.ZIP || '',
|
||||
Province: row.Province || '',
|
||||
City: row.City || '',
|
||||
Country: row.Country || 'Indonesia',
|
||||
Phone: row.Phone || '',
|
||||
MobilePhone: row.MobilePhone || '',
|
||||
EmailAddress1: row.EmailAddress1 || '',
|
||||
EmailAddress2: row.EmailAddress2 || '',
|
||||
PatIdt: row.PatIdt || { IdentifierType: '', Identifier: '' },
|
||||
Race: row.Race || '',
|
||||
MaritalStatus: row.MaritalStatus || '',
|
||||
Religion: row.Religion || '',
|
||||
Ethnic: row.Ethnic || '',
|
||||
DeathIndicator: row.DeathIndicator || 'N',
|
||||
TimeOfDeath: row.TimeOfDeath ? row.TimeOfDeath.split('T')[0] : '',
|
||||
PatCom: row.PatCom || '',
|
||||
InternalPID: patient.InternalPID,
|
||||
PatientID: patient.PatientID || '',
|
||||
Prefix: patient.Prefix || '',
|
||||
NameFirst: patient.NameFirst || '',
|
||||
NameMiddle: patient.NameMiddle || '',
|
||||
NameLast: patient.NameLast || '',
|
||||
NameMaiden: patient.NameMaiden || '',
|
||||
Suffix: patient.Suffix || '',
|
||||
Sex: patient.Sex || '',
|
||||
Birthdate: patient.Birthdate ? patient.Birthdate.split('T')[0] : '',
|
||||
PlaceOfBirth: patient.PlaceOfBirth || '',
|
||||
Citizenship: patient.Citizenship || '',
|
||||
Street_1: patient.Street_1 || '',
|
||||
Street_2: patient.Street_2 || '',
|
||||
Street_3: patient.Street_3 || '',
|
||||
ZIP: patient.ZIP || '',
|
||||
Province: patient.Province || '',
|
||||
City: patient.City || '',
|
||||
Country: patient.Country || 'Indonesia',
|
||||
Phone: patient.Phone || '',
|
||||
MobilePhone: patient.MobilePhone || '',
|
||||
EmailAddress1: patient.EmailAddress1 || '',
|
||||
EmailAddress2: patient.EmailAddress2 || '',
|
||||
PatIdt: patient.PatIdt || { IdentifierType: '', Identifier: '' },
|
||||
Race: patient.Race || '',
|
||||
MaritalStatus: patient.MaritalStatus || '',
|
||||
Religion: patient.Religion || '',
|
||||
Ethnic: patient.Ethnic || '',
|
||||
DeathIndicator: patient.DeathIndicator || 'N',
|
||||
TimeOfDeath: patient.TimeOfDeath ? patient.TimeOfDeath.split('T')[0] : '',
|
||||
PatCom: patient.PatCom || '',
|
||||
};
|
||||
|
||||
// Load cities if province is set
|
||||
if (formData.Province) {
|
||||
loadCities(formData.Province);
|
||||
await loadCities(formData.Province);
|
||||
}
|
||||
|
||||
modalOpen = true;
|
||||
} catch (err) {
|
||||
toastError(err.message || 'Failed to load patient details');
|
||||
} finally {
|
||||
modalLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function validateStep(step) {
|
||||
@ -358,10 +355,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
function handleProvinceChange() {
|
||||
$effect(() => {
|
||||
if (formData.Province !== previousProvince) {
|
||||
previousProvince = formData.Province;
|
||||
formData.City = '';
|
||||
loadCities(formData.Province);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="p-6">
|
||||
@ -401,10 +401,10 @@
|
||||
{columns}
|
||||
data={patients.map((p) => ({
|
||||
...p,
|
||||
FullName: [p.Prefix, p.NameFirst, p.NameMiddle, p.NameLast, p.Suffix]
|
||||
FullName: p.FullName || [p.Prefix, p.NameFirst, p.NameMiddle, p.NameLast, p.Suffix]
|
||||
.filter(Boolean)
|
||||
.join(' '),
|
||||
SexLabel: p.Sex === '1' ? 'Female' : p.Sex === '2' ? 'Male' : '-',
|
||||
.join(' ') || '-',
|
||||
SexLabel: p.SexLabel || (p.Sex === '1' ? 'Female' : p.Sex === '2' ? 'Male' : '-'),
|
||||
BirthdateFormatted: p.Birthdate
|
||||
? new Date(p.Birthdate).toLocaleDateString()
|
||||
: '-',
|
||||
@ -464,69 +464,64 @@
|
||||
<Modal
|
||||
bind:open={modalOpen}
|
||||
title={modalMode === 'create' ? 'Add Patient' : 'Edit Patient'}
|
||||
size="xl"
|
||||
size="lg"
|
||||
>
|
||||
<!-- Step Indicator -->
|
||||
<!-- Tabs -->
|
||||
<div class="mb-6">
|
||||
<div class="flex items-center justify-center gap-4">
|
||||
<div class="flex items-center justify-center gap-2">
|
||||
<button
|
||||
class="flex items-center gap-2 px-4 py-2 rounded-full transition-colors"
|
||||
class:bg-emerald-100={currentStep === 1}
|
||||
class:text-emerald-700={currentStep === 1}
|
||||
class="px-4 py-2 rounded-lg transition-colors"
|
||||
class:bg-emerald-600={currentStep === 1}
|
||||
class:text-white={currentStep === 1}
|
||||
class:bg-gray-100={currentStep !== 1}
|
||||
class:text-gray-600={currentStep !== 1}
|
||||
class:font-semibold={currentStep === 1}
|
||||
onclick={() => (currentStep = 1)}
|
||||
>
|
||||
<span class="w-6 h-6 rounded-full bg-current text-white flex items-center justify-center text-sm font-medium">
|
||||
1
|
||||
</span>
|
||||
<span class="font-medium">Basic Info</span>
|
||||
Basic Info
|
||||
</button>
|
||||
<div class="w-8 h-px bg-gray-300"></div>
|
||||
<button
|
||||
class="flex items-center gap-2 px-4 py-2 rounded-full transition-colors"
|
||||
class:bg-emerald-100={currentStep === 2}
|
||||
class:text-emerald-700={currentStep === 2}
|
||||
class="px-4 py-2 rounded-lg transition-colors"
|
||||
class:bg-emerald-600={currentStep === 2}
|
||||
class:text-white={currentStep === 2}
|
||||
class:bg-gray-100={currentStep !== 2}
|
||||
class:text-gray-600={currentStep !== 2}
|
||||
onclick={() => currentStep > 2 && (currentStep = 2)}
|
||||
class:font-semibold={currentStep === 2}
|
||||
onclick={() => (currentStep = 2)}
|
||||
>
|
||||
<span class="w-6 h-6 rounded-full bg-current text-white flex items-center justify-center text-sm font-medium">
|
||||
2
|
||||
</span>
|
||||
<span class="font-medium">Address</span>
|
||||
Address
|
||||
</button>
|
||||
<div class="w-8 h-px bg-gray-300"></div>
|
||||
<button
|
||||
class="flex items-center gap-2 px-4 py-2 rounded-full transition-colors"
|
||||
class:bg-emerald-100={currentStep === 3}
|
||||
class:text-emerald-700={currentStep === 3}
|
||||
class="px-4 py-2 rounded-lg transition-colors"
|
||||
class:bg-emerald-600={currentStep === 3}
|
||||
class:text-white={currentStep === 3}
|
||||
class:bg-gray-100={currentStep !== 3}
|
||||
class:text-gray-600={currentStep !== 3}
|
||||
onclick={() => currentStep > 3 && (currentStep = 3)}
|
||||
class:font-semibold={currentStep === 3}
|
||||
onclick={() => (currentStep = 3)}
|
||||
>
|
||||
<span class="w-6 h-6 rounded-full bg-current text-white flex items-center justify-center text-sm font-medium">
|
||||
3
|
||||
</span>
|
||||
<span class="font-medium">Contact & ID</span>
|
||||
Contact & ID
|
||||
</button>
|
||||
<div class="w-8 h-px bg-gray-300"></div>
|
||||
<button
|
||||
class="flex items-center gap-2 px-4 py-2 rounded-full transition-colors"
|
||||
class:bg-emerald-100={currentStep === 4}
|
||||
class:text-emerald-700={currentStep === 4}
|
||||
class="px-4 py-2 rounded-lg transition-colors"
|
||||
class:bg-emerald-600={currentStep === 4}
|
||||
class:text-white={currentStep === 4}
|
||||
class:bg-gray-100={currentStep !== 4}
|
||||
class:text-gray-600={currentStep !== 4}
|
||||
onclick={() => currentStep > 4 && (currentStep = 4)}
|
||||
class:font-semibold={currentStep === 4}
|
||||
onclick={() => (currentStep = 4)}
|
||||
>
|
||||
<span class="w-6 h-6 rounded-full bg-current text-white flex items-center justify-center text-sm font-medium">
|
||||
4
|
||||
</span>
|
||||
<span class="font-medium">Additional</span>
|
||||
Additional
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if modalLoading}
|
||||
<div class="flex flex-col items-center justify-center py-12">
|
||||
<span class="loading loading-spinner loading-lg text-primary"></span>
|
||||
<p class="text-gray-500 mt-4">Loading patient data...</p>
|
||||
</div>
|
||||
{:else}
|
||||
<form class="space-y-5" onsubmit={(e) => e.preventDefault()}>
|
||||
<!-- Step 1: Basic Information -->
|
||||
{#if currentStep === 1}
|
||||
@ -635,7 +630,7 @@
|
||||
label="Sex"
|
||||
name="sex"
|
||||
bind:value={formData.Sex}
|
||||
options={sexOptions}
|
||||
valueSetKey="sex"
|
||||
placeholder="Select sex..."
|
||||
required={true}
|
||||
/>
|
||||
@ -748,7 +743,6 @@
|
||||
label="Province"
|
||||
name="province"
|
||||
bind:value={formData.Province}
|
||||
onchange={handleProvinceChange}
|
||||
options={provinceOptions}
|
||||
placeholder="Select province..."
|
||||
/>
|
||||
@ -841,7 +835,7 @@
|
||||
label="ID Type"
|
||||
name="idType"
|
||||
bind:value={formData.PatIdt.IdentifierType}
|
||||
options={identifierTypeOptions}
|
||||
valueSetKey="identifier_type"
|
||||
placeholder="Select ID type..."
|
||||
/>
|
||||
<div class="form-control">
|
||||
@ -869,49 +863,34 @@
|
||||
label="Marital Status"
|
||||
name="maritalStatus"
|
||||
bind:value={formData.MaritalStatus}
|
||||
options={maritalStatusOptions}
|
||||
valueSetKey="marital_status"
|
||||
placeholder="Select status..."
|
||||
/>
|
||||
<div class="form-control">
|
||||
<label class="label" for="religion">
|
||||
<span class="label-text font-medium">Religion</span>
|
||||
</label>
|
||||
<input
|
||||
id="religion"
|
||||
type="text"
|
||||
class="input input-bordered w-full"
|
||||
<SelectDropdown
|
||||
label="Religion"
|
||||
name="religion"
|
||||
bind:value={formData.Religion}
|
||||
placeholder="Enter religion"
|
||||
valueSetKey="religion"
|
||||
placeholder="Select religion..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label" for="race">
|
||||
<span class="label-text font-medium">Race</span>
|
||||
</label>
|
||||
<input
|
||||
id="race"
|
||||
type="text"
|
||||
class="input input-bordered w-full"
|
||||
<SelectDropdown
|
||||
label="Race"
|
||||
name="race"
|
||||
bind:value={formData.Race}
|
||||
placeholder="Enter race"
|
||||
valueSetKey="race"
|
||||
placeholder="Select race..."
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="ethnic">
|
||||
<span class="label-text font-medium">Ethnicity</span>
|
||||
</label>
|
||||
<input
|
||||
id="ethnic"
|
||||
type="text"
|
||||
class="input input-bordered w-full"
|
||||
<SelectDropdown
|
||||
label="Ethnicity"
|
||||
name="ethnic"
|
||||
bind:value={formData.Ethnic}
|
||||
placeholder="Enter ethnicity"
|
||||
valueSetKey="ethnic"
|
||||
placeholder="Select ethnicity..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-base-200 pt-5 mt-5">
|
||||
<h4 class="font-medium text-gray-700 mb-4">Other Information</h4>
|
||||
@ -920,10 +899,7 @@
|
||||
label="Death Indicator"
|
||||
name="deathIndicator"
|
||||
bind:value={formData.DeathIndicator}
|
||||
options={[
|
||||
{ value: 'N', label: 'No' },
|
||||
{ value: 'Y', label: 'Yes' },
|
||||
]}
|
||||
valueSetKey="death_indicator"
|
||||
placeholder="Select..."
|
||||
/>
|
||||
{#if formData.DeathIndicator === 'Y'}
|
||||
@ -957,6 +933,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
{#snippet footer()}
|
||||
<div class="flex justify-between w-full">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user