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>
This commit is contained in:
amirhosein.ashourloo
2026-09-06 07:53:20 +03:30
co-authored by Claude Sonnet 5
parent 2249a07dee
commit 0ffbd953b3
4 changed files with 84 additions and 33 deletions
+31 -21
View File
@@ -12,10 +12,11 @@ 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, not the
// image's position in the list or its (business-typed, sometimes wrong)
// titleFa. 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.
// 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,
@@ -29,9 +30,10 @@ export default function CategoryCarousel({
const catSlug = categorySlugFromLinkUrl(img.linkUrl);
const cat = catSlug ? findNodeBySlug(roots, catSlug) : undefined;
if (!cat) return null;
return { key: img.id, art: img.url, cat };
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; cat: CategoryNode } => Boolean(x));
.filter((x): x is { key: string; art: string; label: string; href: string } => Boolean(x));
if (items.length === 0) return null;
@@ -49,21 +51,29 @@ export default function CategoryCarousel({
1024: { slidesPerView: 7 },
}}
>
{items.map(({ key, art, cat }) => {
const label = cat.nameFa || cat.name;
return (
<SwiperSlide key={key}>
<Link href={categoryHref(cat.id, cat.nameFa, cat.name)} className="group flex flex-col items-center gap-2">
<span className="relative size-[104px] overflow-hidden rounded-full bg-surface transition group-hover:bg-brand-soft sm:size-[124px] lg:size-[140px]">
<Image src={art} alt={label} fill sizes="140px" className="object-contain p-3" />
</span>
<span className="text-center text-[13.5px] text-ink transition group-hover:text-brand">
{label}
</span>
</Link>
</SwiperSlide>
);
})}
{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>
);