- Created `/frontend/app/api/votes/check/route.ts` to handle GET requests for checking if a user has voted in a specific election. - Added error handling for unauthorized access and missing election ID. - Forwarded requests to the backend API and returned appropriate responses. - Updated `/frontend/app/api/votes/history/route.ts` to fetch user's voting history with error handling. - Ensured both endpoints utilize the authorization token for secure access.
7.9 KiB
7.9 KiB
🧩 Documentation des Composants
Vue d'ensemble
Tous les composants sont dans src/components/ et réutilisables dans l'ensemble de l'application.
Header
Barre de navigation principale de l'application.
Props
Header.propTypes = {
voter: PropTypes.object, // Données de l'utilisateur connecté
onLogout: PropTypes.func.isRequired, // Callback de déconnexion
}
Utilisation
import Header from './components/Header';
<Header voter={voter} onLogout={handleLogout} />
Fonctionnalités
- Logo cliquable
- Navigation responsive (menu hamburger sur mobile)
- Liens différents selon la connexion
- Profil utilisateur
- Bouton de déconnexion
Footer
Pied de page avec liens et informations.
Utilisation
import Footer from './components/Footer';
<Footer />
Sections
- À propos
- Liens rapides
- Légal
- Contact
VoteCard
Affiche un vote sous forme de carte.
Props
VoteCard.propTypes = {
vote: PropTypes.object.isRequired, // Objet vote
onVote: PropTypes.func, // Callback pour voter
userVote: PropTypes.string, // Le vote de l'utilisateur (si votant)
showResult: PropTypes.bool, // Afficher les résultats
}
Utilisation
import VoteCard from './components/VoteCard';
<VoteCard
vote={vote}
onVote={handleVote}
userVote="Oui"
showResult={true}
/>
États
- Actif: Bouton "VOTER MAINTENANT"
- Déjà voté: Bouton désactivé "DÉJÀ VOTÉ" avec checkmark
- Fermé: Bouton "Voir les Détails"
- Futur: Bouton "M'alerter"
Affichage des Résultats
Si showResult={true} et le vote est fermé:
- Graphique en barres avec pourcentages
- Nombre total de votes
Alert
Notifications avec différents types.
Props
Alert.propTypes = {
type: PropTypes.oneOf(['success', 'error', 'warning', 'info']),
title: PropTypes.string, // Titre optionnel
message: PropTypes.string.isRequired, // Message d'alerte
icon: PropTypes.elementType, // Icône personnalisée
onClose: PropTypes.func, // Callback de fermeture
}
Utilisation
import Alert from './components/Alert';
// Simple
<Alert type="success" message="Succès!" />
// Avec titre et fermeture
<Alert
type="error"
title="Erreur"
message="Une erreur s'est produite"
onClose={() => setError('')}
/>
Types
| Type | Couleur | Utilisation |
|---|---|---|
success |
Vert | Confirmations, réussite |
error |
Rouge | Erreurs |
warning |
Orange | Avertissements, actions irréversibles |
info |
Bleu | Informations générales |
Modal
Boîte de dialogue modale.
Props
Modal.propTypes = {
isOpen: PropTypes.bool.isRequired, // Afficher/Masquer la modale
title: PropTypes.string, // Titre optionnel
children: PropTypes.node.isRequired, // Contenu
onClose: PropTypes.func.isRequired, // Fermeture
onConfirm: PropTypes.func, // Action de confirmation
confirmText: PropTypes.string, // Texte du bouton confirm (défaut: "Confirmer")
cancelText: PropTypes.string, // Texte du bouton cancel (défaut: "Annuler")
type: PropTypes.oneOf(['default', 'danger']), // Type d'alerte
}
Utilisation
import Modal from './components/Modal';
<Modal
isOpen={showModal}
title="Confirmer votre vote"
onClose={() => setShowModal(false)}
onConfirm={handleVote}
confirmText="Confirmer"
cancelText="Annuler"
>
<p>Êtes-vous sûr de votre choix?</p>
</Modal>
LoadingSpinner
Indicateur de chargement.
Props
LoadingSpinner.propTypes = {
fullscreen: PropTypes.bool, // Mode plein écran avec overlay
}
Utilisation
import LoadingSpinner from './components/LoadingSpinner';
// Inline
<LoadingSpinner />
// Plein écran
<LoadingSpinner fullscreen={true} />
Patterns de Composants
Pattern 1: Formulaire avec Validation
import { useForm } from '../hooks/useApi';
function MyForm() {
const { values, errors, handleChange, handleSubmit } = useForm(
{ email: '', password: '' },
async (values) => {
// Submit logic
}
);
return (
<form onSubmit={handleSubmit}>
<input
name="email"
value={values.email}
onChange={handleChange}
/>
{errors.email && <span>{errors.email}</span>}
<button type="submit">Envoyer</button>
</form>
);
}
Pattern 2: Chargement de Données
import { useApi } from '../hooks/useApi';
function MyComponent() {
const { data, loading, error } = useApi('/api/endpoint');
if (loading) return <LoadingSpinner />;
if (error) return <Alert type="error" message={error} />;
return <div>{data}</div>;
}
Pattern 3: Modal de Confirmation
function MyComponentWithConfirmation() {
const [showModal, setShowModal] = useState(false);
const handleDelete = async () => {
// Delete logic
setShowModal(false);
};
return (
<>
<button onClick={() => setShowModal(true)}>Supprimer</button>
<Modal
isOpen={showModal}
title="Confirmer la suppression"
onClose={() => setShowModal(false)}
onConfirm={handleDelete}
type="danger"
>
<p>Cette action est irréversible.</p>
</Modal>
</>
);
}
Styling des Composants
Tous les composants utilisent des classes CSS dans le fichier styles/components.css.
Classes Disponibles
<!-- Boutons -->
<button class="btn btn-primary">Primaire</button>
<button class="btn btn-secondary">Secondaire</button>
<button class="btn btn-success">Succès</button>
<button class="btn btn-danger">Danger</button>
<button class="btn btn-warning">Warning</button>
<button class="btn btn-ghost">Ghost</button>
<!-- Tailles -->
<button class="btn btn-sm">Petit</button>
<button class="btn btn-lg">Grand</button>
<!-- Autre -->
<button class="btn btn-block">Pleine largeur</button>
Personnalisation
Modifiez les styles dans src/styles/components.css:
.btn-primary {
background-color: var(--primary-blue);
/* ... */
}
Accessibilité
Tous les composants respectent les standards WCAG 2.1:
- ✅ Navigation au clavier (Tab, Enter, Escape)
- ✅ Contraste de couleur minimum AA
- ✅ Textes alternatifs pour les icônes
- ✅ Labels associés aux inputs
- ✅ Sémantique HTML correcte
- ✅ Focus visible
- ✅ Aria attributes
Exemple
<button
aria-label="Fermer le menu"
onClick={close}
>
✕
</button>
Performance
Code Splitting
Utilisez React.lazy() pour charger les pages à la demande:
const DashboardPage = React.lazy(() => import('./pages/DashboardPage'));
<Suspense fallback={<LoadingSpinner />}>
<DashboardPage />
</Suspense>
Memoization
Pour les composants coûteux:
import { memo } from 'react';
const VoteCard = memo(({ vote, onVote }) => {
// Component
});
Troubleshooting
Le composant ne s'affiche pas
- Vérifiez que les props requises sont passées
- Vérifiez les erreurs dans la console
- Vérifiez les imports
Les styles ne s'appliquent pas
- Vérifiez que le CSS est importé dans
index.js - Vérifiez la spécificité des classes CSS
- Utilisez DevTools pour inspecter les styles
Le composant est lent
- Utilisez
React.memo()si le composant dépend de props simples - Utilisez
useMemo()pour les calculs coûteux - Vérifiez les rendus inutiles avec DevTools
Améliorations Futures
- Ajouter des animations avec Framer Motion
- Ajouter un thème sombre
- Composants Storybook
- Tests unitaires pour tous les composants
- Gérer l'internationalization (i18n)
Pour toute question, consultez la documentation officielle React.