diff --git a/src/lib/components/order/ordertest/config/ordertest-config.js b/src/lib/components/order/ordertest/config/ordertest-config.js index 619bf53..c1f6db0 100644 --- a/src/lib/components/order/ordertest/config/ordertest-config.js +++ b/src/lib/components/order/ordertest/config/ordertest-config.js @@ -49,7 +49,7 @@ export const detailSections = [ }, ]; -export function orderTestActions(masterDetail, selectedPatient) { +export function orderTestActions(masterDetail, selectedPatient, selectedVisit) { return [ { Icon: PlusIcon, diff --git a/src/lib/components/order/ordertest/config/ordertest-form-config.js b/src/lib/components/order/ordertest/config/ordertest-form-config.js index 26f1a46..1f6c2cd 100644 --- a/src/lib/components/order/ordertest/config/ordertest-form-config.js +++ b/src/lib/components/order/ordertest/config/ordertest-form-config.js @@ -5,11 +5,129 @@ import { cleanEmptyStrings } from "$lib/utils/cleanEmptyStrings"; export const orderTestSchema = z.object({}); -export const orderTestInitialForm = {}; +export const orderTestInitialForm = { + InternalOID: '', + OrderID: '', + PlacerID: '', + InternalPID: '', + SiteID: '', + PVADTID: '', + ReqApp: '', + Priority: '', + TrnDate: '', + EffDate: '', + Comment: '', + OrderAtt: '', + Tests: '', +}; export const orderTestDefaultErrors = {}; -export const orderTestFormFields = []; +export const orderTestFormFields = [ + { + title: "Order Details", + rows: [ + { + type: "row", + columns: [ + { + key: "SiteID", + label: "Site", + required: false, + type: "select", + optionsEndpoint: `${API.BASE_URL}${API.SITE}`, + valueKey: "SiteID", + labelKey: "SiteName", + }, + { + key: "Priority", + label: "Priority", + required: false, + type: "select", + optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/order_priority`, + }, + ] + }, + { + type: "row", + columns: [ + { + key: "PlacerID", + label: "Placer ID", + required: false, + type: "text", + }, + { + key: "ReqApp", + label: "Requested Application", + required: false, + type: "text", + }, + ] + }, + { + type: "row", + columns: [ + { + key: "TrnDate", + label: "Transaction Date", + required: false, + type: "date", + allowFuture: true + }, + { + key: "EffDate", + label: "Effective Date", + required: false, + type: "date", + allowFuture: true + }, + ] + }, + { + type: "row", + columns: [ + { + key: "Comment", + label: "Comment", + required: false, + type: "textarea", + }, + ] + }, + { + type: "row", + columns: [ + { + key: "OrderAtt", + label: "Attachment", + required: false, + type: "fileupload", + }, + ] + }, + ] + }, + { + title: "Tests", + rows: [ + { + type: "row", + columns: [ + { + key: "Tests", + label: "Search Test", + required: false, + type: "tests", + optionsEndpoint: `${API.BASE_URL}${API.TEST}`, + valueKey: 'TestSiteCode', + labelKey: (item) => `${item.TestSiteCode} - ${item.TestSiteName}`, + }, + ] + }, + ] + }, +]; export function getOrderTestFormActions(handlers) { return [ diff --git a/src/lib/components/order/ordertest/modal/search-param-modal.svelte b/src/lib/components/order/ordertest/modal/search-param-modal.svelte index ca93487..b2a8764 100644 --- a/src/lib/components/order/ordertest/modal/search-param-modal.svelte +++ b/src/lib/components/order/ordertest/modal/search-param-modal.svelte @@ -59,15 +59,14 @@ function handleButtonClick() { if (tempSelectedVisit) { - props.onConfirm(tempSelectedPatient); + props.onConfirm(tempSelectedPatient, tempSelectedVisit); tempSelectedVisit = null; } } - $inspect(tempSelectedVisit) -
+
@@ -113,7 +112,7 @@
{#if props.search.searchData && props.search.searchData.length > 0} -
+
@@ -154,25 +153,26 @@
{/if}
-
+
{#if isLoadingVisit} {:else if visitData && visitData.data?.length > 0}
- Active Visit Visit ID Visit Date + Location + Doctor {#each visitData.data as visit, i} handleCheckboxVisit(visit)} > e.stopPropagation()}> @@ -182,8 +182,23 @@ onCheckedChange={() => handleCheckboxVisit(visit)} /> - {visit.PVID} - {formatUTCDate(visit.PVCreateDate)} + {visit.PVID} + +
{formatUTCDate(visit.PVCreateDate).split(' ')[0]}
+
{formatUTCDate(visit.PVCreateDate).split(' ')[1]}
+
+ +
+ {visit.LocCode ?? "-"} + {visit.LocFull ?? "-"} +
+
+ +
+ {visit.AttDocContactCode ?? "-"} + {visit.AttDocFirstName} {visit.AttDocLastName} +
+
{/each}
diff --git a/src/lib/components/order/ordertest/page/create-page.svelte b/src/lib/components/order/ordertest/page/create-page.svelte index e69de29..fdedf93 100644 --- a/src/lib/components/order/ordertest/page/create-page.svelte +++ b/src/lib/components/order/ordertest/page/create-page.svelte @@ -0,0 +1,66 @@ + + + + + + + \ No newline at end of file diff --git a/src/lib/components/order/ordertest/page/master-page.svelte b/src/lib/components/order/ordertest/page/master-page.svelte index 4ade52e..d6923b1 100644 --- a/src/lib/components/order/ordertest/page/master-page.svelte +++ b/src/lib/components/order/ordertest/page/master-page.svelte @@ -14,17 +14,19 @@ import { page } from '$app/stores'; import { getPatient } from "$lib/components/patient/list/api/patient-list-api"; import { patientStore } from "$lib/components/patient/list/store/patient-store.svelte"; + import { formatUTCDate } from "$lib/utils/formatUTCDate"; let props = $props(); let selectedPatient = $state(null); + let selectedVisit = $state(null); let isLoading = $state(false); let searchData = $state([]); - +$inspect(selectedPatient) const search = useSearch(searchFields, searchParam); let actions = $derived.by(() => { - return orderTestActions(props.masterDetail, selectedPatient).map(action => { + return orderTestActions(props.masterDetail, selectedPatient, selectedVisit).map(action => { if (action.label === 'Search Parameters') { return { ...action, popoverContent: searchParamSnippet }; } @@ -34,12 +36,13 @@ let activeRowId = $state(null); - async function handlePatientConfirm(patient) { + async function handlePatientConfirm(patient, visit) { selectedPatient = patient; + selectedVisit = visit; isLoading = true; try { searchData = await getOrderList({ - InternalPID: selectedPatient.InternalPID + PVADTID: selectedVisit.PVADTID }); } catch (error) { console.error('Search failed:', error); @@ -61,31 +64,15 @@ SexLabel: `${patient.patient.SexLabel}`, }; console.log(transformedPatient); - // return { - // InternalPID: p.InternalPID, - // PatientID: p.PatientID, - // FullName: [p.NameFirst, p.NameMiddle, p.NameLast].filter(Boolean).join(' '), - // Sex: p.Sex, - // Birthdate: p.Birthdate, - // Email: p.Email, - // MobilePhone: p.MobilePhone, - // SexLabel: p.SexLabel, - // } handlePatientConfirm(transformedPatient); } onMount(() => { - // const pid = $page.url.searchParams.get('InternalPID'); //kalau dari url parameter - // if (pid) { - // loadPatientFromUrl(pid); - // } if (patientStore.pending) { loadPatientFromUrl(patientStore.pending.InternalPID); patientStore.pending = null; } }) - - $inspect(patientStore) {#snippet searchParamSnippet()} @@ -131,13 +118,18 @@ {selectedPatient?.FullName} + + {selectedPatient?.Age ? selectedPatient.Age.split(' ').slice(0, 2).join(' ') : 'N/A'} - + {selectedPatient?.SexLabel} - + {selectedPatient?.BirthdateConversion} +
- - {selectedPatient?.Birthdate} + + {selectedVisit?.PVID} - - {selectedPatient?.SexLabel} + + {formatUTCDate(selectedVisit?.PVCreateDate)}
@@ -147,7 +139,8 @@ {:else}
- + +
{/if}
diff --git a/src/lib/components/order/ordertest/table/ordertest-columns.js b/src/lib/components/order/ordertest/table/ordertest-columns.js index 842a4ab..d205cf3 100644 --- a/src/lib/components/order/ordertest/table/ordertest-columns.js +++ b/src/lib/components/order/ordertest/table/ordertest-columns.js @@ -7,8 +7,4 @@ export const orderTestColumns = [ accessorKey: "PlacerID", header: "Host ID", }, - { - accessorKey: "Priority", - header: "Priority", - }, ]; \ No newline at end of file diff --git a/src/lib/components/patient/reusable/form-page-container.svelte b/src/lib/components/patient/reusable/form-page-container.svelte index 055784e..07f35e4 100644 --- a/src/lib/components/patient/reusable/form-page-container.svelte +++ b/src/lib/components/patient/reusable/form-page-container.svelte @@ -15,7 +15,7 @@ -
+
{@render children()} diff --git a/src/lib/components/reusable/form/dictionary-form-renderer.svelte b/src/lib/components/reusable/form/dictionary-form-renderer.svelte index 45c636b..ee71c76 100644 --- a/src/lib/components/reusable/form/dictionary-form-renderer.svelte +++ b/src/lib/components/reusable/form/dictionary-form-renderer.svelte @@ -21,6 +21,7 @@ import CornerDownLeftIcon from '@lucide/svelte/icons/corner-down-left'; import CheckIcon from "@lucide/svelte/icons/check"; import XIcon from "@lucide/svelte/icons/x"; + import ReusableUpload from "$lib/components/reusable/reusable-upload.svelte"; let { formState, @@ -860,6 +861,10 @@ {isOn ? toggleOn.label : toggleOff.label}
+ {:else if type === "fileupload"} +
+ +
{:else} + import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js'; + import * as Select from '$lib/components/ui/select/index.js'; + import * as Popover from "$lib/components/ui/popover/index.js"; + import { Toggle } from '$lib/components/ui/toggle/index.js'; + import { Button } from '$lib/components/ui/button/index.js'; + import { Input } from '$lib/components/ui/input/index.js'; + import { Label } from '$lib/components/ui/label/index.js'; + import { Spinner } from '$lib/components/ui/spinner/index.js'; + import ReusableCalendar from '$lib/components/reusable/reusable-calendar.svelte'; + import { Badge } from '$lib/components/ui/badge/index.js'; + import * as InputGroup from '$lib/components/ui/input-group/index.js'; + import ChevronDownIcon from '@lucide/svelte/icons/chevron-down'; + import { Textarea } from '$lib/components/ui/textarea/index.js'; + import MoveLeftIcon from '@lucide/svelte/icons/move-left'; + import MoveRightIcon from '@lucide/svelte/icons/move-right'; + import BrushCleaningIcon from '@lucide/svelte/icons/brush-cleaning'; + import DeleteIcon from '@lucide/svelte/icons/delete'; + import Trash2Icon from '@lucide/svelte/icons/trash-2'; + import PlusIcon from '@lucide/svelte/icons/plus'; + import CornerDownLeftIcon from '@lucide/svelte/icons/corner-down-left'; + import CheckIcon from "@lucide/svelte/icons/check"; + import XIcon from "@lucide/svelte/icons/x"; + import ReusableUpload from "$lib/components/reusable/reusable-upload.svelte"; + import * as Table from "$lib/components/ui/table/index.js"; + import * as Tooltip from "$lib/components/ui/tooltip/index.js"; + + let { + formState, + formFields, + uploadErrors, + mode = 'create', + } = $props(); + + let searchQuery = $state({}); + + const leftGroups = $derived([formFields[0]]); + const rightGroups = $derived(formFields.slice(1)); + + function getFilteredOptions(key) { + const query = searchQuery[key] || ''; + if (!query) return formState.selectOptions?.[key] ?? []; + + return (formState.selectOptions?.[key] ?? []).filter((opt) => + opt.label.toLowerCase().includes(query.toLowerCase()) + ); + } + + async function initializeDefaultValues() { + for (const group of formFields) { + for (const row of group.rows) { + for (const col of row.columns) { + if (col.type === 'group') { + for (const child of col.columns) { + await handleDefaultValue(child); + } + } else { + await handleDefaultValue(col); + } + } + } + } + } + + async function handleDefaultValue(field) { + if (!field.defaultValue || !field.optionsEndpoint) return; + + await formState.fetchOptions(field, formState.form); + + if (!formState.form[field.key]) { + formState.form[field.key] = field.defaultValue; + } + } + + $effect(() => { + initializeDefaultValues(); + }); + + +{#snippet Fieldset({ + key, + label, + required, + type, + optionsEndpoint, + options, + validateOn, + allowFuture, + valueKey, + labelKey, +})} +
+
+ + {#if required} + * + {/if} + {#if key === 'FormulaCode' && formState.form.FormulaInput?.length} + {@const inputStatus = onGetErrorStatus?.()} +
+ Must included : + +
+ {#each inputStatus as item (item.value)} + + {item.value} + + {/each} +
+
+ {/if} +
+ +
+ {#if type === 'text'} + { + if (validateOn?.includes('input')) { + formState.validateField(key, formState.form[key], false); + } + }} + /> + {:else if type === 'textarea'} +