dstpabuaran.com/resources/js/hooks/use-infinite-scroll.ts
Yoga Pangestu e7aa582572 feat: add cutting management functionality with CRUD operations
- Implemented CuttingIndex component for listing and managing cuttings.
- Added routes for cutting management in web.php.
- Created CuttingTest for testing cutting-related features including authorization, validation, and stock management.
- Updated roles create and edit pages to include necessary imports.
- Refactored settings and profile pages to streamline imports.
- Enhanced permissions checks for cutting management actions.
2026-08-04 02:24:11 +07:00

94 lines
2.5 KiB
TypeScript

import { router } from '@inertiajs/react';
import { useCallback, useEffect, useRef, useState } from 'react';
type PaginatedData<T> = {
data: T[];
current_page: number;
last_page: number;
per_page: number;
total: number;
};
type UseInfiniteScrollOptions<T> = {
initialData: PaginatedData<T>;
fetchUrl: string;
perPage?: number;
};
export function useInfiniteScroll<T>({
initialData,
fetchUrl,
perPage = 20,
}: UseInfiniteScrollOptions<T>) {
const [items, setItems] = useState<T[]>(initialData.data);
const [currentPage, setCurrentPage] = useState(initialData.current_page);
const [lastPage, setLastPage] = useState(initialData.last_page);
const [loading, setLoading] = useState(false);
const sentinelRef = useRef<HTMLDivElement | null>(null);
const loadMore = useCallback(() => {
if (loading || currentPage >= lastPage) {
return;
}
setLoading(true);
const url = new URL(fetchUrl, window.location.origin);
url.searchParams.set('page', String(currentPage + 1));
url.searchParams.set('per_page', String(perPage));
router.get(
url.pathname + url.search,
{},
{
preserveState: true,
replace: true,
only: ['mutations'],
onSuccess: (page: any) => {
const newMutations = (page.props as Record<string, unknown>)
.mutations as PaginatedData<T>;
setItems((prev) => [...prev, ...newMutations.data]);
setCurrentPage(newMutations.current_page);
setLastPage(newMutations.last_page);
setLoading(false);
},
onError: () => {
setLoading(false);
},
},
);
}, [fetchUrl, currentPage, lastPage, loading, perPage]);
useEffect(() => {
const sentinel = sentinelRef.current;
if (!sentinel) {
return;
}
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
loadMore();
}
},
{ threshold: 0.1 },
);
observer.observe(sentinel);
return () => observer.disconnect();
}, [loadMore]);
const hasNextPage = currentPage < lastPage;
return {
items,
loading,
hasNextPage,
sentinelRef,
};
}