214 lines
8.6 KiB
TypeScript
214 lines
8.6 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
import Link from 'next/link';
|
|
import { Search, MapPin, ChevronLeft, ChevronRight, ArrowRight } from 'lucide-react';
|
|
|
|
interface HomeSlide {
|
|
id: string;
|
|
type: 'BUSINESS' | 'CUSTOM';
|
|
businessId?: string | null;
|
|
business?: any;
|
|
title?: string | null;
|
|
subtitle?: string | null;
|
|
imageUrl?: string | null;
|
|
linkUrl?: string | null;
|
|
}
|
|
|
|
interface Props {
|
|
slides: HomeSlide[];
|
|
onSearch: (searchTerm: string, locationTerm: string) => void;
|
|
}
|
|
|
|
const HomeSlider = ({ slides, onSearch }: Props) => {
|
|
const [current, setCurrent] = useState(0);
|
|
const [searchTerm, setSearchTerm] = useState('');
|
|
const [locationTerm, setLocationTerm] = useState('');
|
|
|
|
// Auto-slide every 6 seconds
|
|
useEffect(() => {
|
|
if (slides.length <= 1) return;
|
|
const interval = setInterval(() => {
|
|
setCurrent((prev) => (prev === slides.length - 1 ? 0 : prev + 1));
|
|
}, 6000);
|
|
return () => clearInterval(interval);
|
|
}, [slides.length]);
|
|
|
|
const handleSearchSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
onSearch(searchTerm, locationTerm);
|
|
};
|
|
|
|
// If no slides, show default hero
|
|
if (slides.length === 0) {
|
|
return (
|
|
<div className="relative bg-dark-900 overflow-hidden min-h-[600px] flex items-center">
|
|
<div className="absolute inset-0 opacity-40">
|
|
<img
|
|
src="https://images.unsplash.com/photo-1522071820081-009f0129c71c?ixlib=rb-4.0.3&auto=format&fit=crop&w=1740&q=80"
|
|
className="w-full h-full object-cover"
|
|
alt="Default Banner"
|
|
/>
|
|
</div>
|
|
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24 lg:py-32 w-full">
|
|
<div className="max-w-3xl">
|
|
<h1 className="text-4xl md:text-6xl font-serif font-bold text-white mb-6 tracking-tight animate-in fade-in slide-in-from-left-4 duration-700">
|
|
Boostez votre visibilité dans <br />
|
|
<span className="text-brand-500">l'écosystème africain</span>
|
|
</h1>
|
|
<p className="text-xl text-gray-300 mb-8 max-w-2xl animate-in fade-in slide-in-from-left-4 duration-700 delay-150">
|
|
L'annuaire 2.0 qui connecte les talents, les entrepreneurs et les entreprises de la diaspora et du continent.
|
|
</p>
|
|
<SearchForm
|
|
searchTerm={searchTerm}
|
|
setSearchTerm={setSearchTerm}
|
|
locationTerm={locationTerm}
|
|
setLocationTerm={setLocationTerm}
|
|
onSubmit={handleSearchSubmit}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="relative h-[650px] md:h-[700px] overflow-hidden bg-black">
|
|
{slides.map((slide, index) => (
|
|
<div
|
|
key={slide.id}
|
|
className={`absolute inset-0 transition-opacity duration-1000 ease-in-out ${
|
|
index === current ? 'opacity-100 z-10' : 'opacity-0 z-0'
|
|
}`}
|
|
>
|
|
{/* Background Image */}
|
|
<div className="absolute inset-0">
|
|
<img
|
|
src={slide.imageUrl || slide.business?.coverUrl || slide.business?.logoUrl || ''}
|
|
className={`w-full h-full object-cover transition-transform duration-[6000ms] ease-linear ${
|
|
index === current ? 'scale-110' : 'scale-100'
|
|
}`}
|
|
alt=""
|
|
/>
|
|
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/40 to-transparent" />
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="relative h-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex items-center">
|
|
<div className={`max-w-3xl transition-all duration-700 delay-300 ${
|
|
index === current ? 'translate-x-0 opacity-100' : '-translate-x-10 opacity-0'
|
|
}`}>
|
|
{slide.type === 'BUSINESS' && (
|
|
<div className="inline-flex items-center gap-2 px-3 py-1 bg-brand-600/20 border border-brand-500/30 rounded-full mb-6">
|
|
<span className="w-2 h-2 bg-brand-500 rounded-full animate-pulse" />
|
|
<span className="text-brand-400 text-xs font-bold uppercase tracking-widest">Entreprise à la une</span>
|
|
</div>
|
|
)}
|
|
|
|
<h1 className="text-4xl md:text-6xl font-serif font-bold text-white mb-6 tracking-tight leading-tight">
|
|
{slide.type === 'BUSINESS' ? (
|
|
<>Découvrez <span className="text-brand-500">{slide.business?.name}</span></>
|
|
) : (
|
|
slide.title
|
|
)}
|
|
</h1>
|
|
|
|
<p className="text-xl text-gray-300 mb-8 max-w-2xl line-clamp-3">
|
|
{slide.type === 'BUSINESS' ? (
|
|
slide.business?.description
|
|
) : (
|
|
slide.subtitle
|
|
)}
|
|
</p>
|
|
|
|
<div className="flex flex-wrap gap-4 items-center">
|
|
<Link
|
|
href={slide.type === 'BUSINESS' ? `/annuaire/${slide.business?.id}` : (slide.linkUrl || '#')}
|
|
className="inline-flex items-center gap-2 bg-brand-600 text-white px-8 py-4 rounded-lg font-bold hover:bg-brand-700 transition-all shadow-lg shadow-brand-600/20 hover:scale-105"
|
|
>
|
|
{slide.type === 'BUSINESS' ? 'Voir la fiche' : 'En savoir plus'}
|
|
<ArrowRight className="w-5 h-5" />
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
|
|
{/* Static Search Overlay (Always visible on top of slides) */}
|
|
<div className="absolute bottom-12 md:bottom-20 left-0 right-0 z-20">
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<SearchForm
|
|
searchTerm={searchTerm}
|
|
setSearchTerm={setSearchTerm}
|
|
locationTerm={locationTerm}
|
|
setLocationTerm={setLocationTerm}
|
|
onSubmit={handleSearchSubmit}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Navigation Arrows */}
|
|
{slides.length > 1 && (
|
|
<>
|
|
<button
|
|
onClick={() => setCurrent(current === 0 ? slides.length - 1 : current - 1)}
|
|
className="absolute left-4 top-1/2 -translate-y-1/2 z-30 p-3 rounded-full bg-white/10 text-white hover:bg-white/20 transition-all backdrop-blur-sm"
|
|
>
|
|
<ChevronLeft className="w-6 h-6" />
|
|
</button>
|
|
<button
|
|
onClick={() => setCurrent(current === slides.length - 1 ? 0 : current + 1)}
|
|
className="absolute right-4 top-1/2 -translate-y-1/2 z-30 p-3 rounded-full bg-white/10 text-white hover:bg-white/20 transition-all backdrop-blur-sm"
|
|
>
|
|
<ChevronRight className="w-6 h-6" />
|
|
</button>
|
|
|
|
{/* Indicators */}
|
|
<div className="absolute bottom-6 left-1/2 -translate-x-1/2 z-30 flex gap-2">
|
|
{slides.map((_, i) => (
|
|
<button
|
|
key={i}
|
|
onClick={() => setCurrent(i)}
|
|
className={`w-2.5 h-2.5 rounded-full transition-all ${
|
|
i === current ? 'bg-brand-500 w-8' : 'bg-white/40'
|
|
}`}
|
|
/>
|
|
))}
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const SearchForm = ({ searchTerm, setSearchTerm, locationTerm, setLocationTerm, onSubmit }: any) => (
|
|
<form onSubmit={onSubmit} className="max-w-4xl bg-white/95 backdrop-blur-md p-2 rounded-xl shadow-2xl flex flex-col md:flex-row gap-2 border border-white/20 animate-in fade-in slide-in-from-bottom-4 duration-700 delay-500">
|
|
<div className="flex-1 relative">
|
|
<Search className="absolute left-3 top-3.5 text-gray-400 w-5 h-5" />
|
|
<input
|
|
type="text"
|
|
placeholder="Que recherchez-vous ? (ex: Traiteur, Photographe...)"
|
|
className="w-full pl-10 pr-4 py-3.5 rounded-lg focus:outline-none text-gray-900 font-medium"
|
|
value={searchTerm}
|
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
/>
|
|
</div>
|
|
<div className="md:w-1/3 relative border-t md:border-t-0 md:border-l border-gray-100">
|
|
<MapPin className="absolute left-3 top-3.5 text-gray-400 w-5 h-5" />
|
|
<input
|
|
type="text"
|
|
placeholder="Où ? (ex: Dakar)"
|
|
className="w-full pl-10 pr-4 py-3.5 rounded-lg focus:outline-none text-gray-900 font-medium"
|
|
value={locationTerm}
|
|
onChange={(e) => setLocationTerm(e.target.value)}
|
|
/>
|
|
</div>
|
|
<button type="submit" className="bg-brand-600 text-white px-10 py-3.5 rounded-lg font-bold hover:bg-brand-700 transition-all hover:scale-105 active:scale-95 shadow-lg shadow-brand-600/30">
|
|
Rechercher
|
|
</button>
|
|
</form>
|
|
);
|
|
|
|
export default HomeSlider;
|