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:
Khafid Prayoga
2025-10-13 11:18:10 +07:00
parent 44433d67c8
commit 46911514fb
16 changed files with 204 additions and 123 deletions
+8 -1
View File
@@ -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"
+21 -6
View File
@@ -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)
}
+4 -4
View File
@@ -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"
+23 -10
View File
@@ -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>
@@ -1,23 +1,23 @@
import { ref, computed, watch, readonly, type Ref } from 'vue'
import { refDebounced } from '@vueuse/core'
import type { PostalCode } from '~/models/postal-code'
import type { PostalRegion } from '~/models/postal-region'
import type { SelectItem } from '~/components/pub/my-ui/form/select.vue'
import * as postalCodeService from '../services/postal-code.service'
import * as postalRegionService from '~/services/postal-region.service'
// Global cache untuk postal codes berdasarkan village code
const postalCodesCache = ref<Map<string, PostalCode[]>>(new Map())
const postalRegionCache = ref<Map<string, PostalRegion[]>>(new Map())
const loadingStates = ref<Map<string, boolean>>(new Map())
const errorStates = ref<Map<string, string | null>>(new Map())
export function usePostalCodes(villageCode: Ref<string | undefined> | string | undefined) {
export function usePostalRegion(villageCode: Ref<string | undefined> | string | undefined) {
// Convert villageCode ke ref jika bukan ref
const villageCodeRef = typeof villageCode === 'string' || villageCode === undefined ? ref(villageCode) : villageCode
// Computed untuk mendapatkan postalCodes berdasarkan village code
const postalCodes = computed(() => {
// Computed untuk mendapatkan postalRegion berdasarkan village code
const postalRegion = computed(() => {
const code = villageCodeRef.value
if (!code) return []
return postalCodesCache.value.get(code) || []
return postalRegionCache.value.get(code) || []
})
// Computed untuk loading state
@@ -35,22 +35,22 @@ export function usePostalCodes(villageCode: Ref<string | undefined> | string | u
})
// Computed untuk format SelectItem
const postalCodeOptions = computed<SelectItem[]>(() => {
return postalCodes.value.map((postalCode) => ({
label: postalCode.code,
value: postalCode.code,
searchValue: postalCode.code,
const postalRegionOptions = computed<SelectItem[]>(() => {
return postalRegion.value.map((postalRegion) => ({
label: postalRegion.code,
value: postalRegion.code,
searchValue: postalRegion.code,
}))
})
// Function untuk fetch postalCodes berdasarkan village code
async function fetchPostalCodes(villageCodeParam?: string, forceRefresh = false, isUserAction = false) {
// Function untuk fetch postalRegion berdasarkan village code
async function fetchpostalRegion(villageCodeParam?: string, forceRefresh = false, isUserAction = false) {
const code = villageCodeParam || villageCodeRef.value
if (!code) return
// Jika user action atau force refresh, selalu fetch
// Jika bukan user action dan sudah ada cache, skip
if (!isUserAction && !forceRefresh && postalCodesCache.value.has(code)) {
if (!isUserAction && !forceRefresh && postalRegionCache.value.has(code)) {
return
}
@@ -71,15 +71,15 @@ export function usePostalCodes(villageCode: Ref<string | undefined> | string | u
errorStates.value.set(code, null)
try {
const response = await postalCodeService.getList({
const response = await postalRegionService.getList({
sort: 'code:asc',
'village-code': code,
'page-no-limit': true,
})
if (response.success) {
const postalCodesData = response.body.data || []
postalCodesCache.value.set(code, postalCodesData)
const postalRegionData = response.body.data || []
postalRegionCache.value.set(code, postalRegionData)
} else {
errorStates.value.set(code, 'Gagal memuat data kode pos')
console.error('Failed to fetch postal codes:', response)
@@ -93,20 +93,20 @@ export function usePostalCodes(villageCode: Ref<string | undefined> | string | u
}
}
// Function untuk mencari postalCode berdasarkan code
function getPostalCodeByCode(code: string): PostalCode | undefined {
// Function untuk mencari postalRegion berdasarkan code
function getpostalRegionByCode(code: string): PostalRegion | undefined {
const villageCode = villageCodeRef.value
if (!villageCode) return undefined
const postalCodesForVillage = postalCodesCache.value.get(villageCode) || []
return postalCodesForVillage.find((postalCode) => postalCode.code === code)
const postalRegionForVillage = postalRegionCache.value.get(villageCode) || []
return postalRegionForVillage.find((postalRegion) => postalRegion.code === code)
}
// Function untuk clear cache village tertentu
function clearCache(villageCodeParam?: string) {
const code = villageCodeParam || villageCodeRef.value
if (code) {
postalCodesCache.value.delete(code)
postalRegionCache.value.delete(code)
loadingStates.value.delete(code)
errorStates.value.delete(code)
}
@@ -114,16 +114,16 @@ export function usePostalCodes(villageCode: Ref<string | undefined> | string | u
// Function untuk clear semua cache
function clearAllCache() {
postalCodesCache.value.clear()
postalRegionCache.value.clear()
loadingStates.value.clear()
errorStates.value.clear()
}
// Function untuk refresh data
function refreshPostalCodes(villageCodeParam?: string) {
function refreshpostalRegion(villageCodeParam?: string) {
const code = villageCodeParam || villageCodeRef.value
if (code) {
return fetchPostalCodes(code, true)
return fetchpostalRegion(code, true)
}
}
@@ -137,7 +137,7 @@ export function usePostalCodes(villageCode: Ref<string | undefined> | string | u
if (newCode && newCode !== oldCode) {
// Jika ada oldCode berarti user action (ganti pilihan)
const isUserAction = !!oldCode
fetchPostalCodes(newCode, false, isUserAction)
fetchpostalRegion(newCode, false, isUserAction)
}
},
{ immediate: true },
@@ -145,25 +145,25 @@ export function usePostalCodes(villageCode: Ref<string | undefined> | string | u
return {
// Data
postalCodes: readonly(postalCodes),
postalCodeOptions,
postalRegion: readonly(postalRegion),
postalRegionOptions,
// State
isLoading: readonly(isLoading),
error: readonly(error),
// Methods
fetchPostalCodes,
refreshPostalCodes,
getPostalCodeByCode,
fetchpostalRegion,
refreshpostalRegion,
getpostalRegionByCode,
clearCache,
clearAllCache,
}
}
// Export untuk direct access ke cached data (jika diperlukan)
export const usePostalCodesCache = () => ({
postalCodesCache: readonly(postalCodesCache),
export const usepostalRegionCache = () => ({
postalRegionCache: readonly(postalRegionCache),
loadingStates: readonly(loadingStates),
errorStates: readonly(errorStates),
})
+3
View File
@@ -85,6 +85,7 @@ export const religionCodes: Record<string, string> = {
hindu: 'Hindu',
buda: 'Buda',
konghucu: 'Konghucu',
other: 'Kepercayaan Lain',
}
export const educationCodes: Record<string, string> = {
@@ -349,3 +350,5 @@ export const addressLocationTypeCode: Record<string, string> = {
identity: 'Alamat KTP',
domicile: 'Alamat Domisili',
}
export type AddressLocationTypeCode = keyof typeof addressLocationTypeCode
+25 -2
View File
@@ -38,6 +38,7 @@ export interface genPatientProps {
}
export function genPatient(props: genPatientProps): Patient {
console.log(props)
const { patient, residentAddress, cardAddress, familyData, contacts, responsible } = props
const addresses: PersonAddress[] = [{ ...genBase(), person_id: 0, ...residentAddress }]
@@ -46,7 +47,29 @@ export function genPatient(props: genPatientProps): Patient {
// jika alamat ktp sama dengan domisili saat ini
if (cardAddress.isSameAddress) {
addresses.push({ ...genBase(), ...residentAddress, person_id: 0, locationType: 'identity' })
addresses.push({
...genBase(),
...residentAddress,
person_id: 0,
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,
locationType_code: cardAddress.locationType_code || 'identity',
address: cardAddress.address || '',
province_code: cardAddress.province_code || '',
regency_code: cardAddress.regency_code || '',
district_code: cardAddress.district_code || '',
village_code: cardAddress.village_code || '',
postalRegion_code: cardAddress.postalRegion_code || '',
rt: cardAddress.rt,
rw: cardAddress.rw,
}
addresses.push(relativeAddress)
}
// add data orang tua
@@ -129,7 +152,7 @@ export function genPatient(props: genPatientProps): Patient {
newBornStatus: patient.isNewBorn,
person_id: 0,
id: 0,
number: '0x000000000000000000000000000000',
number: '',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
+9 -8
View File
@@ -1,25 +1,26 @@
import { type Base, genBase } from './_base'
import type { PostalCode } from './postal-code'
import type { AddressLocationTypeCode } from '~/lib/constants'
import type { PostalRegion } from './postal-region'
import { toTitleCase } from '~/lib/utils'
export interface PersonAddress extends Base {
person_id: number
locationType: string
locationType_code: AddressLocationTypeCode
address: string
rt?: string
rw?: string
postalCode_code?: string
postalRegion_code?: string
village_code: string
// preload
postalCode?: PostalCode | null
postalRegion?: PostalRegion | null
locationType?: AddressLocationTypeCode
}
export function genPersonAddress(): PersonAddress {
return {
...genBase(),
person_id: 0,
locationType: '',
locationType_code: '',
address: '',
village_code: '',
}
@@ -28,7 +29,7 @@ export function genPersonAddress(): PersonAddress {
export function formatAddress(builder?: PersonAddress) {
if (!builder) return ''
const village = builder?.postalCode?.village
const village = builder?.postalRegion?.village
const district = village?.district
const regency = district?.regency
const province = regency?.province
@@ -39,7 +40,7 @@ export function formatAddress(builder?: PersonAddress) {
district?.name,
toTitleCase(regency?.name || ''),
toTitleCase(province?.name || ''),
builder?.postalCode_code,
builder?.postalRegion_code,
].filter(Boolean)
return parts.join(', ')
@@ -1,6 +1,7 @@
import { type Base, genBase } from './_base'
import type { Village } from './village'
export interface PostalCode extends Base {
export interface PostalRegion extends Base {
code: string
village_code: string
@@ -8,7 +9,7 @@ export interface PostalCode extends Base {
village?: Village | null
}
export function genPostalCode(): PostalCode {
export function genPostalRegion(): PostalRegion {
return {
...genBase(),
code: '',
+42 -26
View File
@@ -15,12 +15,12 @@ const IsNewBornSchema = z
const PatientSchema = z
.object({
// Data Diri Pasien
identityNumber: z
.string({
required_error: 'Mohon lengkapi NIK',
})
.min(16, 'NIK harus berupa angka 16 digit')
.regex(/^\d+$/, 'NIK harus berupa angka 16 digit'),
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'),
// identityCardFile: z.instanceof(File, { message: 'File KTP harus dipilih' }),
// familyCardFile: z.instanceof(File, { message: 'File KK harus dipilih' }),
@@ -106,26 +106,42 @@ const PatientSchema = z
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') {
return !!data.disabilityType?.trim()
}
return true
}, {
message: 'Mohon pilih Jenis Disabilitas',
path: ['disabilityType'],
})
.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') {
return !!data.disabilityType?.trim()
}
return true
},
{
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 {
@@ -22,7 +22,7 @@ const PersonAddressRelativeSchema = z
'regency_code',
'district_code',
'village_code',
'postalCode_code',
'postalRegion_code',
'address',
]
@@ -48,7 +48,7 @@ function getRequiredMessage(field: string): string {
regency_code: 'Mohon pilih kabupaten/kota',
district_code: 'Mohon pilih kecamatan',
village_code: 'Mohon pilih kelurahan',
postalCode_code: 'Mohon lengkapi kode pos',
postalRegion_code: 'Mohon lengkapi kode pos',
address: 'Mohon lengkapi alamat',
}
return messages[field] || `${field} wajib diisi`
+2 -2
View File
@@ -1,7 +1,7 @@
import { z } from 'zod'
const PersonAddressSchema = z.object({
locationType: z.string({
locationType_code: z.string({
required_error: 'Mohon pilih jenis alamat',
}),
address: z.string({
@@ -19,7 +19,7 @@ const PersonAddressSchema = z.object({
village_code: z.string({
required_error: 'Mohon pilih kelurahan',
}),
postalCode_code: z.string({
postalRegion_code: z.string({
required_error: 'Mohon lengkapi kode pos',
}),
// .min(5, 'Kode pos harus berupa angka 5 digit')
@@ -1,8 +1,8 @@
// Base service for postal code operations
import * as base from './_crud-base'
const path = '/api/v1/postal-code'
const name = 'postal-code'
const path = '/api/v1/postal-region'
const name = 'postal-region'
export function create(data: any) {
return base.create(path, data, name)