Files
moalem-shop/src/components/CategoryCarousel.tsx
T
amirhosein.ashourlooandClaude Sonnet 5 0ffbd953b3 Show real titleFa captions, 3x logo size, fix category hover border
- 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>
2026-09-06 07:53:20 +03:30

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>
);
}