import { useCallback, useMemo, useState } from 'react'; interface UseListOptions { initialData?: T[]; initialPageSize?: number; initialSortBy?: keyof T; initialSortOrder?: 'asc' | 'desc'; filterFn?: (item: T, query: string) => boolean; } interface UseListReturn { // Data data: T[]; filteredData: T[]; paginatedData: T[]; // Filtering filter: string; setFilter: (filter: string) => void; // Sorting sortBy: keyof T | null; sortOrder: 'asc' | 'desc'; setSorting: (sortBy: keyof T, sortOrder?: 'asc' | 'desc') => void; // Pagination page: number; pageSize: number; totalPages: number; setPage: (page: number) => void; setPageSize: (pageSize: number) => void; // Computed totalItems: number; hasNextPage: boolean; hasPrevPage: boolean; } /** * Hook for managing list data with filtering, sorting, and pagination */ export function useList>( data: T[] = [], options: UseListOptions = {} ): UseListReturn { const { initialPageSize = 10, initialSortBy, initialSortOrder = 'asc', filterFn } = options; const [filter, setFilter] = useState(''); const [sortBy, setSortBy] = useState(initialSortBy || null); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>(initialSortOrder); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(initialPageSize); // Filter data const filteredData = useMemo(() => { if (!filter) return data; const defaultFilterFn = (item: T, query: string) => { return Object.values(item).some((value) => String(value).toLowerCase().includes(query.toLowerCase()) ); }; const filterFunction = filterFn || defaultFilterFn; return data.filter((item) => filterFunction(item, filter)); }, [data, filter, filterFn]); // Sort data const sortedData = useMemo(() => { if (!sortBy) return filteredData; return [...filteredData].sort((a, b) => { const aValue = a[sortBy]; const bValue = b[sortBy]; if (aValue < bValue) return sortOrder === 'asc' ? -1 : 1; if (aValue > bValue) return sortOrder === 'asc' ? 1 : -1; return 0; }); }, [filteredData, sortBy, sortOrder]); // Paginate data const paginatedData = useMemo(() => { const startIndex = (page - 1) * pageSize; const endIndex = startIndex + pageSize; return sortedData.slice(startIndex, endIndex); }, [sortedData, page, pageSize]); // Computed values const totalItems = sortedData.length; const totalPages = Math.ceil(totalItems / pageSize); const hasNextPage = page < totalPages; const hasPrevPage = page > 1; const setSorting = useCallback( (newSortBy: keyof T, newSortOrder?: 'asc' | 'desc') => { if (sortBy === newSortBy && !newSortOrder) { // Toggle sort order if same column setSortOrder((prev) => (prev === 'asc' ? 'desc' : 'asc')); } else { setSortBy(newSortBy); setSortOrder(newSortOrder || 'asc'); } setPage(1); // Reset to first page when sorting changes }, [sortBy] ); const handleSetFilter = useCallback((newFilter: string) => { setFilter(newFilter); setPage(1); // Reset to first page when filter changes }, []); const handleSetPageSize = useCallback((newPageSize: number) => { setPageSize(newPageSize); setPage(1); // Reset to first page when page size changes }, []); return { data, filteredData: sortedData, paginatedData, filter, setFilter: handleSetFilter, sortBy, sortOrder, setSorting, page, pageSize, totalPages, setPage, setPageSize: handleSetPageSize, totalItems, hasNextPage, hasPrevPage, }; }