"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import type { Paged } from "@/lib/api"; const PAGE_SIZE = 25; export type PagedListState = { items: T[]; total: number; page: number; pageSize: number; totalPages: number; loading: boolean; error: string | null; setPage: (p: number) => void; refresh: () => void; }; /** * Shared search + offset-pagination logic for the admin list pages. `fetcher` * receives (limit, offset) plus whatever extra deps the caller closes over * (search query, status filter, report type); pass those in `deps` so a change * resets to page 1 and refetches. A request token guards against out-of-order * responses when the user types/pages quickly. */ export function usePagedList( fetcher: (limit: number, offset: number) => Promise>, deps: unknown[], pageSize = PAGE_SIZE, ): PagedListState { const [items, setItems] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const token = useRef(0); // Reset to page 1 whenever the external filters change. // eslint-disable-next-line react-hooks/exhaustive-deps useEffect(() => setPage(1), deps); const load = useCallback(() => { const my = ++token.current; setLoading(true); setError(null); fetcher(pageSize, (page - 1) * pageSize) .then((res) => { if (my !== token.current) return; setItems(res.items); setTotal(res.total); }) .catch((e) => { if (my !== token.current) return; setError(e instanceof Error ? e.message : "Could not load."); }) .finally(() => { if (my === token.current) setLoading(false); }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [page, pageSize, ...deps]); useEffect(() => load(), [load]); return { items, total, page, pageSize, totalPages: Math.max(1, Math.ceil(total / pageSize)), loading, error, setPage, refresh: load, }; }