"use client"; import React, { useEffect, useMemo, useState } from 'react'; import Link from 'next/link'; import { useParams, useRouter } from 'next/navigation'; import { ArrowLeft, MapPin, Globe, Mail, Phone, CheckCircle, Star, Facebook, Linkedin, Instagram, Twitter, Play, Share2, Send, Award, Quote, MessageCircle, Lock, Loader2, Link2, ShoppingBag } from 'lucide-react'; import { Business, OfferType, Rating } from '../../../types'; import { toast } from 'react-hot-toast'; import { useUser } from '../../../components/UserProvider'; const BusinessDetailPage = () => { const { id } = useParams(); const router = useRouter(); const { user } = useUser(); const [business, setBusiness] = useState(null); const [loading, setLoading] = useState(true); const [existingConversationId, setExistingConversationId] = useState(null); const [isShareOpen, setIsShareOpen] = useState(false); const [selectedOffer, setSelectedOffer] = useState(null); const [userRating, setUserRating] = useState(null); const [hoverRating, setHoverRating] = useState(null); const [isRating, setIsRating] = useState(false); const [ratings, setRatings] = useState([]); const [reviewComment, setReviewComment] = useState(''); const [userRatingStatus, setUserRatingStatus] = useState<'PENDING' | 'APPROVED' | 'REJECTED' | null>(null); const [loadingRatings, setLoadingRatings] = useState(false); const [replyingTo, setReplyingTo] = useState(null); const [replyText, setReplyText] = useState(''); const [isSubmittingReply, setIsSubmittingReply] = useState(false); const isOwner = user && business && user.id === business.ownerId; const contactRef = React.useRef(null); const [contactForm, setContactForm] = useState({ name: '', email: '', message: '' }); const [formStatus, setFormStatus] = useState<'idle' | 'sending' | 'success'>('idle'); const handleOrder = (offer: any) => { setContactForm(prev => ({ ...prev, message: `Bonjour, je souhaiterais commander ou avoir plus d'informations sur votre offre : "${offer.title}". Pouvez-vous me recontacter ?` })); setSelectedOffer(null); // Scroll to contact form contactRef.current?.scrollIntoView({ behavior: 'smooth' }); // Focus textarea after scroll setTimeout(() => { const textarea = document.getElementById('contact-message'); if (textarea) textarea.focus(); }, 800); toast.success("Message préparé ! Vous pouvez l'envoyer ci-dessous."); }; const businessOffers = useMemo(() => { if (!business) return []; if (business.offers && business.offers.length > 0) return business.offers; return []; }, [business]); useEffect(() => { window.scrollTo(0, 0); const loadBusiness = async () => { setLoading(true); // Sync with DB directly // 2. Try API (Database) try { const res = await fetch(`/api/businesses/${id}`); if (res.ok) { const data = await res.json(); setBusiness(data); // 3. Track view event // Only track if not owner const currentUserStr = localStorage.getItem('afro_user'); const currentUser = currentUserStr ? JSON.parse(currentUserStr) : null; if (!currentUser || currentUser.id !== data.ownerId) { fetch('/api/analytics', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 'BUSINESS_VIEW', path: window.location.pathname, label: data.name, metadata: { businessId: data.id, businessName: data.name, userId: currentUser?.id || 'anonymous' } }) }).catch(console.error); } // Also increment views count in DB (Legacy support) fetch(`/api/businesses/${id}/view`, { method: 'POST' }).catch(console.error); } else { setBusiness(null); } } catch (err) { console.error("Error fetching business:", err); setBusiness(null); } finally { setLoading(false); } // 4. Fetch user rating if logged in if (user) { try { const ratingRes = await fetch(`/api/businesses/${id}/rating/me`, { headers: { 'x-user-id': user.id } }); const ratingData = await ratingRes.json(); if (ratingData.rating) { setUserRating(ratingData.rating); setReviewComment(ratingData.comment || ''); setUserRatingStatus(ratingData.status); } } catch (e) { console.error('Error fetching user rating:', e); } } // 5. Fetch all ratings try { setLoadingRatings(true); const ratingsRes = await fetch(`/api/businesses/${id}/ratings`); if (ratingsRes.ok) { const ratingsData = await ratingsRes.json(); setRatings(ratingsData); } } catch (e) { console.error('Error fetching ratings list:', e); } finally { setLoadingRatings(false); } }; if (id) loadBusiness(); }, [id]); useEffect(() => { const checkExistingConversation = async () => { if (!user || user.id === 'undefined' || !business?.id) return; try { const res = await fetch(`/api/conversations?businessId=${business.id}`, { headers: { 'x-user-id': user.id } }); const data = await res.json(); if (Array.isArray(data) && data.length > 0) { setExistingConversationId(data[0].id); } } catch (error) { console.error('Error checking existing conversation:', error); } }; if (user && business) checkExistingConversation(); }, [user, business]); if (loading) { return (

Chargement du profil...

); } if (!business) { return (

Entreprise introuvable

Retour à l'annuaire
); } // Helper to get YouTube embed URL const getEmbedUrl = (url: string) => { const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/; const match = url.match(regExp); return (match && match[2].length === 11) ? `https://www.youtube.com/embed/${match[2]}` : null; }; const videoEmbedUrl = business.videoUrl ? getEmbedUrl(business.videoUrl) : null; const handleTrackEvent = async (type: string, label?: string) => { if (!business?.id) return; // Don't track if the current user is the owner if (user && user.id === business.ownerId) return; try { fetch('/api/analytics', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type, path: window.location.pathname, label: label || business.name, metadata: { businessId: business.id, businessName: business.name, userId: user?.id || 'anonymous' } }) }); } catch (e) { console.error('Analytics error:', e); } }; const handleContactSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!user) { toast.error('Veuillez vous connecter pour envoyer un message'); router.push('/login'); return; } setFormStatus('sending'); try { const res = await fetch('/api/messages', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-user-id': user.id }, body: JSON.stringify({ businessId: business.id, content: contactForm.message }) }); const data = await res.json(); if (!data.error) { // Track contact event handleTrackEvent('BUSINESS_CONTACT_CLICK', 'Message Sent'); setFormStatus('success'); toast.success('Message envoyé ! Redirection vers votre messagerie...'); setContactForm({ ...contactForm, message: '' }); setTimeout(() => { router.push('/dashboard?view=messages'); // Hypothetical view param handle later }, 2000); } else { toast.error(data.error); setFormStatus('idle'); } } catch (error) { toast.error("Erreur lors de l'envoi"); setFormStatus('idle'); } }; const handleShare = (platform: string) => { if (!business) return; const url = encodeURIComponent(window.location.href); const text = encodeURIComponent(`Découvrez ${business.name} sur Afrohub`); let shareLink = ''; switch(platform) { case 'facebook': shareLink = `https://www.facebook.com/sharer/sharer.php?u=${url}`; break; case 'twitter': shareLink = `https://twitter.com/intent/tweet?url=${url}&text=${text}`; break; case 'linkedin': shareLink = `https://www.linkedin.com/sharing/share-offsite/?url=${url}`; break; case 'instagram': toast("Pour partager sur Instagram, copiez le lien ou faites une capture d'écran.", { icon: '📸' }); setIsShareOpen(false); return; case 'copy': navigator.clipboard.writeText(window.location.href); toast.success("Lien copié dans le presse-papier !"); setIsShareOpen(false); return; } if (shareLink) { window.open(shareLink, '_blank', 'width=600,height=400'); } setIsShareOpen(false); }; const handleRate = async (value: number) => { if (!user) { toast.error('Connectez-vous pour voter'); router.push('/login'); return; } if (isOwner) { toast.error("Vous ne pouvez pas noter votre propre entreprise"); return; } if (isRating) return; setIsRating(true); try { const res = await fetch(`/api/businesses/${id}/rate`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-user-id': user.id }, body: JSON.stringify({ value, comment: reviewComment }) }); const data = await res.json(); if (!data.error) { setUserRating(value); setBusiness(prev => prev ? { ...prev, rating: data.rating, ratingCount: data.ratingCount } : null); toast.success('Merci pour votre vote !'); handleTrackEvent('BUSINESS_RATING_CAST', `Stars: ${value}`); // Refresh ratings list const ratingsRes = await fetch(`/api/businesses/${id}/ratings`); if (ratingsRes.ok) { const ratingsData = await ratingsRes.json(); setRatings(ratingsData); } } else { toast.error(data.error); } } catch (error) { toast.error("Erreur lors de l'enregistrement du vote"); } finally { setIsRating(false); } }; const handleReply = async (ratingId: string) => { if (!user || !isOwner) return; if (!replyText.trim()) { toast.error("La réponse ne peut pas être vide"); return; } setIsSubmittingReply(true); try { const res = await fetch(`/api/ratings/${ratingId}/reply`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-user-id': user.id }, body: JSON.stringify({ reply: replyText }) }); if (res.ok) { toast.success("Réponse publiée !"); setReplyingTo(null); setReplyText(''); // Refresh ratings const ratingsRes = await fetch(`/api/businesses/${id}/ratings`); if (ratingsRes.ok) { const ratingsData = await ratingsRes.json(); setRatings(ratingsData); } } else { const data = await res.json(); toast.error(data.error || "Erreur lors de la réponse"); } } catch (e) { toast.error("Erreur de connexion"); } finally { setIsSubmittingReply(false); } }; return (
{/* Header Banner */}
{business.coverUrl ? ( Couverture ) : (
)}
{/* Profile Header */}
{business.name} {business.isFeatured && (
)}

