mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-28 15:55:54 +07:00
initial ordertest page & redirect function
This commit is contained in:
parent
11db828e41
commit
1ddaa8e29f
@ -18,7 +18,6 @@ function cleanQuery(searchQuery) {
|
|||||||
export async function getById(endpoint, id, returnAll = false) {
|
export async function getById(endpoint, id, returnAll = false) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API.BASE_URL}${endpoint}/${id}`);
|
const res = await fetch(`${API.BASE_URL}${endpoint}/${id}`);
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const error = await res.json();
|
const error = await res.json();
|
||||||
console.error('API Error:', error);
|
console.error('API Error:', error);
|
||||||
@ -45,10 +44,9 @@ export async function searchWithParams(endpoint, searchQuery) {
|
|||||||
const url = params
|
const url = params
|
||||||
? `${API.BASE_URL}${endpoint}?${params}`
|
? `${API.BASE_URL}${endpoint}?${params}`
|
||||||
: `${API.BASE_URL}${endpoint}`;
|
: `${API.BASE_URL}${endpoint}`;
|
||||||
console.log(`params: ${params}`);
|
|
||||||
console.log(`url: ${url}`);
|
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
console.log(url);
|
||||||
return data.data || [];
|
return data.data || [];
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Search Error:', err);
|
console.error('Search Error:', err);
|
||||||
|
|||||||
@ -47,8 +47,8 @@
|
|||||||
icon: ReceiptTextIcon,
|
icon: ReceiptTextIcon,
|
||||||
submenus: [
|
submenus: [
|
||||||
{
|
{
|
||||||
title: "Test Order",
|
title: "Order Test",
|
||||||
url: "/testorder",
|
url: "/ordertest",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -53,7 +53,6 @@ export function useMasterDetail(options = {}) {
|
|||||||
|
|
||||||
async function select(item) {
|
async function select(item) {
|
||||||
mode = "view";
|
mode = "view";
|
||||||
|
|
||||||
if (onSelect) {
|
if (onSelect) {
|
||||||
isLoadingDetail = true;
|
isLoadingDetail = true;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -5,18 +5,18 @@ export async function searchParam(searchQuery) {
|
|||||||
return await searchWithParams(API.PATIENTS, searchQuery)
|
return await searchWithParams(API.PATIENTS, searchQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getVisitList(searchQuery) {
|
export async function getOrderList(searchQuery) {
|
||||||
return await getById(API.VISITLIST, searchQuery, true)
|
return await searchWithParams(API.ORDERTEST, searchQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getVisit(searchQuery) {
|
export async function getOrder(searchQuery) {
|
||||||
return await getById(API.PATVISIT, searchQuery)
|
return await getById(API.ORDERTEST, searchQuery, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createOrder(newOrderForm) {
|
export async function createOrder(newOrderForm) {
|
||||||
return await create(API.ORDER, newOrderForm)
|
return await create(API.ORDERTEST, newOrderForm)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function editOrder(editOrderForm) {
|
export async function editOrder(editOrderForm) {
|
||||||
return await update(API.ORDER, editOrderForm)
|
return await update(API.ORDERTEST, editOrderForm)
|
||||||
}
|
}
|
||||||
@ -23,8 +23,8 @@ export const searchFields = [
|
|||||||
type: "date"
|
type: "date"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "Identifier",
|
key: "OrderID",
|
||||||
label: "Identifier",
|
label: "Order ID",
|
||||||
type: "text"
|
type: "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -39,9 +39,17 @@ export const searchFields = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const detailSections = [];
|
export const detailSections = [
|
||||||
|
{
|
||||||
|
class: "grid grid-cols-2 gap-4 items-center",
|
||||||
|
fields: [
|
||||||
|
{ key: "OrderID", label: "Order ID" },
|
||||||
|
{ key: "PlacerID", label: "Host ID" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export function orderActions(masterDetail, selectedPatient) {
|
export function orderTestActions(masterDetail, selectedPatient) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
Icon: PlusIcon,
|
Icon: PlusIcon,
|
||||||
@ -3,15 +3,15 @@ import EraserIcon from "@lucide/svelte/icons/eraser";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { cleanEmptyStrings } from "$lib/utils/cleanEmptyStrings";
|
import { cleanEmptyStrings } from "$lib/utils/cleanEmptyStrings";
|
||||||
|
|
||||||
export const orderSchema = z.object({});
|
export const orderTestSchema = z.object({});
|
||||||
|
|
||||||
export const orderInitialForm = {};
|
export const orderTestInitialForm = {};
|
||||||
|
|
||||||
export const orderDefaultErrors = {};
|
export const orderTestDefaultErrors = {};
|
||||||
|
|
||||||
export const orderFormFields = [];
|
export const orderTestFormFields = [];
|
||||||
|
|
||||||
export function getOrderFormActions(handlers) {
|
export function getOrderTestFormActions(handlers) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
Icon: EraserIcon,
|
Icon: EraserIcon,
|
||||||
@ -7,13 +7,14 @@
|
|||||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
import * as Select from "$lib/components/ui/select/index.js";
|
import * as Select from "$lib/components/ui/select/index.js";
|
||||||
import { useSearch } from "$lib/components/composable/use-search.svelte";
|
import { useSearch } from "$lib/components/composable/use-search.svelte";
|
||||||
import { searchFields } from "$lib/components/order/config/order-config";
|
import { searchFields } from "$lib/components/order/ordertest/config/ordertest-config";
|
||||||
import { searchParam } from "$lib/components/order/api/order-api";
|
import { searchParam } from "$lib/components/order/ordertest/api/ordertest-api";
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import { Checkbox } from "$lib/components/ui/checkbox/index.js";
|
import { Checkbox } from "$lib/components/ui/checkbox/index.js";
|
||||||
import * as Popover from "$lib/components/ui/popover/index.js";
|
import * as Popover from "$lib/components/ui/popover/index.js";
|
||||||
|
|
||||||
let { selectedPatient = $bindable(null), ...props } = $props();
|
// let { selectedPatient = $bindable(null), ...props } = $props();
|
||||||
|
let props = $props();
|
||||||
let tempSelectedPatient = $state(null);
|
let tempSelectedPatient = $state(null);
|
||||||
|
|
||||||
let activeRowId = $state(null);
|
let activeRowId = $state(null);
|
||||||
@ -26,7 +27,6 @@
|
|||||||
|
|
||||||
function handleButtonClick() {
|
function handleButtonClick() {
|
||||||
if (tempSelectedPatient) {
|
if (tempSelectedPatient) {
|
||||||
selectedPatient = tempSelectedPatient;
|
|
||||||
props.onConfirm(tempSelectedPatient);
|
props.onConfirm(tempSelectedPatient);
|
||||||
|
|
||||||
tempSelectedPatient = null;
|
tempSelectedPatient = null;
|
||||||
@ -34,7 +34,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="w-full h-110">
|
<div class="w-180 h-110">
|
||||||
<div class="flex gap-4 h-full">
|
<div class="flex gap-4 h-full">
|
||||||
<div class="flex flex-col w-1/3 h-full">
|
<div class="flex flex-col w-1/3 h-full">
|
||||||
<div class="flex-1 overflow-y-auto min-h-0 space-y-2 px-2">
|
<div class="flex-1 overflow-y-auto min-h-0 space-y-2 px-2">
|
||||||
@ -69,7 +69,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end gap-2 mt-4">
|
<div class="flex justify-end gap-2 mt-4">
|
||||||
<Button variant="outline" size="sm" class="cursor-pointer" onclick={props.search.handleReset}>Reset</Button>
|
<Button variant="outline" size="sm" class="cursor-pointer" onclick={props.search.handleReset}>Reset</Button>
|
||||||
<Button size="sm" class="cursor-pointer" onclick={props.search.handleSearch} disabled={props.search.isLoading}>
|
<Button size="sm" class="cursor-pointer" onclick={() => props.search.handleSearch()} disabled={props.search.isLoading}>
|
||||||
{#if props.search.isLoading}
|
{#if props.search.isLoading}
|
||||||
<Spinner />
|
<Spinner />
|
||||||
{:else}
|
{:else}
|
||||||
@ -118,12 +118,12 @@
|
|||||||
<div class="flex justify-end gap-2 mt-4 w-full">
|
<div class="flex justify-end gap-2 mt-4 w-full">
|
||||||
<Popover.Close>
|
<Popover.Close>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
disabled={isPatientEmpty}
|
disabled={isPatientEmpty}
|
||||||
onclick={handleButtonClick}
|
onclick={handleButtonClick}
|
||||||
>
|
>
|
||||||
Select Patient
|
Select Patient
|
||||||
</Button>
|
</Button>
|
||||||
</Popover.Close>
|
</Popover.Close>
|
||||||
</div>
|
</div>
|
||||||
@ -133,10 +133,5 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col w-1/3 h-full">
|
|
||||||
<div class="flex h-full">
|
|
||||||
<ReusableEmpty desc="Try searching from search parameters"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
157
src/lib/components/order/ordertest/page/master-page.svelte
Normal file
157
src/lib/components/order/ordertest/page/master-page.svelte
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
<script>
|
||||||
|
import { orderTestColumns } from "$lib/components/order/ordertest/table/ordertest-columns";
|
||||||
|
import { searchParam, getOrderList } from "$lib/components/order/ordertest/api/ordertest-api";
|
||||||
|
import { useSearch } from "$lib/components/composable/use-search.svelte";
|
||||||
|
import { searchFields, orderTestActions } from "$lib/components/order/ordertest/config/ordertest-config";
|
||||||
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
|
import ReusableSearchParam from "$lib/components/reusable/reusable-search-param.svelte";
|
||||||
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
|
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||||
|
import SearchParamModal from "$lib/components/order/ordertest/modal/search-param-modal.svelte";
|
||||||
|
import ClipboardXIcon from "@lucide/svelte/icons/clipboard-x";
|
||||||
|
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
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";
|
||||||
|
|
||||||
|
let props = $props();
|
||||||
|
|
||||||
|
let selectedPatient = $state(null);
|
||||||
|
let isLoading = $state(false);
|
||||||
|
let searchData = $state([]);
|
||||||
|
|
||||||
|
const search = useSearch(searchFields, searchParam);
|
||||||
|
|
||||||
|
let actions = $derived.by(() => {
|
||||||
|
return orderTestActions(props.masterDetail, selectedPatient).map(action => {
|
||||||
|
if (action.label === 'Search Parameters') {
|
||||||
|
return { ...action, popoverContent: searchParamSnippet };
|
||||||
|
}
|
||||||
|
return action;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let activeRowId = $state(null);
|
||||||
|
|
||||||
|
async function handlePatientConfirm(patient) {
|
||||||
|
selectedPatient = patient;
|
||||||
|
isLoading = true;
|
||||||
|
try {
|
||||||
|
searchData = await getOrderList({
|
||||||
|
InternalPID: selectedPatient.InternalPID
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Search failed:', error);
|
||||||
|
} finally {
|
||||||
|
isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadPatientFromUrl(pid) {
|
||||||
|
const patient = await getPatient(pid);
|
||||||
|
const transformedPatient = {
|
||||||
|
InternalPID: `${patient.patient.InternalPID}`,
|
||||||
|
PatientID: `${patient.patient.PatientID}`,
|
||||||
|
FullName: `${patient.patient.NameFirst} ${patient.patient.NameMiddle} ${patient.patient.NameLast}`.replace(/\s+/g, ' ').trim(),
|
||||||
|
Sex: `${patient.patient.Sex}`,
|
||||||
|
Birthdate: `${patient.patient.Birthdate}`,
|
||||||
|
Email: `${patient.patient.EmailAddress1}`,
|
||||||
|
MobilePhone: `${patient.patient.MobilePhone}`,
|
||||||
|
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)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#snippet searchParamSnippet()}
|
||||||
|
<SearchParamModal {search} {searchFields} onConfirm={handlePatientConfirm}/>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
onclick={() => props.masterDetail.isFormMode && props.masterDetail.exitForm()}
|
||||||
|
onkeydown={(e) => e.key === 'Enter' && props.masterDetail.isFormMode && props.masterDetail.exitForm()}
|
||||||
|
class={`
|
||||||
|
${props.masterDetail.isMobile ? "w-full" : props.masterDetail.isFormMode ? "w-[3%] cursor-pointer" : "w-[35%]"}
|
||||||
|
transition-all duration-300 flex flex-col items-center p-2 h-full overflow-y-auto
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
||||||
|
{#if props.masterDetail.isFormMode}
|
||||||
|
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||||
|
<MoveLeftIcon />
|
||||||
|
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||||
|
{#each "ORDER TEST".split("") as c}
|
||||||
|
<span class="leading-none">{c}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if !props.masterDetail.isFormMode}
|
||||||
|
<div role="button" tabindex="0" class="flex flex-1 flex-col gap-2" onclick={(e) => e.stopPropagation()} onkeydown={(e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<TopbarWrapper {actions}/>
|
||||||
|
{#if selectedPatient}
|
||||||
|
<div class="flex justify-between w-full rounded-md border p-2">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="font-bold tracking-wide underline">
|
||||||
|
{selectedPatient?.PatientID}
|
||||||
|
</span>
|
||||||
|
<span class="font-semibold">
|
||||||
|
{selectedPatient?.FullName}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col justify-end items-end">
|
||||||
|
<span class="font-bold tracking-wide">
|
||||||
|
{selectedPatient?.Birthdate}
|
||||||
|
</span>
|
||||||
|
<span class="font-semibold">
|
||||||
|
{selectedPatient?.SexLabel}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div class="flex-1 w-full h-full">
|
||||||
|
{#if searchData?.length > 0}
|
||||||
|
<ReusableDataTable data={searchData} columns={orderTestColumns} handleRowClick={props.masterDetail.select} {activeRowId} rowIdKey="InternalOID" offset="7"/>
|
||||||
|
{:else}
|
||||||
|
<div class="flex h-full">
|
||||||
|
<ReusableEmpty icon={ClipboardXIcon} desc="Try searching from search parameters"/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
72
src/lib/components/order/ordertest/page/view-page.svelte
Normal file
72
src/lib/components/order/ordertest/page/view-page.svelte
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<script>
|
||||||
|
import { formatUTCDate } from "$lib/utils/formatUTCDate";
|
||||||
|
import { detailSections, viewActions } from "$lib/components/order/ordertest/config/ordertest-config";
|
||||||
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
|
|
||||||
|
let props = $props();
|
||||||
|
|
||||||
|
const { masterDetail, formFields, formActions, schema } = props.context;
|
||||||
|
|
||||||
|
let order = $derived(masterDetail?.selectedItem?.data);
|
||||||
|
|
||||||
|
const handlers = {
|
||||||
|
editOrder: () => masterDetail.enterEdit("data"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const actions = viewActions(handlers);
|
||||||
|
|
||||||
|
function getFieldValue(field) {
|
||||||
|
if (!order) return "-";
|
||||||
|
|
||||||
|
if (field.keys) {
|
||||||
|
return field.keys
|
||||||
|
.map(k => field.parentKey ? order[field.parentKey]?.[k] : order[k])
|
||||||
|
.filter(val => val && val.trim() !== "")
|
||||||
|
.join(" / ");
|
||||||
|
}
|
||||||
|
|
||||||
|
return field.parentKey ? order[field.parentKey]?.[field.key] : order[field.key];
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#snippet Fieldset({ value, label, isUTCDate = false })}
|
||||||
|
<div class="space-y-1.5">
|
||||||
|
<dt class="text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||||
|
{label}
|
||||||
|
</dt>
|
||||||
|
<dd class="text-sm font-medium">
|
||||||
|
{#if isUTCDate}
|
||||||
|
{formatUTCDate(value)}
|
||||||
|
{:else}
|
||||||
|
{value ?? "-"}
|
||||||
|
{/if}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
{#if masterDetail.selectedItem}
|
||||||
|
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||||
|
<TopbarWrapper
|
||||||
|
title={masterDetail.selectedItem.data.OrderID}
|
||||||
|
{actions}
|
||||||
|
/>
|
||||||
|
<div class="flex-1 min-h-0 overflow-y-auto space-y-4">
|
||||||
|
{#each detailSections as section}
|
||||||
|
<div class="p-4">
|
||||||
|
<div class={section.class}>
|
||||||
|
{#each section.fields as field}
|
||||||
|
{@render Fieldset({
|
||||||
|
label: field.label,
|
||||||
|
value: getFieldValue(field),
|
||||||
|
isUTCDate: field.isUTCDate
|
||||||
|
})}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<ReusableEmpty desc="Select a visit to see details"/>
|
||||||
|
{/if}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
export const orderTestColumns = [
|
||||||
|
{
|
||||||
|
accessorKey: "OrderID",
|
||||||
|
header: "Order ID"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "PlacerID",
|
||||||
|
header: "Host ID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "Priority",
|
||||||
|
header: "Priority",
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -1,90 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { orderColumns } from "$lib/components/order/table/order-columns";
|
|
||||||
import { searchParam, getVisitList } from "$lib/components/order/api/order-api";
|
|
||||||
import { useSearch } from "$lib/components/composable/use-search.svelte";
|
|
||||||
import { searchFields, orderActions } from "$lib/components/order/config/order-config";
|
|
||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
|
||||||
import ReusableSearchParam from "$lib/components/reusable/reusable-search-param.svelte";
|
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
|
||||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
|
||||||
import SearchParamModal from "$lib/components/order/modal/search-param-modal.svelte";
|
|
||||||
|
|
||||||
let props = $props();
|
|
||||||
|
|
||||||
let selectedPID = $state(null);
|
|
||||||
let selectedPatient = $state(null);
|
|
||||||
let tableData = $state([]);
|
|
||||||
let isLoading = $state(false);
|
|
||||||
let searchData = $state([]);
|
|
||||||
|
|
||||||
const search = useSearch(searchFields, searchParam);
|
|
||||||
|
|
||||||
let actions = $derived.by(() => {
|
|
||||||
return orderActions(props.masterDetail, selectedPatient).map(action => {
|
|
||||||
if (action.label === 'Search Parameters') {
|
|
||||||
return { ...action, popoverContent: searchParamSnippet };
|
|
||||||
}
|
|
||||||
return action;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
let activeRowId = $state(null);
|
|
||||||
|
|
||||||
async function handlePatientConfirm(patient) {
|
|
||||||
selectedPatient = patient;
|
|
||||||
selectedPID = patient.InternalPID;
|
|
||||||
isLoading = true;
|
|
||||||
try {
|
|
||||||
searchData = await getVisitList(patient.InternalPID);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Search failed:', error);
|
|
||||||
} finally {
|
|
||||||
isLoading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#snippet searchParamSnippet()}
|
|
||||||
<SearchParamModal {search} {searchFields} bind:selectedPatient onConfirm={handlePatientConfirm}/>
|
|
||||||
{/snippet}
|
|
||||||
|
|
||||||
<div
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
onclick={() => props.masterDetail.isFormMode && props.masterDetail.exitForm()}
|
|
||||||
onkeydown={(e) => e.key === 'Enter' && props.masterDetail.isFormMode && props.masterDetail.exitForm()}
|
|
||||||
class={`
|
|
||||||
${props.masterDetail.isMobile ? "w-full" : props.masterDetail.isFormMode ? "w-[3%] cursor-pointer" : "w-[35%]"}
|
|
||||||
transition-all duration-300 flex flex-col items-center p-2 h-full overflow-y-auto
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
|
||||||
{#if props.masterDetail.isFormMode}
|
|
||||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
|
||||||
{#each "ADMISSION".split("") as c}
|
|
||||||
<span class="leading-none">{c}</span>
|
|
||||||
{/each}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if !props.masterDetail.isFormMode}
|
|
||||||
<div role="button" tabindex="0" class="flex flex-1 flex-col" onclick={(e) => e.stopPropagation()} onkeydown={(e) => {
|
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
<TopbarWrapper {actions}/>
|
|
||||||
<div class="flex-1 w-full h-full">
|
|
||||||
{#if searchData?.data?.length > 0}
|
|
||||||
<ReusableDataTable data={searchData.data} columns={visitColumns} handleRowClick={props.masterDetail.select} {activeRowId} rowIdKey="InternalPVID"/>
|
|
||||||
{:else}
|
|
||||||
<div class="flex h-full">
|
|
||||||
<ReusableEmpty desc="Try searching from search parameters"/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
export const orderColumns = [
|
|
||||||
{
|
|
||||||
accessorKey: "InternalPVID",
|
|
||||||
header: "Patient ID"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "PVID",
|
|
||||||
header: "Visit ID",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "EpisodeID",
|
|
||||||
header: "Episode ID",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
@ -7,9 +7,8 @@
|
|||||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
import * as Select from "$lib/components/ui/select/index.js";
|
import * as Select from "$lib/components/ui/select/index.js";
|
||||||
import { useSearch } from "$lib/components/composable/use-search.svelte";
|
import { useSearch } from "$lib/components/composable/use-search.svelte";
|
||||||
import { searchFields } from "../config/admission-config";
|
import { searchFields } from "$lib/components/patient/admission/config/admission-config";
|
||||||
import { searchParam } from "../api/patient-admission-api";
|
import { searchParam } from "$lib/components/patient/admission/api/patient-admission-api";
|
||||||
import { patientColumns } from "../table/patient-colums";
|
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import { Checkbox } from "$lib/components/ui/checkbox/index.js";
|
import { Checkbox } from "$lib/components/ui/checkbox/index.js";
|
||||||
import * as Popover from "$lib/components/ui/popover/index.js";
|
import * as Popover from "$lib/components/ui/popover/index.js";
|
||||||
|
|||||||
@ -58,7 +58,6 @@
|
|||||||
transition-all duration-300 flex flex-col items-center p-2 h-full overflow-y-auto
|
transition-all duration-300 flex flex-col items-center p-2 h-full overflow-y-auto
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
|
|
||||||
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
||||||
{#if props.masterDetail.isFormMode}
|
{#if props.masterDetail.isFormMode}
|
||||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
||||||
@ -98,8 +97,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<div class="flex-1 w-full h-full">
|
<div class="flex-1 w-full h-full">
|
||||||
{#if searchData?.data?.length > 0}
|
{#if searchData?.data?.length > 0}
|
||||||
<ReusableDataTable data={searchData.data} columns={visitColumns} handleRowClick={props.masterDetail.select} {activeRowId} rowIdKey="InternalPVID"
|
<ReusableDataTable data={searchData.data} columns={visitColumns} handleRowClick={props.masterDetail.select} {activeRowId} rowIdKey="InternalPVID" offset="7"/>
|
||||||
offset="7"/>
|
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex h-full">
|
<div class="flex h-full">
|
||||||
<ReusableEmpty title="No Visit" desc={selectedPatient ? "No visit available" : "Try searching from search parameters"}/>
|
<ReusableEmpty title="No Visit" desc={selectedPatient ? "No visit available" : "Try searching from search parameters"}/>
|
||||||
|
|||||||
@ -28,7 +28,6 @@
|
|||||||
|
|
||||||
return field.parentKey ? visit[field.parentKey]?.[field.key] : visit[field.key];
|
return field.parentKey ? visit[field.parentKey]?.[field.key] : visit[field.key];
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet Fieldset({ value, label, isUTCDate = false })}
|
{#snippet Fieldset({ value, label, isUTCDate = false })}
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
import { patientStore } from "$lib/components/patient/list/store/patient-store.svelte";
|
||||||
|
import { goto } from "$app/navigation";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
@ -13,7 +15,10 @@
|
|||||||
let patient = $derived(masterDetail?.selectedItem?.patient);
|
let patient = $derived(masterDetail?.selectedItem?.patient);
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
orderLab: () => console.log('order lab'),
|
orderLab: () => {
|
||||||
|
patientStore.pending = { InternalPID: '1' },
|
||||||
|
goto('/order/ordertest');
|
||||||
|
},
|
||||||
medicalRecord: () => console.log('medical record'),
|
medicalRecord: () => console.log('medical record'),
|
||||||
auditPatient: () => console.log('audit patient'),
|
auditPatient: () => console.log('audit patient'),
|
||||||
editPatient: () => masterDetail.enterEdit("patient"),
|
editPatient: () => masterDetail.enterEdit("patient"),
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
export const patientStore = $state({ pending: null });
|
||||||
@ -27,5 +27,6 @@ export const API = {
|
|||||||
HOSTAPP: '/api/organization/hostapp',
|
HOSTAPP: '/api/organization/hostapp',
|
||||||
TEST: '/api/test',
|
TEST: '/api/test',
|
||||||
TESTMAP: '/api/test/testmap',
|
TESTMAP: '/api/test/testmap',
|
||||||
EQUIPMENT: '/api/equipmentlist'
|
EQUIPMENT: '/api/equipmentlist',
|
||||||
|
ORDERTEST: '/api/ordertest',
|
||||||
};
|
};
|
||||||
|
|||||||
@ -42,6 +42,9 @@ export async function load({ url }) {
|
|||||||
'/dictionary/testmap': {
|
'/dictionary/testmap': {
|
||||||
title: 'Test Map'
|
title: 'Test Map'
|
||||||
},
|
},
|
||||||
|
'/order/ordertest': {
|
||||||
|
title: 'Order Test'
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = routeConfig[url.pathname] || {
|
const config = routeConfig[url.pathname] || {
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
<script>
|
<script>
|
||||||
import { Separator } from "$lib/components/ui/separator/index.js";
|
import { Separator } from "$lib/components/ui/separator/index.js";
|
||||||
import { useMasterDetail } from "$lib/components/composable/use-master-detail.svelte";
|
import { useMasterDetail } from "$lib/components/composable/use-master-detail.svelte";
|
||||||
import { getVisit, createOrder, editOrder } from "$lib/components/order/api/order-api";
|
import { getOrder, createOrder, editOrder } from "$lib/components/order/ordertest/api/ordertest-api";
|
||||||
import MasterPage from "$lib/components/order/page/master-page.svelte";
|
import MasterPage from "$lib/components/order/ordertest/page/master-page.svelte";
|
||||||
import ViewPage from "$lib/components/order/page/view-page.svelte";
|
import ViewPage from "$lib/components/order/ordertest/page/view-page.svelte";
|
||||||
import CreatePage from "$lib/components/order/page/create-page.svelte";
|
import CreatePage from "$lib/components/order/ordertest/page/create-page.svelte";
|
||||||
import EditPage from "$lib/components/order/page/edit-page.svelte";
|
import EditPage from "$lib/components/order/ordertest/page/edit-page.svelte";
|
||||||
import { orderSchema, orderInitialForm, orderDefaultErrors, orderFormFields, getOrderFormActions } from "$lib/components/order/config/order-form-config";
|
import { orderTestSchema, orderTestInitialForm, orderTestDefaultErrors, orderTestFormFields, getOrderTestFormActions } from "$lib/components/order/ordertest/config/ordertest-form-config";
|
||||||
|
|
||||||
const masterDetail = useMasterDetail({
|
const masterDetail = useMasterDetail({
|
||||||
onSelect: async (row) => {
|
onSelect: async (row) => {
|
||||||
return await getVisit(row.PVID);
|
return await getOrder(row.OrderID);
|
||||||
},
|
},
|
||||||
formConfig: {
|
formConfig: {
|
||||||
schema: orderSchema,
|
schema: orderTestSchema,
|
||||||
initialForm: orderInitialForm,
|
initialForm: orderTestInitialForm,
|
||||||
defaultErrors: orderDefaultErrors,
|
defaultErrors: orderTestDefaultErrors,
|
||||||
mode: 'create',
|
mode: 'create',
|
||||||
modeOpt: 'default',
|
modeOpt: 'default',
|
||||||
saveEndpoint: createOrder,
|
saveEndpoint: createOrder,
|
||||||
@ -25,12 +25,12 @@
|
|||||||
|
|
||||||
const pageContext = {
|
const pageContext = {
|
||||||
masterDetail,
|
masterDetail,
|
||||||
formFields: orderFormFields,
|
formFields: orderTestFormFields,
|
||||||
formActions: getOrderFormActions,
|
formActions: getOrderTestFormActions,
|
||||||
schema: orderSchema,
|
schema: orderTestSchema,
|
||||||
initialForm: orderInitialForm,
|
initialForm: orderTestInitialForm,
|
||||||
defaultErrors: {
|
defaultErrors: {
|
||||||
create: orderDefaultErrors,
|
create: orderTestDefaultErrors,
|
||||||
edit: {}
|
edit: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user