import React, { useCallback, useEffect, useState } from 'react';
import {
deleteAdminUser,
getAdminUsers,
updateAdminUser,
} from '../services/adminService.js';
import {
createUser,
getUsers,
} from '../services/userService.js';
import {
isCPValid,
isUserMajeur,
isEmailValid,
isStringValide,
} from '../utils/module.js';
/**
* Valeurs initiales du formulaire d'inscription.
* @type {{name: string, firstName: string, birthDate: string, email: string, city: string, postalCode: string}}
*/
const initialFormValues = {
name: '',
firstName: '',
birthDate: '',
email: '',
city: '',
postalCode: '',
};
const errorStyle = { color: 'red' };
const successStyle = { color: 'green' };
const noop = () => {};
function toFormValues(user) {
return {
name: user.name,
firstName: user.firstName,
birthDate: String(user.birthDate).slice(0, 10),
email: user.email,
city: user.city,
postalCode: user.postalCode,
};
}
function validateUser(data) {
const nextErrors = {};
if (!isStringValide(data.name)) {
nextErrors.name = 'Le nom est invalide.';
}
if (!isStringValide(data.firstName)) {
nextErrors.firstName = 'Le prénom est invalide.';
}
if (!isStringValide(data.city)) {
nextErrors.city = 'La ville est invalide.';
}
if (!isEmailValid(data.email)) {
nextErrors.email = "L'email est invalide.";
}
if (!isCPValid(data.postalCode)) {
nextErrors.postalCode = "Le code postal est invalide.";
}
if (!data.birthDate) {
nextErrors.birthDate = 'La date de naissance est obligatoire.';
} else {
const user = { birth: new Date(data.birthDate) };
if (!isUserMajeur(user)) {
nextErrors.birthDate = 'Vous devez être majeur pour soumettre ce formulaire.';
}
}
return nextErrors;
}
/**
* Affiche le formulaire d'inscription, les messages de validation
* et la liste des inscrits recuperes depuis l'API.
* @param {Object} [props={}] Props du composant.
* @param {Function} [props.onUsersChange] Callback appelé avec le nombre d'utilisateurs.
* @returns {JSX.Element} Composant formulaire.
*/
export default function Form({ adminToken = '', isAdmin = false, onUsersChange = noop }) {
const [formValues, setFormValues] = useState(initialFormValues);
const [users, setUsers] = useState([]);
const [editingUserId, setEditingUserId] = useState(null);
const [errors, setErrors] = useState({});
const [successMessage, setSuccessMessage] = useState('');
const [apiError, setApiError] = useState('');
const [isLoading, setIsLoading] = useState(true);
const isSubmitDisabled = Object.values(formValues).some((value) => value.trim() === '');
const loadUsers = useCallback(async () => {
setIsLoading(true);
try {
const apiUsers = isAdmin
? await getAdminUsers(adminToken)
: await getUsers();
setUsers(apiUsers);
onUsersChange(apiUsers.length);
setApiError('');
} catch (error) {
console.error(error);
setApiError("Impossible de récupérer les utilisateurs depuis l'API.");
} finally {
setIsLoading(false);
}
}, [adminToken, isAdmin, onUsersChange]);
useEffect(() => {
loadUsers();
}, [loadUsers]);
const syncUsers = (users) => {
setUsers(users);
onUsersChange(users.length);
};
const resetForm = () => {
setFormValues(initialFormValues);
setEditingUserId(null);
};
/**
* Met a jour le champ modifie dans l'etat local du formulaire.
* @param {React.ChangeEvent<HTMLInputElement>} event Evenement de changement.
* @returns {void}
*/
const handleChange = (event) => {
const { name, value } = event.target;
setFormValues((currentValues) => ({
...currentValues,
[name]: value,
}));
};
/**
* Valide le formulaire, enregistre l'utilisateur via l'API
* puis rafraichit la liste si tout est correct.
* @param {React.FormEvent<HTMLFormElement>} event Evenement de soumission du formulaire.
* @returns {Promise<void>}
*/
const handleSubmit = async (event) => {
event.preventDefault();
const data = {
name: formValues.name.trim(),
firstName: formValues.firstName.trim(),
birthDate: formValues.birthDate.trim(),
email: formValues.email.trim(),
city: formValues.city.trim(),
postalCode: formValues.postalCode.trim(),
};
const nextErrors = validateUser(data);
if (Object.keys(nextErrors).length > 0) {
setErrors(nextErrors);
setSuccessMessage('');
return;
}
try {
if (editingUserId) {
const updatedUser = await updateAdminUser(editingUserId, data, adminToken);
const updatedUsers = users.map((user) => (
user.id === editingUserId ? updatedUser : user
));
syncUsers(updatedUsers);
setSuccessMessage('Utilisateur mis à jour avec succès !');
} else {
await createUser(data);
await loadUsers();
setSuccessMessage('Utilisateur enregistré avec succès !');
}
setErrors({});
setApiError('');
resetForm();
} catch (error) {
console.error(error);
setApiError("Impossible d'enregistrer l'utilisateur depuis l'API.");
}
};
const handleDelete = async (userId) => {
try {
await deleteAdminUser(userId, adminToken);
const updatedUsers = users.filter((user) => user.id !== userId);
syncUsers(updatedUsers);
if (editingUserId === userId) {
resetForm();
}
setApiError('');
setSuccessMessage('Utilisateur supprimé avec succès !');
} catch (error) {
console.error(error);
setApiError("Impossible de supprimer l'utilisateur depuis l'API.");
}
};
const handleEdit = (user) => {
setEditingUserId(user.id);
setFormValues(toFormValues(user));
setErrors({});
setSuccessMessage('');
setApiError('');
};
const handleCancelEdit = () => {
resetForm();
setErrors({});
setSuccessMessage('');
};
return (
<>
<div className="form">
<h1>Formulaire</h1>
<form className='iner-form' onSubmit={handleSubmit} noValidate>
<div className="box-col">
<label>
Nom :
<input className="form-input-text" type="text" name="name" value={formValues.name} onChange={handleChange} />
</label>
{errors.name && <p role="alert" style={errorStyle}>{errors.name}</p>}
<label>
Prénom :
<input className="form-input-text" type="text" name="firstName" value={formValues.firstName} onChange={handleChange} />
</label>
{errors.firstName && <p role="alert" style={errorStyle}>{errors.firstName}</p>}
<label>
Date de naissance :
<input className="form-input-text" type="date" name="birthDate" value={formValues.birthDate} onChange={handleChange} />
</label>
{errors.birthDate && <p role="alert" style={errorStyle}>{errors.birthDate}</p>}
</div>
<div className="box-col" >
<label>
Mail :
<input className="form-input-text" type="email" name="email" value={formValues.email} onChange={handleChange} />
</label>
{errors.email && <p role="alert" style={errorStyle}>{errors.email}</p>}
<label>
Ville :
<input className="form-input-text" type="text" name="city" value={formValues.city} onChange={handleChange} />
</label>
{errors.city && <p role="alert" style={errorStyle}>{errors.city}</p>}
<label>
Code postal :
<input className="form-input-text" type="text" name="postalCode" value={formValues.postalCode} onChange={handleChange} />
</label>
{errors.postalCode && <p role="alert" style={errorStyle}>{errors.postalCode}</p>}
<input className="button" type="submit" value={editingUserId ? 'Mettre à jour' : 'Sauvegarder'} disabled={isSubmitDisabled} />
{editingUserId && <button className="button" type="button" onClick={handleCancelEdit}>Annuler</button>}
{successMessage && <p role="status" style={successStyle}>{successMessage}</p>}
{apiError && <p role="alert" style={errorStyle}>{apiError}</p>}
</div>
</form>
<h2>Liste des inscrits</h2>
{isLoading ? (
<p>Chargement des utilisateurs...</p>
) : users.length === 0 ? (
<p>Aucun inscrit pour le moment.</p>
) : (
<table className='table'>
<thead>
<tr>
<th scope="col">Nom</th>
<th scope="col">Prénom</th>
<th scope="col">Ville</th>
{isAdmin && <th scope="col">Email</th>}
{isAdmin && <th scope="col">Code postal</th>}
{isAdmin && <th scope="col">Date de naissance</th>}
{isAdmin && <th scope="col">Actions</th>}
</tr>
</thead>
<tbody>
{users.map((user) => (
<tr key={user.id}>
<td>{user.name}</td>
<td>{user.firstName}</td>
<td>{user.city}</td>
{isAdmin && <td>{user.email}</td>}
{isAdmin && <td>{user.postalCode}</td>}
{isAdmin && <td>{String(user.birthDate).slice(0, 10)}</td>}
{isAdmin && (
<td>
<button className="button" type="button" onClick={() => handleEdit(user)}>Modifier</button>
<button className="button" type="button" onClick={() => handleDelete(user.id)}>Supprimer</button>
</td>
)}
</tr>
))}
</tbody>
</table>
)}
</div>
</>
);
}