feat(patient): add patient handler and refactor form submission
- Introduce new patient handler using genCrudHandler for CRUD operations - Refactor patient entry form to use handler for save operations - Separate form data composition from submission logic - Handle file uploads and navigation after successful submission
This commit is contained in:
@@ -10,14 +10,28 @@ import { PersonAddressSchema } from '~/schemas/person-address.schema'
|
||||
import { PersonContactListSchema } from '~/schemas/person-contact.schema'
|
||||
import { PersonFamiliesSchema } from '~/schemas/person-family.schema'
|
||||
import { ResponsiblePersonSchema } from '~/schemas/person-relative.schema'
|
||||
import { postPatient, uploadAttachment } from '~/services/patient.service'
|
||||
import { uploadCode } from '~/lib/constants'
|
||||
import { uploadAttachment } from '~/services/patient.service'
|
||||
|
||||
import {
|
||||
// for form entry
|
||||
isReadonly,
|
||||
isProcessing,
|
||||
isFormEntryDialogOpen,
|
||||
isRecordConfirmationOpen,
|
||||
onResetState,
|
||||
handleActionSave,
|
||||
handleCancelForm,
|
||||
} from '~/handlers/patient.handler'
|
||||
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
|
||||
// #region Props & Emits
|
||||
const props = defineProps<{
|
||||
callbackUrl?: string
|
||||
}>()
|
||||
const payload = ref<Patient>()
|
||||
|
||||
const residentIdentityFile = ref<File>()
|
||||
const familyCardFile = ref<File>()
|
||||
|
||||
// form related state
|
||||
const personAddressForm = ref<ExposedForm<any> | null>(null)
|
||||
@@ -65,7 +79,7 @@ onMounted(() => {
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
async function sendRequest() {
|
||||
async function composeFormData(): Promise<Patient> {
|
||||
const [patient, address, addressRelative, families, contacts, emergencyContact] = await Promise.all([
|
||||
personPatientForm.value?.validate(),
|
||||
personAddressForm.value?.validate(),
|
||||
@@ -81,7 +95,7 @@ async function sendRequest() {
|
||||
|
||||
// exit, if form errors happend during validation
|
||||
// for example: dropdown not selected
|
||||
if (!allValid) return
|
||||
if (!allValid) return Promise.reject('Form validation failed')
|
||||
|
||||
const formDataRequest: genPatientProps = {
|
||||
patient: patient?.values,
|
||||
@@ -93,57 +107,64 @@ async function sendRequest() {
|
||||
}
|
||||
|
||||
const formData = genPatient(formDataRequest)
|
||||
console.log(formData)
|
||||
payload.value = formData
|
||||
|
||||
try {
|
||||
const result = await postPatient(formData)
|
||||
const patientData: PatientBase = result.body.data
|
||||
if (result.success) {
|
||||
console.log('Patient created successfully:', patientData)
|
||||
const createdPatientId = patientData.id
|
||||
|
||||
// void run uploadAttachment in background so this try-catch non blocking
|
||||
// behavior: fire-and-forget
|
||||
if (patient?.values.residentIdentityFile) {
|
||||
void uploadAttachment(patient?.values.residentIdentityFile, createdPatientId, 'ktp')
|
||||
}
|
||||
|
||||
if (patient?.values.familyIdentityFile) {
|
||||
void uploadAttachment(patient?.values.familyIdentityFile, createdPatientId, 'kk')
|
||||
}
|
||||
// 30s
|
||||
await new Promise((r) => setTimeout(r, 30_000))
|
||||
|
||||
// If has callback provided redirect to callback with patientData
|
||||
if (props.callbackUrl) {
|
||||
await navigateTo(props.callbackUrl + '?patient-id=' + patientData.id)
|
||||
return
|
||||
}
|
||||
|
||||
// Navigate to patient list or show success message
|
||||
await navigateTo('/client/patient')
|
||||
} else {
|
||||
console.error('Failed to create patient:', result)
|
||||
// Handle error - show error message to user
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating patient:', error)
|
||||
if (patient?.values.residentIdentityFile) {
|
||||
residentIdentityFile.value = patient?.values.residentIdentityFile
|
||||
}
|
||||
|
||||
if (patient?.values.familyIdentityFile) {
|
||||
familyCardFile.value = patient?.values.familyIdentityFile
|
||||
}
|
||||
|
||||
return new Promise((resolve) => resolve(formData))
|
||||
}
|
||||
// #endregion region
|
||||
|
||||
// #region Utilities & event handlers
|
||||
async function handleActionClick(eventType: string) {
|
||||
if (eventType === 'submit') {
|
||||
await sendRequest()
|
||||
const patient: Patient = await composeFormData()
|
||||
let createdPatientId = 0
|
||||
|
||||
const response = await handleActionSave(
|
||||
patient,
|
||||
() => {},
|
||||
() => {},
|
||||
toast,
|
||||
)
|
||||
|
||||
const data = (response?.body?.data ?? null) as PatientBase | null
|
||||
if (!data) return
|
||||
createdPatientId = data.id
|
||||
|
||||
if (residentIdentityFile.value) {
|
||||
void uploadAttachment(residentIdentityFile.value, createdPatientId, 'ktp')
|
||||
}
|
||||
if (familyCardFile.value) {
|
||||
void uploadAttachment(familyCardFile.value, createdPatientId, 'kk')
|
||||
}
|
||||
|
||||
// If has callback provided redirect to callback with patientData
|
||||
if (props.callbackUrl) {
|
||||
await navigateTo(props.callbackUrl + '?patient-id=' + patient.id)
|
||||
return
|
||||
}
|
||||
|
||||
// Navigate to patient list or show success message
|
||||
await navigateTo('/client/patient')
|
||||
return
|
||||
}
|
||||
|
||||
if (eventType === 'cancel') {
|
||||
navigateTo({
|
||||
if (props.callbackUrl) {
|
||||
await navigateTo(props.callbackUrl)
|
||||
return
|
||||
}
|
||||
|
||||
await navigateTo({
|
||||
name: 'client-patient',
|
||||
})
|
||||
// handleCancelForm()
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
@@ -129,23 +129,34 @@ export function genCrudHandler<T = any>(crud: {
|
||||
recItem.value = null
|
||||
}
|
||||
|
||||
async function handleActionSave(values: any, refresh: () => void, reset: () => void, toast: ToastFn) {
|
||||
async function handleActionSave(
|
||||
values: any,
|
||||
refresh: () => void,
|
||||
reset: () => void,
|
||||
toast: ToastFn,
|
||||
): Promise<any | null> {
|
||||
isProcessing.value = true
|
||||
|
||||
let successResponse: any = null
|
||||
|
||||
await handleAsyncAction<[any], any>({
|
||||
action: crud.create,
|
||||
args: [values],
|
||||
toast,
|
||||
successMessage: 'Data berhasil disimpan',
|
||||
errorMessage: 'Gagal menyimpan data',
|
||||
onSuccess: () => {
|
||||
onSuccess: (result) => {
|
||||
isFormEntryDialogOpen.value = false
|
||||
if (refresh) refresh()
|
||||
successResponse = result
|
||||
},
|
||||
onFinally: (isSuccess: boolean) => {
|
||||
if (isSuccess) setTimeout(reset, 500)
|
||||
isProcessing.value = false
|
||||
},
|
||||
})
|
||||
|
||||
return successResponse
|
||||
}
|
||||
|
||||
async function handleActionEdit(
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Handlers
|
||||
import { genCrudHandler } from '~/handlers/_handler'
|
||||
|
||||
// Services
|
||||
import { postPatient as create, patchPatient as update, removePatient as remove } from '~/services/patient.service'
|
||||
|
||||
export const {
|
||||
recId,
|
||||
recAction,
|
||||
recItem,
|
||||
isReadonly,
|
||||
isProcessing,
|
||||
isFormEntryDialogOpen,
|
||||
isRecordConfirmationOpen,
|
||||
onResetState,
|
||||
handleActionSave,
|
||||
handleActionEdit,
|
||||
handleActionRemove,
|
||||
handleCancelForm,
|
||||
} = genCrudHandler({
|
||||
create,
|
||||
update,
|
||||
remove,
|
||||
})
|
||||
Reference in New Issue
Block a user