From 1c2d1eb31cb142236bac3a97d920366c14294908 Mon Sep 17 00:00:00 2001 From: Alireza Hassani Date: Mon, 17 Aug 2026 09:55:05 +0330 Subject: [PATCH] Wire homepage images to Meshkee slots and publish the catalog. The dashboard can refresh these keys from the live site. Co-authored-by: Cursor --- next.config.ts | 2 +- src/app/meshkee/static-image-slots/route.ts | 14 +++ src/app/page.tsx | 22 ++++- src/components/home/FeaturedCategories.tsx | 70 ++++++++----- src/components/home/HeroSlider.tsx | 74 ++++++++++---- src/components/home/SpecialSaleCarousel.tsx | 35 +++++-- src/components/home/TwoBanners.tsx | 103 +++++++++++-------- src/components/ui/SlotImage.tsx | 37 +++++++ src/lib/meshkee.ts | 104 ++++++++++++++++++++ 9 files changed, 364 insertions(+), 97 deletions(-) create mode 100644 src/app/meshkee/static-image-slots/route.ts create mode 100644 src/components/ui/SlotImage.tsx create mode 100644 src/lib/meshkee.ts diff --git a/next.config.ts b/next.config.ts index e9ffa30..ef8450f 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + allowedDevOrigins: ["sanihome.com", "localhost"], }; export default nextConfig; diff --git a/src/app/meshkee/static-image-slots/route.ts b/src/app/meshkee/static-image-slots/route.ts new file mode 100644 index 0000000..49e9367 --- /dev/null +++ b/src/app/meshkee/static-image-slots/route.ts @@ -0,0 +1,14 @@ +import { NextResponse } from "next/server"; +import { STATIC_IMAGE_SLOT_CATALOG } from "@/lib/meshkee"; + +export function GET() { + return NextResponse.json( + { slots: STATIC_IMAGE_SLOT_CATALOG }, + { + headers: { + "Access-Control-Allow-Origin": "*", + "Cache-Control": "public, max-age=60", + }, + }, + ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 3fef222..739c819 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -11,18 +11,30 @@ import { SamsungCarousel } from "@/components/home/SamsungCarousel"; import { AboutSanihome } from "@/components/home/AboutSanihome"; import { BlogCarousel } from "@/components/home/BlogCarousel"; import { Footer } from "@/components/home/Footer"; +import { + STATIC_IMAGE_KEYS, + firstImageForSlot, + getStaticImageSlots, + imagesForSlot, +} from "@/lib/meshkee"; + +export default async function Home() { + const slots = await getStaticImageSlots(); -export default function Home() { return ( <>
- - + + - - + + diff --git a/src/components/home/FeaturedCategories.tsx b/src/components/home/FeaturedCategories.tsx index 4cebead..7a1fae4 100644 --- a/src/components/home/FeaturedCategories.tsx +++ b/src/components/home/FeaturedCategories.tsx @@ -1,8 +1,14 @@ import { Placeholder } from "@/components/ui/Placeholder"; import { SectionTitle } from "@/components/ui/SectionTitle"; +import { SlotImage } from "@/components/ui/SlotImage"; import { categories } from "@/data/sample"; +import type { StaticImageWithUrl } from "@/lib/meshkee"; -export function FeaturedCategories() { +type FeaturedCategoriesProps = { + images?: StaticImageWithUrl[]; +}; + +export function FeaturedCategories({ images = [] }: FeaturedCategoriesProps) { return (
@@ -12,27 +18,47 @@ export function FeaturedCategories() { action="همه دسته‌ها" />
- {categories.map((cat, i) => ( - - - - {cat.name} - - {cat.count} محصول - - ))} + {images.length > 0 + ? images.map((image, i) => ( +
+ + {image.title ? ( + + {image.title} + + ) : null} +
+ )) + : categories.map((cat, i) => ( + + + + {cat.name} + + {cat.count} محصول + + ))}
diff --git a/src/components/home/HeroSlider.tsx b/src/components/home/HeroSlider.tsx index 3622f5d..3eda2b0 100644 --- a/src/components/home/HeroSlider.tsx +++ b/src/components/home/HeroSlider.tsx @@ -2,15 +2,34 @@ import { useState } from "react"; import { Placeholder } from "@/components/ui/Placeholder"; +import { SlotImage } from "@/components/ui/SlotImage"; import { sliderSlides } from "@/data/sample"; +import type { StaticImageWithUrl } from "@/lib/meshkee"; -export function HeroSlider() { - const [active, setActive] = useState(1); +type HeroSliderProps = { + images?: StaticImageWithUrl[]; +}; - const getIndex = (offset: number) => { - const len = sliderSlides.length; - return (active + offset + len) % len; - }; +export function HeroSlider({ images = [] }: HeroSliderProps) { + const remote = images.length > 0; + const slides = remote + ? images.map((image) => ({ + id: image.id, + title: image.title ?? "", + src: image.url, + linkUrl: image.linkUrl, + })) + : sliderSlides.map((slide) => ({ + id: String(slide.id), + title: slide.title, + src: slide.image, + linkUrl: undefined as string | null | undefined, + })); + + const [active, setActive] = useState(0); + const len = slides.length; + + const getIndex = (offset: number) => (active + offset + len) % len; const positions = [ { offset: -1, scale: "scale-[0.92]", opacity: "opacity-55", z: "z-10" }, @@ -24,7 +43,30 @@ export function HeroSlider() {
{positions.map(({ offset, scale, opacity, z }) => { const isCenter = offset === 0; - const slide = sliderSlides[getIndex(offset)]; + if (!isCenter && len < 2) return null; + const slide = slides[getIndex(offset)]; + + const heightClass = `w-full h-[200px] sm:h-[260px] md:h-[310px] ${ + isCenter ? "lg:h-[380px]" : "lg:h-[350px]" + }`; + + const media = remote ? ( + + ) : ( + + ); return (
- + {media}
); @@ -56,16 +90,14 @@ export function HeroSlider() {
- {sliderSlides.map((slide, i) => ( + {slides.map((slide, i) => (