Add Torob tags, Meshkee SEO routes, and updated static-image slots.

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>
This commit is contained in:
Alireza Hassani
2026-09-01 16:12:26 +03:30
co-authored by Cursor
parent f42c30a4b7
commit c84147e765
40 changed files with 838 additions and 288 deletions
+29 -12
View File
@@ -2,11 +2,21 @@ 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 function Categories({ images = [] }: { images?: StaticImageWithUrl[] }) {
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">
@@ -18,27 +28,31 @@ export function Categories({ images = [] }: { images?: StaticImageWithUrl[] }) {
<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 label = slotAlt(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("1:1")}>
<div
className="size-[72px] sm:size-20 shrink-0 overflow-hidden"
style={slotAspectStyle(aspectRatio, "1:1")}
>
<SlotImage
image={{ ...image, linkUrl: null }}
alt={label}
alt={slotAlt(image, nameFa || nameEn || "دسته‌بندی")}
imgClassName="h-full w-full object-contain"
/>
</div>
{label || image.subtext ? (
{nameFa || nameEn ? (
<span className="min-w-0">
{label ? (
<span className="block text-lg font-medium text-ink leading-none">{label}</span>
{nameFa ? (
<span className="block text-lg font-medium text-ink leading-none">{nameFa}</span>
) : null}
{image.subtext ? (
{nameEn ? (
<span
dir="ltr"
className="mt-1 block font-oswald text-[10px] font-normal tracking-[0.32em] text-zinc-400 uppercase"
>
{image.subtext}
{nameEn}
</span>
) : null}
</span>
@@ -54,10 +68,12 @@ export function Categories({ images = [] }: { images?: StaticImageWithUrl[] }) {
<div key={image.id}>{card}</div>
);
})
: categories.map((cat) => (
: categories.map((cat) => {
const api = bySlug.get(cat.slug);
return (
<Link
key={cat.slug}
href={`/products/${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
@@ -78,7 +94,8 @@ export function Categories({ images = [] }: { images?: StaticImageWithUrl[] }) {
</span>
<DrawFrame />
</Link>
))}
);
})}
</div>
</section>
);