"use client"; import React, { useState, useEffect } from 'react'; import { Image as ImageIcon, Sparkles, Youtube, X, Globe, Facebook, Linkedin, Instagram, Twitter, CheckCircle, AlertCircle, Hash } from 'lucide-react'; import { Business, Country } from '../../types'; import TagInput from '../TagInput'; import { generateBusinessDescription } from '../../lib/geminiService'; import { toast } from 'react-hot-toast'; import { useUser } from '../UserProvider'; import { uploadImage } from '../../app/actions/upload'; import { Loader2, Upload, Link as LinkIcon } from 'lucide-react'; // Helper to extract youtube ID const getYouTubeId = (url: string) => { const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/; const match = url.match(regExp); return (match && match[2].length === 11) ? match[2] : null; }; const normalizeBusinessData = (b: Business): Business => ({ ...b, name: b.name || 'Nouvelle Entreprise', category: b.category || 'Autre', categoryId: b.categoryId || '', suggestedCategory: b.suggestedCategory || '', description: b.description || '', location: b.location || '', countryId: b.countryId || '', city: b.city || '', contactEmail: b.contactEmail || '', showEmail: b.showEmail ?? true, showPhone: b.showPhone ?? true, showSocials: b.showSocials ?? true, slug: b.slug || '', videoUrl: b.videoUrl || '', coverUrl: b.coverUrl || '', coverPosition: b.coverPosition || '50% 50%', coverZoom: b.coverZoom || 1, contactPhone: b.contactPhone || '', websiteUrl: b.websiteUrl || '', socialLinks: { facebook: b.socialLinks?.facebook || '', linkedin: b.socialLinks?.linkedin || '', instagram: b.socialLinks?.instagram || '', twitter: b.socialLinks?.twitter || '', website: b.socialLinks?.website || '', } }); const DashboardProfile = ({ business, setBusiness }: { business: Business, setBusiness: (b: Business) => void }) => { const { login } = useUser(); const [formData, setFormData] = useState(normalizeBusinessData(business)); const [countries, setCountries] = useState([]); const [categories, setCategories] = useState([]); const [videoInput, setVideoInput] = useState(business.videoUrl || ''); const [videoPreviewId, setVideoPreviewId] = useState(getYouTubeId(business.videoUrl || '')); const [isGenerating, setIsGenerating] = useState(false); const [isMounted, setIsMounted] = useState(false); const [allTags, setAllTags] = useState([]); const [isUploadingLogo, setIsUploadingLogo] = useState(false); const [isUploadingCover, setIsUploadingCover] = useState(false); const [logoInputMode, setLogoInputMode] = useState<'url' | 'upload'>('upload'); const [coverInputMode, setCoverInputMode] = useState<'url' | 'upload'>('upload'); const logoFileInputRef = React.useRef(null); const coverFileInputRef = React.useRef(null); // Fetch countries, categories and tags React.useEffect(() => { const fetchData = async () => { try { const [cRes, catRes, tagsRes] = await Promise.all([ fetch('/api/countries'), fetch('/api/categories'), fetch('/api/tags') ]); const cData = await cRes.json(); const catData = await catRes.json(); const tagsData = await tagsRes.json(); if (!cData.error) setCountries(cData); if (!catData.error) setCategories(catData); if (!tagsData.error) setAllTags(tagsData); } catch (error) { console.error('Error fetching metadata:', error); } }; fetchData(); }, []); // Sync formData when business prop changes (initial fetch) React.useEffect(() => { setFormData(normalizeBusinessData(business)); setVideoInput(business.videoUrl || ''); setVideoPreviewId(getYouTubeId(business.videoUrl || '')); }, [business]); React.useEffect(() => { setIsMounted(true); }, []); const handleInputChange = (e: React.ChangeEvent) => { setFormData({ ...formData, [e.target.name]: e.target.value }); }; const handleSocialChange = (network: keyof typeof formData.socialLinks, value: string) => { setFormData({ ...formData, socialLinks: { ...formData.socialLinks, [network]: value } }); }; const handleVideoChange = (e: React.ChangeEvent) => { const url = e.target.value; setVideoInput(url); setVideoPreviewId(getYouTubeId(url)); setFormData({ ...formData, videoUrl: url }); }; const handleAiGenerate = async () => { setIsGenerating(true); const text = await generateBusinessDescription(formData.name, formData.category, "Innovation, Qualité, Service client"); setFormData(prev => ({ ...prev, description: text })); setIsGenerating(false); }; const handleLogoUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; if (!file.type.startsWith('image/')) { toast.error("Le fichier doit être une image"); return; } if (file.size > 5 * 1024 * 1024) { toast.error("L'image ne doit pas dépasser 5 Mo"); return; } setIsUploadingLogo(true); const uploadFormData = new FormData(); uploadFormData.append('file', file); try { const result = await uploadImage(uploadFormData); if (result.success && result.url) { setFormData({ ...formData, logoUrl: result.url }); toast.success("Logo mis à jour !"); } else { toast.error(result.error || "Erreur lors de l'upload"); } } catch (error) { toast.error("Erreur réseau lors de l'upload"); } finally { setIsUploadingLogo(false); if (logoFileInputRef.current) logoFileInputRef.current.value = ''; } }; const handleCoverUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; if (!file.type.startsWith('image/')) { toast.error("Le fichier doit être une image"); return; } if (file.size > 5 * 1024 * 1024) { toast.error("L'image ne doit pas dépasser 5 Mo"); return; } setIsUploadingCover(true); const uploadFormData = new FormData(); uploadFormData.append('file', file); try { const result = await uploadImage(uploadFormData); if (result.success && result.url) { setFormData({ ...formData, coverUrl: result.url }); toast.success("Bannière mise à jour !"); } else { toast.error(result.error || "Erreur lors de l'upload"); } } catch (error) { toast.error("Erreur réseau lors de l'upload"); } finally { setIsUploadingCover(false); if (coverFileInputRef.current) coverFileInputRef.current.value = ''; } }; const handleSave = async () => { try { const res = await fetch('/api/businesses', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-user-id': business?.ownerId || (typeof window !== 'undefined' ? JSON.parse(localStorage.getItem('afro_user') || '{}').id : '') }, body: JSON.stringify({ ...formData, ownerId: business?.ownerId || (typeof window !== 'undefined' ? JSON.parse(localStorage.getItem('afro_user') || '{}').id : null) }) }); const data = await res.json(); if (data.error) { toast.error(data.error); } else { setBusiness(data); if (data.owner && login) { login(data.owner); } toast.success('Profil mis à jour avec succès !'); // Optional: refresh page to show in annuaire } } catch (error) { console.error('Save error:', error); toast.error('Erreur lors de la sauvegarde'); } }; const handleDelete = async () => { if (!window.confirm("⚠️ ATTENTION : Êtes-vous sûr de vouloir supprimer définitivement votre boutique ? Cette action supprimera également vos offres, vos messages et vos statistiques. Cette action est irréversible.")) { return; } try { const res = await fetch(`/api/businesses/${business.id}`, { method: 'DELETE', headers: { 'x-user-id': business.ownerId } }); const data = await res.json(); if (data.success) { toast.success('Boutique supprimée avec succès.'); // Upgrade local storage user if role changed const localUser = JSON.parse(localStorage.getItem('afro_user') || '{}'); if (localUser.id === business.ownerId) { localUser.role = 'VISITOR'; localStorage.setItem('afro_user', JSON.stringify(localUser)); if (login) login(localUser); } setTimeout(() => window.location.href = '/dashboard', 1500); } else { toast.error(data.error || 'Erreur lors de la suppression'); } } catch (error) { console.error('Delete error:', error); toast.error('Erreur réseau lors de la suppression'); } }; const isNameOk = formData.name && formData.name !== "Nouvelle Entreprise"; const isDescOk = formData.description && formData.description.length >= 20; const isLocOk = (formData.countryId && formData.city) || (formData.location && formData.location !== "Ma Ville"); const isEmailOk = !!formData.contactEmail; const isPhoneOk = !!formData.contactPhone; const isActive = isNameOk && isDescOk && isLocOk && isEmailOk && isPhoneOk; return (

