From 3cac23ce8a2bf82b53d7644047eff8d40db6aaad Mon Sep 17 00:00:00 2001 From: Khafid Prayoga Date: Wed, 10 Dec 2025 12:12:35 +0700 Subject: [PATCH] fix edit form state fix: id list for contacts and address list fix warning fix duplicate contacts responsible: true fix edit family fix nik required --- app/components/app/patient/entry-form.vue | 5 ++- app/components/content/patient/form.vue | 48 +++++++++-------------- app/models/patient.ts | 12 ++++-- app/models/person.ts | 2 +- app/schemas/patient.schema.ts | 24 ++++++++---- app/schemas/person-address.schema.ts | 2 + app/schemas/person-contact.schema.ts | 2 + app/schemas/person-family.schema.ts | 2 + app/schemas/person-relative.schema.ts | 2 + 9 files changed, 54 insertions(+), 45 deletions(-) diff --git a/app/components/app/patient/entry-form.vue b/app/components/app/patient/entry-form.vue index 7b6c578f..5bb367fe 100644 --- a/app/components/app/patient/entry-form.vue +++ b/app/components/app/patient/entry-form.vue @@ -30,7 +30,7 @@ import { } from './fields' interface FormData extends PatientFormData { - _calculatedAge: string + _calculatedAge: string | Date } // Type untuk initial values (sebelum transform schema) @@ -66,7 +66,7 @@ interface Props { const props = defineProps() const formSchema = toTypedSchema(PatientSchema) -const { values, resetForm, setValues, setFieldValue, validate } = useForm({ +const { values, resetForm, setValues, setFieldValue, validate, setFieldError } = useForm({ name: 'patientForm', validationSchema: formSchema, initialValues: (props.initialValues ?? {}) as any, @@ -106,6 +106,7 @@ watch( placeholder="Masukkan NIK" numeric-only :is-disabled="isReadonly" + :max-length="16" /> { // Computed: unwrap alamat domisili (alamat sekarang) const addressFormInitialValues = computed(() => { - const addresses = patientDetail.value.person?.addresses || patientDetail.value.personAddresses || [] + if (!patientDetail.value.person?.addresses) return {} + + const addresses = patientDetail.value.person?.addresses const domicileAddress = addresses.find((a: PersonAddress) => a.locationType_code === 'domicile') if (!domicileAddress) return undefined @@ -125,6 +127,7 @@ const addressFormInitialValues = computed(() => { const province = regency?.province return { + id: domicileAddress.id || 0, locationType_code: 'domicile', province_code: province?.code || '', regency_code: regency?.code || '', @@ -139,30 +142,11 @@ const addressFormInitialValues = computed(() => { // Computed: unwrap alamat KTP (identity) const addressRelativeFormInitialValues = computed(() => { - 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') + if (!patientDetail.value.person?.addresses) return {} - // 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', - } - } + const addresses = patientDetail.value.person?.addresses + const domicileAddress = addresses.find((a: PersonAddress) => a.locationType_code === 'domicile')! + const identityAddress = addresses.find((a: PersonAddress) => a.locationType_code === 'identity')! // extract kode wilayah dari preload data const village = identityAddress.postalRegion?.village @@ -170,8 +154,11 @@ const addressRelativeFormInitialValues = computed(() => { const regency = district?.regency const province = regency?.province + const isSame = domicileAddress.village_code === identityAddress.village_code ? '1' : '0' + return { - isSameAddress: '0', + isSameAddress: isSame, + id: identityAddress.id || 0, locationType_code: 'identity', province_code: province?.code || '', regency_code: regency?.code || '', @@ -201,6 +188,7 @@ const familyFormInitialValues = computed(() => { return { shareFamilyData: '1', families: parents.map((parent: PersonRelative) => ({ + id: parent.id || 0, relation: parent.relationship_code || '', name: parent.name || '', education: parent.education_code || '', @@ -216,6 +204,7 @@ const contactFormInitialValues = computed(() => { return { contacts: contacts.map((contact: PersonContact) => ({ + id: contact.id || 0, contactType: reverseContactTypeMapping[contact.type_code] || contact.type_code || '', contactNumber: contact.value || '', })), @@ -231,6 +220,7 @@ const responsibleFormInitialValues = computed(() => { return { contacts: responsibles.map((r: PersonRelative) => ({ + id: r.id || 0, relation: r.relationship_code || '', name: r.name || '', address: r.address || '', @@ -272,6 +262,7 @@ async function composeFormData(): Promise { ...patient?.values, // casting comp. val to backend well known reflect value + ethnic: patient?.values.nationality === 'WNI' ? patient?.values.ethnic : null, birthDate: parseISO(patient?.values.birthDate || ''), isNewBorn: patient?.values.isNewBorn === 'yes', communicationBarrier: patient?.values.communicationBarrier === 'yes', @@ -327,8 +318,7 @@ async function handleActionClick(eventType: string) { let createdPatientId = 0 let response: any - - // If edit mode, update patient + // return if (props.mode === 'edit' && props.patientId) { response = await handleActionEdit( patientDetail.value.id, @@ -337,9 +327,7 @@ async function handleActionClick(eventType: string) { () => {}, toast, ) - } - // If create mode, create patient - else { + } else { response = await handleActionSave( patient, () => {}, diff --git a/app/models/patient.ts b/app/models/patient.ts index 47621fb7..80ab6e06 100644 --- a/app/models/patient.ts +++ b/app/models/patient.ts @@ -15,7 +15,7 @@ import { contactTypeMapping } from '~/lib/constants' export interface PatientBase extends Base { person_id?: number | null - newBornStatus?: boolean + newBornStatus?: boolean | string registeredAt?: Date | string | null status_code?: string | null number?: string | null @@ -40,6 +40,7 @@ export interface genPatientProps { export function genPatientEntity(props: genPatientProps, patientData: PatientEntity | null): PatientEntity { const { patient, residentAddress, cardAddress, familyData, contacts, responsible } = props + // const val = toRaw(patientData) const addresses: PersonAddress[] = [{ ...genBase(), person_id: patientData?.person?.id || 0, ...residentAddress }] const familiesContact: PersonRelative[] = [] const personContacts: PersonContact[] = [] @@ -49,6 +50,7 @@ export function genPatientEntity(props: genPatientProps, patientData: PatientEnt addresses.push({ ...genBase(), ...residentAddress, + id: cardAddress.id || 0, person_id: 0, locationType_code: cardAddress.locationType_code || 'identity', }) @@ -57,6 +59,7 @@ export function genPatientEntity(props: genPatientProps, patientData: PatientEnt // Pastikan semua field yang diperlukan ada const relativeAddress = { ...genBase(), + id: cardAddress.id || 0, person_id: patientData?.person?.id || 0, locationType_code: cardAddress.locationType_code || 'identity', address: cardAddress.address || '', @@ -75,7 +78,7 @@ export function genPatientEntity(props: genPatientProps, patientData: PatientEnt if (familyData.shareFamilyData === '1') { for (const family of familyData.families) { familiesContact.push({ - id: 0, + id: family.id || 0, relationship_code: family.relation, name: family.name, education_code: family.education, @@ -94,6 +97,7 @@ export function genPatientEntity(props: genPatientProps, patientData: PatientEnt personContacts.push({ ...genBase(), + id: contact.id || 0, person_id: patientData?.person?.id || 0, type_code: mappedContactType || '', value: contact.contactNumber, @@ -105,7 +109,7 @@ export function genPatientEntity(props: genPatientProps, patientData: PatientEnt if (responsible) { for (const contact of responsible.contacts) { familiesContact.push({ - id: 0, + id: contact.id || 0, relationship_code: contact.relation, name: contact.name, address: contact.address, @@ -164,7 +168,7 @@ export function genPatientEntity(props: genPatientProps, patientData: PatientEnt export interface Patient extends Base { person_id?: number | null person: Person - newBornStatus?: boolean + newBornStatus?: boolean | string registeredAt?: Date | string | null status_code?: string | null number?: string | null diff --git a/app/models/person.ts b/app/models/person.ts index f7130d31..863abe38 100644 --- a/app/models/person.ts +++ b/app/models/person.ts @@ -26,7 +26,7 @@ export interface Person extends Base { ethnic_code?: string | null language_code?: string | null nationality?: string | null - communicationIssueStatus?: boolean + communicationIssueStatus?: boolean | string disability?: string | null residentIdentityFileUrl?: string | null passportFileUrl?: string | null diff --git a/app/schemas/patient.schema.ts b/app/schemas/patient.schema.ts index c961264e..1c9472f9 100644 --- a/app/schemas/patient.schema.ts +++ b/app/schemas/patient.schema.ts @@ -6,11 +6,6 @@ const PatientSchema = z .object({ // Data Diri Pasien identityNumber: z.string().optional(), - // .string({ - // required_error: 'Mohon lengkapi NIK', - // }) - // .min(16, 'NIK harus berupa angka 16 digit') - // .regex(/^\d+$/, 'NIK harus berupa angka 16 digit'), residentIdentityFile: z .any() .optional() @@ -58,8 +53,8 @@ const PatientSchema = z nationality: z.string({ required_error: 'Pilih Kebangsaan', }), - isNewBorn: z.string({ - required_error: 'Mohon lengkapi status pasien', + isNewBorn: z.union([z.boolean(), z.string()], { + required_error: 'Mohon lengkapi Status Disabilitas', }), language: z.string({ required_error: 'Mohon pilih Preferensi Bahasa', @@ -84,7 +79,7 @@ const PatientSchema = z // Informasi Kontak passportNumber: z.string().optional(), - communicationBarrier: z.string({ + communicationBarrier: z.union([z.boolean(), z.string()], { required_error: 'Mohon lengkapi Status Hambatan Berkomunikasi', }), @@ -103,6 +98,19 @@ const PatientSchema = z path: ['disabilityType'], }, ) + .refine( + (data) => { + if (data.nationality === 'WNI') { + const nik = data.identityNumber?.trim() + return !!nik && nik.length === 16 && /^\d+$/.test(nik) + } + return true + }, + { + message: 'NIK harus berupa angka 16 digit', + path: ['identityNumber'], + }, + ) // .transform((data) => { // return { // ...data, diff --git a/app/schemas/person-address.schema.ts b/app/schemas/person-address.schema.ts index 3e991bad..707667d6 100644 --- a/app/schemas/person-address.schema.ts +++ b/app/schemas/person-address.schema.ts @@ -1,6 +1,8 @@ import { z } from 'zod' const PersonAddressSchema = z.object({ + id: z.number().optional(), + locationType_code: z.string({ required_error: 'Mohon pilih jenis alamat', }), diff --git a/app/schemas/person-contact.schema.ts b/app/schemas/person-contact.schema.ts index b77e8a94..04455c6b 100644 --- a/app/schemas/person-contact.schema.ts +++ b/app/schemas/person-contact.schema.ts @@ -1,6 +1,8 @@ import { z } from 'zod' const PersonContactBaseSchema = z.object({ + id: z.number().optional(), + contactType: z.string({ required_error: 'Mohon pilih tipe kontak' }).min(1, 'Mohon pilih tipe kontak'), contactNumber: z.string({ required_error: 'Nomor kontak harus diisi' }).min(8, 'Nomor minimal 8 digit'), }) diff --git a/app/schemas/person-family.schema.ts b/app/schemas/person-family.schema.ts index 683bf4cc..398f6b0f 100644 --- a/app/schemas/person-family.schema.ts +++ b/app/schemas/person-family.schema.ts @@ -1,6 +1,7 @@ import { z } from 'zod' const PersonFamilySchema = z.object({ + id: z.number().optional(), relation: z.enum(['mother', 'father', 'guardian', 'emergency_contact']), name: z .string({ @@ -29,6 +30,7 @@ const PersonFamiliesSchema = z.discriminatedUnion('shareFamilyData', [ interface PersonFamiliesFormData { shareFamilyData: '0' | '1' families: { + id?: number relation: 'mother' | 'father' | 'guardian' | 'emergency_contact' name: string education: string diff --git a/app/schemas/person-relative.schema.ts b/app/schemas/person-relative.schema.ts index 06644de6..d7f32454 100644 --- a/app/schemas/person-relative.schema.ts +++ b/app/schemas/person-relative.schema.ts @@ -1,6 +1,8 @@ import { z } from 'zod' const ResponsibleContactPersonSchema = z.object({ + id: z.number().optional(), + relation: z.string({ required_error: 'Pilih jenis Penanggung Jawab' }).min(1, 'Pilih jenis Penanggung Jawab'), name: z.string({ required_error: 'Mohon lengkapi Nama' }).min(3, 'Mohon lengkapi Nama'), address: z.string({ required_error: 'Mohon lengkapi Alamat' }).min(3, 'Mohon lengkapi Alamat'),