{business.name}

{business.verified && }
{business.category}
{business.isFeatured && (
AFROSHINE
)}
{isShareOpen && (
setIsShareOpen(false)} >
Partager la fiche
)}
Contacter
{business.location}
{[1, 2, 3, 4, 5].map((star) => ( ))}
{business.rating.toFixed(1)} ({(business as any).ratingCount || 0} votes • {business.viewCount} vues)
{business.tags.map(tag => ( #{tag} ))}
{/* Left Column: Main Content */}
{/* Presentation */}

À propos

{business.description}

{/* Founder Section (Specifically for Featured or if data exists) */} {(business.founderName || business.founderImageUrl) && (

Le Fondateur

{business.founderImageUrl && ( {business.founderName} )}

{business.founderName}

CEO & Fondateur

"Notre mission est d'apporter des solutions concrètes et adaptées aux réalités locales, tout en visant l'excellence internationale."

{business.keyMetric && (
🚀 {business.keyMetric}
)}
)} {/* Video */} {videoEmbedUrl && (

Présentation Vidéo

)} {/* Offers */} {businessOffers.length > 0 && (

Produits & Services

{businessOffers.map(offer => (
setSelectedOffer(offer)} className="border border-gray-200 rounded-lg overflow-hidden hover:shadow-md transition-all cursor-pointer group hover:scale-[1.01]" >
{offer.title} {offer.type === OfferType.PRODUCT ? 'Produit' : 'Service'}

{offer.title}

{new Intl.NumberFormat('fr-FR').format(offer.price)} {offer.currency}

))}
)} {/* Reviews Section */}

Avis & Témoignages ({ratings.length} avis)

{[1, 2, 3, 4, 5].map((s) => ( ))}
{business.rating.toFixed(1)}
{/* Leave a Review Form */}

{isOwner ? "Votre Fiche" : (userRating ? "Modifier votre avis" : "Laisser un avis")}

{isOwner ? (

Vous consultez votre propre fiche. Vous pouvez répondre aux avis des clients ci-dessous.

) : (
{[1, 2, 3, 4, 5].map((star) => ( ))} {userRating && Vous avez mis {userRating} étoiles}
{userRatingStatus === 'PENDING' && (

Votre avis est reçu ! Il est actuellement en cours de modération par notre équipe pour assurer la qualité des retours.

)} {userRating && userRatingStatus !== 'PENDING' &&

Conformément à nos règles, la note peut uniquement être réévaluée à la hausse.

}