- CategoryCarousel: caption under each circle now comes from the slot image's own titleFa (as the business typed it) instead of the resolved category's API name - identity resolution via linkUrl slug is still used for the href, just not for the visible label - Replace the hover ring (box-shadow) with a real 1px border - the ring rendered oddly against Swiper's transformed slide track, looking like it "leaked" into the row above; a border participates in normal box layout and doesn't have that problem (border-box sizing means zero layout shift) - Header/footer logo: h-9 (36px) -> h-[108px], 3x on request; header row height bumped to fit. Note: the currently-uploaded (wrong-business) logo has a lot of baked-in padding, so it still reads small even at 3x - that's the asset, not the sizing - Re-verified the static-image-slot wiring end to end against the original spec (url->img, linkUrl->href, aspectRatio from API, placeholder on empty/fail) - all still holds Also add a Deployment section to AGENTS.md: this repo's whole point is CI/CD from origin/main, and local commits sitting unpushed (as 3 commits were, until now) look indistinguishable from unfixed bugs to whoever is testing the deployed site. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
81 lines
3.2 KiB
TypeScript
81 lines
3.2 KiB
TypeScript
"use client";
|
|
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import { FreeMode } from "swiper/modules";
|
|
import { Swiper, SwiperSlide } from "swiper/react";
|
|
import "swiper/css";
|
|
import "swiper/css/free-mode";
|
|
import { findNodeBySlug } from "@/lib/categoryTree";
|
|
import { categoryHref, categorySlugFromLinkUrl } from "@/lib/slug";
|
|
import type { CategoryNode, StaticImageSlot } from "@/lib/types";
|
|
|
|
// Fully slot-driven: each image in the "categories" slot carries its own
|
|
// linkUrl (e.g. "...?category=mobile"), whose `category` param is the real
|
|
// DB slug — that's what identifies which category a photo is FOR (used to
|
|
// build the real, in-site link target). The visible caption is the
|
|
// business's own titleFa for that image, taken as typed — a category the
|
|
// business hasn't uploaded a photo for simply isn't shown here, no
|
|
// placeholder, per the "only what's in the panel" brief.
|
|
// See AGENTS.md "Static-image slots" for the mismatch this replaced.
|
|
export default function CategoryCarousel({
|
|
roots,
|
|
slot,
|
|
}: {
|
|
roots: CategoryNode[];
|
|
slot?: StaticImageSlot;
|
|
}) {
|
|
const items = (slot?.images ?? [])
|
|
.map((img) => {
|
|
const catSlug = categorySlugFromLinkUrl(img.linkUrl);
|
|
const cat = catSlug ? findNodeBySlug(roots, catSlug) : undefined;
|
|
if (!cat) return null;
|
|
const label = img.titleFa || img.titleEn || cat.nameFa || cat.name;
|
|
return { key: img.id, art: img.url, label, href: categoryHref(cat.id, cat.nameFa, cat.name) };
|
|
})
|
|
.filter((x): x is { key: string; art: string; label: string; href: string } => Boolean(x));
|
|
|
|
if (items.length === 0) return null;
|
|
|
|
return (
|
|
<section className="boxed mt-8" aria-label="دستهبندی محصولات">
|
|
<Swiper
|
|
modules={[FreeMode]}
|
|
dir="rtl"
|
|
freeMode
|
|
slidesPerView={2.8}
|
|
spaceBetween={12}
|
|
breakpoints={{
|
|
480: { slidesPerView: 3.6 },
|
|
768: { slidesPerView: 5 },
|
|
1024: { slidesPerView: 7 },
|
|
}}
|
|
>
|
|
{items.map(({ key, art, label, href }) => (
|
|
<SwiperSlide key={key}>
|
|
<Link href={href} className="group flex flex-col items-center gap-2">
|
|
{/* A real border, not ring/box-shadow — box-shadow rings can
|
|
paint oddly against Swiper's transformed slide track
|
|
(looked like the border "leaking" into the row above).
|
|
border-box sizing (this site's global reset) means the
|
|
1px border adds no layout shift. */}
|
|
<span className="relative size-[104px] overflow-hidden rounded-full border border-transparent bg-surface transition-all duration-300 group-hover:border-brand group-hover:shadow-md sm:size-[124px] lg:size-[140px]">
|
|
<Image
|
|
src={art}
|
|
alt={label}
|
|
fill
|
|
sizes="140px"
|
|
className="rounded-full object-contain p-3 transition-transform duration-300 group-hover:scale-110"
|
|
/>
|
|
</span>
|
|
<span className="text-center text-[13.5px] text-ink transition group-hover:text-brand">
|
|
{label}
|
|
</span>
|
|
</Link>
|
|
</SwiperSlide>
|
|
))}
|
|
</Swiper>
|
|
</section>
|
|
);
|
|
}
|