Connect storefront to backend catalog, search, and home slots.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
2f857f1a90
commit
0074ab6629
@@ -0,0 +1,301 @@
|
||||
import type { Category } from "@/data/home";
|
||||
import {
|
||||
ABOUT,
|
||||
BLACK_ITEMS,
|
||||
CATEGORIES,
|
||||
MEGA_MENU_TABS,
|
||||
} from "@/data/home";
|
||||
import type { HeroSlideData } from "@/components/home/HeroSlider";
|
||||
import { categoryPath } from "@/lib/category-path";
|
||||
import type { PublicWebsiteSlotItem } from "@/lib/website-slots";
|
||||
import { firstForSlot, itemsForSlot } from "@/lib/website-slots";
|
||||
|
||||
export type HomeSlotContent = {
|
||||
heroSlides: HeroSlideData[];
|
||||
categories: Category[];
|
||||
leather: {
|
||||
image: string;
|
||||
nameFa: string;
|
||||
nameEn: string;
|
||||
subtext: string;
|
||||
linkText: string;
|
||||
link: string;
|
||||
};
|
||||
promoSale: {
|
||||
image?: string;
|
||||
nameFa?: string;
|
||||
nameEn?: string;
|
||||
subtext?: string;
|
||||
link?: string;
|
||||
} | null;
|
||||
promoDiscount: {
|
||||
image?: string;
|
||||
nameFa?: string;
|
||||
nameEn?: string;
|
||||
subtext?: string;
|
||||
link?: string;
|
||||
} | null;
|
||||
promoAutumn: {
|
||||
image?: string;
|
||||
nameFa?: string;
|
||||
nameEn?: string;
|
||||
subtext?: string;
|
||||
link?: string;
|
||||
} | null;
|
||||
blackItems: Array<{
|
||||
id: string;
|
||||
image: string;
|
||||
alt: string;
|
||||
nameFa: string;
|
||||
nameEn: string;
|
||||
href: string;
|
||||
}>;
|
||||
about: {
|
||||
titleFa: string;
|
||||
titleEn: string;
|
||||
lead: string;
|
||||
body: string;
|
||||
image: string;
|
||||
imageSecondary: string;
|
||||
};
|
||||
paymentBadges: string;
|
||||
megaTabs: Array<{
|
||||
id: string;
|
||||
nameFa: string;
|
||||
nameEn: string;
|
||||
href: string;
|
||||
image: string;
|
||||
links: readonly { label: string; href: string }[];
|
||||
columns?: readonly {
|
||||
title: string;
|
||||
href: string;
|
||||
links: readonly { label: string; href: string }[];
|
||||
}[];
|
||||
}>;
|
||||
};
|
||||
|
||||
function slotItems(
|
||||
items: PublicWebsiteSlotItem[],
|
||||
slug: string,
|
||||
...legacy: string[]
|
||||
) {
|
||||
const modern = itemsForSlot(items, slug);
|
||||
if (modern.length) return modern;
|
||||
for (const key of legacy) {
|
||||
const found = itemsForSlot(items, key);
|
||||
if (found.length) return found;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function firstSlot(
|
||||
items: PublicWebsiteSlotItem[],
|
||||
slug: string,
|
||||
...legacy: string[]
|
||||
) {
|
||||
return slotItems(items, slug, ...legacy)[0] ?? null;
|
||||
}
|
||||
|
||||
export function mapHomeSlots(
|
||||
items: PublicWebsiteSlotItem[],
|
||||
): HomeSlotContent {
|
||||
const heroItems = slotItems(items, "hero-slider", "home.hero");
|
||||
const heroSlides: HeroSlideData[] =
|
||||
heroItems.length > 0
|
||||
? heroItems.map((item, i) => ({
|
||||
id: item.id,
|
||||
image: item.imageUrl || "/images/home/banners/hero.jpg",
|
||||
imagePosition:
|
||||
i === 0
|
||||
? "object-[center_20%]"
|
||||
: i === 1
|
||||
? "object-[center_40%]"
|
||||
: "object-[center_55%]",
|
||||
title: item.nameFa || item.nameEn || "مانا کالکشن",
|
||||
subtitle: item.nameEn || item.subtext || "",
|
||||
cta: {
|
||||
label: item.linkText || "مشاهده",
|
||||
href: item.link || "/products",
|
||||
},
|
||||
}))
|
||||
: [];
|
||||
|
||||
const categoryItems = slotItems(items, "category-strip");
|
||||
const categories: Category[] =
|
||||
categoryItems.length > 0
|
||||
? categoryItems.map((item, index) => {
|
||||
const fallback = CATEGORIES[index % CATEGORIES.length];
|
||||
return {
|
||||
id: item.id || `cat-${index}`,
|
||||
nameFa: item.nameFa || `دسته ${index + 1}`,
|
||||
nameEn: item.nameEn || "",
|
||||
image: item.imageUrl || fallback.image,
|
||||
href:
|
||||
item.link ||
|
||||
(fallback
|
||||
? categoryPath(fallback)
|
||||
: categoryPath({ id: "coat", nameEn: "Coat", nameFa: "کت" })),
|
||||
};
|
||||
})
|
||||
: CATEGORIES.map((cat) => {
|
||||
const legacy = firstForSlot(items, `home.category.${cat.id}`);
|
||||
if (!legacy) return cat;
|
||||
return {
|
||||
...cat,
|
||||
nameFa: legacy.nameFa || cat.nameFa,
|
||||
nameEn: legacy.nameEn || cat.nameEn,
|
||||
image: legacy.imageUrl || cat.image,
|
||||
href: legacy.link || cat.href || categoryPath(cat),
|
||||
};
|
||||
});
|
||||
|
||||
const leatherSlot = firstSlot(
|
||||
items,
|
||||
"leather-banner",
|
||||
"home.banner.leather",
|
||||
);
|
||||
const leather = {
|
||||
image: leatherSlot?.imageUrl || "/images/home/banners/leather.png",
|
||||
nameFa: leatherSlot?.nameFa || "هنر پوشیدن",
|
||||
nameEn: leatherSlot?.nameEn || "THE ART OF DRESSING",
|
||||
subtext: leatherSlot?.subtext || "",
|
||||
linkText: leatherSlot?.linkText || "کالکشن چرم",
|
||||
link: leatherSlot?.link || "/products?collection=leather",
|
||||
};
|
||||
|
||||
const saleSlot = firstSlot(
|
||||
items,
|
||||
"promo-1",
|
||||
"promo-sale",
|
||||
"home.promo.sale",
|
||||
);
|
||||
const promoSale = saleSlot
|
||||
? {
|
||||
image: saleSlot.imageUrl || undefined,
|
||||
nameEn: saleSlot.nameEn || "",
|
||||
nameFa: saleSlot.nameFa || "",
|
||||
subtext: saleSlot.subtext || "",
|
||||
link: saleSlot.link || "/products?sale=1",
|
||||
}
|
||||
: null;
|
||||
|
||||
const discountSlot = firstSlot(items, "promo-2", "promo-discount");
|
||||
const promoDiscount = discountSlot
|
||||
? {
|
||||
image: discountSlot.imageUrl || undefined,
|
||||
nameFa: discountSlot.nameFa || discountSlot.linkText || "",
|
||||
nameEn: discountSlot.nameEn || "",
|
||||
subtext: discountSlot.subtext || "",
|
||||
link: discountSlot.link || "/products?sale=1",
|
||||
}
|
||||
: null;
|
||||
|
||||
const autumnSlot = firstSlot(
|
||||
items,
|
||||
"promo-3",
|
||||
"promo-autumn",
|
||||
"home.promo.autumn",
|
||||
);
|
||||
const promoAutumn = autumnSlot
|
||||
? {
|
||||
image: autumnSlot.imageUrl || undefined,
|
||||
nameEn: autumnSlot.nameEn || "",
|
||||
nameFa: autumnSlot.nameFa || autumnSlot.linkText || "",
|
||||
subtext: autumnSlot.subtext || "",
|
||||
link: autumnSlot.link || "/products?collection=autumn",
|
||||
}
|
||||
: null;
|
||||
|
||||
const portraitItems = slotItems(items, "four-portrait-banner");
|
||||
const blackItems =
|
||||
portraitItems.length > 0
|
||||
? Array.from({ length: 4 }).map((_, index) => {
|
||||
const slot =
|
||||
portraitItems.find((row) => row.sortOrder === index) ??
|
||||
portraitItems[index];
|
||||
const fallback = BLACK_ITEMS[index] ?? BLACK_ITEMS[0];
|
||||
return {
|
||||
id: slot?.id || fallback.id,
|
||||
image: slot?.imageUrl || fallback.image,
|
||||
alt: slot?.nameFa || fallback.alt,
|
||||
nameFa: slot?.nameFa || fallback.nameFa,
|
||||
nameEn: slot?.nameEn || fallback.nameEn,
|
||||
href:
|
||||
slot?.link ||
|
||||
`/products?color=black&item=${fallback.id}`,
|
||||
};
|
||||
})
|
||||
: BLACK_ITEMS.map((item) => {
|
||||
const slot = firstForSlot(items, `home.black.${item.id}`);
|
||||
return {
|
||||
id: item.id,
|
||||
image: slot?.imageUrl || item.image,
|
||||
alt: slot?.nameFa || item.alt,
|
||||
nameFa: slot?.nameFa || item.nameFa,
|
||||
nameEn: slot?.nameEn || item.nameEn,
|
||||
href: slot?.link || `/products?color=black&item=${item.id}`,
|
||||
};
|
||||
});
|
||||
|
||||
const aboutItems = slotItems(items, "about");
|
||||
const aboutPrimary =
|
||||
aboutItems.find((row) => row.sortOrder === 0) ??
|
||||
aboutItems[0] ??
|
||||
firstSlot(items, "about-primary", "home.about.primary");
|
||||
const aboutSecondary =
|
||||
aboutItems.find((row) => row.sortOrder === 1) ??
|
||||
aboutItems[1] ??
|
||||
firstSlot(items, "about-secondary", "home.about.secondary");
|
||||
const about = {
|
||||
titleFa: aboutPrimary?.nameFa || ABOUT.titleFa,
|
||||
titleEn: aboutPrimary?.nameEn || ABOUT.titleEn,
|
||||
lead: aboutPrimary?.subtext || ABOUT.lead,
|
||||
body: ABOUT.body,
|
||||
image: aboutPrimary?.imageUrl || ABOUT.image,
|
||||
imageSecondary: aboutSecondary?.imageUrl || ABOUT.imageSecondary,
|
||||
};
|
||||
|
||||
const paymentSlot = firstSlot(
|
||||
items,
|
||||
"payment-badges",
|
||||
"home.footer.payment_badges",
|
||||
);
|
||||
const paymentBadges =
|
||||
paymentSlot?.imageUrl || "/images/home/banners/payment-badges.png";
|
||||
|
||||
const megaItems = slotItems(items, "mega-menu");
|
||||
const megaTabs = MEGA_MENU_TABS.map((tab, index) => {
|
||||
const slot =
|
||||
megaItems.find((row) => row.sortOrder === index) ??
|
||||
megaItems[index] ??
|
||||
firstForSlot(items, `nav.mega.${tab.id}`);
|
||||
return {
|
||||
id: tab.id,
|
||||
nameFa: slot?.nameFa || tab.nameFa,
|
||||
nameEn: slot?.nameEn || tab.nameEn,
|
||||
href: slot?.link || tab.href,
|
||||
image: slot?.imageUrl || tab.image,
|
||||
links: tab.links,
|
||||
columns: [
|
||||
{
|
||||
title: slot?.nameFa || tab.nameFa,
|
||||
href: slot?.link || tab.href,
|
||||
links: tab.links,
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
heroSlides,
|
||||
categories,
|
||||
leather,
|
||||
promoSale,
|
||||
promoDiscount,
|
||||
promoAutumn,
|
||||
blackItems,
|
||||
about,
|
||||
paymentBadges,
|
||||
megaTabs,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user