edit-mode: unwrap detail data and use strict type
refactor(patient-form): simplify address synchronization logic - Extract address sync logic into helper functions - Remove unused schema imports - Streamline mounted hook with new loadInitData function - Consolidate watchers into single efficient watcher feat(forms): add readonly support across all form components Implement readonly state handling for patient, relative, contact, and address forms Add isDisabled prop to all form fields to support readonly mode Update form components to respect isReadonly prop from parent refactor(patient-form): reorganize imports and improve type usage - Group related imports and move type imports to the top - Replace genPatient with genPatientEntity for better type safety - Remove console.log statement - Fix formatting and indentation issues
This commit is contained in:
No files matched your search
@@ -27,8 +27,35 @@ import {
|
|||||||
} from './fields'
|
} from './fields'
|
||||||
|
|
||||||
interface FormData extends PatientFormData {}
|
interface FormData extends PatientFormData {}
|
||||||
|
|
||||||
|
// Type untuk initial values (sebelum transform schema)
|
||||||
|
interface PatientFormInput {
|
||||||
|
identityNumber?: string
|
||||||
|
drivingLicenseNumber?: string
|
||||||
|
passportNumber?: string
|
||||||
|
fullName?: string
|
||||||
|
isNewBorn?: 'YA' | 'TIDAK'
|
||||||
|
gender?: string
|
||||||
|
birthPlace?: string
|
||||||
|
birthDate?: string
|
||||||
|
education?: string
|
||||||
|
job?: string
|
||||||
|
maritalStatus?: string
|
||||||
|
nationality?: string
|
||||||
|
ethnicity?: string
|
||||||
|
language?: string
|
||||||
|
religion?: string
|
||||||
|
communicationBarrier?: 'YA' | 'TIDAK'
|
||||||
|
disability?: 'YA' | 'TIDAK'
|
||||||
|
disabilityType?: string
|
||||||
|
note?: string
|
||||||
|
residentIdentityFile?: File
|
||||||
|
familyIdentityFile?: File
|
||||||
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
initialValues?: FormData
|
isReadonly: boolean
|
||||||
|
initialValues?: PatientFormInput
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -37,7 +64,7 @@ const formSchema = toTypedSchema(PatientSchema)
|
|||||||
const { values, resetForm, setValues, validate } = useForm<FormData>({
|
const { values, resetForm, setValues, validate } = useForm<FormData>({
|
||||||
name: 'patientForm',
|
name: 'patientForm',
|
||||||
validationSchema: formSchema,
|
validationSchema: formSchema,
|
||||||
initialValues: props.initialValues && {},
|
initialValues: (props.initialValues ?? {}) as any,
|
||||||
validateOnMount: false,
|
validateOnMount: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -61,6 +88,7 @@ defineExpose({
|
|||||||
label="No. KTP"
|
label="No. KTP"
|
||||||
placeholder="Masukkan NIK"
|
placeholder="Masukkan NIK"
|
||||||
numeric-only
|
numeric-only
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<InputBase
|
<InputBase
|
||||||
field-name="drivingLicenseNumber"
|
field-name="drivingLicenseNumber"
|
||||||
@@ -68,12 +96,14 @@ defineExpose({
|
|||||||
placeholder="Masukkan nomor SIM"
|
placeholder="Masukkan nomor SIM"
|
||||||
numeric-only
|
numeric-only
|
||||||
:max-length="20"
|
:max-length="20"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<InputBase
|
<InputBase
|
||||||
field-name="passportNumber"
|
field-name="passportNumber"
|
||||||
label="No. Paspor"
|
label="No. Paspor"
|
||||||
placeholder="Masukkan nomor paspor"
|
placeholder="Masukkan nomor paspor"
|
||||||
:max-length="20"
|
:max-length="20"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<InputName
|
<InputName
|
||||||
field-name-alias="alias"
|
field-name-alias="alias"
|
||||||
@@ -81,46 +111,54 @@ defineExpose({
|
|||||||
label-for-input="Nama Lengkap"
|
label-for-input="Nama Lengkap"
|
||||||
placeholder="Masukkan nama lengkap pasien"
|
placeholder="Masukkan nama lengkap pasien"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<RadioNewborn
|
<RadioNewborn
|
||||||
field-name="isNewBorn"
|
field-name="isNewBorn"
|
||||||
label="Pasien Bayi"
|
label="Pasien Bayi"
|
||||||
placeholder="Pilih status pasien"
|
placeholder="Pilih status pasien"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<SelectGender
|
<SelectGender
|
||||||
field-name="gender"
|
field-name="gender"
|
||||||
label="Jenis Kelamin"
|
label="Jenis Kelamin"
|
||||||
placeholder="Pilih jenis kelamin"
|
placeholder="Pilih jenis kelamin"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<SelectBirthPlace
|
<SelectBirthPlace
|
||||||
field-name="birthPlace"
|
field-name="birthPlace"
|
||||||
label="Tempat Lahir"
|
label="Tempat Lahir"
|
||||||
placeholder="Pilih tempat lahir"
|
placeholder="Pilih tempat lahir"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<SelectDob
|
<SelectDob
|
||||||
label="Tanggal Lahir"
|
label="Tanggal Lahir"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<SelectEducation
|
<SelectEducation
|
||||||
field-name="education"
|
field-name="education"
|
||||||
label="Pendidikan"
|
label="Pendidikan"
|
||||||
placeholder="Pilih pendidikan"
|
placeholder="Pilih pendidikan"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<SelectJob
|
<SelectJob
|
||||||
field-name="job"
|
field-name="job"
|
||||||
label="Pekerjaan"
|
label="Pekerjaan"
|
||||||
placeholder="Pilih pekerjaan"
|
placeholder="Pilih pekerjaan"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<SelectMaritalStatus
|
<SelectMaritalStatus
|
||||||
field-name="maritalStatus"
|
field-name="maritalStatus"
|
||||||
label="Status Perkawinan"
|
label="Status Perkawinan"
|
||||||
placeholder="Pilih status Perkawinan"
|
placeholder="Pilih status Perkawinan"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<DE.Cell />
|
<DE.Cell />
|
||||||
<RadioNationality
|
<RadioNationality
|
||||||
@@ -128,45 +166,51 @@ defineExpose({
|
|||||||
label="Kebangsaan"
|
label="Kebangsaan"
|
||||||
placeholder="Pilih kebangsaan"
|
placeholder="Pilih kebangsaan"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<SelectEthnicity
|
<SelectEthnicity
|
||||||
field-name="ethnicity"
|
field-name="ethnicity"
|
||||||
label="Suku"
|
label="Suku"
|
||||||
placeholder="Pilih suku bangsa"
|
placeholder="Pilih suku bangsa"
|
||||||
:is-disabled="values.nationality !== 'WNI'"
|
:is-disabled="isReadonly || values.nationality !== 'WNI'"
|
||||||
/>
|
/>
|
||||||
<SelectLanguage
|
<SelectLanguage
|
||||||
field-name="language"
|
field-name="language"
|
||||||
label="Bahasa"
|
label="Bahasa"
|
||||||
placeholder="Pilih preferensi bahasa"
|
placeholder="Pilih preferensi bahasa"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<SelectReligion
|
<SelectReligion
|
||||||
field-name="religion"
|
field-name="religion"
|
||||||
label="Agama"
|
label="Agama"
|
||||||
placeholder="Pilih agama"
|
placeholder="Pilih agama"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<RadioCommunicationBarrier
|
<RadioCommunicationBarrier
|
||||||
field-name="communicationBarrier"
|
field-name="communicationBarrier"
|
||||||
label="Hambatan Berkomunikasi"
|
label="Hambatan Berkomunikasi"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<RadioDisability
|
<RadioDisability
|
||||||
field-name="disability"
|
field-name="disability"
|
||||||
label="Disabilitas"
|
label="Disabilitas"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<SelectDisability
|
<SelectDisability
|
||||||
label="Jenis Disabilitas"
|
label="Jenis Disabilitas"
|
||||||
field-name="disabilityType"
|
field-name="disabilityType"
|
||||||
:is-disabled="values.disability !== 'YA'"
|
:is-disabled="isReadonly || values.disability !== 'YA'"
|
||||||
:is-required="values.disability === 'YA'"
|
:is-required="values.disability === 'YA'"
|
||||||
/>
|
/>
|
||||||
<InputBase
|
<InputBase
|
||||||
field-name="note"
|
field-name="note"
|
||||||
label="Kepercayaan"
|
label="Kepercayaan"
|
||||||
placeholder="Contoh: tidak ingin diperiksa oleh dokter laki-laki"
|
placeholder="Contoh: tidak ingin diperiksa oleh dokter laki-laki"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
</DE.Block>
|
</DE.Block>
|
||||||
|
|
||||||
@@ -183,6 +227,7 @@ defineExpose({
|
|||||||
placeholder="Unggah scan dokumen KTP"
|
placeholder="Unggah scan dokumen KTP"
|
||||||
:accept="['pdf', 'jpg', 'png']"
|
:accept="['pdf', 'jpg', 'png']"
|
||||||
:max-size-mb="1"
|
:max-size-mb="1"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<FileUpload
|
<FileUpload
|
||||||
field-name="familyCardFile"
|
field-name="familyCardFile"
|
||||||
@@ -190,6 +235,7 @@ defineExpose({
|
|||||||
placeholder="Unggah scan dokumen KK"
|
placeholder="Unggah scan dokumen KK"
|
||||||
:accept="['pdf', 'jpg', 'png']"
|
:accept="['pdf', 'jpg', 'png']"
|
||||||
:max-size-mb="1"
|
:max-size-mb="1"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
</DE.Block>
|
</DE.Block>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ defineProps<{
|
|||||||
labelClass?: string
|
labelClass?: string
|
||||||
maxLength?: number
|
maxLength?: number
|
||||||
isRequired?: boolean
|
isRequired?: boolean
|
||||||
|
isDisabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ defineProps<{
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
|
:disabled="isDisabled"
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const props = defineProps<{
|
|||||||
radioItemClass?: string
|
radioItemClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
isRequired?: boolean
|
isRequired?: boolean
|
||||||
|
isDisabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -55,6 +56,7 @@ const genderOptions = [
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
|
:disabled="isDisabled"
|
||||||
:class="cn('flex flex-row flex-wrap gap-4 sm:gap-6', radioGroupClass)"
|
:class="cn('flex flex-row flex-wrap gap-4 sm:gap-6', radioGroupClass)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -63,20 +65,24 @@ const genderOptions = [
|
|||||||
:class="cn('flex min-w-fit items-center space-x-2', radioItemClass)"
|
:class="cn('flex min-w-fit items-center space-x-2', radioItemClass)"
|
||||||
>
|
>
|
||||||
<RadioGroupItem
|
<RadioGroupItem
|
||||||
|
:disabled="isDisabled"
|
||||||
:id="`${fieldName}-${index}`"
|
:id="`${fieldName}-${index}`"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'relative h-4 w-4 rounded-full border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5',
|
'peer relative h-4 w-4 rounded-full border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5',
|
||||||
containerClass,
|
containerClass,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<RadioLabel
|
<RadioLabel
|
||||||
:for="`${fieldName}-${index}`"
|
:for="isDisabled ? undefined : `${fieldName}-${index}`"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'cursor-pointer select-none font-normal text-xs leading-none transition-colors hover:text-primary peer-disabled:cursor-not-allowed peer-disabled:opacity-70 sm:text-sm',
|
'select-none text-xs !font-normal leading-none transition-colors sm:text-sm',
|
||||||
|
isDisabled
|
||||||
|
? 'cursor-not-allowed opacity-70'
|
||||||
|
: 'cursor-pointer hover:text-primary',
|
||||||
labelClass,
|
labelClass,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const props = defineProps<{
|
|||||||
radioItemClass?: string
|
radioItemClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
isRequired?: boolean
|
isRequired?: boolean
|
||||||
|
isDisabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -55,6 +56,7 @@ const dissabilityOptions = [
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
|
:disabled="isDisabled"
|
||||||
:class="cn('flex flex-row flex-wrap gap-4 sm:gap-6', radioGroupClass)"
|
:class="cn('flex flex-row flex-wrap gap-4 sm:gap-6', radioGroupClass)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -63,20 +65,24 @@ const dissabilityOptions = [
|
|||||||
:class="cn('flex min-w-fit items-center space-x-2', radioItemClass)"
|
:class="cn('flex min-w-fit items-center space-x-2', radioItemClass)"
|
||||||
>
|
>
|
||||||
<RadioGroupItem
|
<RadioGroupItem
|
||||||
|
:disabled="isDisabled"
|
||||||
:id="`${fieldName}-${index}`"
|
:id="`${fieldName}-${index}`"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'relative h-4 w-4 rounded-full border-1 border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5',
|
'peer border-1 relative h-4 w-4 rounded-full border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5',
|
||||||
containerClass,
|
containerClass,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<RadioLabel
|
<RadioLabel
|
||||||
:for="`${fieldName}-${index}`"
|
:for="isDisabled ? undefined : `${fieldName}-${index}`"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'cursor-pointer select-none text-xs !font-normal leading-none transition-colors hover:text-primary peer-disabled:cursor-not-allowed peer-disabled:opacity-70 sm:text-sm',
|
'select-none text-xs !font-normal leading-none transition-colors',
|
||||||
|
isDisabled
|
||||||
|
? 'cursor-not-allowed opacity-70'
|
||||||
|
: 'cursor-pointer hover:text-primary',
|
||||||
labelClass,
|
labelClass,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const props = defineProps<{
|
|||||||
radioItemClass?: string
|
radioItemClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
isRequired?: boolean
|
isRequired?: boolean
|
||||||
|
isDisabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -55,6 +56,7 @@ const nationalityOptions = [
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
|
:disabled="isDisabled"
|
||||||
:class="cn('flex flex-row flex-wrap gap-4 sm:gap-6', radioGroupClass)"
|
:class="cn('flex flex-row flex-wrap gap-4 sm:gap-6', radioGroupClass)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -63,20 +65,24 @@ const nationalityOptions = [
|
|||||||
:class="cn('flex min-w-fit items-center space-x-2', radioItemClass)"
|
:class="cn('flex min-w-fit items-center space-x-2', radioItemClass)"
|
||||||
>
|
>
|
||||||
<RadioGroupItem
|
<RadioGroupItem
|
||||||
|
:disabled="isDisabled"
|
||||||
:id="`${fieldName}-${index}`"
|
:id="`${fieldName}-${index}`"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'relative h-4 w-4 rounded-full border-1 border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5',
|
'peer relative h-4 w-4 rounded-full border-1 border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5',
|
||||||
containerClass,
|
containerClass,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<RadioLabel
|
<RadioLabel
|
||||||
:for="`${fieldName}-${index}`"
|
:for="isDisabled ? undefined : `${fieldName}-${index}`"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'cursor-pointer select-none text-xs !font-normal leading-none transition-colors hover:text-primary peer-disabled:cursor-not-allowed peer-disabled:opacity-70 sm:text-sm',
|
'select-none text-xs !font-normal leading-none transition-colors sm:text-sm',
|
||||||
|
isDisabled
|
||||||
|
? 'cursor-not-allowed opacity-70'
|
||||||
|
: 'cursor-pointer hover:text-primary',
|
||||||
labelClass,
|
labelClass,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const props = defineProps<{
|
|||||||
radioItemClass?: string
|
radioItemClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
isRequired?: boolean
|
isRequired?: boolean
|
||||||
|
isDisabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -34,7 +35,10 @@ const newbornOptions = [
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<DE.Cell :class="cn('radio-group-field', containerClass)" :col-span="2">
|
<DE.Cell
|
||||||
|
:class="cn('radio-group-field', containerClass)"
|
||||||
|
:col-span="2"
|
||||||
|
>
|
||||||
<DE.Label
|
<DE.Label
|
||||||
:label-for="fieldName"
|
:label-for="fieldName"
|
||||||
:is-required="isRequired"
|
:is-required="isRequired"
|
||||||
@@ -52,6 +56,7 @@ const newbornOptions = [
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
|
:disabled="isDisabled"
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
:class="cn('flex flex-row flex-wrap gap-4 sm:gap-6', radioGroupClass)"
|
:class="cn('flex flex-row flex-wrap gap-4 sm:gap-6', radioGroupClass)"
|
||||||
>
|
>
|
||||||
@@ -61,20 +66,24 @@ const newbornOptions = [
|
|||||||
:class="cn('flex min-w-fit items-center space-x-2 pt-1', radioItemClass)"
|
:class="cn('flex min-w-fit items-center space-x-2 pt-1', radioItemClass)"
|
||||||
>
|
>
|
||||||
<RadioGroupItem
|
<RadioGroupItem
|
||||||
|
:disabled="isDisabled"
|
||||||
:id="`${fieldName}-${index}`"
|
:id="`${fieldName}-${index}`"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'relative h-4 w-4 rounded-full border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5',
|
'peer relative h-4 w-4 rounded-full border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5',
|
||||||
containerClass,
|
containerClass,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<RadioLabel
|
<RadioLabel
|
||||||
:for="`${fieldName}-${index}`"
|
:for="isDisabled ? undefined : `${fieldName}-${index}`"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'cursor-pointer select-none text-xs font-normal leading-none transition-colors hover:text-primary peer-disabled:cursor-not-allowed peer-disabled:opacity-70 sm:text-sm',
|
'select-none text-xs !font-normal leading-none transition-colors',
|
||||||
|
isDisabled
|
||||||
|
? 'cursor-not-allowed opacity-70'
|
||||||
|
: 'cursor-pointer hover:text-primary',
|
||||||
labelClass,
|
labelClass,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const props = defineProps<{
|
|||||||
fieldGroupClass?: string
|
fieldGroupClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
isRequired?: boolean
|
isRequired?: boolean
|
||||||
|
isDisabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -95,6 +96,7 @@ function calculateAge(birthDate: string | Date | undefined): string {
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
|
:disabled="isDisabled"
|
||||||
id="birthDate"
|
id="birthDate"
|
||||||
type="date"
|
type="date"
|
||||||
min="1900-01-01"
|
min="1900-01-01"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const props = defineProps<{
|
|||||||
fieldGroupClass?: string
|
fieldGroupClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
isRequired?: boolean
|
isRequired?: boolean
|
||||||
|
isDisabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -53,8 +54,9 @@ const jobOptions = mapToComboboxOptList(occupationCodes)
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Combobox
|
<Combobox
|
||||||
:id="fieldName"
|
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
|
:is-disabled="isDisabled"
|
||||||
|
:id="fieldName"
|
||||||
:items="jobOptions"
|
:items="jobOptions"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
search-placeholder="Cari..."
|
search-placeholder="Cari..."
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const props = defineProps<{
|
|||||||
fieldGroupClass?: string
|
fieldGroupClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
isRequired?: boolean
|
isRequired?: boolean
|
||||||
|
isDisabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -60,6 +61,7 @@ const langOptions = [
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Select
|
<Select
|
||||||
|
:is-disabled="isDisabled"
|
||||||
:id="fieldName"
|
:id="fieldName"
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
:items="langOptions"
|
:items="langOptions"
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const props = defineProps<{
|
|||||||
fieldGroupClass?: string
|
fieldGroupClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
isRequired?: boolean
|
isRequired?: boolean
|
||||||
|
isDisabled?: boolean
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
@@ -56,8 +57,9 @@ const maritalStatusOptions = [
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Select
|
<Select
|
||||||
:id="fieldName"
|
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
|
:is-disabled="isDisabled"
|
||||||
|
:id="fieldName"
|
||||||
:items="maritalStatusOptions"
|
:items="maritalStatusOptions"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:preserve-order="true"
|
:preserve-order="true"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const props = defineProps<{
|
|||||||
fieldGroupClass?: string
|
fieldGroupClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
isRequired?: boolean
|
isRequired?: boolean
|
||||||
|
isDisabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -68,6 +69,7 @@ const religionOptions = Array.from(
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Select
|
<Select
|
||||||
|
:is-disabled="isDisabled"
|
||||||
:id="fieldName"
|
:id="fieldName"
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
:items="religionOptions"
|
:items="religionOptions"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ interface FormData extends PersonAddressRelativeFormData {}
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
|
isReadonly: boolean
|
||||||
conf?: {
|
conf?: {
|
||||||
withAddressName?: boolean
|
withAddressName?: boolean
|
||||||
}
|
}
|
||||||
@@ -313,6 +314,7 @@ watch(
|
|||||||
class="flex min-w-fit items-center space-x-2"
|
class="flex min-w-fit items-center space-x-2"
|
||||||
>
|
>
|
||||||
<RadioGroupItem
|
<RadioGroupItem
|
||||||
|
:disabled="isReadonly"
|
||||||
:id="`isSameAddress-${index}`"
|
:id="`isSameAddress-${index}`"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
class="relative h-4 w-4 rounded-full border-2 border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5"
|
class="relative h-4 w-4 rounded-full border-2 border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5"
|
||||||
@@ -334,32 +336,32 @@ watch(
|
|||||||
<SelectProvince
|
<SelectProvince
|
||||||
field-name="province_code"
|
field-name="province_code"
|
||||||
placeholder="Pilih"
|
placeholder="Pilih"
|
||||||
:is-disabled="isSameAddress"
|
:is-disabled="isReadonly || isSameAddress"
|
||||||
:is-required="!isSameAddress"
|
:is-required="!isSameAddress"
|
||||||
/>
|
/>
|
||||||
<SelectRegency
|
<SelectRegency
|
||||||
field-name="regency_code"
|
field-name="regency_code"
|
||||||
:province-code="values.province_code"
|
:province-code="values.province_code"
|
||||||
:is-disabled="getFieldState('regency_code').disabled"
|
:is-disabled="isReadonly || getFieldState('regency_code').disabled"
|
||||||
:is-required="!isSameAddress"
|
:is-required="!isSameAddress"
|
||||||
/>
|
/>
|
||||||
<SelectDistrict
|
<SelectDistrict
|
||||||
field-name="district_code"
|
field-name="district_code"
|
||||||
:regency-code="values.regency_code"
|
:regency-code="values.regency_code"
|
||||||
:is-disabled="getFieldState('district_code').disabled"
|
:is-disabled="isReadonly || getFieldState('district_code').disabled"
|
||||||
:is-required="!isSameAddress"
|
:is-required="!isSameAddress"
|
||||||
/>
|
/>
|
||||||
<SelectVillage
|
<SelectVillage
|
||||||
field-name="village_code"
|
field-name="village_code"
|
||||||
:district-code="values.district_code"
|
:district-code="values.district_code"
|
||||||
:is-disabled="getFieldState('village_code').disabled"
|
:is-disabled="isReadonly || getFieldState('village_code').disabled"
|
||||||
:is-required="!isSameAddress"
|
:is-required="!isSameAddress"
|
||||||
/>
|
/>
|
||||||
<InputBase
|
<InputBase
|
||||||
field-name="address"
|
field-name="address"
|
||||||
label="Alamat"
|
label="Alamat"
|
||||||
:placeholder="getFieldState('address').placeholder"
|
:placeholder="getFieldState('address').placeholder"
|
||||||
:is-disabled="getFieldState('address').disabled"
|
:is-disabled="isReadonly || getFieldState('address').disabled"
|
||||||
:is-required="!isSameAddress"
|
:is-required="!isSameAddress"
|
||||||
:col-span="2"
|
:col-span="2"
|
||||||
/>
|
/>
|
||||||
@@ -370,13 +372,13 @@ watch(
|
|||||||
numeric-only
|
numeric-only
|
||||||
:max-length="2"
|
:max-length="2"
|
||||||
:placeholder="getFieldState('rt').placeholder"
|
:placeholder="getFieldState('rt').placeholder"
|
||||||
:is-disabled="getFieldState('rt').disabled"
|
:is-disabled="isReadonly || getFieldState('rt').disabled"
|
||||||
/>
|
/>
|
||||||
<InputBase
|
<InputBase
|
||||||
field-name="rw"
|
field-name="rw"
|
||||||
label="RW"
|
label="RW"
|
||||||
:placeholder="getFieldState('rw').placeholder"
|
:placeholder="getFieldState('rw').placeholder"
|
||||||
:is-disabled="getFieldState('rw').disabled"
|
:is-disabled="isReadonly || getFieldState('rw').disabled"
|
||||||
:max-length="2"
|
:max-length="2"
|
||||||
numeric-only
|
numeric-only
|
||||||
/>
|
/>
|
||||||
@@ -385,7 +387,7 @@ watch(
|
|||||||
field-name="postalRegion_code"
|
field-name="postalRegion_code"
|
||||||
:village-code="values.village_code"
|
:village-code="values.village_code"
|
||||||
:placeholder="getFieldState('postalRegion_code').placeholder"
|
:placeholder="getFieldState('postalRegion_code').placeholder"
|
||||||
:is-disabled="getFieldState('postalRegion_code').disabled || !values.village_code"
|
:is-disabled="isReadonly || getFieldState('postalRegion_code').disabled || !values.village_code"
|
||||||
/>
|
/>
|
||||||
</DE.Block>
|
</DE.Block>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ interface FormData extends PersonAddressFormData {}
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
|
isReadonly: boolean
|
||||||
conf?: {
|
conf?: {
|
||||||
withAddressName?: boolean
|
withAddressName?: boolean
|
||||||
}
|
}
|
||||||
@@ -188,21 +189,25 @@ watch(
|
|||||||
<SelectProvince
|
<SelectProvince
|
||||||
field-name="province_code"
|
field-name="province_code"
|
||||||
placeholder="Pilih"
|
placeholder="Pilih"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
is-required
|
is-required
|
||||||
/>
|
/>
|
||||||
<SelectRegency
|
<SelectRegency
|
||||||
field-name="regency_code"
|
field-name="regency_code"
|
||||||
:province-code="values.province_code"
|
:province-code="values.province_code"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
is-required
|
is-required
|
||||||
/>
|
/>
|
||||||
<SelectDistrict
|
<SelectDistrict
|
||||||
field-name="district_code"
|
field-name="district_code"
|
||||||
:regency-code="values.regency_code"
|
:regency-code="values.regency_code"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
is-required
|
is-required
|
||||||
/>
|
/>
|
||||||
<SelectVillage
|
<SelectVillage
|
||||||
field-name="village_code"
|
field-name="village_code"
|
||||||
:district-code="values.district_code"
|
:district-code="values.district_code"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
is-required
|
is-required
|
||||||
/>
|
/>
|
||||||
<InputBase
|
<InputBase
|
||||||
@@ -210,6 +215,7 @@ watch(
|
|||||||
label="Alamat"
|
label="Alamat"
|
||||||
placeholder="Masukkan alamat"
|
placeholder="Masukkan alamat"
|
||||||
is-required
|
is-required
|
||||||
|
:is-disabled="isReadonly"
|
||||||
:col-span="2"
|
:col-span="2"
|
||||||
/>
|
/>
|
||||||
<DE.Cell class="flex-row gap-2">
|
<DE.Cell class="flex-row gap-2">
|
||||||
@@ -220,6 +226,7 @@ watch(
|
|||||||
placeholder="01"
|
placeholder="01"
|
||||||
numeric-only
|
numeric-only
|
||||||
:max-length="2"
|
:max-length="2"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<InputBase
|
<InputBase
|
||||||
field-name="rw"
|
field-name="rw"
|
||||||
@@ -227,6 +234,7 @@ watch(
|
|||||||
placeholder="02"
|
placeholder="02"
|
||||||
:max-length="2"
|
:max-length="2"
|
||||||
numeric-only
|
numeric-only
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ interface FormData extends PersonContactFormData {}
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
|
isReadonly: boolean
|
||||||
contactLimit?: number
|
contactLimit?: number
|
||||||
initialValues?: any
|
initialValues?: any
|
||||||
isReadonly?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -36,8 +36,6 @@ defineExpose({
|
|||||||
setValues,
|
setValues,
|
||||||
values,
|
values,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { title = 'Kontak Pasien', isReadonly = false } = props
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -94,7 +92,7 @@ const { title = 'Kontak Pasien', isReadonly = false } = props
|
|||||||
preset="add"
|
preset="add"
|
||||||
label="Tambah Kontak"
|
label="Tambah Kontak"
|
||||||
title="Tambah Kontak ke Daftar Kontak"
|
title="Tambah Kontak ke Daftar Kontak"
|
||||||
:disabled="fields.length >= contactLimit || isReadonly"
|
:disabled="isReadonly || fields.length >= contactLimit"
|
||||||
:full-width-mobile="true"
|
:full-width-mobile="true"
|
||||||
class="mt-4"
|
class="mt-4"
|
||||||
@click="push({ contactType: '', contactNumber: '' })"
|
@click="push({ contactType: '', contactNumber: '' })"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ interface FormData extends PersonRelativeFormData {}
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
isReadonly?: boolean
|
isReadonly: boolean
|
||||||
initialValues?: any
|
initialValues?: any
|
||||||
contactLimit?: number
|
contactLimit?: number
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,7 @@ defineExpose({
|
|||||||
values: values,
|
values: values,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { title = 'Kontak Pasien', isReadonly = false, contactLimit = 5 } = props
|
const { title = 'Kontak Pasien', contactLimit = 5 } = props
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ interface FormData extends PersonFamiliesFormData {}
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
|
isReadonly: boolean
|
||||||
initialValues?: any
|
initialValues?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +85,10 @@ watch(
|
|||||||
|
|
||||||
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<RadioParentsInput field-name="shareFamilyData" />
|
<RadioParentsInput
|
||||||
|
field-name="shareFamilyData"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -111,21 +115,21 @@ watch(
|
|||||||
:field-name="`families[${idx}].name`"
|
:field-name="`families[${idx}].name`"
|
||||||
label="Nama"
|
label="Nama"
|
||||||
placeholder="Masukkan nama"
|
placeholder="Masukkan nama"
|
||||||
:is-disabled="isFamilyFormDisabled"
|
:is-disabled="isReadonly || isFamilyFormDisabled"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectEducation
|
<SelectEducation
|
||||||
:field-name="`families[${idx}].education`"
|
:field-name="`families[${idx}].education`"
|
||||||
label="Pendidikan"
|
label="Pendidikan"
|
||||||
placeholder="Pilih"
|
placeholder="Pilih"
|
||||||
:is-disabled="isFamilyFormDisabled"
|
:is-disabled="isReadonly || isFamilyFormDisabled"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InputBase
|
<InputBase
|
||||||
:field-name="`families[${idx}].occupation`"
|
:field-name="`families[${idx}].occupation`"
|
||||||
label="Pekerjaan"
|
label="Pekerjaan"
|
||||||
placeholder="Masukkan pekerjaan"
|
placeholder="Masukkan pekerjaan"
|
||||||
:is-disabled="isFamilyFormDisabled"
|
:is-disabled="isReadonly || isFamilyFormDisabled"
|
||||||
/>
|
/>
|
||||||
</DE.Block>
|
</DE.Block>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const props = defineProps<{
|
|||||||
radioItemClass?: string
|
radioItemClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
isRequired?: boolean
|
isRequired?: boolean
|
||||||
|
isDisabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -55,6 +56,7 @@ const residenceOptions = [
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
|
:disabled="isDisabled"
|
||||||
:class="cn('flex flex-row flex-wrap gap-4 sm:gap-6', radioGroupClass)"
|
:class="cn('flex flex-row flex-wrap gap-4 sm:gap-6', radioGroupClass)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -63,20 +65,22 @@ const residenceOptions = [
|
|||||||
:class="cn('flex min-w-fit items-center space-x-2', radioItemClass)"
|
:class="cn('flex min-w-fit items-center space-x-2', radioItemClass)"
|
||||||
>
|
>
|
||||||
<RadioGroupItem
|
<RadioGroupItem
|
||||||
|
:disabled="isDisabled"
|
||||||
:id="`${fieldName}-${index}`"
|
:id="`${fieldName}-${index}`"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'relative h-4 w-4 rounded-full border-2 border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5',
|
'peer relative h-4 w-4 rounded-full border-2 border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5',
|
||||||
containerClass,
|
containerClass,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<RadioLabel
|
<RadioLabel
|
||||||
:for="`${fieldName}-${index}`"
|
:for="isDisabled ? undefined : `${fieldName}-${index}`"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'cursor-pointer select-none text-xs font-medium leading-none transition-colors hover:text-primary peer-disabled:cursor-not-allowed peer-disabled:opacity-70 sm:text-sm',
|
'select-none text-xs font-medium leading-none transition-colors sm:text-sm',
|
||||||
|
isDisabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer hover:text-primary',
|
||||||
labelClass,
|
labelClass,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -1,20 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// type
|
// type
|
||||||
import { withBase } from '~/models/_base'
|
import { withBase } from '~/models/_base'
|
||||||
import type { PatientEntity, PatientBase, Patient, genPatientProps } from '~/models/patient'
|
|
||||||
import type { Person } from '~/models/person'
|
import type { Person } from '~/models/person'
|
||||||
|
import type { PersonAddress } from '~/models/person-address'
|
||||||
|
import type { PersonContact } from '~/models/person-contact'
|
||||||
|
import type { PersonRelative } from '~/models/person-relative'
|
||||||
import type { ExposedForm } from '~/types/form'
|
import type { ExposedForm } from '~/types/form'
|
||||||
import type { HeaderPrep } from '~/components/pub/my-ui/data/types'
|
import type { PatientEntity, PatientBase, Patient, genPatientProps } from '~/models/patient'
|
||||||
|
import { genPatientEntity } from '~/models/patient'
|
||||||
// schema and models
|
|
||||||
import { genPatient } from '~/models/patient'
|
|
||||||
import { PatientSchema } from '~/schemas/patient.schema'
|
|
||||||
import { PersonAddressRelativeSchema } from '~/schemas/person-address-relative.schema'
|
|
||||||
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'
|
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
||||||
@@ -29,7 +22,6 @@ import AppPersonRelativeEntryForm from '~/components/app/person-relative/entry-f
|
|||||||
import { getPatientDetail, uploadAttachment } from '~/services/patient.service'
|
import { getPatientDetail, uploadAttachment } from '~/services/patient.service'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
// for form entry
|
|
||||||
isReadonly,
|
isReadonly,
|
||||||
isProcessing,
|
isProcessing,
|
||||||
isFormEntryDialogOpen,
|
isFormEntryDialogOpen,
|
||||||
@@ -41,6 +33,14 @@ import {
|
|||||||
|
|
||||||
import { toast } from '~/components/pub/ui/toast'
|
import { toast } from '~/components/pub/ui/toast'
|
||||||
|
|
||||||
|
// reverse mapping untuk contact type (backend → UI)
|
||||||
|
const reverseContactTypeMapping: Record<string, string> = {
|
||||||
|
'm-phone': 'phoneNumber',
|
||||||
|
phone: 'homePhoneNumber',
|
||||||
|
email: 'email',
|
||||||
|
fax: 'fax',
|
||||||
|
}
|
||||||
|
|
||||||
// #region Props & Emits
|
// #region Props & Emits
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
callbackUrl?: string
|
callbackUrl?: string
|
||||||
@@ -70,51 +70,178 @@ const patient = ref(
|
|||||||
personRelatives: [],
|
personRelatives: [],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Key untuk memaksa re-render form saat data berubah
|
||||||
|
const formKey = ref(0)
|
||||||
|
|
||||||
|
// Computed: unwrap patient data untuk form patient
|
||||||
|
const patientFormInitialValues = computed(() => {
|
||||||
|
const p = patient.value
|
||||||
|
const person = p.person
|
||||||
|
if (!person || !person.name) return undefined
|
||||||
|
|
||||||
|
return {
|
||||||
|
identityNumber: person.residentIdentityNumber || '',
|
||||||
|
drivingLicenseNumber: person.drivingLicenseNumber || '',
|
||||||
|
passportNumber: person.passportNumber || '',
|
||||||
|
fullName: person.name || '',
|
||||||
|
isNewBorn: (p.newBornStatus ? 'YA' : 'TIDAK') as 'YA' | 'TIDAK',
|
||||||
|
gender: person.gender_code || '',
|
||||||
|
birthPlace: person.birthRegency_code || '',
|
||||||
|
birthDate: person.birthDate ? new Date(person.birthDate).toISOString().split('T')[0] : '',
|
||||||
|
education: person.education_code || '',
|
||||||
|
job: person.occupation_code || '',
|
||||||
|
maritalStatus: '', // perlu mapping jika ada field ini
|
||||||
|
nationality: person.nationality || 'WNI',
|
||||||
|
ethnicity: person.ethnic_code || '',
|
||||||
|
language: person.language_code || '',
|
||||||
|
religion: person.religion_code || '',
|
||||||
|
communicationBarrier: (person.communicationIssueStatus ? 'YA' : 'TIDAK') as 'YA' | 'TIDAK',
|
||||||
|
disability: (person.disability ? 'YA' : 'TIDAK') as 'YA' | 'TIDAK',
|
||||||
|
disabilityType: person.disability || '',
|
||||||
|
note: '',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Computed: unwrap alamat domisili (alamat sekarang)
|
||||||
|
const addressFormInitialValues = computed(() => {
|
||||||
|
const addresses = patient.value.person?.addresses || patient.value.personAddresses || []
|
||||||
|
const domicileAddress = addresses.find((a: PersonAddress) => a.locationType_code === 'domicile')
|
||||||
|
if (!domicileAddress) return undefined
|
||||||
|
|
||||||
|
// extract kode wilayah dari preload data
|
||||||
|
const village = domicileAddress.postalRegion?.village
|
||||||
|
const district = village?.district
|
||||||
|
const regency = district?.regency
|
||||||
|
const province = regency?.province
|
||||||
|
|
||||||
|
return {
|
||||||
|
locationType_code: 'domicile',
|
||||||
|
province_code: province?.code || '',
|
||||||
|
regency_code: regency?.code || '',
|
||||||
|
district_code: district?.code || '',
|
||||||
|
village_code: village?.code || domicileAddress.village_code || '',
|
||||||
|
postalRegion_code: domicileAddress.postalRegion_code || '',
|
||||||
|
address: domicileAddress.address || '',
|
||||||
|
rt: domicileAddress.rt || '',
|
||||||
|
rw: domicileAddress.rw || '',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Computed: unwrap alamat KTP (identity)
|
||||||
|
const addressRelativeFormInitialValues = computed(() => {
|
||||||
|
const addresses = patient.value.person?.addresses || patient.value.personAddresses || []
|
||||||
|
const domicileAddress = addresses.find((a: PersonAddress) => a.locationType_code === 'domicile')
|
||||||
|
const identityAddress = addresses.find((a: PersonAddress) => a.locationType_code === 'identity')
|
||||||
|
|
||||||
|
// Jika tidak ada alamat KTP terpisah, berarti sama dengan domisili
|
||||||
|
if (!identityAddress) {
|
||||||
|
return {
|
||||||
|
isSameAddress: '1',
|
||||||
|
locationType_code: 'identity',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cek apakah alamat sama dengan domisili
|
||||||
|
const isSame =
|
||||||
|
domicileAddress &&
|
||||||
|
identityAddress.village_code === domicileAddress.village_code &&
|
||||||
|
identityAddress.address === domicileAddress.address
|
||||||
|
|
||||||
|
if (isSame) {
|
||||||
|
return {
|
||||||
|
isSameAddress: '1',
|
||||||
|
locationType_code: 'identity',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract kode wilayah dari preload data
|
||||||
|
const village = identityAddress.postalRegion?.village
|
||||||
|
const district = village?.district
|
||||||
|
const regency = district?.regency
|
||||||
|
const province = regency?.province
|
||||||
|
|
||||||
|
return {
|
||||||
|
isSameAddress: '0',
|
||||||
|
locationType_code: 'identity',
|
||||||
|
province_code: province?.code || '',
|
||||||
|
regency_code: regency?.code || '',
|
||||||
|
district_code: district?.code || '',
|
||||||
|
village_code: village?.code || identityAddress.village_code || '',
|
||||||
|
postalRegion_code: identityAddress.postalRegion_code || '',
|
||||||
|
address: identityAddress.address || '',
|
||||||
|
rt: identityAddress.rt || '',
|
||||||
|
rw: identityAddress.rw || '',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Computed: unwrap data orang tua
|
||||||
|
const familyFormInitialValues = computed(() => {
|
||||||
|
const relatives = patient.value.person?.relatives || patient.value.personRelatives || []
|
||||||
|
const parents = relatives.filter(
|
||||||
|
(r: PersonRelative) => !r.responsible && (r.relationship_code === 'mother' || r.relationship_code === 'father'),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (parents.length === 0) {
|
||||||
|
return {
|
||||||
|
shareFamilyData: '0',
|
||||||
|
families: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
shareFamilyData: '1',
|
||||||
|
families: parents.map((parent: PersonRelative) => ({
|
||||||
|
relation: parent.relationship_code || '',
|
||||||
|
name: parent.name || '',
|
||||||
|
education: parent.education_code || '',
|
||||||
|
occupation: parent.occupation_name || parent.occupation_code || '',
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Computed: unwrap kontak pasien
|
||||||
|
const contactFormInitialValues = computed(() => {
|
||||||
|
const contacts = patient.value.person?.contacts || patient.value.personContacts || []
|
||||||
|
if (contacts.length === 0) return undefined
|
||||||
|
|
||||||
|
return {
|
||||||
|
contacts: contacts.map((contact: PersonContact) => ({
|
||||||
|
contactType: reverseContactTypeMapping[contact.type_code] || contact.type_code || '',
|
||||||
|
contactNumber: contact.value || '',
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Computed: unwrap penanggung jawab
|
||||||
|
const responsibleFormInitialValues = computed(() => {
|
||||||
|
const relatives = patient.value.person?.relatives || patient.value.personRelatives || []
|
||||||
|
const responsibles = relatives.filter((r: PersonRelative) => r.responsible === true)
|
||||||
|
|
||||||
|
if (responsibles.length === 0) return undefined
|
||||||
|
|
||||||
|
return {
|
||||||
|
contacts: responsibles.map((r: PersonRelative) => ({
|
||||||
|
relation: r.relationship_code || '',
|
||||||
|
name: r.name || '',
|
||||||
|
address: r.address || '',
|
||||||
|
phone: r.phoneNumber || '',
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
})
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
// #region Lifecycle Hooks
|
// #region Lifecycle Hooks
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
// Initial synchronization when forms are mounted and isSameAddress is true by default
|
|
||||||
nextTick(() => {
|
|
||||||
const isSameAddress = personAddressRelativeForm.value?.values?.isSameAddress
|
|
||||||
if (
|
|
||||||
(isSameAddress === true || isSameAddress === '1') &&
|
|
||||||
personAddressForm.value?.values &&
|
|
||||||
personAddressRelativeForm.value
|
|
||||||
) {
|
|
||||||
const currentAddressValues = personAddressForm.value.values
|
|
||||||
if (Object.keys(currentAddressValues).length > 0) {
|
|
||||||
personAddressRelativeForm.value.setValues(
|
|
||||||
{
|
|
||||||
...personAddressRelativeForm.value.values,
|
|
||||||
province_code: currentAddressValues.province_code || undefined,
|
|
||||||
regency_code: currentAddressValues.regency_code || undefined,
|
|
||||||
district_code: currentAddressValues.district_code || undefined,
|
|
||||||
village_code: currentAddressValues.village_code || undefined,
|
|
||||||
postalRegion_code: currentAddressValues.postalRegion_code || undefined,
|
|
||||||
address: currentAddressValues.address || undefined,
|
|
||||||
rt: currentAddressValues.rt || undefined,
|
|
||||||
rw: currentAddressValues.rw || undefined,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// if edit mode, fetch patient detail
|
// if edit mode, fetch patient detail
|
||||||
if (props.mode === 'edit' && props.patientId) {
|
if (props.mode === 'edit' && props.patientId) {
|
||||||
void getPatientDetail(props.patientId as number).then((v) => {
|
await loadInitData(props.patientId)
|
||||||
if (v.success) {
|
|
||||||
patient.value = v.body.data || {}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
// #region Functions
|
// #region Functions
|
||||||
async function composeFormData(): Promise<Patient> {
|
async function composeFormData(): Promise<PatientEntity> {
|
||||||
const [patient, address, addressRelative, families, contacts, emergencyContact] = await Promise.all([
|
const [patient, address, addressRelative, families, contacts, emergencyContact] = await Promise.all([
|
||||||
personPatientForm.value?.validate(),
|
personPatientForm.value?.validate(),
|
||||||
personAddressForm.value?.validate(),
|
personAddressForm.value?.validate(),
|
||||||
@@ -141,7 +268,7 @@ async function composeFormData(): Promise<Patient> {
|
|||||||
responsible: emergencyContact?.values,
|
responsible: emergencyContact?.values,
|
||||||
}
|
}
|
||||||
|
|
||||||
const formData = genPatient()
|
const formData = genPatientEntity(formDataRequest)
|
||||||
|
|
||||||
if (patient?.values.residentIdentityFile) {
|
if (patient?.values.residentIdentityFile) {
|
||||||
residentIdentityFile.value = patient?.values.residentIdentityFile
|
residentIdentityFile.value = patient?.values.residentIdentityFile
|
||||||
@@ -156,6 +283,20 @@ async function composeFormData(): Promise<Patient> {
|
|||||||
// #endregion region
|
// #endregion region
|
||||||
|
|
||||||
// #region Utilities & event handlers
|
// #region Utilities & event handlers
|
||||||
|
async function loadInitData(id: number | string) {
|
||||||
|
isProcessing.value = true
|
||||||
|
try {
|
||||||
|
const response = await getPatientDetail(id as number)
|
||||||
|
if (response.success) {
|
||||||
|
patient.value = response.body.data || {}
|
||||||
|
// Increment key untuk memaksa re-render form dengan data baru
|
||||||
|
formKey.value++
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
isProcessing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleActionClick(eventType: string) {
|
async function handleActionClick(eventType: string) {
|
||||||
try {
|
try {
|
||||||
if (eventType === 'submit') {
|
if (eventType === 'submit') {
|
||||||
@@ -197,10 +338,12 @@ async function handleActionClick(eventType: string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleCancelForm()
|
||||||
|
}
|
||||||
|
if (eventType === 'back') {
|
||||||
await navigateTo({
|
await navigateTo({
|
||||||
name: 'client-patient',
|
name: 'client-patient',
|
||||||
})
|
})
|
||||||
// handleCancelForm()
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Show error toast to user
|
// Show error toast to user
|
||||||
@@ -228,124 +371,95 @@ async function handleActionClick(eventType: string) {
|
|||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
// #region Watchers
|
// #region Watchers
|
||||||
// Watcher untuk sinkronisasi initial ketika kedua form sudah ready
|
// Helper: Cek apakah isSameAddress aktif
|
||||||
|
const isSameAddressActive = () => {
|
||||||
|
const val = personAddressRelativeForm.value?.values?.isSameAddress
|
||||||
|
return val === true || val === '1'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: Sinkronkan alamat sekarang ke alamat KTP
|
||||||
|
const syncAddressToRelative = () => {
|
||||||
|
const source = personAddressForm.value?.values
|
||||||
|
const target = personAddressRelativeForm.value
|
||||||
|
|
||||||
|
if (!source || !target) return
|
||||||
|
|
||||||
|
const addressFields = [
|
||||||
|
'province_code',
|
||||||
|
'regency_code',
|
||||||
|
'district_code',
|
||||||
|
'village_code',
|
||||||
|
'postalRegion_code',
|
||||||
|
'address',
|
||||||
|
'rt',
|
||||||
|
'rw',
|
||||||
|
] as const
|
||||||
|
const syncedValues = Object.fromEntries(addressFields.map((key) => [key, source[key] || undefined]))
|
||||||
|
|
||||||
|
target.setValues({ ...target.values, ...syncedValues }, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watcher: Sinkronisasi saat nilai alamat berubah atau isSameAddress diaktifkan
|
||||||
watch(
|
watch(
|
||||||
[() => personAddressForm.value, () => personAddressRelativeForm.value],
|
[() => personAddressForm.value?.values, () => personAddressRelativeForm.value?.values?.isSameAddress],
|
||||||
([addressForm, relativeForm]) => {
|
() => {
|
||||||
if (addressForm && relativeForm) {
|
if (isSameAddressActive()) {
|
||||||
// Trigger initial sync jika isSameAddress adalah true
|
syncAddressToRelative()
|
||||||
nextTick(() => {
|
|
||||||
const isSameAddress = relativeForm.values?.isSameAddress
|
|
||||||
if ((isSameAddress === true || isSameAddress === '1') && addressForm.values) {
|
|
||||||
const currentAddressValues = addressForm.values
|
|
||||||
if (Object.keys(currentAddressValues).length > 0) {
|
|
||||||
relativeForm.setValues(
|
|
||||||
{
|
|
||||||
...relativeForm.values,
|
|
||||||
province_code: currentAddressValues.province_code || undefined,
|
|
||||||
regency_code: currentAddressValues.regency_code || undefined,
|
|
||||||
district_code: currentAddressValues.district_code || undefined,
|
|
||||||
village_code: currentAddressValues.village_code || undefined,
|
|
||||||
postalRegion_code: currentAddressValues.postalRegion_code || undefined,
|
|
||||||
address: currentAddressValues.address || undefined,
|
|
||||||
rt: currentAddressValues.rt || undefined,
|
|
||||||
rw: currentAddressValues.rw || undefined,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
// Watcher untuk sinkronisasi alamat ketika isSameAddress = true
|
|
||||||
watch(
|
|
||||||
() => personAddressForm.value?.values,
|
|
||||||
(newAddressValues) => {
|
|
||||||
// Cek apakah alamat KTP harus sama dengan alamat sekarang
|
|
||||||
const isSameAddress = personAddressRelativeForm.value?.values?.isSameAddress
|
|
||||||
|
|
||||||
if ((isSameAddress === true || isSameAddress === '1') && newAddressValues && personAddressRelativeForm.value) {
|
|
||||||
// Sinkronkan semua field alamat dari alamat sekarang ke alamat KTP
|
|
||||||
personAddressRelativeForm.value.setValues(
|
|
||||||
{
|
|
||||||
...personAddressRelativeForm.value.values,
|
|
||||||
province_code: newAddressValues.province_code || undefined,
|
|
||||||
regency_code: newAddressValues.regency_code || undefined,
|
|
||||||
district_code: newAddressValues.district_code || undefined,
|
|
||||||
village_code: newAddressValues.village_code || undefined,
|
|
||||||
postalRegion_code: newAddressValues.postalRegion_code || undefined,
|
|
||||||
address: newAddressValues.address || undefined,
|
|
||||||
rt: newAddressValues.rt || undefined,
|
|
||||||
rw: newAddressValues.rw || undefined,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ deep: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
// Watcher untuk memantau perubahan isSameAddress
|
|
||||||
watch(
|
|
||||||
() => personAddressRelativeForm.value?.values?.isSameAddress,
|
|
||||||
(isSameAddress) => {
|
|
||||||
if (
|
|
||||||
(isSameAddress === true || isSameAddress === '1') &&
|
|
||||||
personAddressForm.value?.values &&
|
|
||||||
personAddressRelativeForm.value?.values
|
|
||||||
) {
|
|
||||||
// Ketika isSameAddress diubah menjadi true, copy alamat sekarang ke alamat KTP
|
|
||||||
const currentAddressValues = personAddressForm.value.values
|
|
||||||
personAddressRelativeForm.value.setValues(
|
|
||||||
{
|
|
||||||
...personAddressRelativeForm.value.values,
|
|
||||||
province_code: currentAddressValues.province_code || undefined,
|
|
||||||
regency_code: currentAddressValues.regency_code || undefined,
|
|
||||||
district_code: currentAddressValues.district_code || undefined,
|
|
||||||
village_code: currentAddressValues.village_code || undefined,
|
|
||||||
postalRegion_code: currentAddressValues.postalRegion_code || undefined,
|
|
||||||
address: currentAddressValues.address || undefined,
|
|
||||||
rt: currentAddressValues.rt || undefined,
|
|
||||||
rw: currentAddressValues.rw || undefined,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{ deep: true, immediate: true },
|
||||||
)
|
)
|
||||||
// #endregion
|
// #endregion
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg font-semibold xl:text-xl">Tambah Pasien</div>
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg font-semibold xl:text-xl">
|
||||||
<AppPatientEntryForm ref="personPatientForm" />
|
{{ mode === 'edit' ? 'Edit Pasien' : 'Tambah Pasien' }}
|
||||||
|
</div>
|
||||||
|
<AppPatientEntryForm
|
||||||
|
:key="`patient-${formKey}`"
|
||||||
|
ref="personPatientForm"
|
||||||
|
:is-readonly="isProcessing || isReadonly"
|
||||||
|
:initial-values="patientFormInitialValues"
|
||||||
|
/>
|
||||||
<div class="h-6"></div>
|
<div class="h-6"></div>
|
||||||
<AppPersonAddressEntryForm
|
<AppPersonAddressEntryForm
|
||||||
|
:key="`address-${formKey}`"
|
||||||
ref="personAddressForm"
|
ref="personAddressForm"
|
||||||
title="Alamat Sekarang"
|
title="Alamat Sekarang"
|
||||||
|
:is-readonly="isProcessing || isReadonly"
|
||||||
|
:initial-values="addressFormInitialValues"
|
||||||
/>
|
/>
|
||||||
<div class="h-6"></div>
|
<div class="h-6"></div>
|
||||||
<AppPersonAddressEntryFormRelative
|
<AppPersonAddressEntryFormRelative
|
||||||
|
:key="`address-rel-${formKey}`"
|
||||||
ref="personAddressRelativeForm"
|
ref="personAddressRelativeForm"
|
||||||
title="Alamat KTP"
|
title="Alamat KTP"
|
||||||
|
:is-readonly="isProcessing || isReadonly"
|
||||||
|
:initial-values="addressRelativeFormInitialValues"
|
||||||
/>
|
/>
|
||||||
<div class="h-6"></div>
|
<div class="h-6"></div>
|
||||||
<AppPersonFamilyParentsForm
|
<AppPersonFamilyParentsForm
|
||||||
|
:key="`family-${formKey}`"
|
||||||
ref="personFamilyForm"
|
ref="personFamilyForm"
|
||||||
title="Identitas Orang Tua"
|
title="Identitas Orang Tua"
|
||||||
|
:is-readonly="isProcessing || isReadonly"
|
||||||
|
:initial-values="familyFormInitialValues"
|
||||||
/>
|
/>
|
||||||
<div class="h-6"></div>
|
<div class="h-6"></div>
|
||||||
<AppPersonContactEntryForm
|
<AppPersonContactEntryForm
|
||||||
|
:key="`contact-${formKey}`"
|
||||||
ref="personContactForm"
|
ref="personContactForm"
|
||||||
title="Kontak Pasien"
|
title="Kontak Pasien"
|
||||||
|
:is-readonly="isProcessing || isReadonly"
|
||||||
|
:initial-values="contactFormInitialValues"
|
||||||
/>
|
/>
|
||||||
<AppPersonRelativeEntryForm
|
<AppPersonRelativeEntryForm
|
||||||
|
:key="`responsible-${formKey}`"
|
||||||
ref="personEmergencyContactRelative"
|
ref="personEmergencyContactRelative"
|
||||||
title="Penanggung Jawab"
|
title="Penanggung Jawab"
|
||||||
|
:is-readonly="isProcessing || isReadonly"
|
||||||
|
:initial-values="responsibleFormInitialValues"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="my-2 flex justify-end py-2">
|
<div class="my-2 flex justify-end py-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user