/**
* @module Register
* @description Page d'inscription de l'application.
* Contient le formulaire d'inscription avec validation en temps réel et gestion des erreurs.
*/
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { isValidName, isValidEmail, isValidAge, isValidZipCode } from './validator';
import { registerUserAPI } from './api';
/**
* Composant Register.
* Gère le formulaire d'inscription, la validation des champs, l'affichage des erreurs et la soumission.
* Redirige vers la page d'accueil après une inscription réussie.
*
* @component
* @param {Object} props - Les propriétés du composant.
* @param {Array<Object>} props.users - La liste actuelle des utilisateurs (pour l'ajout).
* @param {Function} props.setUsers - Fonction pour mettre à jour la liste des utilisateurs.
* @returns {JSX.Element} Le formulaire d'inscription rendu.
*/
export default function Register({ users, setUsers }) {
const navigate = useNavigate();
const [formData, setFormData] = useState({
lastName: '',
firstName: '',
email: '',
birthDate: '',
zipCode: '',
city: ''
});
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
const [apiError, setApiError] = useState('');
/**
* Valide l'intégralité des champs du formulaire en utilisant le module validator.
* @function validate
* @inner
* @param {Object} values - Les données actuelles du formulaire.
* @returns {Object} Un objet contenant les messages d'erreur par champ.
*/
const validate = (values) => {
const newErrors = {};
try { isValidName(values.lastName); } catch (e) { newErrors.lastName = e.message; }
try { isValidName(values.firstName); } catch (e) { newErrors.firstName = e.message; }
try { isValidEmail(values.email); } catch (e) { newErrors.email = e.message; }
try { isValidZipCode(values.zipCode); } catch (e) { newErrors.zipCode = e.message; }
try {
if (!values.city) throw new Error("La ville est requise");
isValidName(values.city);
} catch (e) { newErrors.city = e.message; }
try {
if (!values.birthDate) throw new Error("Date requise");
isValidAge(new Date(values.birthDate));
} catch (e) { newErrors.birthDate = e.message; }
return newErrors;
};
/**
* Effectue la validation à chaque modification des données du formulaire.
*/
useEffect(() => {
const currentErrors = validate(formData);
setErrors(currentErrors);
}, [formData]);
/**
* Gère la mise à jour des données du formulaire lors de la saisie utilisateur.
* @function handleChange
* @inner
* @param {Object} e - L'événement de changement du DOM.
*/
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
/**
* Marque un champ comme "touché" lors du focus out pour déclencher l'affichage de l'erreur
* @function handleBlur
* @inner
* @param {Event} e - L'événement de perte de focus (blur).
*/
const handleBlur = (e) => {
const { name } = e.target;
setTouched(prev => ({ ...prev, [name]: true }));
};
/**
* Traite la soumission, ajoute l'utilisateur et redirige vers l'accueil.
* Gère également l'affichage des erreurs API (ex: email en doublon, erreur réseau).
* @async
* @function handleSubmit
* @inner
* @param {Event} e - L'événement de soumission du formulaire (submit).
*/
const handleSubmit = async (e) => {
e.preventDefault();
setApiError('');
const currentErrors = validate(formData);
if (Object.keys(currentErrors).length === 0) {
try {
const response = await registerUserAPI(formData);
const newUserForState = {
id: response.id,
firstName: formData.firstName,
lastName: formData.lastName
};
setUsers([...users, newUserForState]);
navigate('/');
} catch (error) {
console.error("Erreur d'inscription:", error);
setApiError(error.message || 'Une erreur réseau est survenue. Veuillez réessayer.');
}
}
};
const isFormInvalid = Object.keys(errors).length > 0 ||
Object.values(formData).some(val => val === '');
return (
<div style={{ padding: '20px', maxWidth: '500px', margin: '0 auto' }}>
<h1>Inscription</h1>
{/* Affichage des erreurs réseau ou métier si l'API échoue */}
{apiError && (
<div style={{ color: 'white', backgroundColor: '#dc3545', padding: '10px', borderRadius: '5px', marginBottom: '15px' }}>
{apiError}
</div>
)}
<form data-testid="register-form" onSubmit={handleSubmit}>
<div style={{ marginBottom: '15px' }}>
<label htmlFor="lastName">Nom :</label>
<input id="lastName" type="text" name="lastName" data-cy="input-lastName" value={formData.lastName} onChange={handleChange} onBlur={handleBlur} style={{ display: 'block', width: '100%', padding: '8px' }} />
{touched.lastName && errors.lastName && <span data-cy="error-lastName" style={{ color: 'red', fontSize: '12px' }}>{errors.lastName}</span>}
</div>
<div style={{ marginBottom: '15px' }}>
<label htmlFor="firstName">Prénom :</label>
<input id="firstName" type="text" name="firstName" data-cy="input-firstName" value={formData.firstName} onChange={handleChange} onBlur={handleBlur} style={{ display: 'block', width: '100%', padding: '8px' }} />
{touched.firstName && errors.firstName && <span data-cy="error-firstName" style={{ color: 'red', fontSize: '12px' }}>{errors.firstName}</span>}
</div>
<div style={{ marginBottom: '15px' }}>
<label htmlFor="email">Email :</label>
<input id="email" type="email" name="email" data-cy="input-email" value={formData.email} onChange={handleChange} onBlur={handleBlur} style={{ display: 'block', width: '100%', padding: '8px' }} />
{touched.email && errors.email && <span data-cy="error-email" style={{ color: 'red', fontSize: '12px' }}>{errors.email}</span>}
</div>
<div style={{ marginBottom: '15px' }}>
<label htmlFor="birthDate">Date de naissance :</label>
<input id="birthDate" type="date" name="birthDate" data-cy="input-birthDate" value={formData.birthDate} onChange={handleChange} onBlur={handleBlur} style={{ display: 'block', width: '100%', padding: '8px' }} />
{touched.birthDate && errors.birthDate && <span data-cy="error-birthDate" style={{ color: 'red', fontSize: '12px' }}>{errors.birthDate}</span>}
</div>
<div style={{ marginBottom: '15px' }}>
<label htmlFor="zipCode">Code Postal :</label>
<input id="zipCode" type="text" name="zipCode" data-cy="input-zipCode" value={formData.zipCode} onChange={handleChange} onBlur={handleBlur} style={{ display: 'block', width: '100%', padding: '8px' }} />
{touched.zipCode && errors.zipCode && <span data-cy="error-zipCode" style={{ color: 'red', fontSize: '12px' }}>{errors.zipCode}</span>}
</div>
<div style={{ marginBottom: '15px' }}>
<label htmlFor="city">Ville :</label>
<input id="city" type="text" name="city" data-cy="input-city" value={formData.city} onChange={handleChange} onBlur={handleBlur} style={{ display: 'block', width: '100%', padding: '8px' }} />
{touched.city && errors.city && <span data-cy="error-city" style={{ color: 'red', fontSize: '12px' }}>{errors.city}</span>}
</div>
<button type="submit" data-cy="submit-btn" disabled={isFormInvalid} style={{ padding: '10px 20px', backgroundColor: isFormInvalid ? '#ccc' : '#007bff', color: 'white', border: 'none', cursor: isFormInvalid ? 'not-allowed' : 'pointer' }}>
Envoyer
</button>
</form>
</div>
);
}