mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-22 01:30:14 +07:00
implement selective update patient list
This commit is contained in:
parent
905ce97f5e
commit
45a6f116cc
17
package-lock.json
generated
17
package-lock.json
generated
@ -3724,23 +3724,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.8.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
|
||||
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/eemeli"
|
||||
}
|
||||
},
|
||||
"node_modules/yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
|
||||
@ -15,25 +15,6 @@ function cleanQuery(searchQuery) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// export async function getById(endpoint, id) {
|
||||
// try {
|
||||
// const res = await fetch(`${API.BASE_URL}${endpoint}/${id}`);
|
||||
|
||||
// if (!res.ok) {
|
||||
// const error = await res.json();
|
||||
// console.error('API Error:', error);
|
||||
// return { data: null, error };
|
||||
// }
|
||||
|
||||
// const response = await res.json();
|
||||
// console.log(response);
|
||||
// return { data: response.data?.[0] || response.data, error: null };
|
||||
// } catch (err) {
|
||||
// console.error('Network Error:', err);
|
||||
// return { data: null, error: err };
|
||||
// }
|
||||
// }
|
||||
|
||||
export async function getById(endpoint, id, returnAll = false) {
|
||||
try {
|
||||
const res = await fetch(`${API.BASE_URL}${endpoint}/${id}`);
|
||||
|
||||
@ -157,6 +157,22 @@ export function getTestMapFormActions(handlers) {
|
||||
];
|
||||
}
|
||||
|
||||
export function buildTestPayload() {
|
||||
|
||||
export function buildTestMapPayload({
|
||||
mainForm,
|
||||
tempMap,
|
||||
}) {
|
||||
const { HostTestCode, HostTestName, ClientTestCode, ClientTestName, ConDefID, ...rest } = mainForm;
|
||||
|
||||
let payload = {
|
||||
...rest,
|
||||
details: tempMap.map((item) => ({
|
||||
HostTestCode: item.HostTestCode,
|
||||
HostTestName: item.HostTestName,
|
||||
ClientTestCode: item.ClientTestCode,
|
||||
ClientTestName: item.ClientTestName,
|
||||
ConDefID: item.ConDefID,
|
||||
})),
|
||||
};
|
||||
|
||||
return cleanEmptyStrings(payload);
|
||||
}
|
||||
@ -11,6 +11,7 @@
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import { untrack } from "svelte";
|
||||
import { API } from '$lib/config/api';
|
||||
import { buildTestMapPayload } from "$lib/components/dictionary/testmap/config/testmap-form-config";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -37,21 +38,12 @@
|
||||
function snapshotForm() {
|
||||
return untrack(() => {
|
||||
const f = formState.form;
|
||||
// const options = {};
|
||||
// for (const key in masterDetail.formState.selectOptions) {
|
||||
// options[key] = [...masterDetail.formState.selectOptions[key]];
|
||||
// }
|
||||
return {
|
||||
HostType: f.HostType ?? "",
|
||||
HostID: f.HostID ?? "",
|
||||
HostTestCode: f.HostTestCode ?? "",
|
||||
HostTestName: f.HostTestName ?? "",
|
||||
ClientType: f.ClientType ?? "",
|
||||
ClientID: f.ClientID ?? "",
|
||||
ClientTestCode: f.ClientTestCode ?? "",
|
||||
ClientTestName: f.ClientTestName ?? "",
|
||||
ConDefID: f.ConDefID ?? "",
|
||||
// options: options
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -61,6 +53,18 @@
|
||||
editingId = null;
|
||||
}
|
||||
|
||||
function resetTest() {
|
||||
untrack(() => {
|
||||
const f = formState.form;
|
||||
f.HostTestCode = null;
|
||||
f.HostTestName = null;
|
||||
f.ClientTestCode = null;
|
||||
f.ClientTestName = null;
|
||||
f.ConDefID = null;
|
||||
});
|
||||
editingId = null;
|
||||
}
|
||||
|
||||
function handleInsert() {
|
||||
const row = {
|
||||
id: ++idCounter,
|
||||
@ -69,7 +73,7 @@
|
||||
|
||||
tempMap = [...tempMap, row];
|
||||
|
||||
resetForm();
|
||||
resetTest();
|
||||
}
|
||||
|
||||
async function handleEdit(row) {
|
||||
@ -77,13 +81,12 @@
|
||||
|
||||
untrack(() => {
|
||||
const f = formState.form;
|
||||
console.log(row);
|
||||
f.HostType = row.HostType;
|
||||
f.HostID = row.HostID;
|
||||
// f.HostType = row.HostType;
|
||||
// f.HostID = row.HostID;
|
||||
f.HostTestCode = row.HostTestCode;
|
||||
f.HostTestName = row.HostTestName;
|
||||
f.ClientType = row.ClientType;
|
||||
f.ClientID = row.ClientID;
|
||||
// f.ClientType = row.ClientType;
|
||||
// f.ClientID = row.ClientID;
|
||||
f.ClientTestCode = row.ClientTestCode;
|
||||
f.ClientTestName = row.ClientTestName;
|
||||
f.ConDefID = row.ConDefID;
|
||||
@ -100,22 +103,29 @@
|
||||
tempMap = tempMap.map((row) =>
|
||||
row.id === editingId ? { id: row.id, ...snapshotForm() } : row
|
||||
);
|
||||
resetForm();
|
||||
resetTest();
|
||||
}
|
||||
|
||||
function handleCancelEdit() {
|
||||
resetForm();
|
||||
resetTest();
|
||||
}
|
||||
|
||||
function handleRemove(id) {
|
||||
tempMap = tempMap.filter((row) => row.id !== id);
|
||||
if (editingId === id) {
|
||||
resetForm();
|
||||
resetTest();
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
const result = await formState.save(masterDetail.mode);
|
||||
const mainForm = masterDetail.formState.form;
|
||||
|
||||
const payload = buildTestMapPayload({
|
||||
mainForm,
|
||||
tempMap,
|
||||
});
|
||||
console.log(payload)
|
||||
const result = await formState.save(masterDetail.mode, payload);
|
||||
|
||||
toast('Test Map Created!');
|
||||
masterDetail?.exitForm(true);
|
||||
@ -243,12 +253,12 @@
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row class="hover:bg-transparent">
|
||||
<Table.Head>Host Type</Table.Head>
|
||||
<Table.Head>Host ID</Table.Head>
|
||||
<!-- <Table.Head>Host Type</Table.Head>
|
||||
<Table.Head>Host ID</Table.Head> -->
|
||||
<Table.Head>Host Test Code</Table.Head>
|
||||
<Table.Head>Host Test Name</Table.Head>
|
||||
<Table.Head>Client Type</Table.Head>
|
||||
<Table.Head>Client ID</Table.Head>
|
||||
<!-- <Table.Head>Client Type</Table.Head>
|
||||
<Table.Head>Client ID</Table.Head> -->
|
||||
<Table.Head>Client Test Code</Table.Head>
|
||||
<Table.Head>Client Test Name</Table.Head>
|
||||
<Table.Head>Container</Table.Head>
|
||||
@ -267,12 +277,8 @@
|
||||
<Table.Row
|
||||
class="cursor-pointer hover:bg-muted/50"
|
||||
>
|
||||
<Table.Cell>{row.HostType}</Table.Cell>
|
||||
<Table.Cell>{row.HostID}</Table.Cell>
|
||||
<Table.Cell>{row.HostTestCode}</Table.Cell>
|
||||
<Table.Cell>{row.HostTestName}</Table.Cell>
|
||||
<Table.Cell>{row.ClientType}</Table.Cell>
|
||||
<Table.Cell>{row.ClientID}</Table.Cell>
|
||||
<Table.Cell>{row.ClientTestCode}</Table.Cell>
|
||||
<Table.Cell>{row.ClientTestName}</Table.Cell>
|
||||
<Table.Cell>{row.ConDefID}</Table.Cell>
|
||||
|
||||
22
src/lib/components/order/api/order-api.js
Normal file
22
src/lib/components/order/api/order-api.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { API } from '$lib/config/api.js';
|
||||
import { getById, searchWithParams, create, update } from '$lib/api/api-client';
|
||||
|
||||
export async function searchParam(searchQuery) {
|
||||
return await searchWithParams(API.PATIENTS, searchQuery)
|
||||
}
|
||||
|
||||
export async function getVisitList(searchQuery) {
|
||||
return await getById(API.VISITLIST, searchQuery, true)
|
||||
}
|
||||
|
||||
export async function getVisit(searchQuery) {
|
||||
return await getById(API.PATVISIT, searchQuery)
|
||||
}
|
||||
|
||||
export async function createOrder(newOrderForm) {
|
||||
return await create(API.ORDER, newOrderForm)
|
||||
}
|
||||
|
||||
export async function editOrder(editOrderForm) {
|
||||
return await update(API.ORDER, editOrderForm)
|
||||
}
|
||||
72
src/lib/components/order/config/order-config.js
Normal file
72
src/lib/components/order/config/order-config.js
Normal file
@ -0,0 +1,72 @@
|
||||
import PlusIcon from "@lucide/svelte/icons/plus";
|
||||
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
|
||||
export const searchFields = [
|
||||
{
|
||||
key: "PatientID",
|
||||
label: "Patient ID",
|
||||
placeholder: "",
|
||||
type: "text",
|
||||
defaultValue: "",
|
||||
},
|
||||
{
|
||||
key: "Name",
|
||||
label: "Patient Name",
|
||||
placeholder: "",
|
||||
type: "text",
|
||||
defaultValue: "",
|
||||
},
|
||||
{
|
||||
key: "Birthdate",
|
||||
label: "Birthdate",
|
||||
type: "date"
|
||||
},
|
||||
{
|
||||
key: "Identifier",
|
||||
label: "Identifier",
|
||||
type: "text"
|
||||
},
|
||||
{
|
||||
key: "VisitID",
|
||||
label: "Visit ID",
|
||||
type: "text"
|
||||
},
|
||||
{
|
||||
key: "EpisodeID",
|
||||
label: "Episode ID",
|
||||
type: "text"
|
||||
},
|
||||
];
|
||||
|
||||
export const detailSections = [];
|
||||
|
||||
export function orderActions(masterDetail, selectedPatient) {
|
||||
return [
|
||||
{
|
||||
Icon: PlusIcon,
|
||||
label: 'Add Order',
|
||||
onClick: () => masterDetail.enterCreate({
|
||||
PatientID: selectedPatient?.PatientID,
|
||||
InternalPID: selectedPatient?.InternalPID
|
||||
}),
|
||||
disabled: !selectedPatient,
|
||||
},
|
||||
{
|
||||
Icon: Settings2Icon,
|
||||
label: 'Search Parameters',
|
||||
popoverWidth: "w-full",
|
||||
collisionPadding: 12,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function viewActions(handlers){
|
||||
return [
|
||||
{
|
||||
Icon: PencilIcon,
|
||||
label: 'Edit Order',
|
||||
onClick: handlers.editOrder,
|
||||
},
|
||||
]
|
||||
}
|
||||
22
src/lib/components/order/config/order-form-config.js
Normal file
22
src/lib/components/order/config/order-form-config.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { API } from "$lib/config/api";
|
||||
import EraserIcon from "@lucide/svelte/icons/eraser";
|
||||
import { z } from "zod";
|
||||
import { cleanEmptyStrings } from "$lib/utils/cleanEmptyStrings";
|
||||
|
||||
export const orderSchema = z.object({});
|
||||
|
||||
export const orderInitialForm = {};
|
||||
|
||||
export const orderDefaultErrors = {};
|
||||
|
||||
export const orderFormFields = [];
|
||||
|
||||
export function getOrderFormActions(handlers) {
|
||||
return [
|
||||
{
|
||||
Icon: EraserIcon,
|
||||
label: 'Clear Form',
|
||||
onClick: handlers.clearForm,
|
||||
},
|
||||
];
|
||||
}
|
||||
142
src/lib/components/order/modal/search-param-modal.svelte
Normal file
142
src/lib/components/order/modal/search-param-modal.svelte
Normal file
@ -0,0 +1,142 @@
|
||||
<script>
|
||||
import * as Table from "$lib/components/ui/table/index.js";
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import { Label } from "$lib/components/ui/label/index.js";
|
||||
import { Input } from "$lib/components/ui/input/index.js";
|
||||
import ReusableCalendar from "$lib/components/reusable/reusable-calendar.svelte";
|
||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||
import * as Select from "$lib/components/ui/select/index.js";
|
||||
import { useSearch } from "$lib/components/composable/use-search.svelte";
|
||||
import { searchFields } from "$lib/components/order/config/order-config";
|
||||
import { searchParam } from "$lib/components/order/api/order-api";
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import { Checkbox } from "$lib/components/ui/checkbox/index.js";
|
||||
import * as Popover from "$lib/components/ui/popover/index.js";
|
||||
|
||||
let { selectedPatient = $bindable(null), ...props } = $props();
|
||||
let tempSelectedPatient = $state(null);
|
||||
|
||||
let activeRowId = $state(null);
|
||||
|
||||
let isPatientEmpty = $derived(!tempSelectedPatient);
|
||||
|
||||
function handleCheckboxChange(patient) {
|
||||
tempSelectedPatient = patient;
|
||||
}
|
||||
|
||||
function handleButtonClick() {
|
||||
if (tempSelectedPatient) {
|
||||
selectedPatient = tempSelectedPatient;
|
||||
props.onConfirm(tempSelectedPatient);
|
||||
|
||||
tempSelectedPatient = null;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-full h-110">
|
||||
<div class="flex gap-4 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">
|
||||
{#each props.searchFields as field}
|
||||
{#if field.type === "text"}
|
||||
<div class="space-y-2">
|
||||
<Label for={field.key}>{field.label}</Label>
|
||||
<Input type="text" id={field.key} placeholder={field.placeholder} bind:value={props.search.searchQuery[field.key]} autocomplete=off/>
|
||||
</div>
|
||||
{:else if field.type === "date"}
|
||||
<div class="space-y-2">
|
||||
<ReusableCalendar title={field.label} bind:value={props.search.searchQuery[field.key]}/>
|
||||
</div>
|
||||
{:else if field.type === "select"}
|
||||
<div class="space-y-2">
|
||||
<Label for={field.key}>{field.label}</Label>
|
||||
<Select.Root bind:value={props.search.searchQuery[field.key]}>
|
||||
<Select.Trigger id={field.key}>
|
||||
<Select.Value placeholder={field.placeholder} />
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each field.options as opt}
|
||||
<Select.Item value={opt.value}>
|
||||
{opt.label}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="flex justify-end gap-2 mt-4">
|
||||
<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}>
|
||||
{#if props.search.isLoading}
|
||||
<Spinner />
|
||||
{:else}
|
||||
Search
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col w-2/3 h-full">
|
||||
{#if props.search.searchData && props.search.searchData.length > 0}
|
||||
<div class="flex-1 overflow-y-auto min-h-0 flex flex-col gap-2">
|
||||
<div class="flex-1">
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row class="hover:bg-transparent">
|
||||
<Table.Head class="w-8"></Table.Head>
|
||||
<Table.Head class="w-32">Patient ID</Table.Head>
|
||||
<Table.Head class="w-full">Patient Name</Table.Head>
|
||||
<Table.Head class="w-32">Birthdate</Table.Head>
|
||||
<Table.Head class="w-8">Sex</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{#each props.search.searchData as patient, i}
|
||||
<Table.Row
|
||||
class="cursor-pointer hover:bg-muted/50"
|
||||
onclick={() => handleCheckboxChange(patient)}
|
||||
>
|
||||
<Table.Cell onclick={(e) => e.stopPropagation()}>
|
||||
<Checkbox
|
||||
class="cursor-pointer hover:bg-muted/50"
|
||||
checked={tempSelectedPatient?.InternalPID === patient.InternalPID}
|
||||
onCheckedChange={() => handleCheckboxChange(patient)}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="font-medium">{patient.PatientID}</Table.Cell>
|
||||
<Table.Cell class="">{patient.FullName}</Table.Cell>
|
||||
<Table.Cell class="text-muted-foreground">{patient.Birthdate ? patient.Birthdate.split(" ")[0] : ""}</Table.Cell>
|
||||
<Table.Cell class="font-medium">{patient.SexLabel}</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end gap-2 mt-4 w-full">
|
||||
<Popover.Close>
|
||||
<Button
|
||||
size="sm"
|
||||
class="cursor-pointer"
|
||||
disabled={isPatientEmpty}
|
||||
onclick={handleButtonClick}
|
||||
>
|
||||
Select Patient
|
||||
</Button>
|
||||
</Popover.Close>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex h-full">
|
||||
<ReusableEmpty desc="Try searching from search parameters"/>
|
||||
</div>
|
||||
{/if}
|
||||
</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>
|
||||
0
src/lib/components/order/page/create-page.svelte
Normal file
0
src/lib/components/order/page/create-page.svelte
Normal file
0
src/lib/components/order/page/edit-page.svelte
Normal file
0
src/lib/components/order/page/edit-page.svelte
Normal file
90
src/lib/components/order/page/master-page.svelte
Normal file
90
src/lib/components/order/page/master-page.svelte
Normal file
@ -0,0 +1,90 @@
|
||||
<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>
|
||||
0
src/lib/components/order/page/view-page.svelte
Normal file
0
src/lib/components/order/page/view-page.svelte
Normal file
14
src/lib/components/order/table/order-columns.js
Normal file
14
src/lib/components/order/table/order-columns.js
Normal file
@ -0,0 +1,14 @@
|
||||
export const orderColumns = [
|
||||
{
|
||||
accessorKey: "InternalPVID",
|
||||
header: "Patient ID"
|
||||
},
|
||||
{
|
||||
accessorKey: "PVID",
|
||||
header: "Visit ID",
|
||||
},
|
||||
{
|
||||
accessorKey: "EpisodeID",
|
||||
header: "Episode ID",
|
||||
},
|
||||
];
|
||||
@ -93,7 +93,8 @@ export function admissionActions(masterDetail, selectedPatient) {
|
||||
{
|
||||
Icon: Settings2Icon,
|
||||
label: 'Search Parameters',
|
||||
popoverWidth: "w-256",
|
||||
popoverWidth: "w-full",
|
||||
collisionPadding: 12,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
<div class="w-full h-110">
|
||||
<div class="flex gap-4 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">
|
||||
<div class="flex-1 overflow-y-auto min-h-0 space-y-2 px-2">
|
||||
{#each props.searchFields as field}
|
||||
{#if field.type === "text"}
|
||||
<div class="space-y-2">
|
||||
|
||||
@ -76,10 +76,10 @@ export const detailSections = [
|
||||
{ parentKey: "Custodian", key: "PatientID", label: "Custodian ID" },
|
||||
],
|
||||
},
|
||||
{ key: "DeathIndicatorLabel", label: "Death Indicator" },
|
||||
{ key: "isDeadLabel", label: "Deceased" },
|
||||
{ key: "CreateDate", label: "Create Date", isUTCDate: true },
|
||||
{ key: "DelDate", label: "Disabled Date" },
|
||||
{ key: "TimeOfDeath", label: "Death Date", isUTCDate: true },
|
||||
{ key: "TimeOfDeath", label: "Time of Death", isUTCDate: true },
|
||||
]
|
||||
},
|
||||
// {
|
||||
|
||||
@ -270,7 +270,7 @@ export const patientFormFields = [
|
||||
type: "group",
|
||||
columns: [
|
||||
{
|
||||
key: "DeathIndicator",
|
||||
key: "isDead",
|
||||
label: "Deceased",
|
||||
required: false,
|
||||
type: "select",
|
||||
|
||||
@ -47,7 +47,6 @@
|
||||
});
|
||||
});
|
||||
|
||||
// if (formState.form.ProvinceID && formState.form.CityID) {
|
||||
if (formState.form.Province) {
|
||||
formState.fetchOptions(
|
||||
{
|
||||
@ -61,9 +60,35 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function getChangedFields(original, current) {
|
||||
const changed = {};
|
||||
for (const key in current) {
|
||||
if (JSON.stringify(current[key]) !== JSON.stringify(original[key])) {
|
||||
changed[key] = current[key];
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
async function handleEdit() {
|
||||
const payload = buildPatientPayload(formState.form);
|
||||
const currentPayload = buildPatientPayload(formState.form);
|
||||
const originalPayload = buildPatientPayload(masterDetail.formSnapshot);
|
||||
|
||||
const changedFields = getChangedFields(originalPayload, currentPayload);
|
||||
|
||||
if (Object.keys(changedFields).length === 0) {
|
||||
toast('No changes detected');
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
InternalPID: formState.form.InternalPID,
|
||||
...changedFields
|
||||
};
|
||||
|
||||
console.log('Payload:', payload);
|
||||
|
||||
const result = await formState.save(masterDetail.mode, payload);
|
||||
|
||||
if (result.status === 'success') {
|
||||
|
||||
@ -68,7 +68,7 @@
|
||||
}
|
||||
|
||||
let isDeathDateDisabled = $derived(
|
||||
formState.form.DeathIndicator !== 'Y'
|
||||
formState.form.isDead !== 'Y'
|
||||
);
|
||||
|
||||
$effect(() => {
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
</Button>
|
||||
{/snippet}
|
||||
</Popover.Trigger>
|
||||
<Popover.Content ${props.collisionPadding ?? 0} class={props.popoverWidth ?? "w-72"}>
|
||||
<Popover.Content collisionPadding={props.collisionPadding ?? 0} class={props.popoverWidth ?? "w-72"}>
|
||||
{@render props.popoverContent()}
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
||||
|
||||
55
src/routes/order/+page.svelte
Normal file
55
src/routes/order/+page.svelte
Normal file
@ -0,0 +1,55 @@
|
||||
<script>
|
||||
import { Separator } from "$lib/components/ui/separator/index.js";
|
||||
import { useMasterDetail } from "$lib/components/composable/use-master-detail.svelte";
|
||||
import { getVisit, createOrder, editOrder } from "$lib/components/order/api/order-api";
|
||||
import MasterPage from "$lib/components/order/page/master-page.svelte";
|
||||
import ViewPage from "$lib/components/order/page/view-page.svelte";
|
||||
import CreatePage from "$lib/components/order/page/create-page.svelte";
|
||||
import EditPage from "$lib/components/order/page/edit-page.svelte";
|
||||
import { orderSchema, orderInitialForm, orderDefaultErrors, orderFormFields, getOrderFormActions } from "$lib/components/order/config/order-form-config";
|
||||
|
||||
const masterDetail = useMasterDetail({
|
||||
onSelect: async (row) => {
|
||||
return await getVisit(row.PVID);
|
||||
},
|
||||
formConfig: {
|
||||
schema: orderSchema,
|
||||
initialForm: orderInitialForm,
|
||||
defaultErrors: orderDefaultErrors,
|
||||
mode: 'create',
|
||||
modeOpt: 'default',
|
||||
saveEndpoint: createOrder,
|
||||
editEndpoint: editOrder,
|
||||
}
|
||||
});
|
||||
|
||||
const pageContext = {
|
||||
masterDetail,
|
||||
formFields: orderFormFields,
|
||||
formActions: getOrderFormActions,
|
||||
schema: orderSchema,
|
||||
initialForm: orderInitialForm,
|
||||
defaultErrors: {
|
||||
create: orderDefaultErrors,
|
||||
edit: {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex w-full h-full overflow-hidden">
|
||||
{#if masterDetail.showMaster}
|
||||
<MasterPage {masterDetail} />
|
||||
{/if}
|
||||
<Separator orientation="vertical"/>
|
||||
{#if masterDetail.showDetail}
|
||||
<main class={`${masterDetail.isMobile ? 'w-full' : masterDetail.isFormMode ? 'w-[97%] flex flex-col items-start' : 'w-[65%]'} h-full overflow-y-auto flex flex-col items-center transition-all duration-300`}>
|
||||
{#if masterDetail.mode === "view"}
|
||||
<ViewPage context={pageContext}/>
|
||||
{:else if masterDetail.mode === "create"}
|
||||
<CreatePage context={pageContext}/>
|
||||
{:else if masterDetail.mode === "edit"}
|
||||
<EditPage context={pageContext}/>
|
||||
{/if}
|
||||
</main>
|
||||
{/if}
|
||||
</div>
|
||||
Loading…
x
Reference in New Issue
Block a user