From 0c0bbd6e260745b5d04479e40a0c1d1aeac54d62 Mon Sep 17 00:00:00 2001 From: faiztyanirh Date: Tue, 10 Mar 2026 16:59:44 +0700 Subject: [PATCH] various fix: ubah initialform ke flat, tadinya nested (untuk patidt dan custodian) add payload builder saat save patient fix custodian tidak jalan di edit page fix dictionary usedictionaryform tidak jalan di page selain test hilangkan refnumtype di refnum fix bug di refnum saat insert data ke tempnum --- .../composable/use-dictionary-form.svelte.js | 2 +- .../composable/use-master-detail.svelte.js | 2 +- .../composable/use-patient-form.svelte.js | 8 +- .../test/config/test-form-config.js | 15 +- .../test/page/tabs/calculation09032026.svelte | 128 --- .../dictionary/test/page/tabs/ref-num.svelte | 7 + .../admission/page/create-page copy.svelte | 62 -- .../patient/list/config/patient-config.js | 4 +- .../list/config/patient-form-config.js | 38 +- .../patient/list/modal/custodian-modal.svelte | 16 +- .../patient/list/page/create-page copy.svelte | 403 --------- .../patient/list/page/create-page.svelte | 6 +- .../patient/list/page/edit-page copy.svelte | 523 ------------ .../patient/list/page/edit-page.svelte | 39 +- .../reusable/patient-form-renderer.svelte | 15 +- .../dictionary-form-renderer09032026.svelte | 770 ------------------ src/lib/components/reusable/form/test.js | 290 ------- src/routes/patient/list/+page.svelte | 12 +- 18 files changed, 81 insertions(+), 2259 deletions(-) delete mode 100644 src/lib/components/dictionary/test/page/tabs/calculation09032026.svelte delete mode 100644 src/lib/components/patient/admission/page/create-page copy.svelte delete mode 100644 src/lib/components/patient/list/page/create-page copy.svelte delete mode 100644 src/lib/components/patient/list/page/edit-page copy.svelte delete mode 100644 src/lib/components/reusable/form/dictionary-form-renderer09032026.svelte delete mode 100644 src/lib/components/reusable/form/test.js diff --git a/src/lib/components/composable/use-dictionary-form.svelte.js b/src/lib/components/composable/use-dictionary-form.svelte.js index 03999be..94a93ea 100644 --- a/src/lib/components/composable/use-dictionary-form.svelte.js +++ b/src/lib/components/composable/use-dictionary-form.svelte.js @@ -25,7 +25,7 @@ // } // } -export function useDictionaryForm(formState, getActiveFormStates) { +export function useDictionaryForm(formState, getActiveFormStates = () => ({})) { let hasErrors = $derived.by(() => { const mainHasError = Object.values(formState.errors).some(v => v !== null); const activeHasError = Object.values(getActiveFormStates()).some(fs => diff --git a/src/lib/components/composable/use-master-detail.svelte.js b/src/lib/components/composable/use-master-detail.svelte.js index a063547..d043226 100644 --- a/src/lib/components/composable/use-master-detail.svelte.js +++ b/src/lib/components/composable/use-master-detail.svelte.js @@ -29,7 +29,7 @@ export function useMasterDetail(options = {}) { const isDirty = $derived( JSON.stringify(formState.form) !== JSON.stringify(formSnapshot) ); -// $inspect(formSnapshot) +// $inspect(formState.form) async function select(item) { mode = "view"; diff --git a/src/lib/components/composable/use-patient-form.svelte.js b/src/lib/components/composable/use-patient-form.svelte.js index 152ab20..5c16b5a 100644 --- a/src/lib/components/composable/use-patient-form.svelte.js +++ b/src/lib/components/composable/use-patient-form.svelte.js @@ -40,17 +40,17 @@ export function usePatientForm(formState, patientSchema) { } function validateIdentifier() { - const identifierType = formState.form.PatIdt?.IdentifierType; - const identifierValue = formState.form.PatIdt?.Identifier; + const identifierType = formState.form.PatIdt_IdentifierType; + const identifierValue = formState.form.PatIdt_Identifier; if (!identifierType || !identifierValue) { - formState.errors['PatIdt.Identifier'] = null; + formState.errors.PatIdt_Identifier = null; return; } const schema = getIdentifierValidation(identifierType); const result = schema.safeParse(identifierValue); - formState.errors['PatIdt.Identifier'] = result.success ? null : result.error.issues[0].message; + formState.errors.PatIdt_Identifier = result.success ? null : result.error.issues[0].message; } function getIdentifierValidation(identifierType) { diff --git a/src/lib/components/dictionary/test/config/test-form-config.js b/src/lib/components/dictionary/test/config/test-form-config.js index eb7359e..19a9f25 100644 --- a/src/lib/components/dictionary/test/config/test-form-config.js +++ b/src/lib/components/dictionary/test/config/test-form-config.js @@ -726,18 +726,19 @@ export const refNumFormFields = [ { type: 'row', columns: [ - { - key: 'NumRefType', - label: 'Reference Type', - required: false, - type: 'text' - }, + // { + // key: 'NumRefType', + // label: 'Reference Type', + // required: false, + // type: 'text' + // }, { key: 'RangeType', label: 'Range Type', required: false, type: 'select', - optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/range_type` + optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/range_type`, + fullWidth: false } ] }, diff --git a/src/lib/components/dictionary/test/page/tabs/calculation09032026.svelte b/src/lib/components/dictionary/test/page/tabs/calculation09032026.svelte deleted file mode 100644 index a64e426..0000000 --- a/src/lib/components/dictionary/test/page/tabs/calculation09032026.svelte +++ /dev/null @@ -1,128 +0,0 @@ - - -
- -
\ No newline at end of file diff --git a/src/lib/components/dictionary/test/page/tabs/ref-num.svelte b/src/lib/components/dictionary/test/page/tabs/ref-num.svelte index ada7670..7654e46 100644 --- a/src/lib/components/dictionary/test/page/tabs/ref-num.svelte +++ b/src/lib/components/dictionary/test/page/tabs/ref-num.svelte @@ -54,7 +54,14 @@ } function resetForm() { + const currentRefType = props.refNumState.form.NumRefType; + props.refNumState.reset?.(); + + if (currentRefType) { + props.refNumState.form.NumRefType = currentRefType; + } + joinFields = { AgeStart: { DD: '', MM: '', YY: '' }, AgeEnd: { DD: '', MM: '', YY: '' } diff --git a/src/lib/components/patient/admission/page/create-page copy.svelte b/src/lib/components/patient/admission/page/create-page copy.svelte deleted file mode 100644 index e2d3153..0000000 --- a/src/lib/components/patient/admission/page/create-page copy.svelte +++ /dev/null @@ -1,62 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/lib/components/patient/list/config/patient-config.js b/src/lib/components/patient/list/config/patient-config.js index f02ec6c..2c2dcea 100644 --- a/src/lib/components/patient/list/config/patient-config.js +++ b/src/lib/components/patient/list/config/patient-config.js @@ -52,7 +52,7 @@ export const detailSections = [ isGroup: true, class: "grid grid-cols-2", fields: [ - { key: "City", label: "City" }, + { key: "CityLabel", label: "City" }, { key: "ZIP", label: "ZIP" }, ], }, @@ -62,7 +62,7 @@ export const detailSections = [ isGroup: true, class: "grid grid-cols-2", fields: [ - { key: "Province", label: "Province" }, + { key: "ProvinceLabel", label: "Province" }, { key: "CountryLabel", label: "Country" }, ], }, diff --git a/src/lib/components/patient/list/config/patient-form-config.js b/src/lib/components/patient/list/config/patient-form-config.js index 9b9862a..de396d8 100644 --- a/src/lib/components/patient/list/config/patient-form-config.js +++ b/src/lib/components/patient/list/config/patient-form-config.js @@ -20,10 +20,8 @@ export const patientSchema = z.object({ export const patientInitialForm = { PatientID: "", AlternatePID: "", - PatIdt: { - IdentifierType: "", - Identifier: "" - }, + PatIdt_IdentifierType: "", + PatIdt_Identifier: "", NameFirst: "", Prefix: "", Sex: "", @@ -32,10 +30,8 @@ export const patientInitialForm = { NameMaiden: "", MaritalStatus: "", NameLast: "", - Custodian: { - InternalPID: "", - PatientID: "" - }, + Custodian_InternalPID: "", + Custodian_PatientID: "", Ethnic: "", Suffix: "", PlaceOfBirth: "", @@ -69,7 +65,7 @@ export const patientDefaultErrors = { Birthdate: "Required", EmailAddress1: null, EmailAddress2: null, - 'PatIdt.Identifier': null, + PatIdt_Identifier: null, Phone: null, MobilePhone: null, }; @@ -95,7 +91,7 @@ export const patientFormFields = [ type: "text" }, { - key: "PatIdt.Identifier", + key: "PatIdt_Identifier", label: "Identifier", required: false, type: "identity", @@ -310,4 +306,26 @@ export function getPatientFormActions(handlers) { onClick: handlers.clearForm, }, ]; +} + +export function buildPatientPayload(form) { + const { + PatIdt_IdentifierType, + PatIdt_Identifier, + Custodian_InternalPID, + Custodian_PatientID, + ...rest + } = form; + + return { + ...rest, + PatIdt: { + IdentifierType: PatIdt_IdentifierType, + Identifier: PatIdt_Identifier + }, + Custodian: { + InternalPID: Custodian_InternalPID, + PatientID: Custodian_PatientID + } + }; } \ No newline at end of file diff --git a/src/lib/components/patient/list/modal/custodian-modal.svelte b/src/lib/components/patient/list/modal/custodian-modal.svelte index 75a17a4..e607152 100644 --- a/src/lib/components/patient/list/modal/custodian-modal.svelte +++ b/src/lib/components/patient/list/modal/custodian-modal.svelte @@ -28,17 +28,18 @@ isOpen = open; if (open) { - if (props.formState.form.Custodian) { + if (props.formState.form.Custodian_PatientID) { selectedPatient = { - InternalPID: props.formState.form.Custodian.InternalPID || null, - PatientID: props.formState.form.Custodian.PatientID || null, + InternalPID: props.formState.form.Custodian_InternalPID || null, + PatientID: props.formState.form.Custodian_PatientID || null, }; } } } function confirmCustodian() { - props.formState.form.Custodian = { ...selectedPatient }; + props.formState.form.Custodian_InternalPID = selectedPatient.InternalPID + props.formState.form.Custodian_PatientID = selectedPatient.PatientID selectedPatient = { InternalPID: null, PatientID: null }; isOpen = false; @@ -46,7 +47,12 @@ function populateCustodian() { custodianModalMode.mode = mode; - setSelectedPatient(editPatientForm.Custodian); + if (props.formState.form.Custodian_PatientID) { + selectedPatient = { + InternalPID: props.formState.form.Custodian_InternalPID, + PatientID: props.formState.form.Custodian_PatientID, + } + } } function togglePatientSelection(patient) { diff --git a/src/lib/components/patient/list/page/create-page copy.svelte b/src/lib/components/patient/list/page/create-page copy.svelte deleted file mode 100644 index a758f72..0000000 --- a/src/lib/components/patient/list/page/create-page copy.svelte +++ /dev/null @@ -1,403 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/lib/components/patient/list/page/create-page.svelte b/src/lib/components/patient/list/page/create-page.svelte index 79c7934..a9ee608 100644 --- a/src/lib/components/patient/list/page/create-page.svelte +++ b/src/lib/components/patient/list/page/create-page.svelte @@ -4,6 +4,7 @@ import PatientFormRenderer from "$lib/components/patient/reusable/patient-form-renderer.svelte"; import { toast } from "svelte-sonner"; import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte"; + import { buildPatientPayload } from "$lib/components/patient/list/config/patient-form-config"; let props = $props(); @@ -29,13 +30,14 @@ showConfirm = true; } } - +// $inspect(formState.form) function confirmDiscard() { masterDetail.exitForm(true); } async function handleSave() { - const result = await formState.save(masterDetail.mode); + const payload = buildPatientPayload(formState.form); + const result = await formState.save(masterDetail.mode, payload); if (result.status === 'success') { toast('Patient Created!'); diff --git a/src/lib/components/patient/list/page/edit-page copy.svelte b/src/lib/components/patient/list/page/edit-page copy.svelte deleted file mode 100644 index 9b945a1..0000000 --- a/src/lib/components/patient/list/page/edit-page copy.svelte +++ /dev/null @@ -1,523 +0,0 @@ - - -{#snippet Fieldset({ key, label, required, type, optionsEndpoint, options, validateOn, dependsOn, endpointParamKey })} -
-
- - {#if required} - * - {/if} -
- -
- {#if type === "input" || type === "email" || type === "number"} - { - if (validateOn?.includes("input")) { - formState.validateField(key); - } - }} - onblur={() => { - if (validateOn?.includes("blur")) { - validateFieldAsync(key); - } - }} - /> - {:else if type === "date"} - { - formState.form[key] = dateStr; - if (validateOn?.includes("input")) { - formState.validateField(key, dateStr, false); - } - }} - /> - {:else if type === "datetime"} - - - {:else if type === "textarea"} - - {:else if type === "select"} - {@const selectedLabel = formState.selectOptions[key]?.find(opt => opt.value === formState.form[key])?.label || "Choose"} - {@const filteredOptions = getFilteredOptions(key)} - { - formState.form[key] = val; - if (validateOn?.includes("input")) { - formState.validateField(key, formState.form[key], false); - } - if (key === "Province") { - formState.form.City = ""; - formState.selectOptions.City = []; - formState.lastFetched.City = null; - } - }} - onOpenChange={(open) => { - if (open && optionsEndpoint) { - formState.fetchOptions({ key, optionsEndpoint, dependsOn, endpointParamKey}, formState.form ); - } - }} - > - - {selectedLabel} - - -
- -
- {#if formState.loadingOptions[key]} - Loading... - {:else} - {#if !required} - - None - - {/if} - {#each filteredOptions as option} - - {option.label} - - {/each} - {/if} -
-
- {:else if type === "identity"} - {@const selectedLabel = formState.selectOptions[key]?.find(opt => opt.value === formState.form.PatIdt.IdentifierType)?.label || "Choose"} -
- { - if (open && optionsEndpoint) { - formState.fetchOptions({ key, optionsEndpoint}); - } - }} - onValueChange={(val) => { - formState.form.PatIdt = { - IdentifierType: val, - Identifier:'' - }; - }} - > - - {selectedLabel} - - - {#if formState.loadingOptions[key]} - Loading... - {:else} - {#if !required} - - None - - {/if} - {#each formState.selectOptions[key] ?? [] as option} - - {option.label} - - {/each} - {/if} - - - -
- {:else if type === "custodian"} -
- - -
- {:else if type === "linkto"} -
- - -
- {:else if type === "fileupload"} -
- - {#if Object.keys(uploadErrors).length > 0} -
- {#each Object.entries(uploadErrors) as [file, msg]} - {msg} - {/each} -
- {/if} -
- {:else} - - {/if} - -
- - {#if isChecking[key]} -
- - Checking... -
- {:else if formState.errors[key]} - - {formState.errors[key]} - - {/if} -
-
-
-{/snippet} - - -
- {#each patientFormFields as group} -
- {#if group.title} -
- {group.title} -
- {/if} - - {#each group.rows as row} -
- {#each row.columns as col} - {#if col.type === "group"} -
- {#each col.columns as child} - {@render Fieldset(child)} - {/each} -
- {:else} - {@render Fieldset(col)} - {/if} - {/each} -
- {/each} -
- {/each} -
-
- - \ No newline at end of file diff --git a/src/lib/components/patient/list/page/edit-page.svelte b/src/lib/components/patient/list/page/edit-page.svelte index b2f97b2..0ec37e9 100644 --- a/src/lib/components/patient/list/page/edit-page.svelte +++ b/src/lib/components/patient/list/page/edit-page.svelte @@ -6,10 +6,10 @@ import { untrack } from "svelte"; import { API } from "$lib/config/api"; import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte"; + import { buildPatientPayload } from "$lib/components/patient/list/config/patient-form-config"; let props = $props(); - // const { masterDetail, formFields, formActions, schema, initialForm, defaultError } = props.context; const { masterDetail, formFields, formActions, schema, initialForm } = props.context; const { formState } = masterDetail; @@ -30,38 +30,7 @@ } $effect(() => { - // const backendData = masterDetail?.selectedItem?.patient; - // if (!backendData) return; - untrack(() => { - // const formData = { - // ...backendData, - - // PatIdt: backendData.PatIdt ?? initialForm.PatIdt, - // LinkTo: backendData.LinkTo ?? [], - // Custodian: backendData.Custodian ?? initialForm.Custodian, - - // Sex: backendData.SexKey || backendData.Sex, - // Religion: backendData.ReligionKey || backendData.Religion, - // MaritalStatus: backendData.MaritalStatusKey || backendData.MaritalStatus, - // Ethnic: backendData.EthnicKey || backendData.Ethnic, - // Race: backendData.RaceKey || backendData.Race, - // Country: backendData.CountryKey || backendData.Country, - // DeathIndicator: backendData.DeathIndicatorKey || backendData.DeathIndicator, - // Province: backendData.ProvinceID || backendData.Province, - // City: backendData.CityID || backendData.City, - // }; - - // formState.setForm(formData); - - // Ensure PatIdt is always an object to prevent binding errors - // if (!formState.form.PatIdt) { - // formState.form.PatIdt = { - // IdentifierType: "", - // Identifier: "" - // }; - // } - formFields.forEach(group => { group.rows.forEach(row => { row.columns.forEach(col => { @@ -93,11 +62,11 @@ }); }); -// $inspect(masterDetail?.selectedItem?.patient) +$inspect(masterDetail?.selectedItem?.patient) // $inspect(formState.form) async function handleEdit() { - // console.log(formState.form); - const result = await formState.save(masterDetail.mode); + const payload = buildPatientPayload(formState.form); + const result = await formState.save(masterDetail.mode, payload); if (result.status === 'success') { console.log('Patient updated successfully'); diff --git a/src/lib/components/patient/reusable/patient-form-renderer.svelte b/src/lib/components/patient/reusable/patient-form-renderer.svelte index 24318e1..2be932a 100644 --- a/src/lib/components/patient/reusable/patient-form-renderer.svelte +++ b/src/lib/components/patient/reusable/patient-form-renderer.svelte @@ -220,19 +220,18 @@ {:else if type === "identity"} - {@const selectedLabel = formState.selectOptions[key]?.find(opt => opt.value === formState.form.PatIdt?.IdentifierType)?.label || "Choose"} + {@const selectedLabel = formState.selectOptions[key]?.find(opt => opt.value === formState.form.PatIdt_IdentifierType)?.label || "Choose"}
- { if (open && optionsEndpoint) { formState.fetchOptions({ key, optionsEndpoint}); } }} onValueChange={(val) => { - formState.form.PatIdt = { - IdentifierType: val, - Identifier:'' - }; + formState.form.PatIdt_IdentifierType = val; + formState.form.PatIdt_Identifier = '' + formState.errors.PatIdt_Identifier = null }} > @@ -253,11 +252,11 @@ {/if} - +
{:else if type === "custodian"}
- +
{:else if type === "linkto"} diff --git a/src/lib/components/reusable/form/dictionary-form-renderer09032026.svelte b/src/lib/components/reusable/form/dictionary-form-renderer09032026.svelte deleted file mode 100644 index e7689d9..0000000 --- a/src/lib/components/reusable/form/dictionary-form-renderer09032026.svelte +++ /dev/null @@ -1,770 +0,0 @@ - - -{#snippet Fieldset({ - key, - label, - required, - type, - optionsEndpoint, - options, - validateOn, - dependsOn, - endpointParamKey, - valueKey, - labelKey, - txtKey -})} -
-
- - {#if required} - * - {/if} -
- -
- {#if type === 'text'} - { - if (validateOn?.includes('input')) { - formState.validateField(key, formState.form[key], false); - } - }} - onblur={() => { - if (validateOn?.includes('blur')) { - validateFieldAsync(key, mode, originalData?.[key]); - } - }} - readonly={key === 'NumRefType' || key === 'TxtRefType' || key === 'Level'} - /> - {:else if type === 'email'} - { - if (validateOn?.includes('input')) { - formState.validateField(key, formState.form[key], false); - } - }} - onblur={() => { - if (validateOn?.includes('blur')) { - formState.validateField(key, formState.form[key], false); - } - }} - /> - {:else if type === 'number'} - { - if (validateOn?.includes('input')) { - formState.validateField(key, formState.form[key], false); - } - }} - onblur={() => { - if (validateOn?.includes('blur')) { - formState.validateField(key, formState.form[key], false); - } - }} - onkeydown={(e) => ['e', 'E', '+', '-'].includes(e.key) && e.preventDefault()} - /> - {:else if type === 'textarea'} -