import React, { useState } from 'react'; import { IMaskInput } from 'react-imask'; import axios from 'axios'; export default function ReclamationForm({ objetId, userPrenom = '', userNom = '', userTelephone = '', userEmail = '', isLoggedIn: initialIsLoggedIn = false, loginUrl = '/login', csrfToken = '' }) { const [step, setStep] = useState(1); const [isLoggedIn, setIsLoggedIn] = useState(initialIsLoggedIn); const [showLoginModal, setShowLoginModal] = useState(false); const [formData, setFormData] = useState({ prenom: userPrenom, nom: userNom, telephone: userTelephone, email: userEmail, message: '', fichier: null, consent: false }); const [loginData, setLoginData] = useState({ email: '', password: '' }); const [loginError, setLoginError] = useState(null); const [isLoggingIn, setIsLoggingIn] = useState(false); const [errors, setErrors] = useState({}); const [isSubmitting, setIsSubmitting] = useState(false); const [serverError, setServerError] = useState(null); const [successMessage, setSuccessMessage] = useState(null); const handleLogin = async (e) => { e.preventDefault(); setIsLoggingIn(true); setLoginError(null); try { const params = new URLSearchParams(); params.append('email', loginData.email); params.append('password', loginData.password); params.append('_csrf_token', csrfToken); const response = await axios.post('/login', params, { headers: { 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' } }); if (response.data.success) { const user = response.data.user; setFormData(prev => ({ ...prev, prenom: user.prenom || '', nom: user.nom || '', telephone: user.telephone || '', email: user.email || '' })); setIsLoggedIn(true); setShowLoginModal(false); } } catch (error) { setLoginError(error.response?.data?.error || 'Identifiants invalides.'); } finally { setIsLoggingIn(false); } }; const handleLoginChange = (e) => { const { name, value } = e.target; setLoginData(prev => ({ ...prev, [name]: value })); }; const validateField = (name, value) => { let error = null; switch (name) { case 'prenom': if (!value) error = 'Le prénom est requis'; break; case 'nom': if (!value) error = 'Le nom est requis'; break; case 'telephone': const safeValue = value || ''; const phoneDigits = safeValue.replace(/\D/g, ''); const unmasked = safeValue.replace(/[\s\-_]/g, ''); if (!safeValue || phoneDigits.length <= 4) { error = 'Le téléphone est requis'; } else if (phoneDigits.length < 12 || !/^\+221(7[05678]|30|33)\d{7}$/.test(unmasked)) { error = 'Format invalide (ex: +221 77 123 45 67)'; } break; case 'email': if (value && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) { error = 'Format email invalide'; } break; case 'message': if (!value) error = 'Le message est requis'; else if (value.length < 20) error = 'Veuillez fournir une description plus détaillée (min. 20 caractères)'; break; case 'consent': if (!value) error = 'Vous devez certifier l\'exactitude des informations'; break; case 'fichier': if (value && value.size > 5 * 1024 * 1024) { error = 'Le fichier ne doit pas dépasser 5 Mo'; } break; default: break; } return error; }; const validateStep = (currentStep) => { let newErrors = {}; if (currentStep === 1) { newErrors.prenom = validateField('prenom', formData.prenom); newErrors.nom = validateField('nom', formData.nom); newErrors.telephone = validateField('telephone', formData.telephone); newErrors.email = validateField('email', formData.email); } else if (currentStep === 2) { newErrors.message = validateField('message', formData.message); } else if (currentStep === 3) { newErrors.consent = validateField('consent', formData.consent); } // Filter out null errors const filteredErrors = Object.fromEntries(Object.entries(newErrors).filter(([_, v]) => v !== null)); setErrors(filteredErrors); return Object.keys(filteredErrors).length === 0; }; const handleChange = (e) => { const { name, value, type, checked } = e.target; const newValue = type === 'checkbox' ? checked : value; setFormData(prev => ({ ...prev, [name]: newValue })); // Real-time validation const error = validateField(name, newValue); setErrors(prev => ({ ...prev, [name]: error })); }; const handleFileChange = (e) => { const file = e.target.files[0]; if (file) { const error = validateField('fichier', file); setErrors(prev => ({ ...prev, fichier: error })); if (!error) { setFormData(prev => ({ ...prev, fichier: file })); } else { setFormData(prev => ({ ...prev, fichier: null })); } } }; const nextStep = () => { if (validateStep(step)) { setStep(prev => prev + 1); window.scrollTo({ top: 0, behavior: 'smooth' }); } }; const prevStep = () => { setStep(prev => prev - 1); window.scrollTo({ top: 0, behavior: 'smooth' }); }; const handleSubmit = async (e) => { e.preventDefault(); if (!validateStep(3)) return; setIsSubmitting(true); setServerError(null); const data = new FormData(); data.append('prenom', formData.prenom); data.append('nom', formData.nom); data.append('telephone', formData.telephone.replace(/[\s]/g, '')); if (formData.email) { data.append('email', formData.email); } data.append('message', formData.message); if (formData.fichier) { data.append('fichier', formData.fichier); } try { const response = await axios.post(`/api-reclamation/${objetId}`, data, { headers: { 'Content-Type': 'multipart/form-data' } }); if (response.data.success) { setSuccessMessage(response.data.message); window.scrollTo({ top: 0, behavior: 'smooth' }); setTimeout(() => { window.location.href = response.data.redirectUrl; }, 3000); } } catch (error) { setServerError(error.response?.data?.error || 'Une erreur est survenue lors de l\'envoi.'); setIsSubmitting(false); } }; if (successMessage) { return (

{successMessage}

Redirection en cours vers les détails de l'objet...

Chargement...
); } return (
= 1 ? 'done' : ''} ${step === 1 ? 'active' : ''}`}> {step > 1 ? : 1} Identification
= 2 ? 'done' : ''} ${step === 2 ? 'active' : ''}`}> {step > 2 ? : 2} Réclamation
= 3 ? 'done' : ''} ${step === 3 ? 'active' : ''}`}> 3 Validation
{step === 1 && !isLoggedIn && (

Vous avez déjà un compte ?

pour pré-remplir vos informations automatiquement.

)}

{step === 1 ? 'Vos informations' : step === 2 ? 'Détails de la réclamation' : 'Dernière étape'}

{step === 1 ? 'Commencez par nous dire qui vous êtes.' : step === 2 ? 'Expliquez-nous pourquoi cet objet vous appartient.' : 'Vérifiez et confirmez votre demande.'}

{serverError && (
{serverError}
)}
Tous les champs marqués d'une astérisque (*) sont obligatoires.
{step === 1 && (
{formData.prenom.length}/50
{errors.prenom && {errors.prenom}}
{formData.nom.length}/50
{errors.nom && {errors.nom}}
{ setFormData(prev => ({ ...prev, telephone: value })); const error = validateField('telephone', value); setErrors(prev => ({ ...prev, telephone: error })); }} className={`form-control-custom ${errors.telephone ? 'is-invalid border-danger' : ''}`} style={{ paddingLeft: '40px' }} />
{errors.telephone && {errors.telephone}}
{errors.email && {errors.email}}
)} {step === 2 && (
1900 ? "text-warning" : "text-muted"} style={{ fontSize: '0.75rem' }}> {formData.message.length}/2000
{errors.message && {errors.message}}
Plus votre description est précise, plus vite nous pourrons valider votre demande.
document.getElementById('rec_fichier_react').click()} >

{formData.fichier ? formData.fichier.name : 'Glissez un fichier ou cliquez'}

Carte d'identité, reçu, photo • JPG, PNG, PDF • Max 5 Mo

{errors.fichier && {errors.fichier}}
)} {step === 3 && (
Résumé de votre demande
Nom : {formData.prenom} {formData.nom}
Téléphone : {formData.telephone}
Message : {(formData.message || '').substring(0, 100)}...
{formData.fichier &&
Pièce jointe incluse
}
{errors.consent && {errors.consent}}
)}
{showLoginModal && (

Connexion

{loginError && (
{loginError}
)}
)}
); }