Files
website/src/app/products/page.tsx
T

206 lines
5.9 KiB
TypeScript

import SiteHeader from "@/components/home/SiteHeader";
import SiteFooter from "@/components/home/SiteFooter";
import LeatherBanner from "@/components/home/LeatherBanner";
import PartnerCta from "@/components/home/PartnerCta";
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,
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<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 raw = await searchParams;
const query = parseProductListQuery(raw);
const [slotItems, products, apiCategories] = await Promise.all([
fetchPublicWebsiteSlots(),
getStorefrontProducts({
name: query.q || undefined,
categoryId: query.cat || undefined,
minPrice: query.minPrice,
maxPrice: query.maxPrice,
featuredSlot: query.sale ? "promo-1" : undefined,
}),
fetchPublicCategories(),
]);
const home = mapHomeSlots(slotItems);
const megaTabs = mapCategoriesToMegaTabs(
apiCategories,
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={heroTitle}
titleEn={heroEn}
heroImage={home.heroSlides[0]?.image || LIST_PAGE_META.heroImage}
breadcrumbs={breadcrumbs}
/>
</div>
<div className="mt-6 space-y-6 sm:mt-8 lg:mt-[60px]">
<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">
{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>
<LeatherBanner
image={home.leather.image}
nameFa={home.leather.nameFa}
nameEn={home.leather.nameEn}
linkText={home.leather.linkText}
link={home.leather.link}
/>
<PartnerCta />
<ProductListExtras
stripCategories={stripCategories}
about={home.about}
categorySeo={categorySeo}
/>
<SiteFooter paymentBadges={home.paymentBadges} />
</main>
);
}