- Migrate from React CRA to Next.js 15 with modern architecture - Implement comprehensive shadcn/ui component library - Create complete dashboard system with layouts and navigation - Build authentication pages (login, register) with proper forms - Implement vote management pages (active, upcoming, history, archives) - Add user profile management with security settings - Configure Tailwind CSS with custom dark theme (accent: #e8704b) - Setup TypeScript with strict type checking - Backup old React-based frontend to .backups/frontend-old - All pages compile successfully and build passes linting Pages created: - Home page with hero section and features - Authentication (login/register) - Dashboard with stats and vote cards - Vote management (active, upcoming, history, archives) - User profile with form validation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
102 lines
3.4 KiB
JavaScript
102 lines
3.4 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import LoadingSpinner from '../components/LoadingSpinner';
|
|
import ElectionDetailsModal from '../components/ElectionDetailsModal';
|
|
import './UpcomingVotesPage.css';
|
|
|
|
export default function UpcomingVotesPage({ voter }) {
|
|
const [upcomingElections, setUpcomingElections] = useState([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [selectedElectionId, setSelectedElectionId] = useState(null);
|
|
const navigate = useNavigate();
|
|
|
|
useEffect(() => {
|
|
fetchData();
|
|
}, []);
|
|
|
|
const fetchData = async () => {
|
|
try {
|
|
const token = localStorage.getItem('token');
|
|
|
|
// Fetch upcoming elections
|
|
const electionsResponse = await fetch('http://localhost:8000/api/elections/upcoming', {
|
|
headers: { 'Authorization': `Bearer ${token}` },
|
|
});
|
|
|
|
if (!electionsResponse.ok) throw new Error('Erreur de chargement');
|
|
|
|
const electionsData = await electionsResponse.json();
|
|
setUpcomingElections(electionsData || []);
|
|
|
|
} catch (err) {
|
|
console.error('Erreur:', err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
if (loading) return <LoadingSpinner fullscreen />;
|
|
|
|
return (
|
|
<div className="upcoming-votes-page">
|
|
<div className="container">
|
|
<div className="page-header">
|
|
<button className="back-btn" onClick={() => navigate('/dashboard')}>
|
|
← Retour au Tableau de Bord
|
|
</button>
|
|
<h1>⏳ Votes à Venir</h1>
|
|
<p>Élections qui arriveront prochainement</p>
|
|
</div>
|
|
|
|
{upcomingElections.length === 0 ? (
|
|
<div className="empty-state">
|
|
<div className="empty-icon">📭</div>
|
|
<h3>Aucun vote à venir</h3>
|
|
<p>Aucune élection prévue pour le moment.</p>
|
|
</div>
|
|
) : (
|
|
<>
|
|
<div className="stats-bar">
|
|
<div className="stat">
|
|
<span className="stat-label">Élections à venir</span>
|
|
<span className="stat-value">{upcomingElections.length}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="elections-grid">
|
|
{upcomingElections.map(election => (
|
|
<div key={election.id} className="election-card upcoming">
|
|
<div className="election-card-header">
|
|
<h3>{election.name}</h3>
|
|
<span className="status-badge upcoming">
|
|
⏳ À venir
|
|
</span>
|
|
</div>
|
|
<div className="election-card-body">
|
|
<p><strong>Description :</strong> {election.description || 'N/A'}</p>
|
|
<p><strong>Début :</strong> {new Date(election.start_date).toLocaleDateString('fr-FR')}</p>
|
|
<button
|
|
className="btn btn-primary"
|
|
onClick={() => setSelectedElectionId(election.id)}
|
|
>
|
|
Voir les détails
|
|
</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</>
|
|
)}
|
|
|
|
<ElectionDetailsModal
|
|
electionId={selectedElectionId}
|
|
isOpen={!!selectedElectionId}
|
|
onClose={() => setSelectedElectionId(null)}
|
|
voter={voter}
|
|
type="futur"
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|