Polish storefront listing: filters, category directory, and list extras.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
4089247ad3
commit
fa248d2f6a
+147
-19
@@ -6,26 +6,61 @@ import ProductListHero from "@/components/products/ProductListHero";
|
||||
import ProductFilters from "@/components/products/ProductFilters";
|
||||
import ProductGrid from "@/components/products/ProductGrid";
|
||||
import ProductPagination from "@/components/products/ProductPagination";
|
||||
import ProductEmptyState from "@/components/products/ProductEmptyState";
|
||||
import ProductListExtras from "@/components/products/ProductListExtras";
|
||||
import { LIST_PAGE_META } from "@/data/products";
|
||||
import { mapHomeSlots } from "@/lib/map-home-slots";
|
||||
import { fetchPublicWebsiteSlots } from "@/lib/website-slots";
|
||||
import {
|
||||
fetchPublicWebsiteSlots,
|
||||
itemsForSlot,
|
||||
} from "@/lib/website-slots";
|
||||
import { getStorefrontProducts } from "@/lib/products-api";
|
||||
import {
|
||||
fetchPublicCategories,
|
||||
mapCategoriesToMegaTabs,
|
||||
resolveHomeCategoryStrip,
|
||||
type ApiCategoryNode,
|
||||
} from "@/lib/categories-api";
|
||||
import {
|
||||
paginateItems,
|
||||
parseProductListQuery,
|
||||
type ProductListQuery,
|
||||
} from "@/lib/products-query";
|
||||
|
||||
type PageProps = {
|
||||
searchParams: Promise<{ q?: string; cat?: string }>;
|
||||
searchParams: Promise<ProductListQuery>;
|
||||
};
|
||||
|
||||
function findCategory(
|
||||
nodes: ApiCategoryNode[],
|
||||
id: string,
|
||||
): ApiCategoryNode | null {
|
||||
for (const node of nodes) {
|
||||
if (String(node.id) === id) return node;
|
||||
const child = findCategory(node.children ?? [], id);
|
||||
if (child) return child;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findCategoryName(
|
||||
nodes: ApiCategoryNode[],
|
||||
id: string,
|
||||
): string | null {
|
||||
return findCategory(nodes, id)?.nameFa ?? null;
|
||||
}
|
||||
|
||||
export default async function ProductsPage({ searchParams }: PageProps) {
|
||||
const params = await searchParams;
|
||||
const raw = await searchParams;
|
||||
const query = parseProductListQuery(raw);
|
||||
const [slotItems, products, apiCategories] = await Promise.all([
|
||||
fetchPublicWebsiteSlots(),
|
||||
getStorefrontProducts({
|
||||
name: params.q,
|
||||
categoryId: params.cat,
|
||||
name: query.q || undefined,
|
||||
categoryId: query.cat || undefined,
|
||||
minPrice: query.minPrice,
|
||||
maxPrice: query.maxPrice,
|
||||
featuredSlot: query.sale ? "promo-1" : undefined,
|
||||
}),
|
||||
fetchPublicCategories(),
|
||||
]);
|
||||
@@ -35,31 +70,117 @@ export default async function ProductsPage({ searchParams }: PageProps) {
|
||||
home.megaTabs.map((t) => ({ image: t.image })),
|
||||
);
|
||||
|
||||
const stripCategories = resolveHomeCategoryStrip(
|
||||
apiCategories,
|
||||
home.categories,
|
||||
itemsForSlot(slotItems, "category-strip").length > 0,
|
||||
);
|
||||
|
||||
const catNode = query.cat ? findCategory(apiCategories, query.cat) : null;
|
||||
const categorySeo = catNode?.seoDescription?.trim()
|
||||
? {
|
||||
nameFa: catNode.nameFa,
|
||||
nameEn: catNode.nameEn,
|
||||
description: catNode.seoDescription.trim(),
|
||||
}
|
||||
: null;
|
||||
|
||||
const { pageItems, totalPages, page, total } = paginateItems(
|
||||
products,
|
||||
query.page,
|
||||
);
|
||||
const categoryLabel = query.cat
|
||||
? findCategoryName(apiCategories, query.cat)
|
||||
: null;
|
||||
const hasFilters = Boolean(
|
||||
query.q || query.cat || query.min || query.max || query.sale,
|
||||
);
|
||||
|
||||
const heroTitle = query.sale
|
||||
? "فروش ویژه"
|
||||
: query.q
|
||||
? `جستجو: ${query.q}`
|
||||
: categoryLabel
|
||||
? categoryLabel
|
||||
: LIST_PAGE_META.titleFa;
|
||||
|
||||
const heroEn = query.sale
|
||||
? "Special Sale"
|
||||
: query.q
|
||||
? "Search results"
|
||||
: categoryLabel
|
||||
? ""
|
||||
: LIST_PAGE_META.titleEn;
|
||||
|
||||
const breadcrumbs = query.sale
|
||||
? [
|
||||
{ label: "خانه", href: "/" },
|
||||
{ label: "محصولات", href: "/products" },
|
||||
{ label: "فروش ویژه" },
|
||||
]
|
||||
: query.q
|
||||
? [
|
||||
{ label: "خانه", href: "/" },
|
||||
{ label: "محصولات", href: "/products" },
|
||||
{ label: "جستجو" },
|
||||
]
|
||||
: LIST_PAGE_META.breadcrumbs;
|
||||
|
||||
return (
|
||||
<main className="home-page min-h-dvh bg-white text-ink">
|
||||
<div className="relative">
|
||||
<SiteHeader megaTabs={megaTabs.length ? megaTabs : home.megaTabs} />
|
||||
<ProductListHero
|
||||
titleFa={params.q ? `جستجو: ${params.q}` : LIST_PAGE_META.titleFa}
|
||||
titleEn={params.q ? "Search results" : LIST_PAGE_META.titleEn}
|
||||
titleFa={heroTitle}
|
||||
titleEn={heroEn}
|
||||
heroImage={home.heroSlides[0]?.image || LIST_PAGE_META.heroImage}
|
||||
breadcrumbs={
|
||||
params.q
|
||||
? [
|
||||
{ label: "خانه", href: "/" },
|
||||
{ label: "محصولات", href: "/products" },
|
||||
{ label: "جستجو" },
|
||||
]
|
||||
: LIST_PAGE_META.breadcrumbs
|
||||
}
|
||||
breadcrumbs={breadcrumbs}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 space-y-6 sm:mt-8 lg:mt-[60px]">
|
||||
<ProductFilters />
|
||||
<ProductFilters
|
||||
basePath="/products"
|
||||
initial={{
|
||||
q: query.q,
|
||||
cat: query.cat,
|
||||
min: query.min,
|
||||
max: query.max,
|
||||
sale: query.sale,
|
||||
}}
|
||||
categoryLabel={categoryLabel}
|
||||
/>
|
||||
<div className="space-y-3 sm:space-y-4">
|
||||
<ProductGrid products={products} />
|
||||
<ProductPagination />
|
||||
{total === 0 ? (
|
||||
<ProductEmptyState
|
||||
title={
|
||||
hasFilters
|
||||
? "محصولی با این فیلترها پیدا نشد"
|
||||
: "محصولی موجود نیست"
|
||||
}
|
||||
description={
|
||||
hasFilters
|
||||
? "فیلترها را تغییر دهید یا همه را پاک کنید تا محصولات بیشتری ببینید."
|
||||
: "بهزودی محصولات جدید اضافه میشوند."
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<ProductGrid products={pageItems} />
|
||||
<ProductPagination
|
||||
page={page}
|
||||
totalPages={totalPages}
|
||||
basePath="/products"
|
||||
query={{
|
||||
q: query.q,
|
||||
cat: query.cat,
|
||||
min: query.min,
|
||||
max: query.max,
|
||||
sale: query.sale,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -71,6 +192,13 @@ export default async function ProductsPage({ searchParams }: PageProps) {
|
||||
link={home.leather.link}
|
||||
/>
|
||||
<PartnerCta />
|
||||
|
||||
<ProductListExtras
|
||||
stripCategories={stripCategories}
|
||||
about={home.about}
|
||||
categorySeo={categorySeo}
|
||||
/>
|
||||
|
||||
<SiteFooter paymentBadges={home.paymentBadges} />
|
||||
</main>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user