387 lines
12 KiB
TypeScript
387 lines
12 KiB
TypeScript
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<string, CachedRegencyData>())
|
|
const loadingStates = reactive(new Map<string, boolean>())
|
|
const errorStates = reactive(new Map<string, string | null>())
|
|
|
|
interface UseRegenciesOptions {
|
|
provinceCode?: Ref<string | undefined> | string | undefined
|
|
pageSize?: number
|
|
enablePagination?: boolean
|
|
enableSearch?: boolean
|
|
}
|
|
|
|
export function useRegencies(options: UseRegenciesOptions | Ref<string | undefined> | 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<Item[]>(() => {
|
|
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<RegencyQueryParams>, 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<RegencyQueryParams>) {
|
|
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),
|
|
})
|