Fix category-slot matching: identity by linkUrl slug, not array position

The homepage category carousel and installments highlights matched
slot.images[i] to categoryList[i] by array index, assuming the business's
CMS upload order lines up with this site's own category sort order. It
doesn't - live data showed a laptop photo landing on "موبایل" and "لوازم
برقی" appearing on the site despite no photo ever being uploaded for it,
purely from index luck. titleFa isn't reliable either: the same laptop image
was titled "تبلت" in the CMS, contradicting its own linkUrl.

Fix: resolve each image's category from the `category` query param on its
own linkUrl (categorySlugFromLinkUrl() in slug.ts, matched against
ApiCategory.slug via the new findNodeBySlug()). The homepage carousel is now
fully slot-driven - it iterates slot.images (in the CMS's own sortOrder) and
simply omits any category with no matching image, per explicit product
direction, rather than falling back to a placeholder. Installments keeps its
curated HIGHLIGHT_IDS list with a placeholder for uncovered categories,
since that page always shows the same fixed set, but now uses the same
identity-based lookup instead of position.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
amirhosein.ashourloo
2026-09-06 07:36:24 +03:30
co-authored by Claude Sonnet 5
parent db7d00e473
commit 2249a07dee
6 changed files with 82 additions and 32 deletions
+18 -5
View File
@@ -126,7 +126,7 @@ each is consumed:
| Key | Kind | Consumed by |
|---|---|---|
| `slider` | list | `HeroSlider.tsx` — wins over `FALLBACK_SLIDES` when it has images |
| `categories` | list | `CategoryCarousel.tsx` — images matched **positionally** to the category list (business fills them in the same order); a category past the end of the slot keeps its SVG placeholder |
| `categories` | list | `CategoryCarousel.tsx` — **fully slot-driven**: iterates `slot.images` and resolves each one's category via the `category` query param on its own `linkUrl` (`categorySlugFromLinkUrl()` in `src/lib/slug.ts`, matched against `ApiCategory.slug` with `findNodeBySlug()`). A category with no matching image simply isn't shown — **no placeholder fallback here**, per an explicit product decision (2026-09-06). Position and `titleFa` are **not** used for matching — both were tried and found unreliable in practice (see the history note below) |
| `one-banner-bg` | single | `PromoBanners.tsx` → `OneBanner` |
| `two-banner-bg` | list (2) | `PromoBanners.tsx` → `TwoBanners` |
| `three-banner-bg` | list (3) | `PromoBanners.tsx` → `ThreeBanners` |
@@ -136,6 +136,17 @@ what the component actually renders. If you change a banner's size or ratio,
update its slot entry in the same commit — that file is the only thing
telling the dashboard (and the designer) what to upload.
**Why `categories` matches by `linkUrl` slug, not position:** the first version
matched `slot.images[i]` to `categoryList[i]` by array index, assuming the
business's upload order lines up with this site's category sort order. It
doesn't, and there's no contract that it ever would — live data showed a
laptop photo (`linkUrl` correctly said `?category=laptop`) landing on
"موبایل" and "لوازم برقی" appearing with no photo uploaded for it at all,
purely because of where it fell in the array. `titleFa` isn't reliable
either — the same laptop image was titled "تبلت" (stale/copy-pasted text),
contradicting its own `linkUrl`. The `linkUrl` query param was the one field
that matched the actual photo content every time it was checked.
Homepage banner placement (mirrors the logilook.com reference given for this
layout): hero → categories → hot deals → **three-banner-bg** → موبایل
carousel → **two-banner-bg** → لوازم جانبی → لپ تاپ → **one-banner-bg** →
@@ -154,10 +165,12 @@ carousel → **two-banner-bg** → لوازم جانبی → لپ تاپ → **o
needed for a query-based embed).
- `/installments` — flat, brand-colored hero (no photo, no gradient — this
site stays flat) instead of the reference design's photographic banner;
category highlights reuse the same `categories` static-image slot images
as the homepage carousel, matched by the same positional index. One
reference category ("خانه هوشمند") has no equivalent on this tenant and is
swapped for "لوازم برقی" — see `HIGHLIGHT_IDS` in the page for the mapping.
category highlights are a fixed editorial list (`HIGHLIGHT_IDS`), unlike
the homepage carousel — a highlight with no matching `categories`-slot
image (by `linkUrl` slug, same lookup as the carousel) keeps its SVG
placeholder rather than disappearing, since this page always shows the
same curated set. One reference category ("خانه هوشمند") has no equivalent
on this tenant and is swapped for "لوازم برقی".
## Product / blog pages