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

195 lines
7.3 KiB
JavaScript

import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { BarChart3, Clock, CheckCircle, AlertCircle } from 'lucide-react';
import VoteCard from '../components/VoteCard';
import ElectionDetailsModal from '../components/ElectionDetailsModal';
import LoadingSpinner from '../components/LoadingSpinner';
import './DashboardPage.css';
export default function DashboardPage({ voter }) {
const [votes, setVotes] = useState([]);
const [userVotes, setUserVotes] = useState([]);
const [loading, setLoading] = useState(true);
const [selectedElectionId, setSelectedElectionId] = useState(null);
useEffect(() => {
fetchVotes();
}, []);
const fetchVotes = async () => {
try {
const token = localStorage.getItem('token');
const [activeRes, upcomingRes, completedRes] = await Promise.all([
fetch('http://localhost:8000/api/elections/active', { headers: { 'Authorization': `Bearer ${token}` } }),
fetch('http://localhost:8000/api/elections/upcoming', { headers: { 'Authorization': `Bearer ${token}` } }),
fetch('http://localhost:8000/api/elections/completed', { headers: { 'Authorization': `Bearer ${token}` } })
]);
const active = activeRes.ok ? await activeRes.json() : [];
const upcoming = upcomingRes.ok ? await upcomingRes.json() : [];
const completed = completedRes.ok ? await completedRes.json() : [];
const allVotes = [
...((Array.isArray(active) ? active : [active]).map(v => ({ ...v, status: 'actif' }))),
...upcoming.map(v => ({ ...v, status: 'futur' })),
...completed.map(v => ({ ...v, status: 'ferme' }))
];
setVotes(allVotes);
const votesResponse = await fetch('http://localhost:8000/api/votes/history', {
headers: { 'Authorization': `Bearer ${token}` },
});
if (votesResponse.ok) {
const votesData = await votesResponse.json();
setUserVotes(votesData);
}
} catch (err) {
console.error('Erreur:', err);
} finally {
setLoading(false);
}
};
const activeVotes = votes.filter(v => v.status === 'actif');
const futureVotes = votes.filter(v => v.status === 'futur');
const historyVotes = votes.filter(v => v.status === 'ferme');
if (loading) return <LoadingSpinner fullscreen />;
return (
<div className="dashboard-page">
<div className="container">
<div className="dashboard-header">
<h1>Bienvenue, {voter?.nom}! 👋</h1>
<p>Voici votre tableau de bord personnel</p>
</div>
<div className="stats-grid">
<div className="stat-card">
<div className="stat-icon active"><AlertCircle size={24} /></div>
<div className="stat-content">
<div className="stat-value">{activeVotes.length}</div>
<div className="stat-label">Votes Actifs</div>
<Link to="/dashboard/actifs" className="stat-link">Voir </Link>
</div>
</div>
<div className="stat-card">
<div className="stat-icon future"><Clock size={24} /></div>
<div className="stat-content">
<div className="stat-value">{futureVotes.length}</div>
<div className="stat-label">À Venir</div>
<Link to="/dashboard/futurs" className="stat-link">Voir </Link>
</div>
</div>
<div className="stat-card">
<div className="stat-icon completed"><CheckCircle size={24} /></div>
<div className="stat-content">
<div className="stat-value">{historyVotes.length}</div>
<div className="stat-label">Votes Terminés</div>
<Link to="/dashboard/historique" className="stat-link">Voir </Link>
</div>
</div>
<div className="stat-card">
<div className="stat-icon total"><BarChart3 size={24} /></div>
<div className="stat-content">
<div className="stat-value">{userVotes.length}</div>
<div className="stat-label">Votes Effectués</div>
<Link to="/dashboard/historique" className="stat-link">Voir </Link>
</div>
</div>
</div>
{activeVotes.length > 0 && (
<div className="votes-section">
<h2> Votes Actifs</h2>
<p className="section-subtitle">Votes en cours - Participez maintenant!</p>
<div className="votes-grid">
{activeVotes.slice(0, 2).map(vote => (
<VoteCard
key={vote.id}
vote={vote}
userVote={userVotes.find(v => v.election_id === vote.id)?.choix}
onVote={(id) => window.location.href = `/vote/${id}`}
/>
))}
</div>
{activeVotes.length > 2 && (
<Link to="/dashboard/actifs" className="btn btn-secondary">
Voir tous les votes actifs ({activeVotes.length})
</Link>
)}
</div>
)}
{futureVotes.length > 0 && (
<div className="votes-section">
<h2>🔮 Votes à Venir</h2>
<p className="section-subtitle">Élections qui démarreront bientôt</p>
<div className="votes-grid">
{futureVotes.slice(0, 2).map(vote => (
<VoteCard
key={vote.id}
vote={vote}
context="futur"
onShowDetails={(id) => setSelectedElectionId(id)}
/>
))}
</div>
{futureVotes.length > 2 && (
<Link to="/dashboard/futurs" className="btn btn-secondary">
Voir tous les votes à venir ({futureVotes.length})
</Link>
)}
</div>
)}
{historyVotes.length > 0 && (
<div className="votes-section">
<h2>📋 Mon Historique</h2>
<p className="section-subtitle">Vos 2 derniers votes</p>
<div className="votes-grid">
{historyVotes.slice(0, 2).map(vote => (
<VoteCard
key={vote.id}
vote={vote}
userVote={userVotes.find(v => v.election_id === vote.id)?.choix}
showResult={true}
context="historique"
onShowDetails={(id) => setSelectedElectionId(id)}
/>
))}
</div>
{historyVotes.length > 2 && (
<Link to="/dashboard/historique" className="btn btn-secondary">
Voir tout mon historique ({historyVotes.length})
</Link>
)}
</div>
)}
{activeVotes.length === 0 && futureVotes.length === 0 && historyVotes.length === 0 && (
<div className="votes-section">
<div className="empty-state">
<div className="empty-icon">📭</div>
<h3>Aucun vote disponible</h3>
<p>Il n'y a pas encore de votes disponibles.</p>
</div>
</div>
)}
<ElectionDetailsModal
electionId={selectedElectionId}
isOpen={!!selectedElectionId}
onClose={() => setSelectedElectionId(null)}
voter={voter}
type="futur"
/>
</div>
</div>
);
}