feat: implement business detail client view with interactive features including rating, favoriting, messaging, and analytics tracking

This commit is contained in:
2026-05-09 21:12:01 +02:00
parent 6a42c52b58
commit 6acd5ebe2e
14 changed files with 516 additions and 40 deletions

View File

@@ -439,9 +439,206 @@ const BusinessDetailClient = ({ initialBusiness }: Props) => {
</div>
</div>
)}
{/* Avis et Commentaires */}
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6 md:p-8">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center mb-8 gap-4">
<h2 className="text-xl font-bold font-serif text-gray-900 flex items-center">
<Star className="w-5 h-5 mr-2 text-yellow-400 fill-current" /> Avis et Commentaires
</h2>
{!isOwner && (
<div className="flex flex-col items-end gap-2">
<div className="flex items-center gap-1">
{[1, 2, 3, 4, 5].map((star) => (
<button
key={star}
onMouseEnter={() => setHoverRating(star)}
onMouseLeave={() => setHoverRating(null)}
onClick={() => handleRate(star)}
className="transition-transform active:scale-125 focus:outline-none"
>
<Star
className={`w-8 h-8 ${star <= (hoverRating || userRating || 0) ? 'text-yellow-400 fill-current' : 'text-gray-200'}`}
/>
</button>
))}
</div>
<span className="text-xs text-gray-500 font-medium italic">
{userRating ? "Merci pour votre note !" : "Cliquez sur une étoile pour voter"}
</span>
</div>
)}
</div>
{!isOwner && (
<div className="mb-10 bg-gray-50 rounded-xl p-5 border border-gray-100">
<p className="text-sm font-bold text-gray-700 mb-3">Laissez un commentaire</p>
<div className="relative">
<textarea
value={reviewComment}
onChange={(e) => setReviewComment(e.target.value)}
placeholder="Que pensez-vous de cette entreprise ? (Qualité, service...)"
className="w-full bg-white border border-gray-200 rounded-lg p-4 text-sm focus:ring-2 focus:ring-brand-500 outline-none h-24 resize-none shadow-sm"
/>
<button
onClick={() => handleRate(userRating || 5)}
disabled={isRating}
className="absolute bottom-3 right-3 bg-brand-600 text-white px-4 py-1.5 rounded-md text-xs font-bold hover:bg-brand-700 transition-all flex items-center gap-2 shadow-md"
>
{isRating ? <Loader2 className="w-3 h-3 animate-spin" /> : "Publier"}
</button>
</div>
</div>
)}
<div className="space-y-6">
{loadingRatings ? (
<div className="flex justify-center py-10">
<Loader2 className="w-8 h-8 text-brand-600 animate-spin" />
</div>
) : ratings.length > 0 ? (
ratings.map((rating) => (
<div key={rating.id} className="border-b border-gray-50 last:border-0 pb-6">
<div className="flex justify-between items-start mb-3">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-full bg-brand-50 flex items-center justify-center text-brand-700 font-bold text-sm">
{(rating as any).user?.name?.charAt(0) || 'U'}
</div>
<div>
<p className="font-bold text-gray-900 text-sm">{(rating as any).user?.name}</p>
<div className="flex items-center">
{[1, 2, 3, 4, 5].map((star) => (
<Star
key={star}
className={`w-3 h-3 ${star <= rating.value ? 'text-yellow-400 fill-current' : 'text-gray-200'}`}
/>
))}
<span className="text-[10px] text-gray-400 ml-2 font-medium">
{new Date(rating.createdAt).toLocaleDateString('fr-FR')}
</span>
</div>
</div>
</div>
</div>
<p className="text-gray-600 text-sm leading-relaxed italic ml-13">
<Quote className="w-3 h-3 text-brand-200 inline mr-1" />
{rating.comment || "Pas de commentaire"}
</p>
{/* Reply section for owner */}
{isOwner && !rating.reply && (
<div className="mt-4 ml-13">
{replyingTo === rating.id ? (
<div className="flex flex-col gap-2">
<textarea
value={replyText}
onChange={(e) => setReplyText(e.target.value)}
placeholder="Votre réponse..."
className="w-full bg-gray-50 border border-gray-200 rounded-lg p-3 text-sm outline-none focus:ring-1 focus:ring-brand-500 h-20"
/>
<div className="flex gap-2">
<button onClick={() => handleReply(rating.id)} disabled={isSubmittingReply} className="bg-brand-600 text-white px-3 py-1.5 rounded text-xs font-bold">Répondre</button>
<button onClick={() => setReplyingTo(null)} className="text-gray-500 text-xs">Annuler</button>
</div>
</div>
) : (
<button onClick={() => setReplyingTo(rating.id)} className="text-brand-600 text-xs font-bold hover:underline">Répondre au client</button>
)}
</div>
)}
{rating.reply && (
<div className="mt-4 ml-13 bg-brand-50/50 p-4 rounded-lg border-l-2 border-brand-300">
<div className="flex items-center gap-2 mb-1">
<MessageCircle className="w-3 h-3 text-brand-600" />
<span className="text-[10px] font-bold text-brand-700 uppercase">Réponse de {business.name}</span>
</div>
<p className="text-gray-700 text-sm italic">{rating.reply}</p>
</div>
)}
</div>
))
) : (
<div className="text-center py-10 bg-gray-50 rounded-xl border border-dashed border-gray-200">
<Star className="w-10 h-10 text-gray-200 mx-auto mb-2" />
<p className="text-gray-500 text-sm">Soyez le premier à donner votre avis !</p>
</div>
)}
</div>
</div>
</div>
<div className="space-y-8">
<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">Coordonnées</h2>
<div className="space-y-4">
{business.websiteUrl && (
<a href={business.websiteUrl.startsWith('http') ? business.websiteUrl : `https://${business.websiteUrl}`} target="_blank" rel="noopener noreferrer" className="flex items-center text-gray-600 hover:text-brand-600 transition-colors group">
<div className="w-10 h-10 rounded-lg bg-gray-50 flex items-center justify-center mr-3 group-hover:bg-brand-50">
<Globe className="w-5 h-5 text-brand-600" />
</div>
<div className="flex-1 min-w-0">
<p className="text-xs text-gray-400 uppercase font-bold tracking-wider">Site Web</p>
<p className="text-sm font-medium truncate">{business.websiteUrl.replace(/^https?:\/\//, '')}</p>
</div>
</a>
)}
{business.showEmail && business.contactEmail && (
<a href={`mailto:${business.contactEmail}`} className="flex items-center text-gray-600 hover:text-brand-600 transition-colors group">
<div className="w-10 h-10 rounded-lg bg-gray-50 flex items-center justify-center mr-3 group-hover:bg-brand-50">
<Mail className="w-5 h-5 text-brand-600" />
</div>
<div className="flex-1 min-w-0">
<p className="text-xs text-gray-400 uppercase font-bold tracking-wider">Email</p>
<p className="text-sm font-medium truncate">{business.contactEmail}</p>
</div>
</a>
)}
{business.showPhone && business.contactPhone && (
<a href={`tel:${business.contactPhone}`} className="flex items-center text-gray-600 hover:text-brand-600 transition-colors group">
<div className="w-10 h-10 rounded-lg bg-gray-50 flex items-center justify-center mr-3 group-hover:bg-brand-50">
<Phone className="w-5 h-5 text-brand-600" />
</div>
<div className="flex-1 min-w-0">
<p className="text-xs text-gray-400 uppercase font-bold tracking-wider">Téléphone</p>
<p className="text-sm font-medium truncate">{business.contactPhone}</p>
</div>
</a>
)}
{business.showSocials && business.socialLinks && (
<div className="pt-4 border-t border-gray-50">
<p className="text-xs text-gray-400 uppercase font-bold tracking-wider mb-3">Réseaux Sociaux</p>
<div className="flex gap-2">
{business.socialLinks.facebook && (
<a href={business.socialLinks.facebook} target="_blank" rel="noopener noreferrer" className="w-10 h-10 rounded-lg bg-gray-50 flex items-center justify-center text-gray-400 hover:text-blue-600 hover:bg-blue-50 transition-all">
<Facebook className="w-5 h-5" />
</a>
)}
{business.socialLinks.linkedin && (
<a href={business.socialLinks.linkedin} target="_blank" rel="noopener noreferrer" className="w-10 h-10 rounded-lg bg-gray-50 flex items-center justify-center text-gray-400 hover:text-blue-700 hover:bg-blue-50 transition-all">
<Linkedin className="w-5 h-5" />
</a>
)}
{business.socialLinks.instagram && (
<a href={business.socialLinks.instagram} target="_blank" rel="noopener noreferrer" className="w-10 h-10 rounded-lg bg-gray-50 flex items-center justify-center text-gray-400 hover:text-pink-600 hover:bg-pink-50 transition-all">
<Instagram className="w-5 h-5" />
</a>
)}
{business.socialLinks.twitter && (
<a href={business.socialLinks.twitter} target="_blank" rel="noopener noreferrer" className="w-10 h-10 rounded-lg bg-gray-50 flex items-center justify-center text-gray-400 hover:text-black hover:bg-gray-100 transition-all">
<Twitter className="w-5 h-5" />
</a>
)}
</div>
</div>
)}
</div>
</div>
<div id="contact-form" ref={contactRef} 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">Contacter l'entreprise</h2>
<form onSubmit={handleContactSubmit} className="space-y-4">