import { ref, watch } from 'vue' import { useApi } from '~/composables/useApi' type FilterValue = string | number | boolean | null export interface UseDataTableServerStateOptions { initialPerPage?: number debounceMs?: number } export function useDataTableServerState>( endpoint: string, initialFilters: Record = {}, options: UseDataTableServerStateOptions = {} ) { const api = useApi() const debounceMs = options.debounceMs ?? 300 const initialPerPage = options.initialPerPage ?? 10 const items = ref([]) as { value: T[] } const totalItems = ref(0) const page = ref(1) const perPage = ref(initialPerPage) const filters = ref>({ ...initialFilters }) const loading = ref(false) let debounceTimer: ReturnType | null = null let requestToken = 0 const buildQueryParams = (): Record => { const params: Record = { page: page.value, itemsPerPage: perPage.value } for (const [key, value] of Object.entries(filters.value)) { if (value === '' || value === null) continue params[key] = value as string | number | boolean } return params } const fetchItems = async (): Promise => { const currentToken = ++requestToken loading.value = true try { const data = await api.get<{ member: T[]; totalItems: number }>( endpoint, buildQueryParams(), { toast: false, headers: { Accept: 'application/ld+json' } } ) if (currentToken !== requestToken) return items.value = data?.member ?? [] totalItems.value = data?.totalItems ?? 0 } finally { if (currentToken === requestToken) { loading.value = false } } } const reload = (): void => { if (debounceTimer) { clearTimeout(debounceTimer) debounceTimer = null } void fetchItems() } const scheduleReload = (): void => { if (debounceTimer) clearTimeout(debounceTimer) debounceTimer = setTimeout(() => { debounceTimer = null void fetchItems() }, debounceMs) } watch([page, perPage], () => { reload() }) watch(filters, () => { if (page.value !== 1) { page.value = 1 return } scheduleReload() }, { deep: true }) return { items, totalItems, page, perPage, filters, loading, reload } }