wip: ui fix layout
fix(patient-form): add error handling in patient submission and simplify family form - Wrap patient submission in try-catch to show appropriate error messages - Simplify family parents form by removing conditional rendering and adding disabled state - Update form fields to use consistent labels and disable when not sharing family data feat(family-form): improve family data form handling and UI - Add edit mode detection to conditionally set default family data - Restructure form fields display based on shareFamilyData value - Show disabled placeholder fields when family data is not shared
This commit is contained in:
@@ -4,6 +4,7 @@ import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { FieldArray } from 'vee-validate'
|
||||
|
||||
// component
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import { Form } from '~/components/pub/ui/form'
|
||||
import { SelectEducation } from '~/components/app/patient/fields'
|
||||
import { InputBase } from '~/components/pub/my-ui/form'
|
||||
@@ -17,33 +18,45 @@ const props = defineProps<{
|
||||
|
||||
const formSchema = toTypedSchema(props.schema)
|
||||
const formRef = ref()
|
||||
const isFamilyFormDisabled = ref(true)
|
||||
|
||||
// Watcher untuk mengatur families ketika shareFamilyData berubah
|
||||
watch(
|
||||
() => formRef.value?.values?.shareFamilyData,
|
||||
(newValue) => {
|
||||
if (newValue === '1' && formRef.value) {
|
||||
// Ketika memilih "Ya", pastikan ada data families untuk mother dan father
|
||||
const currentFamilies = formRef.value.values?.families || []
|
||||
if (currentFamilies.length === 0) {
|
||||
formRef.value.setFieldValue('families', [
|
||||
{ relation: 'mother', name: undefined, education: undefined, occupation: undefined },
|
||||
{ relation: 'father', name: undefined, education: undefined, occupation: undefined },
|
||||
])
|
||||
}
|
||||
} else if (newValue === '0' && formRef.value) {
|
||||
// Ketika memilih "Tidak", kosongkan families
|
||||
formRef.value.setFieldValue('families', [])
|
||||
}
|
||||
},
|
||||
{ immediate: false },
|
||||
)
|
||||
const isEditing = computed(() => !!props.initialValues?.id)
|
||||
|
||||
defineExpose({
|
||||
validate: () => formRef.value?.validate(),
|
||||
resetForm: () => formRef.value?.resetForm(),
|
||||
values: computed(() => formRef.value?.values),
|
||||
})
|
||||
|
||||
watch(
|
||||
() => formRef.value?.values?.shareFamilyData,
|
||||
(newValue) => {
|
||||
if (!formRef.value) return
|
||||
|
||||
const currentFamilies = formRef.value.values?.families || []
|
||||
|
||||
if (newValue === '1') {
|
||||
isFamilyFormDisabled.value = false
|
||||
|
||||
// CREATE MODE — Auto default
|
||||
if (!isEditing.value && currentFamilies.length === 0) {
|
||||
formRef.value.setFieldValue('families', [
|
||||
{ relation: 'mother', name: '', education: '', occupation: '' },
|
||||
{ relation: 'father', name: '', education: '', occupation: '' },
|
||||
])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
isFamilyFormDisabled.value = true
|
||||
|
||||
if (!isEditing.value) {
|
||||
formRef.value.setFieldValue('families', [])
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -72,92 +85,91 @@ defineExpose({
|
||||
</div>
|
||||
|
||||
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||
<!-- Radio Button Section - Full Width -->
|
||||
<div class="mb-6">
|
||||
<RadioParentsInput field-name="shareFamilyData" />
|
||||
</div>
|
||||
|
||||
<!-- Form Fields Section - Only show when "Ya" is selected -->
|
||||
<div
|
||||
v-if="values.shareFamilyData === '1'"
|
||||
class="space-y-6"
|
||||
:key="values.shareFamilyData"
|
||||
>
|
||||
<FieldArray
|
||||
v-slot="{ fields }"
|
||||
name="families"
|
||||
>
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<template
|
||||
v-for="(field, idx) in fields"
|
||||
:key="field.key"
|
||||
>
|
||||
<template v-if="values.shareFamilyData === '1'">
|
||||
<FieldArray
|
||||
v-slot="{ fields }"
|
||||
name="families"
|
||||
>
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<div
|
||||
v-for="(field, idx) in fields"
|
||||
:key="field.key"
|
||||
class="space-y-4 rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-700 dark:bg-gray-800/50"
|
||||
>
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{{ (values.families as FamilyData[])?.[idx]?.relation === 'mother' ? 'Data Ibu' : 'Data Ayah' }}
|
||||
</h4>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<InputBase
|
||||
:field-name="`families[${idx}].name`"
|
||||
:label="
|
||||
(values.families as FamilyData[])?.[idx]?.relation === 'mother'
|
||||
? 'Nama Ibu Kandung'
|
||||
: (values.families as FamilyData[])?.[idx]?.relation === 'father'
|
||||
? 'Nama Ayah Kandung'
|
||||
: 'Nama Keluarga'
|
||||
"
|
||||
:placeholder="
|
||||
(values.families as FamilyData[])?.[idx]?.relation === 'mother'
|
||||
? 'Masukkan nama ibu pasien'
|
||||
: (values.families as FamilyData[])?.[idx]?.relation === 'father'
|
||||
? 'Masukkan nama ayah pasien'
|
||||
: 'Masukkan nama'
|
||||
"
|
||||
is-required
|
||||
/>
|
||||
</div>
|
||||
<DE.Block>
|
||||
<InputBase
|
||||
:field-name="`families[${idx}].name`"
|
||||
label="Nama"
|
||||
placeholder="Masukkan nama"
|
||||
:is-disabled="isFamilyFormDisabled"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<SelectEducation
|
||||
:field-name="`families[${idx}].education`"
|
||||
:label="
|
||||
(values.families as FamilyData[])?.[idx]?.relation === 'mother'
|
||||
? 'Pendidikan Ibu'
|
||||
: (values.families as FamilyData[])?.[idx]?.relation === 'father'
|
||||
? 'Pendidikan Ayah'
|
||||
: 'Pendidikan'
|
||||
"
|
||||
placeholder="Pilih"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SelectEducation
|
||||
:field-name="`families[${idx}].education`"
|
||||
label="Pendidikan"
|
||||
placeholder="Pilih"
|
||||
:is-disabled="isFamilyFormDisabled"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<InputBase
|
||||
:field-name="`families[${idx}].occupation`"
|
||||
:label="
|
||||
(values.families as FamilyData[])?.[idx]?.relation === 'mother'
|
||||
? 'Pekerjaan Ibu'
|
||||
: (values.families as FamilyData[])?.[idx]?.relation === 'father'
|
||||
? 'Pekerjaan Ayah'
|
||||
: 'Pekerjaan'
|
||||
"
|
||||
:placeholder="
|
||||
(values.families as FamilyData[])?.[idx]?.relation === 'mother'
|
||||
? 'Masukkan pekerjaan ibu'
|
||||
: (values.families as FamilyData[])?.[idx]?.relation === 'father'
|
||||
? 'Masukkan pekerjaan ayah'
|
||||
: 'Masukkan pekerjaan'
|
||||
"
|
||||
label="Pekerjaan"
|
||||
placeholder="Masukkan pekerjaan"
|
||||
:is-disabled="isFamilyFormDisabled"
|
||||
/>
|
||||
</div>
|
||||
</DE.Block>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</FieldArray>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<div
|
||||
v-for="relation in ['mother', 'father']"
|
||||
:key="relation"
|
||||
class="space-y-4 rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-700 dark:bg-gray-800/50"
|
||||
>
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{{ relation === 'mother' ? 'Data Ibu' : 'Data Ayah' }}
|
||||
</h4>
|
||||
|
||||
<DE.Block>
|
||||
<InputBase
|
||||
:field-name="`families[${relation}].name`"
|
||||
is-disabled
|
||||
label="Nama"
|
||||
placeholder="-"
|
||||
/>
|
||||
|
||||
<SelectEducation
|
||||
:field-name="`families[${relation}].education`"
|
||||
is-disabled
|
||||
label="Pendidikan"
|
||||
placeholder="-"
|
||||
/>
|
||||
|
||||
<InputBase
|
||||
:field-name="`families[${relation}].occupation`"
|
||||
is-disabled
|
||||
label="Pekerjaan"
|
||||
placeholder="-"
|
||||
/>
|
||||
</DE.Block>
|
||||
</div>
|
||||
</div>
|
||||
</FieldArray>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
@@ -135,49 +135,72 @@ async function composeFormData(): Promise<Patient> {
|
||||
|
||||
// #region Utilities & event handlers
|
||||
async function handleActionClick(eventType: string) {
|
||||
if (eventType === 'submit') {
|
||||
const patient: Patient = await composeFormData()
|
||||
let createdPatientId = 0
|
||||
try {
|
||||
if (eventType === 'submit') {
|
||||
const patient: Patient = await composeFormData()
|
||||
let createdPatientId = 0
|
||||
|
||||
const response = await handleActionSave(
|
||||
patient,
|
||||
() => {},
|
||||
() => {},
|
||||
toast,
|
||||
)
|
||||
const response = await handleActionSave(
|
||||
patient,
|
||||
() => {},
|
||||
() => {},
|
||||
toast,
|
||||
)
|
||||
|
||||
const data = (response?.body?.data ?? null) as PatientBase | null
|
||||
if (!data) return
|
||||
createdPatientId = data.id
|
||||
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 (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)
|
||||
// If has callback provided redirect to callback with patientData
|
||||
if (props.callbackUrl && props.callbackUrl.length > 0) {
|
||||
await navigateTo(props.callbackUrl + '?patient-id=' + createdPatientId)
|
||||
return
|
||||
}
|
||||
|
||||
// Navigate to patient list or show success message
|
||||
await navigateTo('/client/patient')
|
||||
return
|
||||
}
|
||||
|
||||
// Navigate to patient list or show success message
|
||||
await navigateTo('/client/patient')
|
||||
return
|
||||
}
|
||||
if (eventType === 'cancel') {
|
||||
if (props.callbackUrl) {
|
||||
await navigateTo(props.callbackUrl)
|
||||
return
|
||||
}
|
||||
|
||||
if (eventType === 'cancel') {
|
||||
if (props.callbackUrl) {
|
||||
await navigateTo(props.callbackUrl)
|
||||
return
|
||||
await navigateTo({
|
||||
name: 'client-patient',
|
||||
})
|
||||
// handleCancelForm()
|
||||
}
|
||||
} catch (error) {
|
||||
// Show error toast to user
|
||||
if (typeof error === 'string') {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: error,
|
||||
variant: 'destructive',
|
||||
})
|
||||
} else if (error instanceof Error) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: error.message || 'Terjadi kesalahan saat menyimpan data',
|
||||
variant: 'destructive',
|
||||
})
|
||||
} else {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'Terjadi kesalahan saat menyimpan data',
|
||||
variant: 'destructive',
|
||||
})
|
||||
}
|
||||
|
||||
await navigateTo({
|
||||
name: 'client-patient',
|
||||
})
|
||||
// handleCancelForm()
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
Reference in New Issue
Block a user