mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-26 19:06:33 +07:00
continue patient list & admission
This commit is contained in:
parent
cc22abb033
commit
a3b8582e57
@ -95,7 +95,6 @@ export async function searchWithPath(endpoint, searchQuery) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function create(endpoint, formData) {
|
export async function create(endpoint, formData) {
|
||||||
console.log(cleanEmptyStrings(formData));
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API.BASE_URL}${endpoint}`, {
|
const res = await fetch(`${API.BASE_URL}${endpoint}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -115,19 +114,19 @@ export async function create(endpoint, formData) {
|
|||||||
|
|
||||||
export async function update(endpoint, formData) {
|
export async function update(endpoint, formData) {
|
||||||
console.log(cleanEmptyStrings(formData));
|
console.log(cleanEmptyStrings(formData));
|
||||||
try {
|
// try {
|
||||||
const res = await fetch(`${API.BASE_URL}${endpoint}`, {
|
// const res = await fetch(`${API.BASE_URL}${endpoint}`, {
|
||||||
method: 'PATCH',
|
// method: 'PATCH',
|
||||||
headers: {
|
// headers: {
|
||||||
'Content-Type': 'application/json',
|
// 'Content-Type': 'application/json',
|
||||||
},
|
// },
|
||||||
body: JSON.stringify(cleanEmptyStrings(formData))
|
// body: JSON.stringify(cleanEmptyStrings(formData))
|
||||||
});
|
// });
|
||||||
|
|
||||||
const data = await res.json();
|
// const data = await res.json();
|
||||||
return data;
|
// return data;
|
||||||
} catch (err) {
|
// } catch (err) {
|
||||||
console.error('Update Error:', err.message);
|
// console.error('Update Error:', err.message);
|
||||||
return { success: false, message: err.message || 'Network error' };
|
// return { success: false, message: err.message || 'Network error' };
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
@ -2,6 +2,10 @@
|
|||||||
import ChartPieIcon from "@lucide/svelte/icons/chart-pie";
|
import ChartPieIcon from "@lucide/svelte/icons/chart-pie";
|
||||||
import FrameIcon from "@lucide/svelte/icons/frame";
|
import FrameIcon from "@lucide/svelte/icons/frame";
|
||||||
import LifeBuoyIcon from "@lucide/svelte/icons/life-buoy";
|
import LifeBuoyIcon from "@lucide/svelte/icons/life-buoy";
|
||||||
|
import ChartColumnIcon from "@lucide/svelte/icons/chart-column";
|
||||||
|
import UserIcon from "@lucide/svelte/icons/user";
|
||||||
|
import ReceiptTextIcon from "@lucide/svelte/icons/receipt-text";
|
||||||
|
import BookOpenIcon from "@lucide/svelte/icons/book-open";
|
||||||
import MapIcon from "@lucide/svelte/icons/map";
|
import MapIcon from "@lucide/svelte/icons/map";
|
||||||
import SendIcon from "@lucide/svelte/icons/send";
|
import SendIcon from "@lucide/svelte/icons/send";
|
||||||
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
||||||
@ -17,12 +21,12 @@
|
|||||||
{
|
{
|
||||||
title: "Dashboard",
|
title: "Dashboard",
|
||||||
url: "/",
|
url: "/",
|
||||||
icon: LifeBuoyIcon,
|
icon: ChartColumnIcon,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Patient",
|
title: "Patient",
|
||||||
url: "/patient",
|
url: "/patient",
|
||||||
icon: LifeBuoyIcon,
|
icon: UserIcon,
|
||||||
submenus: [
|
submenus: [
|
||||||
{
|
{
|
||||||
title: "Patient List",
|
title: "Patient List",
|
||||||
@ -37,7 +41,7 @@
|
|||||||
{
|
{
|
||||||
title: "Order",
|
title: "Order",
|
||||||
url: "/order",
|
url: "/order",
|
||||||
icon: LifeBuoyIcon,
|
icon: ReceiptTextIcon,
|
||||||
submenus: [
|
submenus: [
|
||||||
{
|
{
|
||||||
title: "Test Order",
|
title: "Test Order",
|
||||||
@ -50,7 +54,7 @@
|
|||||||
{
|
{
|
||||||
title: "Admission",
|
title: "Admission",
|
||||||
url: "/admission",
|
url: "/admission",
|
||||||
icon: LifeBuoyIcon,
|
icon: BookOpenIcon,
|
||||||
submenus: [
|
submenus: [
|
||||||
{
|
{
|
||||||
title: "Contact",
|
title: "Contact",
|
||||||
@ -69,7 +73,7 @@
|
|||||||
{
|
{
|
||||||
title: "Value",
|
title: "Value",
|
||||||
url: "#",
|
url: "#",
|
||||||
icon: LifeBuoyIcon,
|
icon: BookOpenIcon,
|
||||||
submenus: [
|
submenus: [
|
||||||
{
|
{
|
||||||
title: "Value Set Def",
|
title: "Value Set Def",
|
||||||
@ -84,7 +88,7 @@
|
|||||||
{
|
{
|
||||||
title: "Sample",
|
title: "Sample",
|
||||||
url: "#",
|
url: "#",
|
||||||
icon: LifeBuoyIcon,
|
icon: BookOpenIcon,
|
||||||
submenus: [
|
submenus: [
|
||||||
{
|
{
|
||||||
title: "Container",
|
title: "Container",
|
||||||
@ -95,7 +99,7 @@
|
|||||||
{
|
{
|
||||||
title: "Organization",
|
title: "Organization",
|
||||||
url: "#",
|
url: "#",
|
||||||
icon: LifeBuoyIcon,
|
icon: BookOpenIcon,
|
||||||
submenus: [
|
submenus: [
|
||||||
{
|
{
|
||||||
title: "Account",
|
title: "Account",
|
||||||
@ -122,7 +126,7 @@
|
|||||||
{
|
{
|
||||||
title: "Test",
|
title: "Test",
|
||||||
url: "#",
|
url: "#",
|
||||||
icon: LifeBuoyIcon,
|
icon: BookOpenIcon,
|
||||||
submenus: [
|
submenus: [
|
||||||
{
|
{
|
||||||
title: "Test Site",
|
title: "Test Site",
|
||||||
|
|||||||
@ -7,12 +7,13 @@ export function useForm({schema, initialForm, defaultErrors, mode, modeOpt, save
|
|||||||
const val = useFormValidation(schema, state.form, defaultErrors, mode);
|
const val = useFormValidation(schema, state.form, defaultErrors, mode);
|
||||||
const options = useFormOptions(modeOpt);
|
const options = useFormOptions(modeOpt);
|
||||||
|
|
||||||
async function save() {
|
async function save(currentMode, customPayload = null) {
|
||||||
state.isSaving.current = true
|
state.isSaving.current = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = { ...state.form };
|
// const payload = { ...state.form };
|
||||||
const result = mode === 'edit' ? await editEndpoint(payload) : await saveEndpoint(payload)
|
const payload = customPayload || { ...state.form };
|
||||||
|
const result = currentMode === 'edit' ? await editEndpoint(payload) : await saveEndpoint(payload);
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Save failed', error);
|
console.error('Save failed', error);
|
||||||
|
|||||||
@ -42,21 +42,20 @@ export function useMasterDetail(options = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function enterCreate(initialData = null) {
|
function enterCreate(initialData = null) {
|
||||||
mode = "create";
|
mode = "create";
|
||||||
selectedItem = null;
|
selectedItem = null;
|
||||||
|
|
||||||
formState.reset();
|
formState.reset();
|
||||||
|
|
||||||
if (initialData) {
|
if (initialData) {
|
||||||
formState.setForm({
|
formState.setForm({
|
||||||
...formState.form,
|
...formState.form,
|
||||||
...initialData
|
...initialData
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function enterEdit(mapToForm = null) {
|
function enterEdit(mapToForm = null) {
|
||||||
if (!selectedItem) return;
|
if (!selectedItem) return;
|
||||||
mode = "edit";
|
mode = "edit";
|
||||||
@ -74,9 +73,6 @@ function enterCreate(initialData = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function exitForm() {
|
function exitForm() {
|
||||||
// const confirmed = confirm('You have unsaved changes. Are you sure you want to exit?');
|
|
||||||
// if (!confirmed) return;
|
|
||||||
|
|
||||||
mode = "view";
|
mode = "view";
|
||||||
selectedItem = null;
|
selectedItem = null;
|
||||||
}
|
}
|
||||||
@ -87,12 +83,10 @@ function enterCreate(initialData = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function saveForm() {
|
function saveForm() {
|
||||||
// Commit changes (mark as saved)
|
|
||||||
formSnapshot = { ...form };
|
formSnapshot = { ...form };
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// State
|
|
||||||
get selectedItem() {
|
get selectedItem() {
|
||||||
return selectedItem;
|
return selectedItem;
|
||||||
},
|
},
|
||||||
@ -127,7 +121,6 @@ function enterCreate(initialData = null) {
|
|||||||
return formState;
|
return formState;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Actions
|
|
||||||
select,
|
select,
|
||||||
enterCreate,
|
enterCreate,
|
||||||
enterEdit,
|
enterEdit,
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export const searchFields = [
|
|||||||
|
|
||||||
export const detailSections = [
|
export const detailSections = [
|
||||||
{
|
{
|
||||||
title: "", // No title for top row
|
title: "",
|
||||||
class: "grid grid-cols-1 md:grid-cols-2 gap-4",
|
class: "grid grid-cols-1 md:grid-cols-2 gap-4",
|
||||||
groups: [
|
groups: [
|
||||||
{
|
{
|
||||||
@ -84,10 +84,6 @@ export function admissionActions(masterDetail, selectedPatient) {
|
|||||||
{
|
{
|
||||||
Icon: PlusIcon,
|
Icon: PlusIcon,
|
||||||
label: 'Add Visit',
|
label: 'Add Visit',
|
||||||
// onClick: masterDetail.enterCreate({
|
|
||||||
// PatientID: selectedPatient?.PatientID,
|
|
||||||
// InternalPID: selectedPatient?.InternalPID
|
|
||||||
// }),
|
|
||||||
onClick: () => masterDetail.enterCreate({
|
onClick: () => masterDetail.enterCreate({
|
||||||
PatientID: selectedPatient?.PatientID,
|
PatientID: selectedPatient?.PatientID,
|
||||||
InternalPID: selectedPatient?.InternalPID
|
InternalPID: selectedPatient?.InternalPID
|
||||||
@ -108,7 +104,6 @@ export function viewActions(handlers){
|
|||||||
Icon: PencilIcon,
|
Icon: PencilIcon,
|
||||||
label: 'Edit Patient',
|
label: 'Edit Patient',
|
||||||
onClick: handlers.editPatient,
|
onClick: handlers.editPatient,
|
||||||
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -18,6 +18,7 @@ export const admissionInitialForm = {
|
|||||||
RefDoc: "",
|
RefDoc: "",
|
||||||
AdmDoc: "",
|
AdmDoc: "",
|
||||||
CnsDoc: "",
|
CnsDoc: "",
|
||||||
|
isDischarge: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export const admissionDefaultErrors = {};
|
export const admissionDefaultErrors = {};
|
||||||
@ -131,7 +132,7 @@ export const admissionFormFields = [
|
|||||||
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/service_classes`,
|
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/service_classes`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "Discharge",
|
key: "isDischarge",
|
||||||
label: "Discharge Status",
|
label: "Discharge Status",
|
||||||
required: false,
|
required: false,
|
||||||
type: "toggle",
|
type: "toggle",
|
||||||
|
|||||||
105
src/lib/components/patient/admission/config/admission-payload.js
Normal file
105
src/lib/components/patient/admission/config/admission-payload.js
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { cleanEmptyStrings } from "$lib/utils/cleanEmptyStrings";
|
||||||
|
|
||||||
|
const ADT_CODE_MAP = {
|
||||||
|
LocationID: "A02",
|
||||||
|
AttDoc: "A54",
|
||||||
|
RefDoc: "A08",
|
||||||
|
AdmDoc: "A08",
|
||||||
|
CnsDoc: "A61",
|
||||||
|
isDischarge: "A03",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function compareAdmissionData(oldData, newData) {
|
||||||
|
let diffs = [];
|
||||||
|
let current = { ...oldData };
|
||||||
|
let sequence = 1;
|
||||||
|
|
||||||
|
for (const key in ADT_CODE_MAP) {
|
||||||
|
// Skip if field tidak ada atau tidak berubah
|
||||||
|
if (!Object.hasOwn(oldData, key) || oldData[key] === newData[key]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track discharge status
|
||||||
|
const originalisDischarge = !!oldData.isDischarge;
|
||||||
|
const newisDischarge = !!newData.isDischarge;
|
||||||
|
|
||||||
|
// Update current state
|
||||||
|
current[key] = newData[key];
|
||||||
|
|
||||||
|
// Build diff entry
|
||||||
|
const filtered = Object.fromEntries(
|
||||||
|
Object.keys(ADT_CODE_MAP).map((k) => [k, current[k]])
|
||||||
|
);
|
||||||
|
|
||||||
|
diffs.push({
|
||||||
|
sequence: sequence++,
|
||||||
|
code: ADT_CODE_MAP[key],
|
||||||
|
originalisDischarge,
|
||||||
|
isDischarge: newisDischarge,
|
||||||
|
...filtered,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return diffs;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildEditAdmissionPayload(form, diffs = []) {
|
||||||
|
const base = {
|
||||||
|
InternalPVID: form.InternalPVID,
|
||||||
|
PVID: form.PVID,
|
||||||
|
EpisodeID: form.EpisodeID,
|
||||||
|
InternalPID: form.InternalPID,
|
||||||
|
PatDiag: {
|
||||||
|
DiagCode: form.DiagCode || null,
|
||||||
|
Diagnosis: form.Diagnosis || null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// No changes, return empty PatVisitADT
|
||||||
|
if (!diffs.length) {
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
PatVisitADT: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map diffs ke PatVisitADT entries
|
||||||
|
const PatVisitADT = diffs.map(diff => {
|
||||||
|
// Determine ADTCode based on discharge status
|
||||||
|
let ADTCode;
|
||||||
|
if (diff.isDischarge) {
|
||||||
|
ADTCode = "A03"; // Discharge
|
||||||
|
} else if (diff.originalisDischarge) {
|
||||||
|
ADTCode = "A13"; // Cancel discharge
|
||||||
|
} else {
|
||||||
|
ADTCode = diff.code; // Normal update
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sequence: diff.sequence,
|
||||||
|
ADTCode,
|
||||||
|
LocationID: diff.LocationID || null,
|
||||||
|
AttDoc: diff.AttDoc || null,
|
||||||
|
RefDoc: diff.RefDoc || null,
|
||||||
|
AdmDoc: diff.AdmDoc || null,
|
||||||
|
CnsDoc: diff.CnsDoc || null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return { ...base, PatVisitADT };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildEditPayloadWithDiff(originalData, formData) {
|
||||||
|
// Clean data
|
||||||
|
const cleanedForm = cleanEmptyStrings(formData);
|
||||||
|
const cleanedOriginal = cleanEmptyStrings(originalData);
|
||||||
|
|
||||||
|
// Compare & get diffs
|
||||||
|
const diffs = compareAdmissionData(cleanedOriginal, cleanedForm);
|
||||||
|
console.log(formData);
|
||||||
|
|
||||||
|
// Build payload
|
||||||
|
const payload = buildEditAdmissionPayload(cleanedForm, diffs);
|
||||||
|
|
||||||
|
return cleanEmptyStrings(payload);
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
<script>
|
||||||
|
import { useForm } from "$lib/components/composable/use-form.svelte";
|
||||||
|
import { admissionSchema, admissionInitialForm, admissionDefaultErrors, admissionFormFields, getAdmissionFormActions, buildPayload } from "$lib/components/patient/admission/config/admission-form-config";
|
||||||
|
import { createAdmission } from "$lib/components/patient/admission/api/patient-admission-api";
|
||||||
|
import { usePatientForm } from "$lib/components/composable/use-patient-form.svelte";
|
||||||
|
import FormPageContainer from "$lib/components/patient/reusable/form-page-container.svelte";
|
||||||
|
import PatientFormRenderer from "$lib/components/patient/reusable/patient-form-renderer.svelte";
|
||||||
|
|
||||||
|
let props = $props();
|
||||||
|
|
||||||
|
let formState = useForm({
|
||||||
|
schema: admissionSchema,
|
||||||
|
initialForm: admissionInitialForm,
|
||||||
|
defaultErrors: admissionDefaultErrors,
|
||||||
|
mode: 'create',
|
||||||
|
modeOpt: 'default',
|
||||||
|
saveEndpoint: createAdmission,
|
||||||
|
editEndpoint: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const helpers = usePatientForm(formState, admissionSchema);
|
||||||
|
|
||||||
|
const handlers = {
|
||||||
|
clearForm: () => {
|
||||||
|
formState.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const actions = getAdmissionFormActions(handlers);
|
||||||
|
|
||||||
|
async function handleSave() {
|
||||||
|
const payload = buildPayload(formState.form);
|
||||||
|
|
||||||
|
console.log(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
const primaryAction = $derived({
|
||||||
|
label: 'Save',
|
||||||
|
onClick: handleSave,
|
||||||
|
disabled: helpers.hasErrors || formState.isSaving.current,
|
||||||
|
loading: formState.isSaving.current
|
||||||
|
});
|
||||||
|
|
||||||
|
const secondaryActions = [];
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (props.masterDetail.form?.PatientID) {
|
||||||
|
formState.setForm({
|
||||||
|
...formState.form,
|
||||||
|
...props.masterDetail.form
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FormPageContainer title="Create Admission" {primaryAction} {secondaryActions} {actions}>
|
||||||
|
<PatientFormRenderer
|
||||||
|
{formState}
|
||||||
|
formFields={admissionFormFields}
|
||||||
|
mode="create"
|
||||||
|
/>
|
||||||
|
</FormPageContainer>
|
||||||
@ -1,24 +1,17 @@
|
|||||||
<script>
|
<script>
|
||||||
import { useForm } from "$lib/components/composable/use-form.svelte";
|
|
||||||
import { admissionSchema, admissionInitialForm, admissionDefaultErrors, admissionFormFields, getAdmissionFormActions, buildPayload } from "$lib/components/patient/admission/config/admission-form-config";
|
|
||||||
import { createAdmission } from "$lib/components/patient/admission/api/patient-admission-api";
|
|
||||||
import { usePatientForm } from "$lib/components/composable/use-patient-form.svelte";
|
import { usePatientForm } from "$lib/components/composable/use-patient-form.svelte";
|
||||||
import FormPageContainer from "$lib/components/patient/reusable/form-page-container.svelte";
|
import FormPageContainer from "$lib/components/patient/reusable/form-page-container.svelte";
|
||||||
import PatientFormRenderer from "$lib/components/patient/reusable/patient-form-renderer.svelte";
|
import PatientFormRenderer from "$lib/components/patient/reusable/patient-form-renderer.svelte";
|
||||||
|
import { buildPayload } from "$lib/components/patient/admission/config/admission-form-config";
|
||||||
|
import { toast } from "svelte-sonner";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
let formState = useForm({
|
const { masterDetail, formFields, formActions, schema } = props.context;
|
||||||
schema: admissionSchema,
|
|
||||||
initialForm: admissionInitialForm,
|
|
||||||
defaultErrors: admissionDefaultErrors,
|
|
||||||
mode: 'create',
|
|
||||||
modeOpt: 'default',
|
|
||||||
saveEndpoint: createAdmission,
|
|
||||||
editEndpoint: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const helpers = usePatientForm(formState, admissionSchema);
|
const { formState } = masterDetail;
|
||||||
|
|
||||||
|
const helpers = usePatientForm(formState, schema);
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
clearForm: () => {
|
clearForm: () => {
|
||||||
@ -26,12 +19,16 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const actions = getAdmissionFormActions(handlers);
|
const actions = formActions(handlers);
|
||||||
|
|
||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
const payload = buildPayload(formState.form);
|
const payload = buildPayload(formState.form);
|
||||||
|
|
||||||
|
// const result = await formState.save(masterDetail.mode, payload);
|
||||||
|
|
||||||
console.log(payload);
|
console.log(payload);
|
||||||
|
toast('Visit Created!');
|
||||||
|
masterDetail?.exitForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryAction = $derived({
|
const primaryAction = $derived({
|
||||||
@ -44,10 +41,10 @@
|
|||||||
const secondaryActions = [];
|
const secondaryActions = [];
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (props.masterDetail.form?.PatientID) {
|
if (masterDetail.form?.PatientID) {
|
||||||
formState.setForm({
|
formState.setForm({
|
||||||
...formState.form,
|
...formState.form,
|
||||||
...props.masterDetail.form
|
...masterDetail.form
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -56,7 +53,7 @@
|
|||||||
<FormPageContainer title="Create Admission" {primaryAction} {secondaryActions} {actions}>
|
<FormPageContainer title="Create Admission" {primaryAction} {secondaryActions} {actions}>
|
||||||
<PatientFormRenderer
|
<PatientFormRenderer
|
||||||
{formState}
|
{formState}
|
||||||
formFields={admissionFormFields}
|
formFields={formFields}
|
||||||
mode="create"
|
mode="create"
|
||||||
/>
|
/>
|
||||||
</FormPageContainer>
|
</FormPageContainer>
|
||||||
@ -1,39 +1,51 @@
|
|||||||
<script>
|
<script>
|
||||||
import { useForm } from "$lib/components/composable/use-form.svelte";
|
|
||||||
import { admissionSchema, admissionInitialForm, admissionDefaultErrors, admissionFormFields, getAdmissionFormActions } from "$lib/components/patient/admission/config/admission-form-config";
|
|
||||||
import { editAdmission } from "$lib/components/patient/admission/api/patient-admission-api";
|
|
||||||
import { usePatientForm } from "$lib/components/composable/use-patient-form.svelte";
|
|
||||||
import FormPageContainer from "$lib/components/patient/reusable/form-page-container.svelte";
|
import FormPageContainer from "$lib/components/patient/reusable/form-page-container.svelte";
|
||||||
import PatientFormRenderer from "$lib/components/patient/reusable/patient-form-renderer.svelte";
|
import PatientFormRenderer from "$lib/components/patient/reusable/patient-form-renderer.svelte";
|
||||||
import { untrack } from "svelte";
|
import { untrack } from "svelte";
|
||||||
|
import { buildPayload } from "$lib/components/patient/admission/config/admission-form-config";
|
||||||
|
import { toast } from "svelte-sonner";
|
||||||
|
import { buildEditPayloadWithDiff } from "$lib/components/patient/admission/config/admission-payload";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
|
const { masterDetail, formFields, formActions, schema, initialForm, defaultError } = props.context;
|
||||||
|
|
||||||
let formState = useForm({
|
const { formState } = masterDetail;
|
||||||
schema: admissionSchema,
|
|
||||||
initialForm: admissionInitialForm,
|
|
||||||
defaultErrors: {},
|
|
||||||
mode: 'edit',
|
|
||||||
modeOpt: 'default',
|
|
||||||
saveEndpoint: null,
|
|
||||||
editEndpoint: editAdmission,
|
|
||||||
});
|
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const backendData = props.masterDetail?.selectedItem.data;
|
const backendData = masterDetail?.selectedItem.data;
|
||||||
if (!backendData) return;
|
if (!backendData) return;
|
||||||
console.log(backendData);
|
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
const formData = {
|
const formData = {
|
||||||
...backendData,
|
...backendData,
|
||||||
};
|
};
|
||||||
|
|
||||||
formState.setForm(formData);
|
formState.setForm(formData);
|
||||||
|
|
||||||
|
formFields.forEach(group => {
|
||||||
|
group.rows.forEach(row => {
|
||||||
|
row.columns.forEach(col => {
|
||||||
|
if (col.type === "select" && col.optionsEndpoint) {
|
||||||
|
formState.fetchOptions(col, formData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$inspect(formState.form)
|
||||||
async function handleEdit() {
|
async function handleEdit() {
|
||||||
console.log('object');
|
// const payload = buildPayload(formState.form);
|
||||||
|
const payload = buildEditPayloadWithDiff(
|
||||||
|
masterDetail.selectedItem?.data,
|
||||||
|
formState.form
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(payload);
|
||||||
|
// toast('Visit Updated!');
|
||||||
|
// masterDetail?.exitForm();
|
||||||
// const result = await formState.save();
|
// const result = await formState.save();
|
||||||
|
|
||||||
// if (result.status === 'success') {
|
// if (result.status === 'success') {
|
||||||
@ -55,7 +67,7 @@
|
|||||||
<FormPageContainer title="Edit Admission" {primaryAction} {secondaryActions}>
|
<FormPageContainer title="Edit Admission" {primaryAction} {secondaryActions}>
|
||||||
<PatientFormRenderer
|
<PatientFormRenderer
|
||||||
{formState}
|
{formState}
|
||||||
formFields={admissionFormFields}
|
formFields={formFields}
|
||||||
mode="edit"
|
mode="edit"
|
||||||
/>
|
/>
|
||||||
</FormPageContainer>
|
</FormPageContainer>
|
||||||
@ -5,11 +5,12 @@
|
|||||||
import { searchFields, admissionActions } from "$lib/components/patient/admission/config/admission-config";
|
import { searchFields, admissionActions } from "$lib/components/patient/admission/config/admission-config";
|
||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
import ReusableSearchParam from "$lib/components/reusable/reusable-search-param.svelte";
|
import ReusableSearchParam from "$lib/components/reusable/reusable-search-param.svelte";
|
||||||
import SearchParamModal from "../modal/search-param-modal.svelte";
|
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||||
|
import SearchParamModal from "../modal/search-param-modal.svelte";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
let selectedPID = $state(null);
|
let selectedPID = $state(null);
|
||||||
let selectedPatient = $state(null);
|
let selectedPatient = $state(null);
|
||||||
let tableData = $state([]);
|
let tableData = $state([]);
|
||||||
|
|||||||
@ -6,10 +6,12 @@
|
|||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
let visit = $derived(props.masterDetail?.selectedItem?.data);
|
const { masterDetail, formFields, formActions, schema } = props.context;
|
||||||
|
|
||||||
|
let visit = $derived(masterDetail?.selectedItem?.data);
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
editPatient: () => props.masterDetail.enterEdit(),
|
editPatient: () => masterDetail.enterEdit(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const actions = viewActions(handlers);
|
const actions = viewActions(handlers);
|
||||||
@ -44,10 +46,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if props.masterDetail.selectedItem}
|
{#if masterDetail.selectedItem}
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||||
<TopbarWrapper
|
<TopbarWrapper
|
||||||
title={props.masterDetail.selectedItem.data.PVID}
|
title={masterDetail.selectedItem.data.PVID}
|
||||||
{actions}
|
{actions}
|
||||||
/>
|
/>
|
||||||
<div class="flex-1 min-h-0 overflow-y-auto space-y-4">
|
<div class="flex-1 min-h-0 overflow-y-auto space-y-4">
|
||||||
@ -86,27 +88,4 @@
|
|||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<ReusableEmpty desc="Select a visit to see details"/>
|
<ReusableEmpty desc="Select a visit to see details"/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- {#if props.masterDetail.selectedItem}
|
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
|
||||||
<TopbarWrapper title={props.masterDetail.selectedItem.data.PVID} {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} -->
|
|
||||||
@ -5,6 +5,7 @@
|
|||||||
import { toast } from "svelte-sonner";
|
import { toast } from "svelte-sonner";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
const { masterDetail, formFields, formActions, schema } = props.context;
|
const { masterDetail, formFields, formActions, schema } = props.context;
|
||||||
|
|
||||||
const { formState } = masterDetail;
|
const { formState } = masterDetail;
|
||||||
@ -20,7 +21,7 @@
|
|||||||
const actions = formActions(handlers);
|
const actions = formActions(handlers);
|
||||||
|
|
||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
const result = await formState.save();
|
const result = await formState.save(masterDetail.mode);
|
||||||
|
|
||||||
if (result.status === 'success') {
|
if (result.status === 'success') {
|
||||||
toast('Patient Created!');
|
toast('Patient Created!');
|
||||||
|
|||||||
@ -7,12 +7,11 @@
|
|||||||
import { API } from "$lib/config/api";
|
import { API } from "$lib/config/api";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
const { masterDetail, formFields, formActions, schema, initialForm } = props.context;
|
|
||||||
|
const { masterDetail, formFields, formActions, schema, initialForm, defaultError } = props.context;
|
||||||
|
|
||||||
const { formState } = masterDetail;
|
const { formState } = masterDetail;
|
||||||
|
|
||||||
console.log(formState);
|
|
||||||
|
|
||||||
const helpers = usePatientForm(formState, schema);
|
const helpers = usePatientForm(formState, schema);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
@ -69,12 +68,13 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
$inspect(formState.form)
|
||||||
async function handleEdit() {
|
async function handleEdit() {
|
||||||
const result = await formState.save();
|
const result = await formState.save(masterDetail.mode);
|
||||||
|
|
||||||
if (result.status === 'success') {
|
if (result.status === 'success') {
|
||||||
console.log('Patient updated successfully');
|
console.log('Patient updated successfully');
|
||||||
|
toast('Patient Updated!');
|
||||||
masterDetail.exitForm();
|
masterDetail.exitForm();
|
||||||
} else {
|
} else {
|
||||||
console.error('Failed to update patient:', result.message);
|
console.error('Failed to update patient:', result.message);
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
const { masterDetail, formFields, formActions, schema } = props.context;
|
const { masterDetail, formFields, formActions, schema } = props.context;
|
||||||
|
|
||||||
let patient = $derived(masterDetail?.selectedItem?.patient);
|
let patient = $derived(masterDetail?.selectedItem?.patient);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";
|
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";
|
||||||
import * as Select from "$lib/components/ui/select/index.js";
|
import * as Select from "$lib/components/ui/select/index.js";
|
||||||
import * as ToggleGroup from "$lib/components/ui/toggle-group/index.js";
|
import { Toggle } from "$lib/components/ui/toggle/index.js";
|
||||||
import { Button } from "$lib/components/ui/button/index.js";
|
import { Button } from "$lib/components/ui/button/index.js";
|
||||||
import { Input } from "$lib/components/ui/input/index.js";
|
import { Input } from "$lib/components/ui/input/index.js";
|
||||||
import { Label } from "$lib/components/ui/label/index.js";
|
import { Label } from "$lib/components/ui/label/index.js";
|
||||||
@ -230,8 +230,8 @@
|
|||||||
}}
|
}}
|
||||||
onValueChange={(val) => {
|
onValueChange={(val) => {
|
||||||
formState.form.PatIdt = {
|
formState.form.PatIdt = {
|
||||||
IdentifierType: val,
|
IdentifierType: val,
|
||||||
Identifier:''
|
Identifier:''
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -284,23 +284,23 @@
|
|||||||
</div>
|
</div>
|
||||||
{:else if type === "toggle"}
|
{:else if type === "toggle"}
|
||||||
<div class="flex items-center w-full">
|
<div class="flex items-center w-full">
|
||||||
<ToggleGroup.Root variant="outline" type="single" class="w-full" bind:value={formState.form[key]} >
|
<Toggle
|
||||||
<ToggleGroup.Item
|
aria-label="Toggle discharge"
|
||||||
value="yes"
|
variant="outline"
|
||||||
aria-label="Toggle Yes"
|
class="w-full transition-all data-[state=on]:text-primary"
|
||||||
class="flex gap-2 px-4 w-1/2 transition-all data-[state=on]:bg-primary/10 data-[state=on]:text-primary data-[state=on]:border-primary/30 data-[state=on]:*:[svg]:stroke-[6px]"
|
bind:pressed={formState.form.isDischarge}
|
||||||
>
|
onPressedChange={(pressed) => {
|
||||||
<CheckIcon class="h-4 w-4" />
|
formState.form.ADTCode = pressed ? "A03" : "";
|
||||||
</ToggleGroup.Item>
|
}}
|
||||||
|
>
|
||||||
<ToggleGroup.Item
|
|
||||||
value="no"
|
{#if formState.form.isDischarge}
|
||||||
aria-label="Toggle No"
|
<XIcon class="mr-2 h-4 w-4" />
|
||||||
class="flex gap-2 px-4 w-1/2 transition-all data-[state=on]:bg-primary/10 data-[state=on]:text-primary data-[state=on]:border-primary/30 data-[state=on]:*:[svg]:stroke-[6px]"
|
{:else}
|
||||||
>
|
<CheckIcon class="mr-2 h-4 w-4" />
|
||||||
<XIcon class="h-4 w-4" />
|
{/if}
|
||||||
</ToggleGroup.Item>
|
{formState.form.isDischarge ? "Discharged" : "Active"}
|
||||||
</ToggleGroup.Root>
|
</Toggle>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -1,17 +1,39 @@
|
|||||||
<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 } from "$lib/components/patient/admission/api/patient-admission-api";
|
import { getVisit, createAdmission, editAdmission } from "$lib/components/patient/admission/api/patient-admission-api";
|
||||||
import MasterPage from "$lib/components/patient/admission/page/master-page.svelte";
|
import MasterPage from "$lib/components/patient/admission/page/master-page.svelte";
|
||||||
import ViewPage from "$lib/components/patient/admission/page/view-page.svelte";
|
import ViewPage from "$lib/components/patient/admission/page/view-page.svelte";
|
||||||
import CreatePage from "$lib/components/patient/admission/page/create-page.svelte";
|
import CreatePage from "$lib/components/patient/admission/page/create-page.svelte";
|
||||||
import EditPage from "$lib/components/patient/admission/page/edit-page.svelte";
|
import EditPage from "$lib/components/patient/admission/page/edit-page.svelte";
|
||||||
|
import { admissionSchema, admissionInitialForm, admissionDefaultErrors, admissionFormFields, getAdmissionFormActions, buildPayload } from "$lib/components/patient/admission/config/admission-form-config";
|
||||||
|
|
||||||
const masterDetail = useMasterDetail({
|
const masterDetail = useMasterDetail({
|
||||||
onSelect: async (row) => {
|
onSelect: async (row) => {
|
||||||
return await getVisit(row.PVID);
|
return await getVisit(row.PVID);
|
||||||
},
|
},
|
||||||
|
formConfig: {
|
||||||
|
schema: admissionSchema,
|
||||||
|
initialForm: admissionInitialForm,
|
||||||
|
defaultErrors: admissionDefaultErrors,
|
||||||
|
mode: 'create',
|
||||||
|
modeOpt: 'default',
|
||||||
|
saveEndpoint: createAdmission,
|
||||||
|
editEndpoint: editAdmission,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const pageContext = {
|
||||||
|
masterDetail,
|
||||||
|
formFields: admissionFormFields,
|
||||||
|
formActions: getAdmissionFormActions,
|
||||||
|
schema: admissionSchema,
|
||||||
|
initialForm: admissionInitialForm,
|
||||||
|
defaultErrors: {
|
||||||
|
create: admissionDefaultErrors,
|
||||||
|
edit: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex w-full h-full overflow-hidden">
|
<div class="flex w-full h-full overflow-hidden">
|
||||||
@ -22,11 +44,11 @@
|
|||||||
{#if masterDetail.showDetail}
|
{#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`}>
|
<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"}
|
{#if masterDetail.mode === "view"}
|
||||||
<ViewPage {masterDetail}/>
|
<ViewPage context={pageContext}/>
|
||||||
{:else if masterDetail.mode === "create"}
|
{:else if masterDetail.mode === "create"}
|
||||||
<CreatePage {masterDetail}/>
|
<CreatePage context={pageContext}/>
|
||||||
{:else if masterDetail.mode === "edit"}
|
{:else if masterDetail.mode === "edit"}
|
||||||
<EditPage {masterDetail}/>
|
<EditPage context={pageContext}/>
|
||||||
{/if}
|
{/if}
|
||||||
</main>
|
</main>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
schema: patientSchema,
|
schema: patientSchema,
|
||||||
initialForm: patientInitialForm,
|
initialForm: patientInitialForm,
|
||||||
defaultErrors: {
|
defaultErrors: {
|
||||||
create: patientDefaultErrors,
|
create: patientDefaultErrors,
|
||||||
edit: {}
|
edit: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user