Product pages emit crawler-readable meta, public URLs use id plus Farsi slugs, and the homepage catalog matches the dashboard slot keys. Co-authored-by: Cursor <cursoragent@cursor.com>
103 lines
4.0 KiB
TypeScript
103 lines
4.0 KiB
TypeScript
import Link from "next/link";
|
|
import Image from "next/image";
|
|
import { SlotImage } from "@/components/SlotImage";
|
|
import { categories } from "@/lib/data";
|
|
import { getCategories } from "@/lib/api";
|
|
import { categoryHref } from "@/lib/paths";
|
|
import type { StaticImageWithUrl } from "@/lib/static-images";
|
|
import { slotAlt, slotAspectStyle } from "@/lib/static-images";
|
|
import { DrawFrame } from "./DrawFrame";
|
|
|
|
export async function Categories({
|
|
images = [],
|
|
aspectRatio,
|
|
}: {
|
|
images?: StaticImageWithUrl[];
|
|
aspectRatio?: string;
|
|
}) {
|
|
const apiCategories = images.length > 0 ? [] : await getCategories();
|
|
const bySlug = new Map(apiCategories.map((item) => [item.slug, item]));
|
|
return (
|
|
<section className="container-site mt-12">
|
|
<div className="flex items-end justify-between mb-6">
|
|
<h2 className="section-title">دستهبندیها</h2>
|
|
<Link href="/products" className="text-sm text-brand">
|
|
همه محصولات
|
|
</Link>
|
|
</div>
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3 md:gap-4">
|
|
{images.length > 0
|
|
? images.map((image) => {
|
|
const nameFa = image.titleFa?.trim();
|
|
const nameEn = image.titleEn?.trim();
|
|
const card = (
|
|
<div className="draw-frame-host relative flex items-center gap-3 rounded-2xl border border-black/5 bg-zinc-50 px-3 py-2 text-brand">
|
|
<div
|
|
className="size-[72px] sm:size-20 shrink-0 overflow-hidden"
|
|
style={slotAspectStyle(aspectRatio, "1:1")}
|
|
>
|
|
<SlotImage
|
|
image={{ ...image, linkUrl: null }}
|
|
alt={slotAlt(image, nameFa || nameEn || "دستهبندی")}
|
|
imgClassName="h-full w-full object-contain"
|
|
/>
|
|
</div>
|
|
{nameFa || nameEn ? (
|
|
<span className="min-w-0">
|
|
{nameFa ? (
|
|
<span className="block text-lg font-medium text-ink leading-none">{nameFa}</span>
|
|
) : null}
|
|
{nameEn ? (
|
|
<span
|
|
dir="ltr"
|
|
className="mt-1 block font-oswald text-[10px] font-normal tracking-[0.32em] text-zinc-400 uppercase"
|
|
>
|
|
{nameEn}
|
|
</span>
|
|
) : null}
|
|
</span>
|
|
) : null}
|
|
<DrawFrame />
|
|
</div>
|
|
);
|
|
return image.linkUrl ? (
|
|
<a key={image.id} href={image.linkUrl}>
|
|
{card}
|
|
</a>
|
|
) : (
|
|
<div key={image.id}>{card}</div>
|
|
);
|
|
})
|
|
: categories.map((cat) => {
|
|
const api = bySlug.get(cat.slug);
|
|
return (
|
|
<Link
|
|
key={cat.slug}
|
|
href={api ? categoryHref(api) : `/products/${cat.slug}`}
|
|
className="draw-frame-host relative flex items-center gap-3 rounded-2xl border border-black/5 bg-zinc-50 px-3 py-2 text-brand"
|
|
>
|
|
<Image
|
|
src={cat.image}
|
|
alt={cat.name}
|
|
width={88}
|
|
height={88}
|
|
className="size-[72px] sm:size-20 shrink-0 object-contain"
|
|
/>
|
|
<span className="min-w-0">
|
|
<span className="block text-lg font-medium text-ink leading-none">{cat.name}</span>
|
|
<span
|
|
dir="ltr"
|
|
className="mt-1 block font-oswald text-[10px] font-normal tracking-[0.32em] text-zinc-400 uppercase"
|
|
>
|
|
{cat.english}
|
|
</span>
|
|
</span>
|
|
<DrawFrame />
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|