import { ref, computed, watch } from 'vue' import { useDebounceFn } from '@vueuse/core' import type { Regency } from '~/models/regency' import type { Item } from '~/components/pub/my-ui/combobox' import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type' import { toTitleCase } from '~/lib/utils' import * as regencyService from '~/services/regency.service' // Interface untuk query parameters interface RegencyQueryParams { 'province-code'?: string search?: string 'page-number'?: number 'page-size'?: number sort?: string } // Interface untuk cached data dengan pagination interface CachedRegencyData { data: Regency[] meta: PaginationMeta queryKey: string // untuk tracking query yang berbeda } // Global cache untuk regencies berdasarkan query key (province-code + search + pagination) const regenciesCache = reactive(new Map()) const loadingStates = reactive(new Map()) const errorStates = reactive(new Map()) interface UseRegenciesOptions { provinceCode?: Ref | string | undefined pageSize?: number enablePagination?: boolean enableSearch?: boolean } export function useRegencies(options: UseRegenciesOptions | Ref | string | undefined = {}) { // Backward compatibility - jika parameter pertama adalah provinceCode const normalizedOptions: UseRegenciesOptions = typeof options === 'object' && 'value' in options ? { provinceCode: options } : typeof options === 'string' || options === undefined ? { provinceCode: options } : options const { provinceCode, pageSize = 10, enablePagination = true, enableSearch = true } = normalizedOptions // Convert provinceCode ke ref jika bukan ref const provinceCodeRef = typeof provinceCode === 'string' || provinceCode === undefined ? ref(provinceCode) : provinceCode || ref(undefined) // State untuk pagination dan search const currentPage = ref(1) const currentPageSize = ref(pageSize) const searchQuery = ref('') // Function untuk generate query key const generateQueryKey = (params: RegencyQueryParams) => { const keyParts = [ params['province-code'] || '', params.search || '', params['page-number'] || 1, params['page-size'] || pageSize, params.sort || 'name:asc', ] return keyParts.join('|') } // Current query key const currentQueryKey = computed(() => { return generateQueryKey({ 'province-code': provinceCodeRef.value, search: enableSearch ? searchQuery.value : undefined, 'page-number': enablePagination ? currentPage.value : undefined, 'page-size': enablePagination ? currentPageSize.value : undefined, sort: 'name:asc', }) }) // Computed untuk mendapatkan regencies berdasarkan current query const regencies = computed(() => { const queryKey = currentQueryKey.value const cachedData = regenciesCache.get(queryKey) return cachedData?.data || [] }) // Computed untuk pagination meta const paginationMeta = computed(() => { const queryKey = currentQueryKey.value const cachedData = regenciesCache.get(queryKey) return ( cachedData?.meta || { recordCount: 0, page: currentPage.value, pageSize: currentPageSize.value, totalPage: 0, hasNext: false, hasPrev: false, } ) }) // Computed untuk loading state const isLoading = computed(() => { const queryKey = currentQueryKey.value return loadingStates.get(queryKey) || false }) // Computed untuk error state const error = computed(() => { const queryKey = currentQueryKey.value return errorStates.get(queryKey) || null }) // Computed untuk format Item const regencyOptions = computed(() => { return regencies.value.map((regency) => ({ label: toTitleCase(regency.name), value: regency.code, searchValue: `${regency.code} ${regency.name}`.trim(), })) }) // Function untuk fetch regencies dengan pagination dan search async function fetchRegencies(params?: Partial, forceRefresh = false) { const queryParams: RegencyQueryParams = { 'province-code': params?.['province-code'] || provinceCodeRef.value, search: enableSearch ? params?.search || searchQuery.value : undefined, 'page-number': enablePagination ? params?.['page-number'] || currentPage.value : undefined, 'page-size': enablePagination ? params?.['page-size'] || currentPageSize.value : undefined, sort: params?.sort || 'name:asc', } // Jika tidak ada province code, return // if (!queryParams['province-code']) return // buat komponen select birth const queryKey = generateQueryKey(queryParams) // Jika tidak force refresh dan sudah ada cache, skip if (!forceRefresh && regenciesCache.has(queryKey)) { return } // Jika sedang loading, skip untuk mencegah duplicate calls if (loadingStates.get(queryKey)) { return } loadingStates.set(queryKey, true) errorStates.set(queryKey, null) try { // Prepare API parameters const apiParams: any = { sort: queryParams.sort, 'province-code': queryParams['province-code'], } // Add pagination or no-limit based on enablePagination if (enablePagination && queryParams['page-number'] && queryParams['page-size']) { apiParams['page-number'] = queryParams['page-number'] apiParams['page-size'] = queryParams['page-size'] } else { apiParams['page-no-limit'] = true } // Add search if enabled and has value if (enableSearch && queryParams.search && queryParams.search.trim().length >= 3) { apiParams.search = queryParams.search.trim() } const response = await regencyService.getList(apiParams) if (response.success) { const regenciesData = response.body.data || [] const meta = response.body.meta || { record_totalCount: regenciesData.length } // Create pagination meta const paginationMeta: PaginationMeta = { recordCount: meta.record_totalCount, page: queryParams['page-number'] || 1, pageSize: queryParams['page-size'] || regenciesData.length, totalPage: enablePagination && queryParams['page-size'] ? Math.ceil(meta.record_totalCount / queryParams['page-size']) : 1, hasNext: false, hasPrev: false, } if (enablePagination && queryParams['page-size']) { paginationMeta.hasNext = paginationMeta.page < paginationMeta.totalPage paginationMeta.hasPrev = paginationMeta.page > 1 } // Cache the result regenciesCache.set(queryKey, { data: [...regenciesData], meta: { ...paginationMeta }, queryKey, }) } else { errorStates.set(queryKey, 'Gagal memuat data kabupaten/kota') console.error('Failed to fetch regencies:', response) } } catch (err) { errorStates.set(queryKey, 'Terjadi kesalahan saat memuat data kabupaten/kota') console.error('Error fetching regencies:', err) } finally { loadingStates.set(queryKey, false) } } // Function untuk mencari regency berdasarkan code (search di semua cached data) function getRegencyByCode(code: string): Regency | undefined { // Search di semua cached data for (const cachedData of regenciesCache.values()) { const found = cachedData.data.find((regency) => regency.code === code) if (found) return found } return undefined } // Function untuk mencari regency berdasarkan name (search di semua cached data) function getRegencyByName(name: string): Regency | undefined { // Search di semua cached data for (const cachedData of regenciesCache.values()) { const found = cachedData.data.find((regency) => regency.name.toLowerCase() === name.toLowerCase()) if (found) return found } return undefined } // Function untuk pagination function goToPage(page: number) { if (page >= 1 && page <= paginationMeta.value.totalPage) { currentPage.value = page } } function nextPage() { if (paginationMeta.value.hasNext) { currentPage.value += 1 } } function prevPage() { if (paginationMeta.value.hasPrev) { currentPage.value -= 1 } } function changePageSize(size: number) { currentPageSize.value = size currentPage.value = 1 // Reset ke halaman pertama } // Function untuk search function setSearch(query: string) { searchQuery.value = query currentPage.value = 1 // Reset ke halaman pertama saat search } function clearSearch() { searchQuery.value = '' currentPage.value = 1 } // Function untuk clear cache berdasarkan query key pattern function clearCache(provinceCodeParam?: string) { const code = provinceCodeParam || provinceCodeRef.value if (code) { // Clear semua cache yang mengandung province code tersebut const keysToDelete: string[] = [] for (const [key] of regenciesCache.entries()) { if (key.startsWith(code + '|')) { keysToDelete.push(key) } } keysToDelete.forEach((key) => { regenciesCache.delete(key) loadingStates.delete(key) errorStates.delete(key) }) } } // Function untuk clear semua cache function clearAllCache() { regenciesCache.clear() loadingStates.clear() errorStates.clear() } // Function untuk refresh data function refreshRegencies(params?: Partial) { return fetchRegencies(params, true) } // Debounced province code untuk mencegah multiple calls const debouncedProvinceCode = refDebounced(provinceCodeRef, 100) // Watch perubahan province code untuk auto fetch watch( debouncedProvinceCode, (newCode, oldCode) => { if (newCode && newCode !== oldCode) { // Reset pagination dan search saat province code berubah currentPage.value = 1 if (enableSearch) { searchQuery.value = '' } fetchRegencies() } }, { immediate: true }, ) const triggerFetchAfterIdle = useDebounceFn(() => { if (enableSearch) { currentPage.value = 1 fetchRegencies() } }, 1000) // Watch perubahan search query untuk auto fetch dan reset halaman watch(searchQuery, (newSearch, oldSearch) => { if (newSearch !== oldSearch) { triggerFetchAfterIdle() } }) // Watch perubahan pagination untuk auto fetch watch([currentPage, currentPageSize], () => { if (enablePagination) { fetchRegencies() } }) watch(regencyOptions, (val) => { console.log('[regencyOptions] updated', val.length) }) return { // Data regencies: readonly(regencies), regencyOptions, // State isLoading: readonly(isLoading), error: readonly(error), paginationMeta: readonly(paginationMeta), // Search state searchQuery: enableSearch ? searchQuery : readonly(ref('')), // Pagination state currentPage: enablePagination ? currentPage : readonly(ref(1)), currentPageSize: enablePagination ? currentPageSize : readonly(ref(pageSize)), // Methods fetchRegencies, refreshRegencies, getRegencyByCode, getRegencyByName, clearCache, clearAllCache, // Pagination methods goToPage: enablePagination ? goToPage : () => {}, nextPage: enablePagination ? nextPage : () => {}, prevPage: enablePagination ? prevPage : () => {}, changePageSize: enablePagination ? changePageSize : () => {}, // Search methods setSearch: enableSearch ? setSearch : () => {}, clearSearch: enableSearch ? clearSearch : () => {}, } } // Export untuk direct access ke cached data (jika diperlukan) export const useRegenciesCache = () => ({ regenciesCache: readonly(regenciesCache), loadingStates: readonly(loadingStates), errorStates: readonly(errorStates), })