ifixkart-storefront/components/store/HeroBanner.tsx

146 lines
6.7 KiB
TypeScript

/**
* @component HeroBanner
* @purpose Main Homepage Hero Banner slider fetching 100% dynamic CMS content from DB 2 via Storefront API.
* @dependencies storefrontService, lucide-react
*/
'use client';
import { useState, useEffect } from 'react';
import Image from 'next/image';
import Link from 'next/link';
import { ChevronLeft, ChevronRight, ShoppingCart } from 'lucide-react';
import { storefrontService, BannerItem } from '@/services/api/storefrontService';
export function HeroBanner() {
const [banners, setBanners] = useState<BannerItem[]>([]);
const [currentSlide, setCurrentSlide] = useState(0);
useEffect(() => {
// Fetch live layout content from backend
fetch('http://127.0.0.1:8000/api/v1/storefront/layout/home?region=hero')
.then((res) => (res.ok ? res.json() : []))
.then((data) => {
if (data && data.length > 0) {
const items: BannerItem[] = data.map((d: any) => ({
id: d.content_id,
title: d.title,
subtitle: d.subtitle || '',
discount_tag: d.metadata?.discount_tag || 'SPECIAL OFFER',
image_url: d.image_url || '',
cta_text: d.button_text || 'Shop Now',
cta_link: d.button_url || '/products',
}));
setBanners(items);
} else {
// Fallback to storefrontService
storefrontService.getBanners().then(setBanners);
}
})
.catch(() => {
storefrontService.getBanners().then(setBanners);
});
}, []);
useEffect(() => {
if (banners.length <= 1) return;
const timer = setInterval(() => {
setCurrentSlide((prev) => (prev + 1) % banners.length);
}, 6000);
return () => clearInterval(timer);
}, [banners.length]);
if (!banners || banners.length === 0) return null;
const current = banners[currentSlide] || banners[0];
return (
<div className="w-full max-w-7xl mx-auto px-4 mt-6">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Main Hero Slider (Takes 2 Columns) */}
<div className="lg:col-span-2 relative h-[360px] md:h-[420px] rounded-2xl overflow-hidden shadow-sm group bg-[#111111] text-white">
<Image
src={current.image_url}
alt={current.title}
fill
priority
className="object-cover opacity-60 group-hover:scale-105 transition-transform duration-700"
/>
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/40 to-transparent p-8 md:p-12 flex flex-col justify-center max-w-xl">
<span className="text-[10px] md:text-xs font-black uppercase tracking-wider text-[#1877f2] bg-blue-500/20 px-3 py-1 rounded-full border border-blue-500/30 w-fit mb-3">
{current.discount_tag}
</span>
<h1 className="text-2xl md:text-4xl font-black text-white leading-tight mb-3">
{current.title}
</h1>
<p className="text-xs md:text-sm text-gray-300 font-medium mb-6 line-clamp-2">
{current.subtitle}
</p>
<Link
href={current.cta_link}
className="bg-[#1877f2] hover:bg-[#1565c0] text-white text-xs font-extrabold px-6 py-3 rounded-xl transition-colors shadow-lg flex items-center gap-2 w-fit cursor-pointer"
>
<span>{current.cta_text}</span>
<ChevronRight className="w-4 h-4" />
</Link>
</div>
{/* Navigation Controls */}
{banners.length > 1 && (
<>
<button
onClick={() => setCurrentSlide((prev) => (prev - 1 + banners.length) % banners.length)}
className="absolute left-4 top-1/2 -translate-y-1/2 p-2 bg-black/40 hover:bg-black/70 text-white rounded-full transition-colors cursor-pointer"
>
<ChevronLeft className="w-5 h-5" />
</button>
<button
onClick={() => setCurrentSlide((prev) => (prev + 1) % banners.length)}
className="absolute right-4 top-1/2 -translate-y-1/2 p-2 bg-black/40 hover:bg-black/70 text-white rounded-full transition-colors cursor-pointer"
>
<ChevronRight className="w-5 h-5" />
</button>
</>
)}
</div>
{/* 2 Right Promo Cards (Takes 1 Column) */}
<div className="space-y-6 flex flex-col justify-between">
<div className="relative h-[170px] md:h-[198px] rounded-2xl overflow-hidden shadow-2xs group bg-gray-900 text-white">
<Image
src="https://images.unsplash.com/photo-1581092160607-ee22621dd758?w=500&auto=format&fit=crop&q=60"
alt="Certified Repair Booking"
fill
className="object-cover opacity-50 group-hover:scale-105 transition-transform duration-500"
/>
<div className="absolute inset-0 p-5 flex flex-col justify-center bg-gradient-to-r from-black/70 to-transparent">
<span className="text-[9px] font-bold text-amber-400 uppercase tracking-wider mb-1">iFixKart Repair Hub</span>
<h3 className="text-base font-black text-white leading-tight">Same-Day Screen & Battery Repair</h3>
<p className="text-[11px] text-gray-300 mt-1">Book certified technician pickup in 60s</p>
<Link href="/services" className="text-xs font-bold text-[#1877f2] hover:underline mt-2 inline-flex items-center gap-1">
Book Repair <ChevronRight className="w-3 h-3" />
</Link>
</div>
</div>
<div className="relative h-[170px] md:h-[198px] rounded-2xl overflow-hidden shadow-2xs group bg-gray-900 text-white">
<Image
src="https://images.unsplash.com/photo-1597740985671-2a8a3b80502e?w=500&auto=format&fit=crop&q=60"
alt="OEM Spare Parts"
fill
className="object-cover opacity-50 group-hover:scale-105 transition-transform duration-500"
/>
<div className="absolute inset-0 p-5 flex flex-col justify-center bg-gradient-to-r from-black/70 to-transparent">
<span className="text-[9px] font-bold text-emerald-400 uppercase tracking-wider mb-1">100% Genuine Spare Parts</span>
<h3 className="text-base font-black text-white leading-tight">Original Display Assemblies & Batteries</h3>
<p className="text-[11px] text-gray-300 mt-1">12-Month replacement warranty</p>
<Link href="/products?category=smartphones" className="text-xs font-bold text-[#1877f2] hover:underline mt-2 inline-flex items-center gap-1">
Browse Parts <ChevronRight className="w-3 h-3" />
</Link>
</div>
</div>
</div>
</div>
</div>
);
}