form cleanup

feat(patient): add edit functionality to patient form

- Modify genPatientEntity to accept existing patient data for updates
- Add handleActionEdit handler for edit mode
- Update form to handle both create and edit modes
- Rename patient ref to patientDetail for clarity

refactor(patient): update marital status codes and job options mapping

- Change marital status enum values to standardized codes (S, M, D, W)
- Simplify job options and marital status options mapping using mapToComboboxOptList
- Add error handling in patient data loading

ajust styling text based on combobox

wip: edit patient redirect

refactor(models): update type definitions and form field handling

- Add field-name prop to SelectDob component for better form handling
- Update Person and Patient interfaces to use null for optional fields
- Add maritalStatus_code field to Person interface
- Improve type safety by replacing undefined with null for optional fields

fix casting radio str to boolean and parsing date error
This commit is contained in:
Khafid Prayoga
2025-12-08 20:43:30 +07:00
parent e967ee1cf0
commit 910b641750
13 changed files with 175 additions and 194 deletions
+7 -6
View File
@@ -34,7 +34,7 @@ interface PatientFormInput {
drivingLicenseNumber?: string
passportNumber?: string
fullName?: string
isNewBorn?: 'YA' | 'TIDAK'
isNewBorn?: string
gender?: string
birthPlace?: string
birthDate?: string
@@ -45,8 +45,8 @@ interface PatientFormInput {
ethnicity?: string
language?: string
religion?: string
communicationBarrier?: 'YA' | 'TIDAK'
disability?: 'YA' | 'TIDAK'
communicationBarrier?: string
disability?: string
disabilityType?: string
note?: string
residentIdentityFile?: File
@@ -61,7 +61,7 @@ interface Props {
const props = defineProps<Props>()
const formSchema = toTypedSchema(PatientSchema)
const { values, resetForm, setValues, validate } = useForm<FormData>({
const { values, resetForm, setValues, setFieldValue, validate } = useForm<FormData>({
name: 'patientForm',
validationSchema: formSchema,
initialValues: (props.initialValues ?? {}) as any,
@@ -135,6 +135,7 @@ defineExpose({
:is-disabled="isReadonly"
/>
<SelectDob
field-name="birthDate"
label="Tanggal Lahir"
is-required
:is-disabled="isReadonly"
@@ -203,8 +204,8 @@ defineExpose({
<SelectDisability
label="Jenis Disabilitas"
field-name="disabilityType"
:is-disabled="isReadonly || values.disability !== 'YA'"
:is-required="values.disability === 'YA'"
:is-disabled="isReadonly || values.disability !== 'yes'"
:is-required="values.disability === 'yes'"
/>
<InputBase
field-name="note"
@@ -29,8 +29,8 @@ const {
} = props
const genderOptions = [
{ label: 'Ya', value: 'YA' },
{ label: 'Tidak', value: 'TIDAK' },
{ label: 'Ya', value: 'yes' },
{ label: 'Tidak', value: 'no' },
]
</script>
@@ -47,7 +47,7 @@ const genderOptions = [
:id="fieldName"
:errors="errors"
class="pt-0.5"
>
>
<FormField
v-slot="{ componentField }"
:name="fieldName"
@@ -80,9 +80,7 @@ const genderOptions = [
:class="
cn(
'select-none text-xs !font-normal leading-none transition-colors sm:text-sm',
isDisabled
? 'cursor-not-allowed opacity-70'
: 'cursor-pointer hover:text-primary',
isDisabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer hover:text-primary',
labelClass,
)
"
@@ -28,9 +28,9 @@ const {
labelClass,
} = props
const dissabilityOptions = [
{ label: 'Ya', value: 'YA' },
{ label: 'Tidak', value: 'TIDAK' },
const disabilityOptions = [
{ label: 'Ya', value: 'yes' },
{ label: 'Tidak', value: 'no' },
]
</script>
@@ -60,7 +60,7 @@ const dissabilityOptions = [
:class="cn('flex flex-row flex-wrap gap-4 sm:gap-6', radioGroupClass)"
>
<div
v-for="(option, index) in dissabilityOptions"
v-for="(option, index) in disabilityOptions"
:key="option.value"
:class="cn('flex min-w-fit items-center space-x-2', radioItemClass)"
>
@@ -70,7 +70,7 @@ const dissabilityOptions = [
:value="option.value"
:class="
cn(
'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',
'border-1 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,
)
"
@@ -80,9 +80,7 @@ const dissabilityOptions = [
:class="
cn(
'select-none text-xs !font-normal leading-none transition-colors',
isDisabled
? 'cursor-not-allowed opacity-70'
: 'cursor-pointer hover:text-primary',
isDisabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer hover:text-primary',
labelClass,
)
"
@@ -29,8 +29,8 @@ const {
} = props
const newbornOptions = [
{ label: 'Ya', value: 'YA' },
{ label: 'Tidak', value: 'TIDAK' },
{ label: 'Ya', value: 'yes' },
{ label: 'Tidak', value: 'no' },
]
</script>
@@ -81,9 +81,7 @@ const newbornOptions = [
:class="
cn(
'select-none text-xs !font-normal leading-none transition-colors',
isDisabled
? 'cursor-not-allowed opacity-70'
: 'cursor-pointer hover:text-primary',
isDisabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer hover:text-primary',
labelClass,
)
"
@@ -30,7 +30,10 @@ const {
} = props
// Generate job options from constants, sama seperti pola genderCodes
const jobOptions = mapToComboboxOptList(occupationCodes)
const jobOptions = mapToComboboxOptList(occupationCodes).map(({ label, value }) => ({
label,
value,
}))
</script>
<template>
@@ -1,7 +1,9 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Select from '~/components/pub/my-ui/form/select.vue'
import { cn } from '~/lib/utils'
import { maritalStatusCodes } from '~/const/key-val/person'
import { cn, mapToComboboxOptList } from '~/lib/utils'
import * as DE from '~/components/pub/my-ui/doc-entry'
@@ -27,13 +29,10 @@ const {
placeholder = 'Pilih',
} = props
const maritalStatusOptions = [
{ label: 'Tidak Diketahui', value: 'TIDAK_DIKETAHUI' },
{ label: 'Belum Kawin', value: 'BELUM_KAWIN' },
{ label: 'Kawin', value: 'KAWIN' },
{ label: 'Cerai Hidup', value: 'CERAI_HIDUP' },
{ label: 'Cerai Mati', value: 'CERAI_MATI' },
]
const maritalStatusOptions = mapToComboboxOptList(maritalStatusCodes).map(({ label, value }) => ({
label,
value,
}))
</script>
<template>
@@ -53,7 +53,6 @@ async function onBack() {
})
}
async function onEdit() {
console.log(props.patientId)
await navigateTo({
name: 'client-patient-id-edit',
params: {
+70 -32
View File
@@ -18,6 +18,10 @@ import AppPersonFamilyParentsForm from '~/components/app/person/family-parents-f
import AppPersonContactEntryForm from '~/components/app/person-contact/entry-form.vue'
import AppPersonRelativeEntryForm from '~/components/app/person-relative/entry-form.vue'
// helper
import { format, parseISO } from 'date-fns'
import { id as localeID } from 'date-fns/locale'
// services
import { getPatientDetail, uploadAttachment } from '~/services/patient.service'
@@ -28,6 +32,7 @@ import {
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleCancelForm,
} from '~/handlers/patient.handler'
@@ -62,7 +67,7 @@ const personPatientForm = ref<ExposedForm<any> | null>(null)
// #endregion
// #region State & Computed
const patient = ref(
const patientDetail = ref(
withBase<PatientEntity>({
person: {} as Person,
personAddresses: [],
@@ -76,36 +81,40 @@ const formKey = ref(0)
// Computed: unwrap patient data untuk form patient
const patientFormInitialValues = computed(() => {
const p = patient.value
const p = patientDetail.value
const person = p.person
if (!person || !person.name) return undefined
const hasDisability = String(person?.disability).length > 0
const date = parseISO(person.birthDate!)
const birthDate = format(date, 'yyyy-MM-dd', { locale: localeID })
return {
identityNumber: person.residentIdentityNumber || '',
drivingLicenseNumber: person.drivingLicenseNumber || '',
passportNumber: person.passportNumber || '',
identityNumber: person.residentIdentityNumber || undefined,
drivingLicenseNumber: person.drivingLicenseNumber || undefined,
passportNumber: person.passportNumber || undefined,
fullName: person.name || '',
isNewBorn: (p.newBornStatus ? 'YA' : 'TIDAK') as 'YA' | 'TIDAK',
isNewBorn: p.newBornStatus ? 'yes' : 'no',
gender: person.gender_code || '',
birthPlace: person.birthRegency_code || '',
birthDate: person.birthDate ? new Date(person.birthDate).toISOString().split('T')[0] : '',
birthDate: birthDate,
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 || '',
maritalStatus: person?.maritalStatus_code,
nationality: person?.nationality || 'WNI',
ethnicity: person?.ethnic_code || '',
language: person?.language_code || '',
religion: person?.religion_code || '',
communicationBarrier: person?.communicationIssueStatus ? 'yes' : 'no',
disability: hasDisability ? 'yes' : 'no',
disabilityType: hasDisability ? String(person?.disability) || '' : '',
note: '',
}
})
// Computed: unwrap alamat domisili (alamat sekarang)
const addressFormInitialValues = computed(() => {
const addresses = patient.value.person?.addresses || patient.value.personAddresses || []
const addresses = patientDetail.value.person?.addresses || patientDetail.value.personAddresses || []
const domicileAddress = addresses.find((a: PersonAddress) => a.locationType_code === 'domicile')
if (!domicileAddress) return undefined
@@ -130,7 +139,7 @@ const addressFormInitialValues = computed(() => {
// Computed: unwrap alamat KTP (identity)
const addressRelativeFormInitialValues = computed(() => {
const addresses = patient.value.person?.addresses || patient.value.personAddresses || []
const addresses = patientDetail.value.person?.addresses || patientDetail.value.personAddresses || []
const domicileAddress = addresses.find((a: PersonAddress) => a.locationType_code === 'domicile')
const identityAddress = addresses.find((a: PersonAddress) => a.locationType_code === 'identity')
@@ -177,7 +186,7 @@ const addressRelativeFormInitialValues = computed(() => {
// Computed: unwrap data orang tua
const familyFormInitialValues = computed(() => {
const relatives = patient.value.person?.relatives || patient.value.personRelatives || []
const relatives = patientDetail.value.person?.relatives || patientDetail.value.personRelatives || []
const parents = relatives.filter(
(r: PersonRelative) => !r.responsible && (r.relationship_code === 'mother' || r.relationship_code === 'father'),
)
@@ -202,7 +211,7 @@ const familyFormInitialValues = computed(() => {
// Computed: unwrap kontak pasien
const contactFormInitialValues = computed(() => {
const contacts = patient.value.person?.contacts || patient.value.personContacts || []
const contacts = patientDetail.value.person?.contacts || patientDetail.value.personContacts || []
if (contacts.length === 0) return undefined
return {
@@ -215,7 +224,7 @@ const contactFormInitialValues = computed(() => {
// Computed: unwrap penanggung jawab
const responsibleFormInitialValues = computed(() => {
const relatives = patient.value.person?.relatives || patient.value.personRelatives || []
const relatives = patientDetail.value.person?.relatives || patientDetail.value.personRelatives || []
const responsibles = relatives.filter((r: PersonRelative) => r.responsible === true)
if (responsibles.length === 0) return undefined
@@ -252,7 +261,6 @@ async function composeFormData(): Promise<PatientEntity> {
])
const results = [patient, address, addressRelative, families, contacts, emergencyContact]
console.log(results)
const allValid = results.every((r) => r?.valid)
// exit, if form errors happend during validation
@@ -260,7 +268,15 @@ async function composeFormData(): Promise<PatientEntity> {
if (!allValid) return Promise.reject('Form validation failed')
const formDataRequest: genPatientProps = {
patient: patient?.values,
patient: {
...patient?.values,
// casting comp. val to backend well known reflect value
birthDate: parseISO(patient?.values.birthDate || ''),
isNewBorn: patient?.values.isNewBorn === 'yes',
communicationBarrier: patient?.values.communicationBarrier === 'yes',
disability: patient?.values.disability === 'yes' ? patient?.values.disabilityType : '',
},
residentAddress: address?.values,
cardAddress: addressRelative?.values,
familyData: families?.values,
@@ -268,7 +284,7 @@ async function composeFormData(): Promise<PatientEntity> {
responsible: emergencyContact?.values,
}
const formData = genPatientEntity(formDataRequest)
const formData = genPatientEntity(formDataRequest, patientDetail.value)
if (patient?.values.residentIdentityFile) {
residentIdentityFile.value = patient?.values.residentIdentityFile
@@ -288,12 +304,19 @@ async function loadInitData(id: number | string) {
try {
const response = await getPatientDetail(id as number)
if (response.success) {
patient.value = response.body.data || {}
patientDetail.value = response.body.data || {}
// Increment key untuk memaksa re-render form dengan data baru
formKey.value++
}
} finally {
isProcessing.value = false
} catch (error) {
console.error(error)
toast({
title: 'Error',
description: 'Terjadi kesalahan saat mengambil data',
variant: 'destructive',
})
}
}
@@ -301,14 +324,29 @@ async function handleActionClick(eventType: string) {
try {
if (eventType === 'submit') {
const patient: Patient = await composeFormData()
let createdPatientId = 0
const response = await handleActionSave(
patient,
() => {},
() => {},
toast,
)
let createdPatientId = 0
let response: any
// If edit mode, update patient
if (props.mode === 'edit' && props.patientId) {
response = await handleActionEdit(
patientDetail.value.id,
patient,
() => {},
() => {},
toast,
)
}
// If create mode, create patient
else {
response = await handleActionSave(
patient,
() => {},
() => {},
toast,
)
}
const data = (response?.body?.data ?? null) as PatientBase | null
if (!data) return
+9 -5
View File
@@ -137,18 +137,22 @@ provide('table_data_loader', isLoading)
// #endregion
// #region Watchers
watch([recId, recAction], () => {
switch (recAction.value) {
watch([recId, recAction], ([newId, newAction]) => {
switch (newAction) {
case ActionEvents.showDetail:
navigateTo({
name: 'client-patient-id',
params: { id: recId.value },
params: { id: newId },
})
break
case ActionEvents.showEdit:
// TODO: Handle edit action
// isFormEntryDialogOpen.value = true
navigateTo({
name: 'client-patient-id-edit',
params: {
id: newId,
},
})
break
case ActionEvents.showConfirmDelete:
+1 -1
View File
@@ -122,7 +122,7 @@ watch(
<SelectTrigger
:class="
cn(
'rounded-md focus:outline-none focus:ring-1 focus:ring-black dark:focus:ring-white',
'h-8 rounded-md font-normal focus:outline-none focus:ring-1 focus:ring-black dark:focus:ring-white md:text-xs 2xl:h-9 2xl:text-sm',
{
'cursor-not-allowed bg-gray-100 opacity-50': isDisabled,
'bg-white text-black dark:bg-gray-800 dark:text-white': !isDisabled,
+23 -21
View File
@@ -1,12 +1,12 @@
import { type Base, genBase } from './_base'
import { type Person, genPerson } from './person'
import type { PatientFormData } from '~/schemas/patient.schema'
import type { PersonAddressFormData } from '~/schemas/person-address.schema'
import type { PersonAddressRelativeFormData } from '~/schemas/person-address-relative.schema'
import type { PersonFamiliesFormData } from '~/schemas/person-family.schema'
import type { PersonContactFormData } from '~/schemas/person-contact.schema'
import type { PersonRelativeFormData } from '~/schemas/person-relative.schema'
import { genPerson, type Person } from './person'
import type { PersonAddress } from './person-address'
import type { PersonContact } from './person-contact'
import type { PersonRelative } from './person-relative'
@@ -14,11 +14,11 @@ import type { PersonRelative } from './person-relative'
import { contactTypeMapping } from '~/lib/constants'
export interface PatientBase extends Base {
person_id?: number
person_id?: number | null
newBornStatus?: boolean
registeredAt?: Date | string | null
status_code?: string
number?: string
status_code?: string | null
number?: string | null
}
export interface PatientEntity extends PatientBase {
@@ -37,10 +37,10 @@ export interface genPatientProps {
responsible: PersonRelativeFormData
}
export function genPatientEntity(props: genPatientProps): PatientEntity {
export function genPatientEntity(props: genPatientProps, patientData: PatientEntity | null): PatientEntity {
const { patient, residentAddress, cardAddress, familyData, contacts, responsible } = props
const addresses: PersonAddress[] = [{ ...genBase(), person_id: 0, ...residentAddress }]
const addresses: PersonAddress[] = [{ ...genBase(), person_id: patientData?.person?.id || 0, ...residentAddress }]
const familiesContact: PersonRelative[] = []
const personContacts: PersonContact[] = []
@@ -50,14 +50,14 @@ export function genPatientEntity(props: genPatientProps): PatientEntity {
...genBase(),
...residentAddress,
person_id: 0,
locationType_code: cardAddress.locationType_code || 'identity'
locationType_code: cardAddress.locationType_code || 'identity',
})
} else {
// jika alamat berbeda, tambahkan alamat relatif
// Pastikan semua field yang diperlukan ada
const relativeAddress = {
...genBase(),
person_id: 0,
person_id: patientData?.person?.id || 0,
locationType_code: cardAddress.locationType_code || 'identity',
address: cardAddress.address || '',
province_code: cardAddress.province_code || '',
@@ -94,7 +94,7 @@ export function genPatientEntity(props: genPatientProps): PatientEntity {
personContacts.push({
...genBase(),
person_id: 0,
person_id: patientData?.person?.id || 0,
type_code: mappedContactType || '',
value: contact.contactNumber,
})
@@ -117,15 +117,15 @@ export function genPatientEntity(props: genPatientProps): PatientEntity {
return {
person: {
id: 0,
id: patientData?.person?.id || 0,
name: patient.fullName,
// alias: patient.alias,
birthDate: patient.birthDate,
birthRegency_code: patient.birthPlace,
gender_code: patient.gender,
residentIdentityNumber: patient.identityNumber,
passportNumber: patient.passportNumber,
drivingLicenseNumber: patient.drivingLicenseNumber,
residentIdentityNumber: patient.identityNumber || null,
passportNumber: patient.passportNumber || null,
drivingLicenseNumber: patient.drivingLicenseNumber || null,
religion_code: patient.religion,
education_code: patient.education,
occupation_code: patient.job,
@@ -139,6 +139,8 @@ export function genPatientEntity(props: genPatientProps): PatientEntity {
// passportFileUrl: patient.passportFileUrl,
// drivingLicenseFileUrl: patient.drivingLicenseFileUrl,
// familyIdentityFileUrl: patient.familyIdentityFileUrl,
maritalStatus_code: patient.maritalStatus,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -149,9 +151,9 @@ export function genPatientEntity(props: genPatientProps): PatientEntity {
registeredAt: new Date(),
status_code: 'active',
newBornStatus: patient.isNewBorn,
person_id: 0,
id: 0,
number: '',
person_id: patientData?.person?.id || null,
id: patientData?.id || 0,
number: patientData?.number || null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -160,12 +162,12 @@ export function genPatientEntity(props: genPatientProps): PatientEntity {
// New model
export interface Patient extends Base {
person_id?: number
person_id?: number | null
person: Person
newBornStatus?: boolean
registeredAt?: Date | string | null
status_code?: string
number?: string
status_code?: string | null
number?: string | null
}
export function genPatient(): Patient {
@@ -178,4 +180,4 @@ export function genPatient(): Patient {
newBornStatus: false,
person: genPerson(),
}
}
}
+20 -19
View File
@@ -9,27 +9,28 @@ import type { Regency } from './regency'
export interface Person extends Base {
name: string
// alias?: string
frontTitle?: string
endTitle?: string
frontTitle?: string | null
endTitle?: string | null
birthDate?: string
birthRegency_code?: string
gender_code?: string
residentIdentityNumber?: string
passportNumber?: string
drivingLicenseNumber?: string
religion_code?: string
education_code?: string
occupation_code?: string
occupation_name?: string
ethnic_code?: string
language_code?: string
nationality?: string
birthRegency_code?: string | null
gender_code?: string | null
residentIdentityNumber?: string | null
passportNumber?: string | null
drivingLicenseNumber?: string | null
religion_code?: string | null
education_code?: string | null
occupation_code?: string | null
occupation_name?: string | null
ethnic_code?: string | null
language_code?: string | null
nationality?: string | null
communicationIssueStatus?: boolean
disability?: string
residentIdentityFileUrl?: string
passportFileUrl?: string
drivingLicenseFileUrl?: string
familyIdentityFileUrl?: string
disability?: string | null
residentIdentityFileUrl?: string | null
passportFileUrl?: string | null
drivingLicenseFileUrl?: string | null
familyIdentityFileUrl?: string | null
maritalStatus_code?: string
// preload data for detail patient
birthRegency?: Regency | null
+21 -81
View File
@@ -1,17 +1,5 @@
import { z } from 'zod'
const CommunicationBarrierSchema = z
.enum(['YA', 'TIDAK'], {
required_error: 'Mohon lengkapi Status Hambatan Berkomunikasi',
})
.transform((val) => val === 'YA')
const IsNewBornSchema = z
.enum(['YA', 'TIDAK'], {
required_error: 'Mohon lengkapi status pasien',
})
.transform((val) => val === 'YA')
const ACCEPTED_UPLOAD_TYPES = ['image/jpeg', 'image/png', 'application/pdf']
const PatientSchema = z
@@ -40,12 +28,7 @@ const PatientSchema = z
message: 'Format file harus JPG, PNG, atau PDF',
})
.refine((f) => !f || f.size <= 1 * 1024 * 1024, { message: 'Maksimal 1MB' }),
// .refine(f => ['image/jpeg', 'image/png'].includes(f.type), 'Hanya JPG/PNG')
// Informasi Dasar
// alias: z.string({
// required_error: 'Mohon pilih sapaan',
// }),
fullName: z.string({
required_error: 'Mohon lengkapi Nama',
}),
@@ -54,35 +37,15 @@ const PatientSchema = z
required_error: 'Mohon lengkapi Tempat Lahir',
})
.min(1, 'Mohon lengkapi Tempat Lahir'),
birthDate: z
.string({
required_error: 'Mohon lengkapi Tanggal Lahir',
})
.refine(
(date) => {
// Jika kosong, return false untuk required validation
if (!date || date.trim() === '') return false
// Jika ada isi, validasi format tanggal
try {
const dateObj = new Date(date)
// Cek apakah tanggal valid dan tahun >= 1900
return !isNaN(dateObj.getTime()) && dateObj.getFullYear() >= 1900
} catch {
return false
}
},
{
message: 'Mohon lengkapi Tanggal Lahir dengan format yang valid',
},
)
.transform((dateStr) => new Date(dateStr).toISOString()),
birthDate: z.string({
required_error: 'Mohon lengkapi Tanggal Lahir',
}),
// Jenis Kelamin & Status
gender: z.string({
required_error: 'Pilih Jenis Kelamin',
}),
maritalStatus: z.enum(['TIDAK_DIKETAHUI', 'BELUM_KAWIN', 'KAWIN', 'CERAI_HIDUP', 'CERAI_MATI'], {
maritalStatus: z.enum(['S', 'M', 'D', 'W'], {
required_error: 'Pilih Status Perkawinan',
}),
@@ -95,7 +58,9 @@ const PatientSchema = z
nationality: z.string({
required_error: 'Pilih Kebangsaan',
}),
isNewBorn: IsNewBornSchema,
isNewBorn: z.string({
required_error: 'Mohon lengkapi status pasien',
}),
language: z.string({
required_error: 'Mohon pilih Preferensi Bahasa',
}),
@@ -112,65 +77,40 @@ const PatientSchema = z
ethnicity: z.string().optional(),
// Disabilitas
disability: z.enum(['YA', 'TIDAK'], {
disability: z.string({
required_error: 'Mohon lengkapi Status Disabilitas',
}),
disabilityType: z.string().optional(),
// Informasi Kontak
passportNumber: z.string().optional(),
communicationBarrier: CommunicationBarrierSchema,
communicationBarrier: z.string({
required_error: 'Mohon lengkapi Status Hambatan Berkomunikasi',
}),
note: z.string().optional(),
drivingLicenseNumber: z.string().optional(),
})
.refine(
(data) => {
// Jika disability = 'TIDAK', maka disabilityType harus kosong atau undefined
if (data.disability === 'TIDAK') {
return !data.disabilityType || data.disabilityType.trim() === ''
}
return true
},
{
message: "Jenis Disabilitas harus kosong jika Status Disabilitas = 'TIDAK'",
path: ['disabilityType'],
},
)
.refine(
(data) => {
// Jika disability = 'YA', maka disabilityType wajib diisi
if (data.disability === 'YA') {
if (data.disability === 'yes') {
return !!data.disabilityType?.trim()
}
return true
},
{
message: 'Mohon pilih Jenis Disabilitas',
message: 'Mohon pilih jenis Disabilitas',
path: ['disabilityType'],
},
)
// .refine((data) => {
// // Jika nationality = 'WNA', maka passportNumber wajib diisi
// if (data.nationality === 'WNA') {
// return !!data.passportNumber?.trim()
// }
// return true
// }, {
// message: 'Nomor Paspor wajib diisi untuk Warga Negara Asing',
// path: ['passportNumber'],
// })
.transform((data) => {
// Transform untuk backend: hanya kirim disabilityType sesuai kondisi
return {
...data,
// Jika disability = 'YA', kirim disabilityType
// Jika disability = 'TIDAK', kirim null untuk disabilityType
disabilityType: data.disability === 'YA' ? data.disabilityType : null,
// Hapus field disability karena yang dikirim ke backend adalah disabilityType
disability: undefined,
}
})
// .transform((data) => {
// return {
// ...data,
// // newBornStatus: data.isNewBorn === 'yes',
// // communicationBarrier: data.communicationBarrier === 'yes',
// // disability: data.disability ? data.disabilityType : null,
// }
// })
type PatientFormData = z.infer<typeof PatientSchema>