ifixkart-storefront/sections/UltimateTechSection.tsx

272 lines
9.3 KiB
TypeScript

"use client";
import React, { useState, useEffect, useMemo } from 'react';
import Link from 'next/link';
import { ProductCard } from '../components/ProductCard';
import { ArrowRight } from 'lucide-react';
import { Swiper, SwiperSlide } from 'swiper/react';
import { Autoplay, Pagination, A11y } from 'swiper/modules';
import { Product } from '../types';
import { getImageUrl } from '../lib/utils';
import { getSectionMeta, getSectionTitle } from '../lib/homepageDefaults';
import 'swiper/css';
import 'swiper/css/pagination';
const DEFAULT_BANNER_IMAGE =
'https://images.unsplash.com/photo-1585790050230-5dd28404ccb9?w=600&auto=format&fit=crop&q=80';
const BANNER_BG = ['#F8EDE6', '#E8F1F8', '#F3EDE8', '#EEF6EC'];
type BannerSlide = {
id: string;
title: string;
subtitle: string;
image: string;
link: string;
cta: string;
bg: string;
};
interface UltimateTechSectionProps {
config?: any;
products?: Product[];
loading?: boolean;
error?: boolean;
}
function buildBannerSlides(meta: Record<string, any>): BannerSlide[] {
const raw = Array.isArray(meta.banners) ? meta.banners : [];
const fromList = raw
.filter((b: any) => b && (b.title || b.image || b.image_url))
.map((b: any, idx: number) => ({
id: String(b.id || `banner-${idx + 1}`),
title: String(b.title || 'Featured'),
subtitle: String(b.subtitle || b.description || 'Smooth in Performance'),
image:
getImageUrl(b.image || b.image_url) || DEFAULT_BANNER_IMAGE,
link: String(
b.buttonUrl || b.button_url || b.link || b.href || '/shop'
),
cta: String(b.buttonText || b.button_text || b.cta || 'Shop Now'),
bg: String(b.bgColor || b.bg || BANNER_BG[idx % BANNER_BG.length]),
}));
if (fromList.length > 0) return fromList;
// Legacy single-banner fields
if (meta.banner_title || meta.banner_image) {
return [
{
id: 'banner-legacy',
title: String(meta.banner_title || 'Active Laptop'),
subtitle: String(meta.banner_subtitle || 'Smooth in Performance'),
image: getImageUrl(meta.banner_image) || DEFAULT_BANNER_IMAGE,
link: String(meta.banner_link || '/shop'),
cta: String(meta.banner_cta || 'Shop Now'),
bg: BANNER_BG[0],
},
];
}
return [
{
id: 'banner-default',
title: 'Smart Watches',
subtitle: 'Smooth in Performance',
image: DEFAULT_BANNER_IMAGE,
link: '/shop?category=smart-watches',
cta: 'Shop Now',
bg: BANNER_BG[0],
},
];
}
/** Full-card promo tile (slides as one unit — not content inside a frame). */
function BannerCard({ slide }: { slide: BannerSlide }) {
return (
<Link
href={slide.link}
className="relative block w-full h-full min-h-[380px] rounded-2xl overflow-hidden flex flex-col group/banner"
style={{ backgroundColor: slide.bg }}
>
<div className="relative z-10 pt-7 px-6 md:px-7 text-left">
<h3 className="text-[22px] md:text-[24px] font-bold text-[#1a1a1a] leading-tight mb-1.5">
{slide.title}
</h3>
{slide.subtitle ? (
<p className="text-[13px] text-[#333] mb-4">{slide.subtitle}</p>
) : (
<div className="mb-4" />
)}
<span className="text-[13px] font-bold text-[#1a1a1a] underline underline-offset-4 decoration-1 group-hover/banner:text-primary transition-colors">
{slide.cta}
</span>
</div>
<div className="absolute inset-x-0 bottom-0 top-[36%] flex items-end justify-center pointer-events-none select-none">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={slide.image}
alt={slide.title}
className="h-[110%] w-auto max-w-[98%] object-contain object-bottom translate-y-[8%]"
/>
</div>
</Link>
);
}
export const UltimateTechSection: React.FC<UltimateTechSectionProps> = ({
config,
products,
error,
}) => {
const [techGadgets, setTechGadgets] = useState<Product[]>([]);
const meta = getSectionMeta(config);
const banners = useMemo(() => buildBannerSlides(meta), [config]);
useEffect(() => {
const pinnedSlugs: string[] = meta.pinned_product_slugs || [];
if (pinnedSlugs.length > 0 && products && products.length > 0) {
const pinned = pinnedSlugs
.map((slug) => products.find((p) => p.slug === slug || p.id === slug))
.filter(Boolean) as Product[];
if (pinned.length > 0) {
setTechGadgets(pinned.slice(0, 4));
return;
}
}
const rawItems = meta.items;
if (rawItems && rawItems.length > 0) {
setTechGadgets(
rawItems.slice(0, 4).map((it: any) => ({
id: it.id || String(Math.random()),
name: it.title || it.name,
price: it.price || 0,
oldPrice: it.oldPrice || it.originalPrice,
discount: it.discount,
image: getImageUrl(it.image),
rating: it.rating || 5.0,
reviewsCount: it.reviewsCount || 8,
category: 'Tech Gadgets',
brand: 'SmartTech',
slug: it.slug || it.id,
}))
);
} else if (products && products.length > 0) {
const gadgets = products.filter((p) => p.isTechGadget);
setTechGadgets(
(gadgets.length > 0 ? gadgets : products).slice(0, 4)
);
} else {
setTechGadgets([]);
}
}, [config, products, error]);
const title = getSectionTitle(config, 'ultimate_tech');
const multiBanner = banners.length > 1;
if (techGadgets.length === 0) {
return (
<section className="py-8 bg-white font-sans">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-[1400px]">
<div className="flex items-center justify-between gap-4 pb-3.5 border-b border-[#e5e5e5]">
<h2 className="text-[18px] md:text-[20px] font-bold text-[#222] tracking-tight">
{title}
</h2>
</div>
<div className="mt-5 border border-[#e5e5e5] rounded-lg p-10 text-center text-[13px] text-[#777]">
Tech gadgets will appear here once products are available.
</div>
</div>
</section>
);
}
return (
<section className="py-8 bg-white font-sans">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-[1400px]">
<div className="flex items-center justify-between gap-4 pb-3.5 border-b border-[#e5e5e5]">
<div className="text-left min-w-0">
<h2 className="text-[18px] md:text-[20px] font-bold text-[#222] tracking-tight">
{title}
</h2>
</div>
<Link
href="/shop?highlight=tech"
className="text-[13px] text-[#333] hover:text-primary transition-colors flex items-center gap-1.5 shrink-0"
>
View All
<ArrowRight size={14} strokeWidth={2} />
</Link>
</div>
<div className="mt-5 flex flex-col lg:flex-row gap-4 lg:gap-5 items-stretch">
<div className="flex-1 min-w-0 border border-[#e5e5e5] bg-white grid grid-cols-2 md:grid-cols-4">
{techGadgets.map((product, idx) => (
<div
key={product.id || product.slug || idx}
className={[
'border-[#e5e5e5]',
idx % 2 === 0 ? 'border-r' : '',
idx < techGadgets.length - 1 ? 'md:border-r' : '',
idx < 2 ? 'border-b md:border-b-0' : '',
].join(' ')}
>
<ProductCard product={product} products={techGadgets} />
</div>
))}
</div>
{/* Side banners: whole-card slide when more than one */}
<div className="relative w-full lg:w-[260px] xl:w-[280px] shrink-0 self-stretch min-h-[380px]">
{multiBanner ? (
<Swiper
modules={[Autoplay, Pagination, A11y]}
slidesPerView={1}
spaceBetween={0}
speed={550}
loop
autoplay={{
delay: 4200,
disableOnInteraction: false,
pauseOnMouseEnter: true,
}}
pagination={{ clickable: true }}
className="ultimate-banner-swiper h-full min-h-[380px] !overflow-hidden rounded-2xl"
>
{banners.map((slide) => (
<SwiperSlide key={slide.id} className="!h-auto min-h-[380px]">
<BannerCard slide={slide} />
</SwiperSlide>
))}
</Swiper>
) : (
<BannerCard slide={banners[0]} />
)}
</div>
</div>
</div>
<style
dangerouslySetInnerHTML={{
__html: `
.ultimate-banner-swiper { height: 100%; }
.ultimate-banner-swiper .swiper-wrapper { align-items: stretch; }
.ultimate-banner-swiper .swiper-slide { height: auto; align-self: stretch; }
.ultimate-banner-swiper .swiper-pagination { bottom: 12px !important; }
.ultimate-banner-swiper .swiper-pagination-bullet {
width: 7px; height: 7px; background: #1a1a1a; opacity: 0.25;
}
.ultimate-banner-swiper .swiper-pagination-bullet-active {
opacity: 0.85; background: #1976f3;
}
`,
}}
/>
</section>
);
};