refactor: postal region, add new field on list
feat: implement postal region model and update address handling - Add new PostalRegion model and service - Replace postalCode with postalRegion in address-related components - Update schemas and models to use locationType_code consistently - Add usePostalRegion composable for postal code selection - Modify patient form to handle address changes more robustly feat(patient): add ID column and improve date formatting - Add patient ID column to patient list - Format dates using 'id-ID' locale in preview - Update identity number display for foreign patients - Include passport number for foreign nationals
This commit is contained in:
@@ -101,7 +101,7 @@ defineExpose({
|
||||
<p class="text-md mt-1 font-semibold">Dokumen Identitas</p>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3">
|
||||
<InputBase
|
||||
<!-- <InputBase
|
||||
field-name="identityNumber"
|
||||
label="No. KTP"
|
||||
placeholder="Masukkan NIK"
|
||||
@@ -109,6 +109,13 @@ defineExpose({
|
||||
numeric-only
|
||||
:max-length="16"
|
||||
is-required
|
||||
/> -->
|
||||
<InputBase
|
||||
field-name="identityNumber"
|
||||
label="No. KTP"
|
||||
placeholder="Masukkan NIK"
|
||||
:errors="errors"
|
||||
numeric-only
|
||||
/>
|
||||
<InputBase
|
||||
field-name="drivingLicenseNumber"
|
||||
|
||||
@@ -17,8 +17,9 @@ export const cols: Col[] = [{}, {}, {}, {}, {}, {}, {}, { width: 5 }]
|
||||
|
||||
export const header: Th[][] = [
|
||||
[
|
||||
{ label: 'ID' },
|
||||
{ label: 'Nama' },
|
||||
{ label: 'NIK' },
|
||||
{ label: 'NIK/No. Paspor' },
|
||||
{ label: 'Tgl Lahir' },
|
||||
{ label: 'Umur' },
|
||||
{ label: 'Jenis Kelamin' },
|
||||
@@ -27,7 +28,16 @@ export const header: Th[][] = [
|
||||
],
|
||||
]
|
||||
|
||||
export const keys = ['name', 'identity_number', 'birth_date', 'patient_age', 'gender', 'education', 'action']
|
||||
export const keys = [
|
||||
'patientId',
|
||||
'name',
|
||||
'identity_number',
|
||||
'birth_date',
|
||||
'patient_age',
|
||||
'gender',
|
||||
'education',
|
||||
'action',
|
||||
]
|
||||
|
||||
export const delKeyNames: KeyLabel[] = [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
@@ -35,6 +45,10 @@ export const delKeyNames: KeyLabel[] = [
|
||||
]
|
||||
|
||||
export const funcParsed: RecStrFuncUnknown = {
|
||||
patientId: (rec: unknown): unknown => {
|
||||
const patient = rec as Patient
|
||||
return patient.number
|
||||
},
|
||||
name: (rec: unknown): unknown => {
|
||||
const { person } = rec as Patient
|
||||
return person.name.trim()
|
||||
@@ -42,16 +56,17 @@ export const funcParsed: RecStrFuncUnknown = {
|
||||
identity_number: (rec: unknown): unknown => {
|
||||
const { person } = rec as Patient
|
||||
|
||||
if (person?.residentIdentityNumber?.substring(0, 5) === 'BLANK') {
|
||||
return '(TANPA NIK)'
|
||||
if (person.nationality == 'WNA') {
|
||||
return person.passportNumber
|
||||
}
|
||||
return person.residentIdentityNumber
|
||||
|
||||
return person.residentIdentityNumber || '-'
|
||||
},
|
||||
birth_date: (rec: unknown): unknown => {
|
||||
const { person } = rec as Patient
|
||||
|
||||
if (typeof person.birthDate == 'object' && person.birthDate) {
|
||||
return (person.birthDate as Date).toLocaleDateString()
|
||||
return (person.birthDate as Date).toLocaleDateString('id-ID')
|
||||
} else if (typeof person.birthDate == 'string') {
|
||||
return (person.birthDate as string).substring(0, 10)
|
||||
}
|
||||
|
||||
@@ -37,13 +37,13 @@ const personContactTypeOptions = mapToComboboxOptList(personContactTypes)
|
||||
// Computed addresses from nested data
|
||||
const domicileAddress = computed(() => {
|
||||
const addresses = props.patient.person.addresses
|
||||
const resident = addresses?.find((addr) => addr.locationType === 'domicile')
|
||||
const resident = addresses?.find((addr) => addr.locationType_code === 'domicile')
|
||||
return formatAddress(resident)
|
||||
})
|
||||
|
||||
const identityAddress = computed(() => {
|
||||
const addresses = props.patient.person.addresses
|
||||
const primary = addresses?.find((addr) => addr.locationType === 'identity')
|
||||
const primary = addresses?.find((addr) => addr.locationType_code === 'identity')
|
||||
return formatAddress(primary)
|
||||
})
|
||||
|
||||
@@ -85,11 +85,11 @@ function onClick(type: string) {
|
||||
<DetailRow label="Nama Lengkap">{{ patient.person.name || '-' }}</DetailRow>
|
||||
<DetailRow label="Tempat, tanggal lahir">
|
||||
{{ patient.person.birthRegency?.name || '-' }},
|
||||
{{ patient.person.birthDate ? new Date(patient.person.birthDate).toLocaleDateString() : '-' }}
|
||||
{{ patient.person.birthDate ? new Date(patient.person.birthDate).toLocaleDateString('id-ID') : '-' }}
|
||||
</DetailRow>
|
||||
<DetailRow label="Usia">{{ patientAge || '-' }} Tahun</DetailRow>
|
||||
<DetailRow label="Tanggal Daftar">
|
||||
{{ patient.person.createdAt ? new Date(patient.person.createdAt).toLocaleDateString() : '-' }}
|
||||
{{ patient.person.createdAt ? new Date(patient.person.createdAt).toLocaleDateString('id-ID') : '-' }}
|
||||
</DetailRow>
|
||||
<DetailRow label="Jenis Kelamin">
|
||||
{{ genderOptions.find((item) => item.code === patient.person.gender_code)?.label || '-' }}
|
||||
|
||||
@@ -18,10 +18,10 @@ const props = defineProps<{
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const { fieldName = 'zipCode', placeholder = 'Kode Pos', errors, class: containerClass, fieldGroupClass } = props
|
||||
const { fieldName = 'postalRegion', placeholder = 'Kode Pos', errors, class: containerClass, fieldGroupClass } = props
|
||||
|
||||
const villageCodeRef = toRef(props, 'villageCode')
|
||||
const { postalCodeOptions, isLoading, error } = usePostalCodes(villageCodeRef)
|
||||
const { postalRegionOptions, isLoading, error } = usePostalRegion(villageCodeRef)
|
||||
|
||||
const dynamicPlaceholder = computed(() => {
|
||||
if (!props.villageCode) return 'Pilih kelurahan terlebih dahulu'
|
||||
@@ -57,7 +57,7 @@ const isFieldDisabled = computed(() => {
|
||||
<Combobox
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="postalCodeOptions"
|
||||
:items="postalRegionOptions"
|
||||
:placeholder="dynamicPlaceholder"
|
||||
:is-disabled="isFieldDisabled"
|
||||
search-placeholder="Cari..."
|
||||
|
||||
@@ -43,7 +43,7 @@ const fieldStates: Record<string, { dependsOn?: string; placeholder: string }> =
|
||||
regency_code: { dependsOn: 'province_code', placeholder: 'Pilih provinsi dahulu' },
|
||||
district_code: { dependsOn: 'regency_code', placeholder: 'Pilih kabupaten/kota dahulu' },
|
||||
village_code: { dependsOn: 'district_code', placeholder: 'Pilih kecamatan dahulu' },
|
||||
postalCode_code: { dependsOn: 'village_code', placeholder: 'Pilih kelurahan dahulu' },
|
||||
postalRegion_code: { dependsOn: 'village_code', placeholder: 'Pilih kelurahan dahulu' },
|
||||
address: { placeholder: 'Masukkan alamat' },
|
||||
rt: { placeholder: '001' },
|
||||
rw: { placeholder: '002' },
|
||||
@@ -73,7 +73,7 @@ function getFieldState(field: string) {
|
||||
const isDisabledByDependency = !dependencyValue
|
||||
|
||||
// Jika isSame, semua field location disabled
|
||||
if (isSame && ['regency_code', 'district_code', 'village_code', 'postalCode_code'].includes(field)) {
|
||||
if (isSame && ['regency_code', 'district_code', 'village_code', 'postalRegion_code'].includes(field)) {
|
||||
return { placeholder: '-', disabled: true }
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ function getFieldState(field: string) {
|
||||
}
|
||||
|
||||
// Jika isSame dan field location, disabled
|
||||
if (isSame && ['regency_code', 'district_code', 'village_code', 'postalCode_code'].includes(field)) {
|
||||
if (isSame && ['regency_code', 'district_code', 'village_code', 'postalRegion_code'].includes(field)) {
|
||||
return { placeholder: '-', disabled: true }
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ watch(
|
||||
regency_code: undefined,
|
||||
district_code: undefined,
|
||||
village_code: undefined,
|
||||
postalCode_code: undefined,
|
||||
postalRegion_code: undefined,
|
||||
},
|
||||
false,
|
||||
)
|
||||
@@ -133,7 +133,7 @@ watch(
|
||||
{
|
||||
district_code: undefined,
|
||||
village_code: undefined,
|
||||
postalCode_code: undefined,
|
||||
postalRegion_code: undefined,
|
||||
},
|
||||
false,
|
||||
)
|
||||
@@ -157,7 +157,7 @@ watch(
|
||||
formRef.value.setValues(
|
||||
{
|
||||
village_code: undefined,
|
||||
postalCode_code: undefined,
|
||||
postalRegion_code: undefined,
|
||||
},
|
||||
false,
|
||||
)
|
||||
@@ -180,7 +180,7 @@ watch(
|
||||
|
||||
formRef.value.setValues(
|
||||
{
|
||||
postalCode_code: undefined,
|
||||
postalRegion_code: undefined,
|
||||
},
|
||||
false,
|
||||
)
|
||||
@@ -214,7 +214,7 @@ watch(
|
||||
if (updatedValues.regency_code === '') updatedValues.regency_code = undefined
|
||||
if (updatedValues.district_code === '') updatedValues.district_code = undefined
|
||||
if (updatedValues.village_code === '') updatedValues.village_code = undefined
|
||||
if (updatedValues.postalCode_code === '') updatedValues.postalCode_code = undefined
|
||||
if (updatedValues.postalRegion_code === '') updatedValues.postalRegion_code = undefined
|
||||
if (updatedValues.address === '') updatedValues.address = undefined
|
||||
|
||||
// Update values dan trigger validasi
|
||||
@@ -235,7 +235,7 @@ watch(
|
||||
formRef.value?.setFieldError('regency_code', undefined)
|
||||
formRef.value?.setFieldError('district_code', undefined)
|
||||
formRef.value?.setFieldError('village_code', undefined)
|
||||
formRef.value?.setFieldError('postalCode_code', undefined)
|
||||
formRef.value?.setFieldError('postalRegion_code', undefined)
|
||||
formRef.value?.setFieldError('address', undefined)
|
||||
formRef.value?.setFieldError('rt', undefined)
|
||||
formRef.value?.setFieldError('rw', undefined)
|
||||
@@ -255,7 +255,7 @@ watch(
|
||||
:validation-schema="formSchema"
|
||||
:validate-on-mount="false"
|
||||
validation-mode="onSubmit"
|
||||
:initial-values="initialValues ?? { isSameAddress: '1', locationType: 'identity' }"
|
||||
:initial-values="initialValues ?? { isSameAddress: '1', locationType_code: 'identity' }"
|
||||
>
|
||||
<div>
|
||||
<p
|
||||
@@ -267,15 +267,15 @@ watch(
|
||||
</div>
|
||||
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2">
|
||||
<!-- LocationType - Hidden field with default value 'identity' -->
|
||||
<!-- locationType_code - Hidden field with default value 'identity' -->
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
name="locationType"
|
||||
name="locationType_code"
|
||||
>
|
||||
<input
|
||||
type="hidden"
|
||||
v-bind="componentField"
|
||||
value="primary"
|
||||
value="identity"
|
||||
/>
|
||||
</FormField>
|
||||
<FieldGroup class="radio-group-field">
|
||||
@@ -403,10 +403,10 @@ watch(
|
||||
|
||||
<div class="min-w-0 flex-[2]">
|
||||
<SelectPostal
|
||||
field-name="postalCode_code"
|
||||
field-name="postalRegion_code"
|
||||
:village-code="values.village_code"
|
||||
:placeholder="getFieldState('postalCode_code').placeholder"
|
||||
:is-disabled="getFieldState('postalCode_code').disabled || !values.village_code"
|
||||
:placeholder="getFieldState('postalRegion_code').placeholder"
|
||||
:is-disabled="getFieldState('postalRegion_code').disabled || !values.village_code"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -160,7 +160,9 @@ watch(
|
||||
:validation-schema="formSchema"
|
||||
:validate-on-mount="false"
|
||||
validation-mode="onSubmit"
|
||||
:initial-values="initialValues ? { locationType: 'domicile', ...initialValues } : { locationType: 'domicile' }"
|
||||
:initial-values="
|
||||
initialValues ? { locationType_code: 'domicile', ...initialValues } : { locationType_code: 'domicile' }
|
||||
"
|
||||
>
|
||||
<div>
|
||||
<p
|
||||
@@ -172,15 +174,15 @@ watch(
|
||||
</div>
|
||||
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2">
|
||||
<!-- LocationType - Hidden field with default value 'domicile' -->
|
||||
<!-- locationType_code - Hidden field with default value 'domicile' -->
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
name="locationType"
|
||||
name="locationType_code"
|
||||
>
|
||||
<input
|
||||
type="hidden"
|
||||
v-bind="componentField"
|
||||
value="resident"
|
||||
value="domicile"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
@@ -251,7 +253,7 @@ watch(
|
||||
|
||||
<div class="min-w-0 flex-[2]">
|
||||
<SelectPostal
|
||||
field-name="postalCode_code"
|
||||
field-name="postalRegion_code"
|
||||
placeholder="Pilih kelurahan dahulu"
|
||||
:village-code="values.village_code"
|
||||
:is-disabled="!values.village_code"
|
||||
|
||||
@@ -50,7 +50,7 @@ onMounted(() => {
|
||||
regency_code: currentAddressValues.regency_code || undefined,
|
||||
district_code: currentAddressValues.district_code || undefined,
|
||||
village_code: currentAddressValues.village_code || undefined,
|
||||
postalCode_code: currentAddressValues.postalCode_code || undefined,
|
||||
postalRegion_code: currentAddressValues.postalRegion_code || undefined,
|
||||
address: currentAddressValues.address || undefined,
|
||||
rt: currentAddressValues.rt || undefined,
|
||||
rw: currentAddressValues.rw || undefined,
|
||||
@@ -64,10 +64,7 @@ onMounted(() => {
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
// #endregion region
|
||||
|
||||
// #region Utilities & event handlers
|
||||
async function submitAll() {
|
||||
async function sendRequest() {
|
||||
const [patient, address, addressRelative, families, contacts, emergencyContact] = await Promise.all([
|
||||
personPatientForm.value?.validate(),
|
||||
personAddressForm.value?.validate(),
|
||||
@@ -95,6 +92,7 @@ async function submitAll() {
|
||||
}
|
||||
|
||||
const formData = genPatient(formDataRequest)
|
||||
console.log(formData)
|
||||
payload.value = formData
|
||||
|
||||
try {
|
||||
@@ -120,6 +118,21 @@ async function submitAll() {
|
||||
// Handle error - show error message to user
|
||||
}
|
||||
}
|
||||
// #endregion region
|
||||
|
||||
// #region Utilities & event handlers
|
||||
async function handleActionClick(eventType: string) {
|
||||
if (eventType === 'submit') {
|
||||
await sendRequest()
|
||||
return
|
||||
}
|
||||
|
||||
if (eventType === 'cancel') {
|
||||
navigateTo({
|
||||
name: 'client-patient',
|
||||
})
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Watchers
|
||||
@@ -141,7 +154,7 @@ watch(
|
||||
regency_code: currentAddressValues.regency_code || undefined,
|
||||
district_code: currentAddressValues.district_code || undefined,
|
||||
village_code: currentAddressValues.village_code || undefined,
|
||||
postalCode_code: currentAddressValues.postalCode_code || undefined,
|
||||
postalRegion_code: currentAddressValues.postalRegion_code || undefined,
|
||||
address: currentAddressValues.address || undefined,
|
||||
rt: currentAddressValues.rt || undefined,
|
||||
rw: currentAddressValues.rw || undefined,
|
||||
@@ -172,7 +185,7 @@ watch(
|
||||
regency_code: newAddressValues.regency_code || undefined,
|
||||
district_code: newAddressValues.district_code || undefined,
|
||||
village_code: newAddressValues.village_code || undefined,
|
||||
postalCode_code: newAddressValues.postalCode_code || undefined,
|
||||
postalRegion_code: newAddressValues.postalRegion_code || undefined,
|
||||
address: newAddressValues.address || undefined,
|
||||
rt: newAddressValues.rt || undefined,
|
||||
rw: newAddressValues.rw || undefined,
|
||||
@@ -191,7 +204,7 @@ watch(
|
||||
if (
|
||||
(isSameAddress === true || isSameAddress === '1') &&
|
||||
personAddressForm.value?.values &&
|
||||
personAddressRelativeForm.value
|
||||
personAddressRelativeForm.value?.values
|
||||
) {
|
||||
// Ketika isSameAddress diubah menjadi true, copy alamat sekarang ke alamat KTP
|
||||
const currentAddressValues = personAddressForm.value.values
|
||||
@@ -202,7 +215,7 @@ watch(
|
||||
regency_code: currentAddressValues.regency_code || undefined,
|
||||
district_code: currentAddressValues.district_code || undefined,
|
||||
village_code: currentAddressValues.village_code || undefined,
|
||||
postalCode_code: currentAddressValues.postalCode_code || undefined,
|
||||
postalRegion_code: currentAddressValues.postalRegion_code || undefined,
|
||||
address: currentAddressValues.address || undefined,
|
||||
rt: currentAddressValues.rt || undefined,
|
||||
rw: currentAddressValues.rw || undefined,
|
||||
@@ -249,7 +262,7 @@ watch(
|
||||
/>
|
||||
|
||||
<div class="my-2 flex justify-end py-2">
|
||||
<Action @click="submitAll" />
|
||||
<Action @click="handleActionClick" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user