import { useState, useRef, useEffect, useMemo } from 'react'; import { API_URL } from '../config'; import { uploadBusinessCover, serializeAmenities } from '../lib/businessSite'; import { ROUTES } from '../lib/routes'; import LocationMapPicker from './LocationMapPicker'; import SahelLogo from './SahelLogo'; import AmenityIcon from './AmenityIcon'; const STEPS_COUNT = 3; const BUSINESS_TYPES = [ { value: 'Restaurant', label: 'Restaurant / Café' }, { value: 'Hotel', label: 'Hôtel / Riad' }, { value: 'Retail', label: 'Boutique / Commerce' }, { value: 'Services', label: 'Services / Conseil' }, { value: 'Tech', label: 'Technologie' }, ]; const AMENITY_TAGS = [ { id: 'wifi', label: 'WiFi', icon: 'wifi' }, { id: 'parking', label: 'Parking', icon: 'local_parking' }, { id: 'card', label: 'Paiement carte', icon: 'payments' }, { id: 'delivery', label: 'Livraison', icon: 'delivery_dining' }, { id: 'access', label: 'Accessibilité', icon: 'accessible' }, ]; const CUSTOM_ICON_CHOICES = [ 'star', 'restaurant', 'local_cafe', 'spa', 'pool', 'ac_unit', 'pets', 'child_care', 'storefront', 'local_laundry_service', 'room_service', 'fitness_center', ]; const PROCESSING_MESSAGES = [ 'Nettoyage des métadonnées...', 'Génération des embeddings vectoriels...', 'Optimisation du moteur de recherche...', 'Finalisation de votre mini-site...', ]; function parseCoord(value) { const trimmed = (value || '').trim(); if (!trimmed) return null; const parsed = Number.parseFloat(trimmed); return Number.isFinite(parsed) ? parsed : null; } function ProgressBar({ currentStep }) { return (
{[0, 1, 2].map((index) => (
))}
); } function OnboardingProcessing({ uploadProgress }) { const [statusText, setStatusText] = useState(PROCESSING_MESSAGES[0]); const [activeProcessStep, setActiveProcessStep] = useState(1); useEffect(() => { let messageIndex = 0; const messageInterval = setInterval(() => { messageIndex = (messageIndex + 1) % PROCESSING_MESSAGES.length; setStatusText(PROCESSING_MESSAGES[messageIndex]); }, 3000); const stepInterval = setInterval(() => { setActiveProcessStep((prev) => (prev < 3 ? prev + 1 : prev)); }, 4500); return () => { clearInterval(messageInterval); clearInterval(stepInterval); }; }, []); const steps = [ { title: 'Création du profil', detail: 'Enregistrement de votre commerce' }, { title: 'Photo & localisation', detail: 'Personnalisation du mini-site' }, { title: 'Indexation IA', detail: uploadProgress || 'Préparation de votre assistant' }, ]; return (
auto_awesome

Traitement en cours...

{statusText}

{steps.map((step, index) => { const stepNum = index + 1; const done = stepNum < activeProcessStep; const current = stepNum === activeProcessStep; return (
{done ? 'check_circle' : current ? 'autorenew' : 'pending'}

{step.title}

{step.detail}

); })}
); } export default function OnboardingWizard({ owner, ownerToken, onComplete, onExit }) { const [currentStep, setCurrentStep] = useState(() => { const saved = localStorage.getItem('sahel_onboarding_step'); if (saved) { const step = parseInt(saved, 10); if (step >= 0 && step < STEPS_COUNT) return step; } return 0; }); const [isSubmitting, setIsSubmitting] = useState(false); const [uploadProgress, setUploadProgress] = useState(''); const [selectedAmenities, setSelectedAmenities] = useState(() => { const saved = localStorage.getItem('sahel_onboarding_amenities'); if (saved) { try { return JSON.parse(saved); } catch (e) {} } return []; }); const [showCustomAmenity, setShowCustomAmenity] = useState(false); const [customLabel, setCustomLabel] = useState(''); const [customIcon, setCustomIcon] = useState('star'); const [customEmoji, setCustomEmoji] = useState(''); const [uploadedFiles, setUploadedFiles] = useState([]); const [form, setForm] = useState(() => { const saved = localStorage.getItem('sahel_onboarding_form'); if (saved) { try { return JSON.parse(saved); } catch (e) {} } return { name: '', business_type: 'Restaurant', city: '', address: '', latitude: '', longitude: '', owner_phone: '', description: '', working_hours: '', primary_services: '', pasted_text: '', }; }); const [file, setFile] = useState(null); const [coverFile, setCoverFile] = useState(null); const [coverPreview, setCoverPreview] = useState(null); const [dragActive, setDragActive] = useState(false); const fileInputRef = useRef(null); const coverInputRef = useRef(null); useEffect(() => { localStorage.setItem('sahel_onboarding_step', String(currentStep)); }, [currentStep]); useEffect(() => { localStorage.setItem('sahel_onboarding_amenities', JSON.stringify(selectedAmenities)); }, [selectedAmenities]); useEffect(() => { localStorage.setItem('sahel_onboarding_form', JSON.stringify(form)); }, [form]); const handleExit = () => { localStorage.removeItem('sahel_onboarding_step'); localStorage.removeItem('sahel_onboarding_amenities'); localStorage.removeItem('sahel_onboarding_form'); onExit(); }; const mapSearchHint = useMemo(() => { return [form.address, form.city, form.name].filter(Boolean).join(', '); }, [form.address, form.city, form.name]); const handleInputChange = (e) => { const { name, value } = e.target; setForm((prev) => ({ ...prev, [name]: value })); }; const isAmenitySelected = (tag) => selectedAmenities.some((a) => a.label === tag.label); const togglePresetAmenity = (tag) => { setSelectedAmenities((prev) => { if (prev.some((a) => a.label === tag.label)) { return prev.filter((a) => a.label !== tag.label); } return [...prev, { label: tag.label, icon: tag.icon }]; }); }; const addCustomAmenity = () => { const label = customLabel.trim(); if (!label) { alert('Donnez un nom à votre service.'); return; } if (selectedAmenities.some((a) => a.label.toLowerCase() === label.toLowerCase())) { alert('Ce service existe déjà.'); return; } const icon = customEmoji.trim() || customIcon; setSelectedAmenities((prev) => [...prev, { label, icon }]); setCustomLabel(''); setCustomEmoji(''); setShowCustomAmenity(false); }; const handleLocationChange = (lat, lng, resolvedAddress) => { setForm((prev) => ({ ...prev, latitude: String(Number(lat).toFixed(6)), longitude: String(Number(lng).toFixed(6)), ...(resolvedAddress ? { address: resolvedAddress } : {}), })); }; const handleCoverSelect = (e) => { const selected = e.target.files?.[0]; if (!selected) return; if (!selected.type.startsWith('image/')) { alert('Veuillez choisir une image (JPG, PNG ou WEBP).'); return; } setCoverFile(selected); setCoverPreview(URL.createObjectURL(selected)); }; const handleDrag = (e) => { e.preventDefault(); e.stopPropagation(); if (e.type === 'dragenter' || e.type === 'dragover') setDragActive(true); else if (e.type === 'dragleave') setDragActive(false); }; const handleDrop = (e) => { e.preventDefault(); e.stopPropagation(); setDragActive(false); if (e.dataTransfer.files?.[0]) addDocumentFile(e.dataTransfer.files[0]); }; const addDocumentFile = (picked) => { setFile(picked); setUploadedFiles([{ name: picked.name, size: picked.size }]); }; const handleFileSelect = (e) => { if (e.target.files?.[0]) addDocumentFile(e.target.files[0]); }; const removeDocument = () => { setFile(null); setUploadedFiles([]); if (fileInputRef.current) fileInputRef.current.value = ''; }; const nextStep = () => { if (currentStep === 0) { if (!form.name.trim()) { alert('Veuillez saisir le nom de votre entreprise.'); return; } if (!form.owner_phone.trim()) { alert('Veuillez saisir votre numéro WhatsApp professionnel.'); return; } } if (currentStep === 1) { if (!form.description.trim()) { alert('Veuillez décrire votre activité.'); return; } if (parseCoord(form.latitude) == null || parseCoord(form.longitude) == null) { alert('Placez votre commerce sur la carte (recherche ou clic sur la carte).'); return; } } if (currentStep < STEPS_COUNT - 1) { setCurrentStep((prev) => prev + 1); } else { submitOnboarding(); } }; const prevStep = () => { if (currentStep > 0) setCurrentStep((prev) => prev - 1); }; const submitOnboarding = async () => { setIsSubmitting(true); setUploadProgress('Création du profil de l\'entreprise...'); const highlights = serializeAmenities(selectedAmenities); const publicKnowledge = form.pasted_text.trim(); try { const businessPayload = { name: form.name.trim(), business_type: form.business_type, description: form.description.trim(), owner_email: owner.email, owner_phone: '+212' + form.owner_phone.trim().replace(/^\+212/, '').replace(/^0/, ''), city: form.city.trim(), address: form.address.trim(), latitude: parseCoord(form.latitude), longitude: parseCoord(form.longitude), working_hours: form.working_hours.trim(), primary_services: form.primary_services.trim(), highlights, public_knowledge: publicKnowledge || null, owner_id: owner.id, }; const response = await fetch(`${API_URL}/businesses`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${ownerToken}`, }, body: JSON.stringify(businessPayload), }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.detail || 'Erreur lors de la création de l\'entreprise.'); } let business = await response.json(); if (coverFile) { setUploadProgress('Téléversement de la photo de couverture...'); business = await uploadBusinessCover(business.id, coverFile, ownerToken); } if (file) { setUploadProgress('Indexation de vos documents...'); const formData = new FormData(); formData.append('file', file); const uploadRes = await fetch(`${API_URL}/businesses/${business.id}/documents`, { method: 'POST', headers: { Authorization: `Bearer ${ownerToken}` }, body: formData, }); if (!uploadRes.ok) { const errDetail = await uploadRes.json().catch(() => ({})); alert( 'Le commerce a été créé, mais l\'indexation du document a échoué : ' + (errDetail.detail || 'Erreur inconnue'), ); } } else if (form.pasted_text.trim()) { setUploadProgress('Indexation de vos connaissances...'); const textBlob = new Blob([form.pasted_text], { type: 'text/plain' }); const textFile = new File([textBlob], 'profil_entreprise.txt', { type: 'text/plain' }); const formData = new FormData(); formData.append('file', textFile); const uploadRes = await fetch(`${API_URL}/businesses/${business.id}/documents`, { method: 'POST', headers: { Authorization: `Bearer ${ownerToken}` }, body: formData, }); if (!uploadRes.ok) { const errDetail = await uploadRes.json().catch(() => ({})); alert( 'Le commerce a été créé, mais l\'indexation du texte a échoué : ' + (errDetail.detail || 'Erreur inconnue'), ); } } setUploadProgress('Configuration terminée !'); localStorage.removeItem('sahel_onboarding_step'); localStorage.removeItem('sahel_onboarding_amenities'); localStorage.removeItem('sahel_onboarding_form'); setTimeout(() => onComplete(business), 800); } catch (error) { alert(error.message || 'Une erreur est survenue lors de l\'installation.'); setIsSubmitting(false); } }; const stepLabels = ['Identité', 'Votre commerce', 'Documents & IA']; return (
{currentStep === 0 && !isSubmitting ? ( Aide ) : onExit ? ( ) : ( )}
{!isSubmitting && (
Étape {currentStep + 1} sur {STEPS_COUNT} {stepLabels[currentStep]}
)}
{isSubmitting ? ( ) : ( <> {currentStep === 0 && (

Bienvenue

Commençons par les informations de base de votre entreprise.

)} {currentStep === 1 && (

Parlez-nous de votre commerce

Ces détails apparaîtront sur votre mini-site et aideront vos clients à vous trouver.

e.preventDefault()}>