Wire featured categories to a static-image slot
Add a featured-categories list slot (published via /meshkee/static-image-slots alongside the existing banner slots) so the homepage category strip can be driven by admin-uploaded images instead of the category API, which has no image field. Falls back to the current category list whenever the slot has no images configured or the fetch fails. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
60d7d55caa
commit
83809815c1
+2
-1
@@ -46,13 +46,14 @@ export default async function Home() {
|
||||
(slot) => slot.key === STATIC_IMAGE_KEYS.threeBannerRow,
|
||||
);
|
||||
const heroImages = imagesForSlot(staticImageSlots, STATIC_IMAGE_KEYS.homeHero);
|
||||
const featuredCategoryImages = imagesForSlot(staticImageSlots, STATIC_IMAGE_KEYS.featuredCategories);
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeroSlider images={heroImages} />
|
||||
<SpecialSaleCarousel products={specialSaleProducts} contactPhone={contactPhone} />
|
||||
<FeatureBar />
|
||||
<FeaturedCategories categories={categories} />
|
||||
<FeaturedCategories categories={categories} images={featuredCategoryImages} />
|
||||
<TwoBanners
|
||||
images={imagesForSlot(staticImageSlots, STATIC_IMAGE_KEYS.twoBanner)}
|
||||
aspectRatio={twoBannerSlot?.aspectRatio}
|
||||
|
||||
@@ -7,9 +7,33 @@ import "swiper/css/free-mode";
|
||||
import { Placeholder } from "@/components/ui/Placeholder";
|
||||
import { SectionTitle } from "@/components/ui/SectionTitle";
|
||||
import { EmptyState } from "@/components/ui/EmptyState";
|
||||
import type { Category } from "@/lib/meshkee";
|
||||
import type { Category, StaticImageWithUrl } from "@/lib/meshkee";
|
||||
|
||||
export function FeaturedCategories({ categories }: { categories: Category[] }) {
|
||||
function CategorySlotCard({ image }: { image: StaticImageWithUrl }) {
|
||||
const label = image.titleFa ?? image.titleEn ?? "";
|
||||
const content = (
|
||||
<div className="group flex h-full flex-col items-center rounded-xl border border-border bg-surface p-3 transition-all hover:border-red hover:shadow-md">
|
||||
<div className="mb-3 aspect-square w-[72px] overflow-hidden rounded-full border border-border">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={image.url} alt={label} className="h-full w-full object-cover" />
|
||||
</div>
|
||||
{label && (
|
||||
<span className="w-full truncate text-center text-xs font-medium leading-relaxed md:text-sm">
|
||||
{label}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
return image.linkUrl ? <a href={image.linkUrl}>{content}</a> : content;
|
||||
}
|
||||
|
||||
export function FeaturedCategories({
|
||||
categories,
|
||||
images = [],
|
||||
}: {
|
||||
categories: Category[];
|
||||
images?: StaticImageWithUrl[];
|
||||
}) {
|
||||
return (
|
||||
<section className="py-10 md:py-14">
|
||||
<div className="mx-auto max-w-7xl px-4">
|
||||
@@ -18,7 +42,29 @@ export function FeaturedCategories({ categories }: { categories: Category[] }) {
|
||||
enTitle="Featured Categories"
|
||||
action="همه دستهها"
|
||||
/>
|
||||
{categories.length === 0 ? (
|
||||
{images.length > 0 ? (
|
||||
<Swiper
|
||||
modules={[FreeMode]}
|
||||
dir="rtl"
|
||||
slidesPerView={2.3}
|
||||
spaceBetween={12}
|
||||
breakpoints={{
|
||||
640: { slidesPerView: 4.3, spaceBetween: 12 },
|
||||
1024: { slidesPerView: 8, spaceBetween: 12 },
|
||||
}}
|
||||
freeMode={{ enabled: true, momentum: true, sticky: true }}
|
||||
grabCursor
|
||||
className="!px-1 !py-2"
|
||||
>
|
||||
{images.map((image, i) => (
|
||||
<SwiperSlide key={image.id}>
|
||||
<div className="animate-slide-in h-full" style={{ animationDelay: `${i * 60}ms` }}>
|
||||
<CategorySlotCard image={image} />
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
) : categories.length === 0 ? (
|
||||
<EmptyState message="هنوز دستهبندیای ثبت نشده است." />
|
||||
) : (
|
||||
<Swiper
|
||||
|
||||
@@ -484,6 +484,7 @@ export const STATIC_IMAGE_KEYS = {
|
||||
homeHero: "home-hero",
|
||||
twoBanner: "two-banner",
|
||||
threeBannerRow: "three-banner-row",
|
||||
featuredCategories: "featured-categories",
|
||||
} as const;
|
||||
|
||||
export const STATIC_IMAGE_SLOT_CATALOG = [
|
||||
@@ -514,6 +515,15 @@ export const STATIC_IMAGE_SLOT_CATALOG = [
|
||||
itemCount: 3,
|
||||
recommendedWidth: 1200,
|
||||
},
|
||||
{
|
||||
key: STATIC_IMAGE_KEYS.featuredCategories,
|
||||
label: "Featured categories",
|
||||
kind: "list" as const,
|
||||
pageKey: "home",
|
||||
aspectRatio: "1:1",
|
||||
itemCount: null,
|
||||
recommendedWidth: 300,
|
||||
},
|
||||
];
|
||||
|
||||
export async function getStaticImageSlots(): Promise<StaticImageSlot[]> {
|
||||
|
||||
Reference in New Issue
Block a user