- 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.
94 lines
2.5 KiB
TypeScript
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,
|
|
};
|
|
}
|