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 (
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 (
{!isSubmitting && (
Étape {currentStep + 1} sur {STEPS_COUNT}
{stepLabels[currentStep]}
)}
{isSubmitting ? (
) : (
<>
{currentStep === 0 && (
)}
{currentStep === 1 && (
)}
{currentStep === 2 && (
fileInputRef.current?.click()}
className={`border-2 border-dashed rounded-lg p-xl flex flex-col items-center justify-center transition-all duration-300 cursor-pointer ${
dragActive
? 'border-primary bg-surface-blue'
: 'border-outline-variant hover:bg-surface-container-low'
}`}
>
upload_file
Glissez-déposez vos fichiers ici
PDF, DOCX jusqu'à 20 Mo
{uploadedFiles.length > 0 && (
{uploadedFiles.map((f) => (
description
{f.name}
{(f.size / 1024 / 1024).toFixed(2)} Mo
))}
)}
verified_user
Données sécurisées
psychology
IA souveraine
language
Serveurs en Europe
)}
>
)}
);
}