- Standardize parameter naming with hyphens instead of underscores - Remove page size limit and enable no-limit flag for all region queries - Improve arrow function syntax consistency and formatting
182 lines
5.6 KiB
TypeScript
182 lines
5.6 KiB
TypeScript
import { ref, computed, watch } from 'vue'
|
|
import { refDebounced } from '@vueuse/core'
|
|
import type { Village } from '~/models/village'
|
|
import type { SelectItem } from '~/components/pub/my-ui/form/select.vue'
|
|
import { toTitleCase } from '~/lib/utils'
|
|
import * as villageService from '~/services/village.service'
|
|
|
|
// Global cache untuk villages berdasarkan district code
|
|
const villagesCache = ref<Map<string, Village[]>>(new Map())
|
|
const loadingStates = ref<Map<string, boolean>>(new Map())
|
|
const errorStates = ref<Map<string, string | null>>(new Map())
|
|
|
|
export function useVillages(districtCode: Ref<string | undefined> | string | undefined) {
|
|
// Convert districtCode ke ref jika bukan ref
|
|
const districtCodeRef =
|
|
typeof districtCode === 'string' || districtCode === undefined ? ref(districtCode) : districtCode
|
|
|
|
// Computed untuk mendapatkan villages berdasarkan district code
|
|
const villages = computed(() => {
|
|
const code = districtCodeRef.value
|
|
if (!code) return []
|
|
return villagesCache.value.get(code) || []
|
|
})
|
|
|
|
// Computed untuk loading state
|
|
const isLoading = computed(() => {
|
|
const code = districtCodeRef.value
|
|
if (!code) return false
|
|
return loadingStates.value.get(code) || false
|
|
})
|
|
|
|
// Computed untuk error state
|
|
const error = computed(() => {
|
|
const code = districtCodeRef.value
|
|
if (!code) return null
|
|
return errorStates.value.get(code) || null
|
|
})
|
|
|
|
// Computed untuk format SelectItem
|
|
const villageOptions = computed<SelectItem[]>(() => {
|
|
return villages.value.map((village) => ({
|
|
label: toTitleCase(village.name),
|
|
value: village.code,
|
|
searchValue: `${village.code} ${village.name}`.trim(),
|
|
}))
|
|
})
|
|
|
|
// Function untuk fetch villages berdasarkan district code
|
|
async function fetchVillages(districtCodeParam?: string, forceRefresh = false, isUserAction = false) {
|
|
const code = districtCodeParam || districtCodeRef.value
|
|
if (!code) return
|
|
|
|
// Jika user action atau force refresh, selalu fetch
|
|
// Jika bukan user action dan sudah ada cache, skip
|
|
if (!isUserAction && !forceRefresh && villagesCache.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 villageService.getList({
|
|
sort: 'name:asc',
|
|
'district-code': code,
|
|
'page-no-limit': true,
|
|
})
|
|
|
|
if (response.success) {
|
|
const villagesData = response.body.data || []
|
|
villagesCache.value.set(code, villagesData)
|
|
} else {
|
|
errorStates.value.set(code, 'Gagal memuat data kelurahan')
|
|
console.error('Failed to fetch villages:', response)
|
|
}
|
|
} catch (err) {
|
|
errorStates.value.set(code, 'Terjadi kesalahan saat memuat data kelurahan')
|
|
console.error('Error fetching villages:', err)
|
|
} finally {
|
|
loadingStates.value.set(code, false)
|
|
loadingStates.value.delete(pendingKey)
|
|
}
|
|
}
|
|
|
|
// Function untuk mencari village berdasarkan code
|
|
function getVillageByCode(code: string): Village | undefined {
|
|
const districtCode = districtCodeRef.value
|
|
if (!districtCode) return undefined
|
|
|
|
const villagesForDistrict = villagesCache.value.get(districtCode) || []
|
|
return villagesForDistrict.find((village) => village.code === code)
|
|
}
|
|
|
|
// Function untuk mencari village berdasarkan name
|
|
function getVillageByName(name: string): Village | undefined {
|
|
const districtCode = districtCodeRef.value
|
|
if (!districtCode) return undefined
|
|
|
|
const villagesForDistrict = villagesCache.value.get(districtCode) || []
|
|
return villagesForDistrict.find((village) => village.name.toLowerCase() === name.toLowerCase())
|
|
}
|
|
|
|
// Function untuk clear cache district tertentu
|
|
function clearCache(districtCodeParam?: string) {
|
|
const code = districtCodeParam || districtCodeRef.value
|
|
if (code) {
|
|
villagesCache.value.delete(code)
|
|
loadingStates.value.delete(code)
|
|
errorStates.value.delete(code)
|
|
}
|
|
}
|
|
|
|
// Function untuk clear semua cache
|
|
function clearAllCache() {
|
|
villagesCache.value.clear()
|
|
loadingStates.value.clear()
|
|
errorStates.value.clear()
|
|
}
|
|
|
|
// Function untuk refresh data
|
|
function refreshVillages(districtCodeParam?: string) {
|
|
const code = districtCodeParam || districtCodeRef.value
|
|
if (code) {
|
|
return fetchVillages(code, true)
|
|
}
|
|
}
|
|
|
|
// Debounced district code untuk mencegah multiple calls
|
|
const debouncedDistrictCode = refDebounced(districtCodeRef, 100)
|
|
|
|
// Watch perubahan district code untuk auto fetch
|
|
watch(
|
|
debouncedDistrictCode,
|
|
(newCode, oldCode) => {
|
|
if (newCode && newCode !== oldCode) {
|
|
// Jika ada oldCode berarti user action (ganti pilihan)
|
|
const isUserAction = !!oldCode
|
|
fetchVillages(newCode, false, isUserAction)
|
|
}
|
|
},
|
|
{ immediate: true },
|
|
)
|
|
|
|
return {
|
|
// Data
|
|
villages: readonly(villages),
|
|
villageOptions,
|
|
|
|
// State
|
|
isLoading: readonly(isLoading),
|
|
error: readonly(error),
|
|
|
|
// Methods
|
|
fetchVillages,
|
|
refreshVillages,
|
|
getVillageByCode,
|
|
getVillageByName,
|
|
clearCache,
|
|
clearAllCache,
|
|
}
|
|
}
|
|
|
|
// Export untuk direct access ke cached data (jika diperlukan)
|
|
export const useVillagesCache = () => ({
|
|
villagesCache: readonly(villagesCache),
|
|
loadingStates: readonly(loadingStates),
|
|
errorStates: readonly(errorStates),
|
|
})
|