Éditer mon profil

{isActive ? ( Boutique Active ) : ( Profil Incomplet )}
{!isActive && (

Votre boutique n'est pas encore visible dans l'annuaire

Pour être activé, vous devez compléter :

{isNameOk ? '✓' : '○'} Nom de boutique {isDescOk ? '✓' : '○'} Description (min 20 chars) {isLocOk ? '✓' : '○'} Localisation {isEmailOk ? '✓' : '○'} Email de contact {isPhoneOk ? '✓' : '○'} Numéro de téléphone
)} {/* A. Bloc Identité Visuelle */}

Identité Visuelle

Logo actuel Aperçu Logo
{logoInputMode === 'upload' ? (
logoFileInputRef.current?.click()} className="group border-2 border-dashed border-gray-300 rounded-xl p-6 flex flex-col items-center justify-center hover:border-brand-400 hover:bg-brand-50 transition-all cursor-pointer relative overflow-hidden" > {isUploadingLogo ? (
Upload en cours...
) : ( <>

Cliquez pour changer le logo

PNG, JPG ou WEBP (Max 5Mo)

)}
) : (
)}
{formData.coverUrl ? ( Couverture ) : (
Aucune bannière personnalisée
)} {isUploadingCover && (
Upload en cours...
)}
{/* ... (Zoom/Position controls stay same) */}
setFormData({...formData, coverZoom: parseFloat(e.target.value)})} className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-brand-600" />
{ const y = (formData.coverPosition || '50% 50%').split(' ')[1] || '50%'; setFormData({...formData, coverPosition: `${e.target.value}% ${y}`}); }} className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-brand-600" />
{ const x = (formData.coverPosition || '50% 50%').split(' ')[0] || '50%'; setFormData({...formData, coverPosition: `${x} ${e.target.value}%`}); }} className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-brand-600" />
{coverInputMode === 'url' && ( )}

Ajustez le zoom et la position pour un rendu optimal sur votre fiche.

/annuaire/ { const val = e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, '-'); setFormData({ ...formData, slug: val }); }} className="flex-1 block w-full border border-gray-300 rounded-none rounded-r-md py-2 px-3 focus:ring-brand-500 focus:border-brand-500 sm:text-sm" />

Uniquement lettres, chiffres et tirets. Laissez vide pour utiliser l'ID par défaut.

{formData.category === 'Autre' && (

Elle sera vérifiée par nos administrateurs avant d'être ajoutée officiellement.

)}