- Migration du blog et des interviews des données mockées vers PostgreSQL (Prisma). - Refonte des pages publiques en Server Components pour de meilleures performances/SEO. - Intégration d'un éditeur de texte riche (React Quill) avec titres H1-H6 et séparateurs. - Correction des erreurs de validation Prisma liées aux paramètres asynchrones (Next.js 15+). - Correction des problèmes d'affichage des modales de suspension via React Portals. - Installation de @tailwindcss/typography et correction du débordement de texte (break-words). - Implémentation des actions de modération (suspension/révocation) pour les utilisateurs et boutiques.
553 lines
32 KiB
TypeScript
553 lines
32 KiB
TypeScript
"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, Play, Share2, Send, Award, Quote, MessageCircle, Lock, Loader2 } from 'lucide-react';
|
|
import { MOCK_BUSINESSES, MOCK_OFFERS } from '../../../lib/mockData';
|
|
import { Business, OfferType } 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<Business | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [existingConversationId, setExistingConversationId] = useState<string | null>(null);
|
|
|
|
const [contactForm, setContactForm] = useState({
|
|
name: '',
|
|
email: '',
|
|
message: ''
|
|
});
|
|
const [formStatus, setFormStatus] = useState<'idle' | 'sending' | 'success'>('idle');
|
|
|
|
const businessOffers = useMemo(() => {
|
|
if (!business) return [];
|
|
// If the business has its own offers (from DB), use them
|
|
if (business.offers && business.offers.length > 0) return business.offers;
|
|
// Otherwise fallback to mock offers for mock businesses
|
|
return MOCK_OFFERS.filter(o => o.businessId === id && o.active);
|
|
}, [business, id]);
|
|
|
|
useEffect(() => {
|
|
window.scrollTo(0, 0);
|
|
|
|
const loadBusiness = async () => {
|
|
setLoading(true);
|
|
|
|
// 1. Try Mock Data First (Instant)
|
|
const mock = MOCK_BUSINESSES.find(b => b.id === id);
|
|
if (mock) {
|
|
setBusiness(mock);
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
};
|
|
|
|
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 (
|
|
<div className="min-h-[60vh] flex flex-col items-center justify-center">
|
|
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-brand-600 mb-4"></div>
|
|
<p className="text-gray-500">Chargement du profil...</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!business) {
|
|
return (
|
|
<div className="min-h-[60vh] flex flex-col items-center justify-center">
|
|
<h2 className="text-2xl font-bold text-gray-900 mb-4">Entreprise introuvable</h2>
|
|
<Link href="/annuaire" className="text-brand-600 hover:text-brand-700 font-medium flex items-center">
|
|
<ArrowLeft className="w-4 h-4 mr-2" /> Retour à l'annuaire
|
|
</Link>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// 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');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="bg-gray-50 min-h-screen pb-12">
|
|
{/* Header Banner */}
|
|
<div className="h-48 md:h-64 bg-gradient-to-r from-gray-900 to-gray-800 relative overflow-hidden">
|
|
<div className="absolute inset-0 opacity-20 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')]"></div>
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-full flex flex-col justify-between py-6">
|
|
<Link href="/annuaire" className="inline-flex items-center text-white/80 hover:text-white transition-colors w-fit">
|
|
<ArrowLeft className="w-4 h-4 mr-2" /> Retour
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 -mt-20 relative z-10">
|
|
<div className="bg-white rounded-xl shadow-lg border border-gray-100 overflow-hidden">
|
|
<div className="p-6 md:p-8">
|
|
{/* Profile Header */}
|
|
<div className="flex flex-col md:flex-row gap-6 items-start">
|
|
<div className="w-32 h-32 md:w-40 md:h-40 rounded-xl bg-white p-1 shadow-md -mt-16 md:-mt-24 border border-gray-100 flex-shrink-0 relative">
|
|
<img src={business.logoUrl} alt={business.name} className="w-full h-full object-cover rounded-lg bg-gray-50" />
|
|
{business.isFeatured && (
|
|
<div className="absolute -top-3 -right-3 bg-yellow-400 text-yellow-900 rounded-full p-2 shadow-md" title="Entrepreneur du Mois">
|
|
<Award className="w-6 h-6" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex-1 w-full">
|
|
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
|
|
<div>
|
|
<div className="flex items-center gap-2">
|
|
<h1 className="text-3xl font-serif font-bold text-gray-900 flex items-center gap-2">
|
|
{business.name}
|
|
</h1>
|
|
{business.verified && <span title="Entreprise Vérifiée"><CheckCircle className="w-6 h-6 text-blue-500" /></span>}
|
|
</div>
|
|
<div className="text-brand-600 font-medium mt-1 uppercase tracking-wide text-sm">{business.category}</div>
|
|
{business.isFeatured && (
|
|
<div className="inline-flex items-center px-2 py-1 rounded bg-yellow-100 text-yellow-800 text-xs font-bold mt-2">
|
|
<Award className="w-3 h-3 mr-1" /> ENTREPRENEUR DU MOIS
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="flex gap-3">
|
|
<button className="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
|
|
<Share2 className="w-4 h-4 mr-2" /> Partager
|
|
</button>
|
|
<a href="#contact-form" className="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-brand-600 hover:bg-brand-700">
|
|
Contacter
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap gap-4 mt-6 text-sm text-gray-500 border-t border-gray-100 pt-4">
|
|
<div className="flex items-center">
|
|
<MapPin className="w-4 h-4 mr-1 text-gray-400" />
|
|
{business.location}
|
|
</div>
|
|
<div className="flex items-center">
|
|
<Star className="w-4 h-4 mr-1 text-yellow-400 fill-current" />
|
|
<span className="font-bold text-gray-700 mr-1">{business.rating}</span>
|
|
({business.viewCount} vues)
|
|
</div>
|
|
{business.tags.map(tag => (
|
|
<span key={tag} className="bg-gray-100 text-gray-600 px-2 py-1 rounded text-xs">#{tag}</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 mt-8">
|
|
{/* Left Column: Main Content */}
|
|
<div className="lg:col-span-2 space-y-8">
|
|
|
|
{/* Presentation */}
|
|
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6 md:p-8">
|
|
<h2 className="text-xl font-bold font-serif text-gray-900 mb-4">À propos</h2>
|
|
<p className="text-gray-600 leading-relaxed whitespace-pre-line">
|
|
{business.description}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Founder Section (Specifically for Featured or if data exists) */}
|
|
{(business.founderName || business.founderImageUrl) && (
|
|
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6 md:p-8 overflow-hidden relative">
|
|
<div className="absolute top-0 right-0 p-4 opacity-10">
|
|
<Quote className="w-24 h-24 text-brand-500" />
|
|
</div>
|
|
<h2 className="text-xl font-bold font-serif text-gray-900 mb-6 relative z-10">Le Fondateur</h2>
|
|
<div className="flex flex-col sm:flex-row gap-6 items-center sm:items-start relative z-10">
|
|
{business.founderImageUrl && (
|
|
<img
|
|
src={business.founderImageUrl}
|
|
alt={business.founderName}
|
|
className="w-32 h-32 rounded-full object-cover border-4 border-gray-50 shadow-md"
|
|
/>
|
|
)}
|
|
<div>
|
|
<h3 className="text-lg font-bold text-gray-900">{business.founderName}</h3>
|
|
<p className="text-brand-600 text-sm font-medium mb-3">CEO & Fondateur</p>
|
|
<p className="text-gray-600 italic">
|
|
"Notre mission est d'apporter des solutions concrètes et adaptées aux réalités locales, tout en visant l'excellence internationale."
|
|
</p>
|
|
{business.keyMetric && (
|
|
<div className="mt-4 inline-block bg-brand-50 text-brand-700 px-3 py-1 rounded-full text-xs font-bold">
|
|
🚀 {business.keyMetric}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Video */}
|
|
{videoEmbedUrl && (
|
|
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6 md:p-8">
|
|
<h2 className="text-xl font-bold font-serif text-gray-900 mb-4 flex items-center">
|
|
<Play className="w-5 h-5 mr-2 text-brand-600" /> Présentation Vidéo
|
|
</h2>
|
|
<div className="aspect-w-16 aspect-h-9 bg-gray-100 rounded-lg overflow-hidden">
|
|
<iframe
|
|
src={videoEmbedUrl}
|
|
title={`Présentation de ${business.name}`}
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
allowFullScreen
|
|
className="w-full h-64 md:h-96 rounded-lg"
|
|
></iframe>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Offers */}
|
|
{businessOffers.length > 0 && (
|
|
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6 md:p-8">
|
|
<h2 className="text-xl font-bold font-serif text-gray-900 mb-6">Produits & Services</h2>
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
|
{businessOffers.map(offer => (
|
|
<div key={offer.id} className="border border-gray-200 rounded-lg overflow-hidden hover:shadow-md transition-shadow">
|
|
<div className="h-40 bg-gray-100 relative">
|
|
<img src={offer.imageUrl} alt={offer.title} className="w-full h-full object-cover" />
|
|
<span className={`absolute top-2 right-2 px-2 py-1 text-xs font-bold rounded uppercase ${offer.type === OfferType.PRODUCT ? 'bg-blue-100 text-blue-800' : 'bg-purple-100 text-purple-800'}`}>
|
|
{offer.type === OfferType.PRODUCT ? 'Produit' : 'Service'}
|
|
</span>
|
|
</div>
|
|
<div className="p-4">
|
|
<h3 className="font-bold text-gray-900 line-clamp-1">{offer.title}</h3>
|
|
<p className="text-brand-600 font-bold mt-1">
|
|
{new Intl.NumberFormat('fr-FR').format(offer.price)} {offer.currency}
|
|
</p>
|
|
<button className="mt-3 w-full block text-center bg-gray-50 text-gray-700 py-2 rounded text-sm font-medium hover:bg-gray-100 transition-colors">
|
|
Commander
|
|
</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Right Column: Sidebar */}
|
|
<div className="space-y-8">
|
|
{/* Contact Card */}
|
|
<div id="contact-form" className="bg-white rounded-xl shadow-sm border border-gray-100 p-6 sticky top-24">
|
|
<h3 className="text-lg font-bold text-gray-900 mb-4">Contact</h3>
|
|
|
|
<div className="space-y-4 mb-6">
|
|
<div className="flex items-center text-gray-600">
|
|
<div className="w-8 h-8 rounded-full bg-brand-50 flex items-center justify-center mr-3 text-brand-600 flex-shrink-0">
|
|
<MapPin className="w-4 h-4" />
|
|
</div>
|
|
<span className="text-sm">{business.location}</span>
|
|
</div>
|
|
{business.showEmail && (
|
|
<a
|
|
href={`mailto:${business.contactEmail}`}
|
|
onClick={() => handleTrackEvent('BUSINESS_CONTACT_CLICK', 'Email')}
|
|
className="flex items-center text-gray-600 hover:text-brand-600 transition-colors"
|
|
>
|
|
<div className="w-8 h-8 rounded-full bg-brand-50 flex items-center justify-center mr-3 text-brand-600 flex-shrink-0">
|
|
<Mail className="w-4 h-4" />
|
|
</div>
|
|
<span className="text-sm truncate">{business.contactEmail}</span>
|
|
</a>
|
|
)}
|
|
{(business.contactPhone && business.showPhone) && (
|
|
<a
|
|
href={`tel:${business.contactPhone}`}
|
|
onClick={() => handleTrackEvent('BUSINESS_CONTACT_CLICK', 'Phone')}
|
|
className="flex items-center text-gray-600 hover:text-brand-600 transition-colors"
|
|
>
|
|
<div className="w-8 h-8 rounded-full bg-brand-50 flex items-center justify-center mr-3 text-brand-600 flex-shrink-0">
|
|
<Phone className="w-4 h-4" />
|
|
</div>
|
|
<span className="text-sm">{business.contactPhone}</span>
|
|
</a>
|
|
)}
|
|
{(business.websiteUrl || business.socialLinks?.website) && (
|
|
<a
|
|
href={business.websiteUrl || business.socialLinks?.website}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
onClick={() => handleTrackEvent('BUSINESS_CONTACT_CLICK', 'Website')}
|
|
className="flex items-center text-gray-600 hover:text-brand-600 transition-colors"
|
|
>
|
|
<div className="w-8 h-8 rounded-full bg-brand-50 flex items-center justify-center mr-3 text-brand-600 flex-shrink-0">
|
|
<Globe className="w-4 h-4" />
|
|
</div>
|
|
<span className="text-sm truncate">Site Web</span>
|
|
</a>
|
|
)}
|
|
</div>
|
|
|
|
{/* Contact Section */}
|
|
<div className="border-t border-gray-100 pt-6">
|
|
<h4 className="text-sm font-semibold text-gray-900 mb-4 flex items-center gap-2">
|
|
<MessageCircle className="w-4 h-4 text-brand-600" />
|
|
Discuter avec l'entrepreneur
|
|
</h4>
|
|
|
|
{existingConversationId ? (
|
|
<div className="space-y-4">
|
|
<p className="text-sm text-gray-500 text-center bg-brand-50 p-4 rounded-xl border border-brand-100">
|
|
Vous avez déjà une discussion en cours avec cet entrepreneur.
|
|
</p>
|
|
<Link
|
|
href={`/dashboard?view=messages&chatId=${existingConversationId}`}
|
|
className="w-full flex justify-center items-center gap-2 px-4 py-3 border border-transparent text-sm font-bold rounded-xl text-white bg-brand-600 hover:bg-brand-700 shadow-lg transition-all transform active:scale-[0.98]"
|
|
>
|
|
<MessageCircle className="w-5 h-5" />
|
|
Continuer la discussion
|
|
</Link>
|
|
</div>
|
|
) : formStatus === 'success' ? (
|
|
<div className="bg-green-50 border border-green-200 text-green-700 px-4 py-8 rounded-xl text-center">
|
|
<CheckCircle className="w-10 h-10 mx-auto mb-2 text-green-500" />
|
|
<h4 className="font-bold">Message envoyé !</h4>
|
|
<p className="text-sm">Redirection vers votre messagerie...</p>
|
|
</div>
|
|
) : (
|
|
<form onSubmit={handleContactSubmit} className="space-y-4">
|
|
<div className="relative">
|
|
<textarea
|
|
placeholder={user ? "Bonjour, j'aimerais en savoir plus sur..." : "Connectez-vous pour envoyer un message"}
|
|
required
|
|
rows={4}
|
|
disabled={!user || formStatus === 'sending'}
|
|
className="w-full px-4 py-3 bg-gray-50 border border-transparent rounded-xl text-sm focus:bg-white focus:ring-2 focus:ring-brand-500 outline-none transition-all resize-none disabled:opacity-60 shadow-inner"
|
|
value={contactForm.message}
|
|
onChange={(e) => setContactForm({ ...contactForm, message: e.target.value })}
|
|
/>
|
|
{!user && (
|
|
<div className="absolute inset-0 bg-white/20 backdrop-blur-[1px] rounded-xl flex items-center justify-center">
|
|
<Lock className="w-5 h-5 text-gray-400" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={!user || formStatus === 'sending' || !contactForm.message.trim()}
|
|
className="w-full flex justify-center items-center gap-2 px-4 py-3 border border-transparent text-sm font-bold rounded-xl text-white bg-brand-600 hover:bg-brand-700 shadow-lg transition-all disabled:opacity-50 disabled:grayscale transform active:scale-[0.98]"
|
|
>
|
|
{formStatus === 'sending' ? (
|
|
<Loader2 className="w-5 h-5 animate-spin text-white" />
|
|
) : (
|
|
<>
|
|
{user ? <Send className="w-4 h-4" /> : <Lock className="w-4 h-4" />}
|
|
{user ? "Lancer la discussion" : "Action réservée aux membres"}
|
|
</>
|
|
)}
|
|
</button>
|
|
|
|
{!user && (
|
|
<p className="text-[10px] text-center text-gray-400">
|
|
Inscrivez-vous gratuitement pour contacter les entrepreneurs.
|
|
</p>
|
|
)}
|
|
</form>
|
|
)}
|
|
</div>
|
|
|
|
<div className="mt-6 pt-6 border-t border-gray-100">
|
|
<div className="flex justify-center space-x-6">
|
|
{business.showSocials && (
|
|
<>
|
|
{business.socialLinks?.facebook && (
|
|
<a href={business.socialLinks.facebook} target="_blank" rel="noopener noreferrer" onClick={() => handleTrackEvent('BUSINESS_CONTACT_CLICK', 'Facebook')} className="text-gray-400 hover:text-[#1877F2] transition-colors">
|
|
<Facebook className="w-5 h-5" />
|
|
</a>
|
|
)}
|
|
{business.socialLinks?.linkedin && (
|
|
<a href={business.socialLinks.linkedin} target="_blank" rel="noopener noreferrer" onClick={() => handleTrackEvent('BUSINESS_CONTACT_CLICK', 'LinkedIn')} className="text-gray-400 hover:text-[#0A66C2] transition-colors">
|
|
<Linkedin className="w-5 h-5" />
|
|
</a>
|
|
)}
|
|
{business.socialLinks?.instagram && (
|
|
<a href={business.socialLinks.instagram} target="_blank" rel="noopener noreferrer" onClick={() => handleTrackEvent('BUSINESS_CONTACT_CLICK', 'Instagram')} className="text-gray-400 hover:text-[#E4405F] transition-colors">
|
|
<Instagram className="w-5 h-5" />
|
|
</a>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Similar Businesses (Mock) */}
|
|
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
|
<h3 className="text-lg font-bold text-gray-900 mb-4">Dans la même catégorie</h3>
|
|
<div className="space-y-4">
|
|
{MOCK_BUSINESSES
|
|
.filter(b => b.category === business.category && b.id !== business.id)
|
|
.slice(0, 3)
|
|
.map(similar => (
|
|
<Link key={similar.id} href={`/annuaire/${similar.id}`} className="flex items-center group">
|
|
<div className="w-12 h-12 rounded bg-gray-100 overflow-hidden flex-shrink-0">
|
|
<img src={similar.logoUrl} alt={similar.name} className="w-full h-full object-cover" />
|
|
</div>
|
|
<div className="ml-3 overflow-hidden">
|
|
<p className="text-sm font-medium text-gray-900 truncate group-hover:text-brand-600 transition-colors">{similar.name}</p>
|
|
<p className="text-xs text-gray-500 truncate">{similar.location}</p>
|
|
</div>
|
|
</Link>
|
|
))
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default BusinessDetailPage;
|