wip: select regency paginated
todo: search reactive wip: paginated regency todo: search bind wip gess
This commit is contained in:
@@ -10,6 +10,7 @@ import RadioDisability from './_common/radio-disability.vue'
|
||||
import RadioGender from './_common/radio-gender.vue'
|
||||
import RadioNationality from './_common/radio-nationality.vue'
|
||||
import RadioNewborn from './_common/radio-newborn.vue'
|
||||
import SelectBirthPlace from '~/components/app/person/_common/select-birth-place.vue'
|
||||
import SelectDisability from './_common/select-disability.vue'
|
||||
import SelectDob from './_common/select-dob.vue'
|
||||
import SelectEducation from './_common/select-education.vue'
|
||||
@@ -66,12 +67,19 @@ defineExpose({
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3">
|
||||
<InputBase
|
||||
<!-- <InputBase
|
||||
field-name="birthPlace"
|
||||
label="Tempat Lahir"
|
||||
placeholder="Malang"
|
||||
:errors="errors"
|
||||
is-required
|
||||
/> -->
|
||||
<SelectBirthPlace
|
||||
field-name="birthPlace"
|
||||
label="Tempat Lahir"
|
||||
placeholder="Pilih tempat lahir"
|
||||
:errors="errors"
|
||||
is-required
|
||||
/>
|
||||
<SelectDob
|
||||
label="Tanggal Lahir"
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import ComboboxPaginated from '~/components/pub/my-ui/combobox/combobox-paginated.vue'
|
||||
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
||||
import Field from '~/components/pub/my-ui/form/field.vue'
|
||||
import Label from '~/components/pub/my-ui/form/label.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
isDisabled?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'birthPlace',
|
||||
placeholder = 'Pilih tempat lahir',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
} = props
|
||||
|
||||
// Gunakan composable untuk mengelola data regencies
|
||||
const {
|
||||
fetchRegencies,
|
||||
regencyOptions,
|
||||
isLoading,
|
||||
error,
|
||||
paginationMeta,
|
||||
nextPage,
|
||||
prevPage,
|
||||
setSearch,
|
||||
} = useRegencies({ enablePagination: true, pageSize: 10,enableSearch:true })
|
||||
|
||||
// Computed untuk menentukan placeholder berdasarkan state
|
||||
const dynamicPlaceholder = computed(() => {
|
||||
if (isLoading.value) return 'Memuat data tempat lahir...'
|
||||
if (error.value) return 'Gagal memuat data'
|
||||
return placeholder
|
||||
})
|
||||
|
||||
// Computed untuk menentukan apakah field disabled
|
||||
const isFieldDisabled = computed(() => {
|
||||
return props.isDisabled || isLoading.value || !!error.value
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
fetchRegencies()
|
||||
})
|
||||
|
||||
function onSearchChange(query: string) {
|
||||
setSearch(query)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FieldGroup :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<Label :label-for="fieldName" :is-required="isRequired">
|
||||
Tempat Lahir
|
||||
</Label>
|
||||
<Field :id="fieldName" :errors="errors" :class="cn('select-field-wrapper')">
|
||||
<FormField v-slot="{ componentField }" :name="fieldName">
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<ComboboxPaginated :id="fieldName" v-bind="componentField" :items="regencyOptions"
|
||||
:placeholder="dynamicPlaceholder" :is-disabled="isFieldDisabled" search-placeholder="Cari tempat lahir..."
|
||||
empty-message="Tempat lahir tidak ditemukan"
|
||||
:page="paginationMeta.page"
|
||||
:total-page="paginationMeta.totalPage"
|
||||
:has-next="paginationMeta.hasNext"
|
||||
:has-prev="paginationMeta.hasPrev"
|
||||
:is-loading="isLoading"
|
||||
@update:searchText="onSearchChange"
|
||||
@next="nextPage()"
|
||||
@prev="prevPage()"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</template>
|
||||
@@ -0,0 +1,208 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '~/lib/utils'
|
||||
import { type Item } from './index'
|
||||
|
||||
const props = defineProps<{
|
||||
id?: string
|
||||
modelValue?: string
|
||||
items: Item[]
|
||||
placeholder?: string
|
||||
searchPlaceholder?: string
|
||||
emptyMessage?: string
|
||||
class?: string
|
||||
isDisabled?: boolean
|
||||
page?: number
|
||||
totalPage?: number
|
||||
hasNext?: boolean
|
||||
hasPrev?: boolean
|
||||
isLoading?: boolean
|
||||
searchText?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string]
|
||||
'update:searchText': [value: string]
|
||||
'page-change': [page: number]
|
||||
next: []
|
||||
prev: []
|
||||
}>()
|
||||
|
||||
const open = ref(false)
|
||||
const internalSearchText = ref(props.searchText || '')
|
||||
|
||||
// Keep internal search text synced with props
|
||||
watch(
|
||||
() => props.searchText,
|
||||
(val) => {
|
||||
if (val !== internalSearchText.value) internalSearchText.value = val || ''
|
||||
},
|
||||
)
|
||||
|
||||
const selectedItem = computed(() => props.items.find((item) => item.value === props.modelValue))
|
||||
const displayText = computed(() => {
|
||||
if (selectedItem.value?.label) return selectedItem.value.label
|
||||
return props.placeholder || 'Pilih item'
|
||||
})
|
||||
|
||||
const searchableItems = computed(() => {
|
||||
const itemsWithSearch = props.items.map((item) => ({
|
||||
...item,
|
||||
searchValue: `${item.code || ''} ${item.label}`.trim(),
|
||||
isSelected: item.value === props.modelValue,
|
||||
}))
|
||||
|
||||
return itemsWithSearch.sort((a, b) => {
|
||||
const aPriority = a.priority ?? 0
|
||||
const bPriority = b.priority ?? 0
|
||||
if (aPriority !== bPriority) return bPriority - aPriority
|
||||
if (a.isSelected && !b.isSelected) return -1
|
||||
if (!a.isSelected && b.isSelected) return 1
|
||||
return a.label.localeCompare(b.label)
|
||||
})
|
||||
})
|
||||
|
||||
function onSelect(item: Item) {
|
||||
emit('update:modelValue', item.value)
|
||||
open.value = false
|
||||
}
|
||||
|
||||
function onSearchInput(value: string) {
|
||||
console.log('[ComboboxPaginated] emit update:searchText', value)
|
||||
internalSearchText.value = value
|
||||
emit('update:searchText', value)
|
||||
}
|
||||
|
||||
function handlePrev() {
|
||||
if (props.hasPrev) emit('prev')
|
||||
}
|
||||
|
||||
function handleNext() {
|
||||
if (props.hasNext) emit('next')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Popover v-model:open="open">
|
||||
<PopoverTrigger as-child>
|
||||
<Button
|
||||
:id="props.id"
|
||||
:disabled="props.isDisabled"
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
:aria-expanded="open"
|
||||
:aria-controls="`${props.id}-list`"
|
||||
:aria-describedby="`${props.id}-search`"
|
||||
:class="
|
||||
cn(
|
||||
'h-8 w-full justify-between rounded-md border px-3 font-normal focus:outline-none focus:ring-1 focus:ring-black dark:!border-slate-400 dark:focus:ring-white md:text-xs 2xl:h-9 2xl:text-sm',
|
||||
{
|
||||
'cursor-not-allowed border-gray-300 bg-gray-100 text-gray-500 opacity-50': props.isDisabled,
|
||||
'border-gray-400 bg-white text-black hover:bg-gray-50 dark:border-gray-600 dark:bg-slate-950 dark:text-white dark:hover:bg-gray-700':
|
||||
!props.isDisabled,
|
||||
'text-gray-400 dark:text-gray-500': !props.modelValue && !props.isDisabled,
|
||||
},
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ displayText }}
|
||||
<Icon
|
||||
name="i-lucide-chevrons-up-down"
|
||||
:class="
|
||||
cn('ml-2 h-4 w-4 shrink-0', {
|
||||
'opacity-30': props.isDisabled,
|
||||
'text-gray-500 opacity-50 dark:text-gray-300': !props.isDisabled,
|
||||
})
|
||||
"
|
||||
/>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
|
||||
<PopoverContent
|
||||
class="w-[var(--radix-popover-trigger-width)] border border-gray-200 bg-white p-0 dark:border-gray-700 dark:bg-gray-800"
|
||||
>
|
||||
<Command class="bg-white dark:bg-gray-800">
|
||||
<CommandInput
|
||||
:id="`${props.id}-search`"
|
||||
class="h-9 border-0 border-b border-gray-200 bg-white text-black focus:ring-0 dark:border-gray-700 dark:bg-gray-800 dark:text-white"
|
||||
:placeholder="searchPlaceholder || 'Cari...'"
|
||||
v-model="internalSearchText"
|
||||
@input="onSearchInput(($event.target as HTMLInputElement).value)"
|
||||
:aria-label="`Cari ${displayText}`"
|
||||
/>
|
||||
|
||||
<CommandEmpty class="py-6 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ emptyMessage || 'Item tidak ditemukan.' }}
|
||||
</CommandEmpty>
|
||||
|
||||
<CommandList
|
||||
:id="`${props.id}-list`"
|
||||
role="listbox"
|
||||
class="max-h-60 overflow-auto"
|
||||
>
|
||||
<CommandGroup>
|
||||
<CommandItem
|
||||
v-for="item in searchableItems"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
:class="
|
||||
cn(
|
||||
'flex w-full cursor-pointer items-center justify-between rounded-sm px-2 py-1.5 md:text-xs xl:text-sm',
|
||||
'text-black focus:outline-none dark:text-white',
|
||||
'hover:bg-primary hover:text-white focus:bg-primary focus:text-white',
|
||||
'data-[highlighted]:bg-primary data-[highlighted]:text-white',
|
||||
)
|
||||
"
|
||||
@select="onSelect(item)"
|
||||
>
|
||||
<div class="flex w-full items-center justify-between">
|
||||
<span>{{ item.label }}</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<span
|
||||
v-if="item.code"
|
||||
class="text-xs text-muted-foreground"
|
||||
>
|
||||
{{ item.code }}
|
||||
</span>
|
||||
<Icon
|
||||
name="i-lucide-check"
|
||||
:class="cn('h-4 w-4', props.modelValue === item.value ? 'opacity-100' : 'opacity-0')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
|
||||
<div class="flex items-center justify-between border-t border-gray-200 p-2 text-xs dark:border-gray-700">
|
||||
<div class="flex items-center gap-2">
|
||||
<Icon
|
||||
v-if="props.isLoading"
|
||||
name="i-lucide-loader-2"
|
||||
class="h-3 w-3 animate-spin"
|
||||
/>
|
||||
<span v-else>{{ props.page || 1 }} / {{ props.totalPage || 1 }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
:disabled="!props.hasPrev || props.isDisabled"
|
||||
@click="handlePrev"
|
||||
>
|
||||
Prev
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
:disabled="!props.hasNext || props.isDisabled"
|
||||
@click="handleNext"
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</template>
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { refDebounced } from '@vueuse/core'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
import type { Regency } from '~/models/regency'
|
||||
import type { SelectItem } from '~/components/pub/my-ui/form/select.vue'
|
||||
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'
|
||||
@@ -23,9 +23,9 @@ interface CachedRegencyData {
|
||||
}
|
||||
|
||||
// Global cache untuk regencies berdasarkan query key (province-code + search + pagination)
|
||||
const regenciesCache = ref<Map<string, CachedRegencyData>>(new Map())
|
||||
const loadingStates = ref<Map<string, boolean>>(new Map())
|
||||
const errorStates = ref<Map<string, string | null>>(new Map())
|
||||
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
|
||||
@@ -36,18 +36,14 @@ interface UseRegenciesOptions {
|
||||
|
||||
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 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
|
||||
const { provinceCode, pageSize = 10, enablePagination = true, enableSearch = true } = normalizedOptions
|
||||
|
||||
// Convert provinceCode ke ref jika bukan ref
|
||||
const provinceCodeRef =
|
||||
@@ -57,7 +53,6 @@ export function useRegencies(options: UseRegenciesOptions | Ref<string | undefin
|
||||
const currentPage = ref(1)
|
||||
const currentPageSize = ref(pageSize)
|
||||
const searchQuery = ref('')
|
||||
const debouncedSearch = refDebounced(searchQuery, 300)
|
||||
|
||||
// Function untuk generate query key
|
||||
const generateQueryKey = (params: RegencyQueryParams) => {
|
||||
@@ -66,7 +61,7 @@ export function useRegencies(options: UseRegenciesOptions | Ref<string | undefin
|
||||
params.search || '',
|
||||
params['page-number'] || 1,
|
||||
params['page-size'] || pageSize,
|
||||
params.sort || 'name:asc'
|
||||
params.sort || 'name:asc',
|
||||
]
|
||||
return keyParts.join('|')
|
||||
}
|
||||
@@ -75,48 +70,50 @@ export function useRegencies(options: UseRegenciesOptions | Ref<string | undefin
|
||||
const currentQueryKey = computed(() => {
|
||||
return generateQueryKey({
|
||||
'province-code': provinceCodeRef.value,
|
||||
search: enableSearch ? debouncedSearch.value : undefined,
|
||||
search: enableSearch ? searchQuery.value : undefined,
|
||||
'page-number': enablePagination ? currentPage.value : undefined,
|
||||
'page-size': enablePagination ? currentPageSize.value : undefined,
|
||||
sort: 'name:asc'
|
||||
sort: 'name:asc',
|
||||
})
|
||||
})
|
||||
|
||||
// Computed untuk mendapatkan regencies berdasarkan current query
|
||||
const regencies = computed(() => {
|
||||
const queryKey = currentQueryKey.value
|
||||
const cachedData = regenciesCache.value.get(queryKey)
|
||||
const cachedData = regenciesCache.get(queryKey)
|
||||
return cachedData?.data || []
|
||||
})
|
||||
|
||||
// Computed untuk pagination meta
|
||||
const paginationMeta = computed(() => {
|
||||
const queryKey = currentQueryKey.value
|
||||
const cachedData = regenciesCache.value.get(queryKey)
|
||||
return cachedData?.meta || {
|
||||
recordCount: 0,
|
||||
page: currentPage.value,
|
||||
pageSize: currentPageSize.value,
|
||||
totalPage: 0,
|
||||
hasNext: false,
|
||||
hasPrev: false,
|
||||
}
|
||||
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.value.get(queryKey) || false
|
||||
return loadingStates.get(queryKey) || false
|
||||
})
|
||||
|
||||
// Computed untuk error state
|
||||
const error = computed(() => {
|
||||
const queryKey = currentQueryKey.value
|
||||
return errorStates.value.get(queryKey) || null
|
||||
return errorStates.get(queryKey) || null
|
||||
})
|
||||
|
||||
// Computed untuk format SelectItem
|
||||
const regencyOptions = computed<SelectItem[]>(() => {
|
||||
// Computed untuk format Item
|
||||
const regencyOptions = computed<Item[]>(() => {
|
||||
return regencies.value.map((regency) => ({
|
||||
label: toTitleCase(regency.name),
|
||||
value: regency.code,
|
||||
@@ -128,29 +125,29 @@ export function useRegencies(options: UseRegenciesOptions | Ref<string | undefin
|
||||
async function fetchRegencies(params?: Partial<RegencyQueryParams>, forceRefresh = false) {
|
||||
const queryParams: RegencyQueryParams = {
|
||||
'province-code': params?.['province-code'] || provinceCodeRef.value,
|
||||
search: enableSearch ? (params?.search || debouncedSearch.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'
|
||||
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
|
||||
// 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.value.has(queryKey)) {
|
||||
if (!forceRefresh && regenciesCache.has(queryKey)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Jika sedang loading, skip untuk mencegah duplicate calls
|
||||
if (loadingStates.value.get(queryKey)) {
|
||||
if (loadingStates.get(queryKey)) {
|
||||
return
|
||||
}
|
||||
|
||||
loadingStates.value.set(queryKey, true)
|
||||
errorStates.value.set(queryKey, null)
|
||||
loadingStates.set(queryKey, true)
|
||||
errorStates.set(queryKey, null)
|
||||
|
||||
try {
|
||||
// Prepare API parameters
|
||||
@@ -183,9 +180,10 @@ export function useRegencies(options: UseRegenciesOptions | Ref<string | undefin
|
||||
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,
|
||||
totalPage:
|
||||
enablePagination && queryParams['page-size']
|
||||
? Math.ceil(meta.record_totalCount / queryParams['page-size'])
|
||||
: 1,
|
||||
hasNext: false,
|
||||
hasPrev: false,
|
||||
}
|
||||
@@ -196,27 +194,27 @@ export function useRegencies(options: UseRegenciesOptions | Ref<string | undefin
|
||||
}
|
||||
|
||||
// Cache the result
|
||||
regenciesCache.value.set(queryKey, {
|
||||
data: regenciesData,
|
||||
meta: paginationMeta,
|
||||
queryKey
|
||||
regenciesCache.set(queryKey, {
|
||||
data: [...regenciesData],
|
||||
meta: { ...paginationMeta },
|
||||
queryKey,
|
||||
})
|
||||
} else {
|
||||
errorStates.value.set(queryKey, 'Gagal memuat data kabupaten/kota')
|
||||
errorStates.set(queryKey, 'Gagal memuat data kabupaten/kota')
|
||||
console.error('Failed to fetch regencies:', response)
|
||||
}
|
||||
} catch (err) {
|
||||
errorStates.value.set(queryKey, 'Terjadi kesalahan saat memuat data kabupaten/kota')
|
||||
errorStates.set(queryKey, 'Terjadi kesalahan saat memuat data kabupaten/kota')
|
||||
console.error('Error fetching regencies:', err)
|
||||
} finally {
|
||||
loadingStates.value.set(queryKey, false)
|
||||
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.value.values()) {
|
||||
for (const cachedData of regenciesCache.values()) {
|
||||
const found = cachedData.data.find((regency) => regency.code === code)
|
||||
if (found) return found
|
||||
}
|
||||
@@ -226,7 +224,7 @@ export function useRegencies(options: UseRegenciesOptions | Ref<string | undefin
|
||||
// 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.value.values()) {
|
||||
for (const cachedData of regenciesCache.values()) {
|
||||
const found = cachedData.data.find((regency) => regency.name.toLowerCase() === name.toLowerCase())
|
||||
if (found) return found
|
||||
}
|
||||
@@ -274,24 +272,24 @@ export function useRegencies(options: UseRegenciesOptions | Ref<string | undefin
|
||||
if (code) {
|
||||
// Clear semua cache yang mengandung province code tersebut
|
||||
const keysToDelete: string[] = []
|
||||
for (const [key] of regenciesCache.value.entries()) {
|
||||
for (const [key] of regenciesCache.entries()) {
|
||||
if (key.startsWith(code + '|')) {
|
||||
keysToDelete.push(key)
|
||||
}
|
||||
}
|
||||
keysToDelete.forEach(key => {
|
||||
regenciesCache.value.delete(key)
|
||||
loadingStates.value.delete(key)
|
||||
errorStates.value.delete(key)
|
||||
keysToDelete.forEach((key) => {
|
||||
regenciesCache.delete(key)
|
||||
loadingStates.delete(key)
|
||||
errorStates.delete(key)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Function untuk clear semua cache
|
||||
function clearAllCache() {
|
||||
regenciesCache.value.clear()
|
||||
loadingStates.value.clear()
|
||||
errorStates.value.clear()
|
||||
regenciesCache.clear()
|
||||
loadingStates.clear()
|
||||
errorStates.clear()
|
||||
}
|
||||
|
||||
// Function untuk refresh data
|
||||
@@ -318,25 +316,30 @@ export function useRegencies(options: UseRegenciesOptions | Ref<string | undefin
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
// Watch perubahan search query untuk auto fetch
|
||||
watch(
|
||||
debouncedSearch,
|
||||
(newSearch, oldSearch) => {
|
||||
if (enableSearch && newSearch !== oldSearch) {
|
||||
fetchRegencies()
|
||||
}
|
||||
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([currentPage, currentPageSize], () => {
|
||||
if (enablePagination) {
|
||||
fetchRegencies()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
watch(regencyOptions, (val) => {
|
||||
console.log('[regencyOptions] updated', val.length)
|
||||
})
|
||||
|
||||
return {
|
||||
// Data
|
||||
|
||||
Reference in New Issue
Block a user