added collection page

This commit is contained in:
Jelaletdin12
2025-12-15 14:33:34 +05:00
parent 633a3c9d47
commit e886359c5c
31 changed files with 2118 additions and 716 deletions

View File

@@ -106,30 +106,61 @@ export default function CategoryPageClient({
}
}, [selectedCategory?.id]);
// Update products list
// Update products list - BU KISIM ÖNEMLİ!
useEffect(() => {
if (productsData?.data) {
setAllProducts((prev) => {
// İlk sayfa ise direkt replace et
if (currentPage === 1) {
return productsData.data;
}
// Sonraki sayfalar için deduplicate et
const existingIds = new Set(prev.map((p) => p.id));
const newProducts = productsData.data.filter(
(p: Product) => !existingIds.has(p.id)
);
// Eğer yeni ürün yoksa, return prev (gereksiz re-render önlenir)
if (newProducts.length === 0) {
return prev;
}
return [...prev, ...newProducts];
});
}
}, [productsData, currentPage]);
}, [productsData?.data, currentPage]); // productsData yerine productsData.data
// hasMore hesaplama - BU KISIM DA ÖNEMLİ!
const hasMore = useMemo(() => {
return !!productsData?.pagination?.next_page_url;
}, [productsData]);
if (!productsData?.pagination) return false;
// pagination.next_page_url varsa devam et
if (productsData.pagination.next_page_url) return true;
// Alternatif olarak: current_page < last_page kontrolü
if (
productsData.pagination.current_page &&
productsData.pagination.last_page
) {
return (
productsData.pagination.current_page < productsData.pagination.last_page
);
}
// Alternatif 2: hasMorePages flag'i varsa
if (productsData.pagination.hasMorePages !== undefined) {
return productsData.pagination.hasMorePages;
}
return false;
}, [productsData?.pagination]);
const loadMoreData = useCallback(() => {
if (!hasMore || isFetching) return;
console.log("Loading page:", currentPage + 1); // Debug için
setCurrentPage((prev) => prev + 1);
}, [hasMore, isFetching]);
}, [hasMore, isFetching, currentPage]);
const sortedProducts = useMemo(() => {
const products = [...allProducts];
@@ -245,6 +276,7 @@ export default function CategoryPageClient({
products={sortedProducts}
hasMore={hasMore}
onLoadMore={loadMoreData}
isFetching={isFetching}
translations={{
loading: t("common.loading"),
no_results: t("no_results"),
@@ -276,4 +308,4 @@ export default function CategoryPageClient({
</CategoryFiltersSheet>
</div>
);
}
}

View File

@@ -6,6 +6,7 @@ interface CategoryProductsGridProps {
products: Product[];
hasMore: boolean;
onLoadMore: () => void;
isFetching?: boolean; // Yeni prop - loading durumu için
translations: {
loading: string;
no_results: string;
@@ -16,9 +17,10 @@ export default function CategoryProductsGrid({
products,
hasMore,
onLoadMore,
isFetching = false,
translations,
}: CategoryProductsGridProps) {
if (products.length === 0) {
if (products.length === 0 && !isFetching) {
return (
<div className="text-center py-8 text-gray-500">
{translations.no_results}
@@ -35,9 +37,19 @@ export default function CategoryProductsGrid({
style={{ overflow: "visible" }}
loader={
<div className="flex justify-center py-4">
<div>{translations.loading}</div>
<div className="flex items-center gap-2">
<div className="w-5 h-5 border-2 border-gray-300 border-t-blue-500 rounded-full animate-spin" />
<span>{translations.loading}</span>
</div>
</div>
}
endMessage={
products.length > 0 && !hasMore ? (
<div className="text-center py-4 text-gray-500 text-sm">
{/* Opsiyonel: "Tüm ürünler yüklendi" mesajı */}
</div>
) : null
}
>
<div className="bg-white rounded-lg grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3">
{products.map((product) => (
@@ -55,6 +67,19 @@ export default function CategoryProductsGrid({
/>
))}
</div>
{/* İlk yükleme için skeleton göster */}
{isFetching && products.length === 0 && (
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3 mt-3">
{Array.from({ length: 6 }).map((_, i) => (
<div key={i} className="animate-pulse">
<div className="bg-gray-200 h-48 rounded-lg mb-2" />
<div className="bg-gray-200 h-4 rounded w-3/4 mb-2" />
<div className="bg-gray-200 h-4 rounded w-1/2" />
</div>
))}
</div>
)}
</InfiniteScroll>
);
}