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
+2 -4
View File
@@ -14,7 +14,7 @@ import {
getStoreSpecials,
safe,
} from "@/lib/api";
import { buildCategoryTree, carouselCategories, findNode, subtreeIds } from "@/lib/categoryTree";
import { buildCategoryTree, findNode, subtreeIds } from "@/lib/categoryTree";
import { productToCard, specialToCards } from "@/lib/cards";
import { blogHref, categoryHref } from "@/lib/slug";
import { HOME_CAROUSELS } from "@/data/site";
@@ -57,8 +57,6 @@ export default async function HomePage() {
safe(getStaticImageSlots("home"), []),
]);
const carouselCats = carouselCategories(categories);
// Empty or failing slots fall through to each section's own placeholder —
// findSlot() already returns undefined for an empty/missing key.
const sliderSlot = findSlot(imageSlots, "slider");
@@ -141,7 +139,7 @@ export default async function HomePage() {
<>
<h1 className="sr-only">فروشگاه موبایل معلم | خرید موبایل، لپ‌تاپ و لوازم جانبی</h1>
<HeroSlider slot={sliderSlot} slides={slides} />
<CategoryCarousel categories={carouselCats} slot={categoriesSlot} />
<CategoryCarousel roots={categories} slot={categoriesSlot} />
<HotDeals products={deals} endsAt={endOfDay.getTime() + 2 * 86_400_000} />
<ThreeBanners slot={threeBannerSlot} />
{mobileCarousel && (