Files
ai-agent/web/components/dashboard/list/use-dashboard-paged-list.ts
T

137 lines
3.4 KiB
TypeScript

"use client"
import { useCallback, useEffect, useRef, useState } from "react"
import { toast } from "sonner"
import {
buildDashboardCrudQuery,
normalizeDashboardCrudPageResult,
type DashboardCrudPageResult,
type DashboardCrudFilterStateConfig,
type DashboardCrudQueryFilter,
type DashboardCrudQueryValue,
} from "@/components/dashboard/crud/dashboard-crud-utils"
import { useDashboardCrudFilters } from "@/components/dashboard/crud/use-dashboard-crud-filters"
export type DashboardPagedListFilter = DashboardCrudQueryFilter &
DashboardCrudFilterStateConfig
export type DashboardPagedListOptions<TItem> = {
filters: DashboardPagedListFilter[]
fetchList: (
query: Record<string, DashboardCrudQueryValue>
) => Promise<DashboardCrudPageResult<TItem>>
pageSize?: number
loadFailed: string
enabled?: boolean
reloadKey?: string | number | null
}
export function useDashboardPagedList<TItem>({
filters,
fetchList,
pageSize = 20,
loadFailed,
enabled = true,
reloadKey,
}: DashboardPagedListOptions<TItem>) {
const fetchListRef = useRef(fetchList)
fetchListRef.current = fetchList
const filtersKey = filters
.map(
(filter) =>
`${filter.name}:${String(filter.defaultValue)}:${String(filter.allValue)}:${filter.trim ? "1" : "0"}:${filter.valueType ?? ""}`
)
.join("|")
const {
draftFilters,
appliedFilters,
setDraftFilter,
applyFilter,
applyFilters,
resetFilters,
} =
useDashboardCrudFilters(filters)
const [page, setPage] = useState(1)
const [limit, setLimit] = useState(pageSize)
const [loading, setLoading] = useState(enabled)
const [result, setResult] = useState<DashboardCrudPageResult<TItem>>({
results: [],
page: { page: 1, limit: pageSize, total: 0 },
})
const loadData = useCallback(async () => {
if (!enabled) {
setLoading(false)
setResult({ results: [], page: { page: 1, limit, total: 0 } })
return
}
setLoading(true)
try {
const data = await fetchListRef.current(
buildDashboardCrudQuery({
values: appliedFilters,
filters,
page,
limit,
})
)
setResult(normalizeDashboardCrudPageResult(data, page, limit))
} catch (error) {
toast.error(error instanceof Error ? error.message : loadFailed)
} finally {
setLoading(false)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [appliedFilters, enabled, filtersKey, limit, loadFailed, page, reloadKey])
useEffect(() => {
void loadData()
}, [loadData])
function applyDraftFilters() {
applyFilters()
setPage(1)
}
function applyDraftFilter(name: string, value: string | number | undefined) {
applyFilter(name, value)
setPage(1)
}
function handlePageChange(nextPage: number) {
if (nextPage < 1 || nextPage === page) return
setPage(nextPage)
}
function handleLimitChange(nextLimit: number) {
if (nextLimit <= 0 || nextLimit === limit) return
setLimit(nextLimit)
setPage(1)
}
function resetDraftFilters() {
resetFilters()
setPage(1)
}
return {
draftFilters,
setDraftFilter,
applyFilter: applyDraftFilter,
applyFilters: applyDraftFilters,
page,
setPage,
limit,
setLimit,
loading,
result,
setResult,
loadData,
handlePageChange,
handleLimitChange,
resetFilters: resetDraftFilters,
}
}