Files
simrsx-fe/app/components/content/patient/entry.vue
Khafid Prayoga 55239606af feat(patient): address integration to backend apis
feat(patient): add newborn status field and validation

- Add radio button component for newborn status selection
- Update patient schema with newborn status validation
- Remove deprecated alias field from person model
- Refactor disability type handling in patient schema

fix(patient): correct address comparison logic and schema

Update the patient address comparison to use boolean instead of string '1' and modify the schema to transform the string value to boolean. This ensures consistent type usage throughout the application.

feat(models): add village and district model interfaces

Add new model interfaces for Village and District with their respective generator functions. These models will be used to handle administrative division data in the application.

feat(address): implement dynamic province selection with caching

- Add province service for CRUD operations
- Create useProvinces composable with caching and loading states
- Update select-province component to use dynamic data
- Export SelectItem interface for type consistency
- Improve combobox styling and accessibility

feat(address-form): implement dynamic regency selection with caching

- Add new regency service for CRUD operations
- Create useRegencies composable with caching and loading states
- Update SelectRegency component to use dynamic data based on province selection
- Improve placeholder and disabled state handling

feat(address-form): implement dynamic district selection

- Add district service for CRUD operations
- Create useDistricts composable with caching and loading states
- Update SelectDistrict component to use dynamic data
- Remove hardcoded district options and implement regency-based filtering

feat(address-form): implement dynamic village selection with caching

- Add village service for CRUD operations
- Create useVillages composable with caching and loading states
- Update SelectVillage component to fetch villages based on district
- Remove hardcoded village options in favor of API-driven data

feat(address-form): improve address selection with debouncing and request deduplication

- Add debouncing to prevent rapid API calls when selecting addresses
- Implement request deduplication to avoid duplicate API calls
- Add delayed form reset to ensure proper composable cleanup
- Add isUserAction flag to force refresh when user changes selection
2025-10-08 15:58:22 +07:00

181 lines
5.9 KiB
Vue

<script setup lang="ts">
import type { PatientEntity, genPatientProps } from '~/models/patient'
import type { ExposedForm } from '~/types/form'
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
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'
import { postPatient } from '~/services/patient.service'
// #region Props & Emits
const payload = ref<PatientEntity>()
// form related state
const personAddressForm = ref<ExposedForm<any> | null>(null)
const personAddressRelativeForm = ref<ExposedForm<any> | null>(null)
const personContactForm = ref<ExposedForm<any> | null>(null)
const personEmergencyContactRelative = ref<ExposedForm<any> | null>(null)
const personFamilyForm = ref<ExposedForm<any> | null>(null)
const personPatientForm = ref<ExposedForm<any> | null>(null)
// #endregion
// #region State & Computed
// #endregion
// #region Lifecycle Hooks
// #endregion
// #region Functions
// #endregion region
// #region Utilities & event handlers
async function submitAll() {
const [patient, address, addressRelative, families, contacts, emergencyContact] = await Promise.all([
personPatientForm.value?.validate(),
personAddressForm.value?.validate(),
personAddressRelativeForm.value?.validate(),
personFamilyForm.value?.validate(),
personContactForm.value?.validate(),
personEmergencyContactRelative.value?.validate(),
])
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
// for example: dropdown not selected
if (!allValid) return
const formDataRequest: genPatientProps = {
patient: patient?.values,
residentAddress: address?.values,
cardAddress: addressRelative?.values,
familyData: families?.values,
contacts: contacts?.values,
responsible: emergencyContact?.values,
}
const formData = genPatient(formDataRequest)
payload.value = formData
try {
const result = await postPatient(formData)
if (result.success) {
console.log('Patient created successfully:', result.body)
// Navigate to patient list or show success message
await navigateTo('/client/patient')
} else {
console.error('Failed to create patient:', result)
// Handle error - show error message to user
}
} catch (error) {
console.error('Error creating patient:', error)
// Handle error - show error message to user
}
}
// #endregion
// #region Watchers
// Watcher untuk sinkronisasi alamat ketika isSameAddress = '1'
watch(
() => personAddressForm.value?.values,
(newAddressValues) => {
// Cek apakah alamat KTP harus sama dengan alamat sekarang
const isSameAddress = personAddressRelativeForm.value?.values?.isSameAddress === '1'
if (isSameAddress && newAddressValues && personAddressRelativeForm.value) {
// Sinkronkan semua field alamat dari alamat sekarang ke alamat KTP
personAddressRelativeForm.value.setValues(
{
...personAddressRelativeForm.value.values,
provinceCode: newAddressValues.provinceCode || '',
regencyId: newAddressValues.regencyId || '',
districtId: newAddressValues.districtId || '',
villageId: newAddressValues.villageId || '',
zipCode: newAddressValues.zipCode || '',
address: newAddressValues.address || '',
rt: newAddressValues.rt || '',
rw: newAddressValues.rw || '',
},
false,
)
}
},
{ deep: true },
)
// Watcher untuk memantau perubahan isSameAddress
watch(
() => personAddressRelativeForm.value?.values?.isSameAddress,
(isSameAddress) => {
if (isSameAddress === '1' && personAddressForm.value?.values && personAddressRelativeForm.value) {
// Ketika isSameAddress diubah menjadi '1', copy alamat sekarang ke alamat KTP
const currentAddressValues = personAddressForm.value.values
personAddressRelativeForm.value.setValues(
{
...personAddressRelativeForm.value.values,
provinceCode: currentAddressValues.provinceCode || '',
regencyId: currentAddressValues.regencyId || '',
districtId: currentAddressValues.districtId || '',
villageId: currentAddressValues.villageId || '',
zipCode: currentAddressValues.zipCode || '',
address: currentAddressValues.address || '',
rt: currentAddressValues.rt || '',
rw: currentAddressValues.rw || '',
},
false,
)
}
},
)
// #endregion
</script>
<template>
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg font-semibold xl:text-xl">Tambah Pasien</div>
<AppPatientEntryForm
ref="personPatientForm"
:schema="PatientSchema"
/>
<AppPersonAddressEntryForm
ref="personAddressForm"
title="Alamat Sekarang"
:schema="PersonAddressSchema"
/>
<AppPersonAddressEntryFormRelative
ref="personAddressRelativeForm"
title="Alamat KTP"
:schema="PersonAddressRelativeSchema"
/>
<AppPersonFamilyParentsForm
ref="personFamilyForm"
title="Identitas Orang Tua"
:schema="PersonFamiliesSchema"
/>
<AppPersonContactEntryForm
ref="personContactForm"
title="Kontak Pasien"
:contact-limit="10"
:schema="PersonContactListSchema"
/>
<AppPersonRelativeEntryForm
ref="personEmergencyContactRelative"
title="Penanggung Jawab"
:schema="ResponsiblePersonSchema"
/>
<div class="my-2 flex justify-end py-2">
<Action @click="submitAll" />
</div>
</template>
<style scoped>
/* component style */
</style>