Files
simrsx-fe/app/composables/usePostalRegion.ts
Khafid Prayoga 46911514fb 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
2025-10-13 13:07:00 +07:00

170 lines
5.3 KiB
TypeScript

import { ref, computed, watch, readonly, type Ref } from 'vue'
import { refDebounced } from '@vueuse/core'
import type { PostalRegion } from '~/models/postal-region'
import type { SelectItem } from '~/components/pub/my-ui/form/select.vue'
import * as postalRegionService from '~/services/postal-region.service'
// Global cache untuk postal codes berdasarkan village code
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 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 postalRegion berdasarkan village code
const postalRegion = computed(() => {
const code = villageCodeRef.value
if (!code) return []
return postalRegionCache.value.get(code) || []
})
// Computed untuk loading state
const isLoading = computed(() => {
const code = villageCodeRef.value
if (!code) return false
return loadingStates.value.get(code) || false
})
// Computed untuk error state
const error = computed(() => {
const code = villageCodeRef.value
if (!code) return null
return errorStates.value.get(code) || null
})
// Computed untuk format SelectItem
const postalRegionOptions = computed<SelectItem[]>(() => {
return postalRegion.value.map((postalRegion) => ({
label: postalRegion.code,
value: postalRegion.code,
searchValue: postalRegion.code,
}))
})
// 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 && postalRegionCache.value.has(code)) {
return
}
// Jika sedang loading, skip untuk mencegah duplicate calls
if (loadingStates.value.get(code)) {
return
}
// Tambahan: Cek apakah ada pending request untuk code yang sama
const pendingKey = `pending_${code}`
if (loadingStates.value.get(pendingKey)) {
return
}
loadingStates.value.set(pendingKey, true)
loadingStates.value.set(code, true)
errorStates.value.set(code, null)
try {
const response = await postalRegionService.getList({
sort: 'code:asc',
'village-code': code,
'page-no-limit': true,
})
if (response.success) {
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)
}
} catch (err) {
errorStates.value.set(code, 'Terjadi kesalahan saat memuat data kode pos')
console.error('Error fetching postal codes:', err)
} finally {
loadingStates.value.set(code, false)
loadingStates.value.delete(pendingKey)
}
}
// Function untuk mencari postalRegion berdasarkan code
function getpostalRegionByCode(code: string): PostalRegion | undefined {
const villageCode = villageCodeRef.value
if (!villageCode) return undefined
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) {
postalRegionCache.value.delete(code)
loadingStates.value.delete(code)
errorStates.value.delete(code)
}
}
// Function untuk clear semua cache
function clearAllCache() {
postalRegionCache.value.clear()
loadingStates.value.clear()
errorStates.value.clear()
}
// Function untuk refresh data
function refreshpostalRegion(villageCodeParam?: string) {
const code = villageCodeParam || villageCodeRef.value
if (code) {
return fetchpostalRegion(code, true)
}
}
// Debounced village code untuk mencegah multiple calls
const debouncedVillageCode = refDebounced(villageCodeRef, 100)
// Watch perubahan village code untuk auto fetch
watch(
debouncedVillageCode,
(newCode, oldCode) => {
if (newCode && newCode !== oldCode) {
// Jika ada oldCode berarti user action (ganti pilihan)
const isUserAction = !!oldCode
fetchpostalRegion(newCode, false, isUserAction)
}
},
{ immediate: true },
)
return {
// Data
postalRegion: readonly(postalRegion),
postalRegionOptions,
// State
isLoading: readonly(isLoading),
error: readonly(error),
// Methods
fetchpostalRegion,
refreshpostalRegion,
getpostalRegionByCode,
clearCache,
clearAllCache,
}
}
// Export untuk direct access ke cached data (jika diperlukan)
export const usepostalRegionCache = () => ({
postalRegionCache: readonly(postalRegionCache),
loadingStates: readonly(loadingStates),
errorStates: readonly(errorStates),
})