import { useState, useCallback } from 'react' import axios from 'axios' export const useTableData = ({ url, pageSize, initialPage = 1, onDataChange, filters = {}, sorter = {} }) => { const [pages, setPages] = useState([]) const [hasMore, setHasMore] = useState(true) const [hasPrevious, setHasPrevious] = useState(initialPage > 1) const [loading, setLoading] = useState(true) const [lazyLoading, setLazyLoading] = useState(false) const [totalPages, setTotalPages] = useState(0) const [loadedPages, setLoadedPages] = useState([]) const [loadingPages, setLoadingPages] = useState(new Set()) const [currentLoadedPageNumber, setCurrentLoadedPageNumber] = useState(initialPage) const createSkeletonData = useCallback(() => { return Array(pageSize) .fill(null) .map(() => ({ _id: `skeleton-${Math.random().toString(36).substring(2, 15)}`, isSkeleton: true })) }, [pageSize]) const fetchData = useCallback( async (pageNum = 1, append = false, prepend = false) => { if (loadingPages.has(pageNum)) { return } try { setLoadingPages((prev) => new Set([...prev, pageNum])) const response = await axios.get(url, { params: { page: pageNum, limit: pageSize, ...filters, sort: sorter.field, order: sorter.order }, headers: { Accept: 'application/json' }, withCredentials: true }) const newData = response.data const totalCount = parseInt( response.headers['x-total-count'] || '0', 10 ) setTotalPages(Math.ceil(totalCount / pageSize)) setHasMore(newData.length >= pageSize) setHasPrevious(pageNum > 1) if (append) { setPages((prev) => { const filteredPages = prev.map((page) => ({ ...page, items: page.items.filter((item) => !item.isSkeleton) })) const relevantPages = filteredPages.slice(-2) return [...relevantPages, { pageNum, items: newData }] }) setLoadedPages((prev) => { const relevantPages = prev.slice(-2) return [...relevantPages, pageNum] }) } else if (prepend) { setPages((prev) => { const filteredPages = prev.map((page) => ({ ...page, items: page.items.filter((item) => !item.isSkeleton) })) const relevantPages = filteredPages.slice(0, 2) return [{ pageNum, items: newData }, ...relevantPages] }) setLoadedPages((prev) => { const relevantPages = prev.slice(0, 2) return [pageNum, ...relevantPages] }) } else { setPages([{ pageNum, items: newData }]) setLoadedPages([pageNum]) } if (onDataChange) { onDataChange(newData) } setLoading(false) setLazyLoading(false) } catch (error) { setPages((prev) => prev.map((page) => ({ ...page, items: page.items.filter((item) => !item.isSkeleton) })) ) setLoading(false) setLazyLoading(false) throw error } finally { setLoadingPages((prev) => { const newSet = new Set(prev) newSet.delete(pageNum) return newSet }) } }, [url, pageSize, filters, sorter, onDataChange, loadingPages] ) const reload = useCallback(() => { setCurrentLoadedPageNumber(1) setLoadedPages([1]) return fetchData(1) }, [fetchData]) const updateData = useCallback((_id, updatedData) => { setPages((prevPages) => prevPages.map((page) => ({ ...page, items: page.items.map((item) => item._id === _id ? { ...item, ...updatedData } : item ) })) ) }, []) const goToPage = useCallback( (pageNum) => { if (pageNum > 0 && pageNum <= totalPages) { setCurrentLoadedPageNumber(pageNum) const pagesToLoad = [pageNum - 1, pageNum, pageNum + 1].filter( (p) => p > 0 && p <= totalPages ) setLoadedPages(pagesToLoad) return Promise.all( pagesToLoad.map((p) => fetchData(p, p > pageNum, p < pageNum)) ) } }, [fetchData, totalPages] ) return { pages, hasMore, hasPrevious, loading, lazyLoading, totalPages, loadedPages, loadingPages, currentLoadedPageNumber, createSkeletonData, fetchData, reload, updateData, goToPage, setCurrentLoadedPageNumber, setLazyLoading, setPages, setLoadedPages } }