Alexis Bruneteau 14eff8d0da feat: Rebuild frontend with Next.js and shadcn/ui components
- 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>
2025-11-06 17:02:14 +01:00

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>
);
}