Alexis Bruneteau 546785ef67 feat: Integrate backend API with frontend - Authentication & Elections
Core Integration:
- Create API client with TypeScript types for all endpoints
- Implement authentication context provider for user state management
- Add protected route component for dashboard access control
- Connect login/register pages to backend authentication endpoints
- Implement user session persistence with localStorage tokens

Authentication:
- Login page now connects to /api/auth/login endpoint
- Register page connects to /api/auth/register with validation
- Password strength requirements (min 8 chars)
- Form validation and error handling
- Automatic redirect to dashboard on successful auth
- Logout functionality with session cleanup

Protected Routes:
- Dashboard pages require authentication
- Non-authenticated users redirected to login
- Loading spinner during auth verification
- User name displayed in dashboard header
- Proper session management

Election/Vote APIs:
- Dashboard fetches active elections from /api/elections/active
- Display real election data with candidates count
- Handle loading and error states
- Skeleton loaders for better UX

Type Safety:
- Full TypeScript interfaces for all API responses
- Proper error handling with try-catch blocks
- API response types: AuthToken, VoterProfile, Election, Candidate, Vote, VoteHistory

Environment:
- API URL configurable via NEXT_PUBLIC_API_URL env variable
- Default to http://localhost:8000 for local development

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-06 17:15:34 +01:00

174 lines
6.5 KiB
TypeScript

"use client"
import Link from "next/link"
import { useState } from "react"
import { useRouter } from "next/navigation"
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Mail, Lock, LogIn, AlertCircle } from "lucide-react"
import { useAuth } from "@/lib/auth-context"
export default function LoginPage() {
const router = useRouter()
const { login, isLoading } = useAuth()
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [error, setError] = useState("")
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setError("")
if (!email || !password) {
setError("Email et mot de passe requis")
return
}
try {
await login(email, password)
router.push("/dashboard")
} catch (err) {
setError("Email ou mot de passe incorrect")
}
}
return (
<div className="min-h-screen grid grid-cols-1 md:grid-cols-2">
{/* Left side - Form */}
<div className="flex items-center justify-center p-4 md:p-8 bg-background">
<div className="w-full max-w-sm space-y-8">
<div className="space-y-2 text-center">
<h1 className="text-3xl font-bold">Se Connecter</h1>
<p className="text-muted-foreground">Accédez à votre tableau de bord</p>
</div>
<Card>
<CardContent className="pt-6">
{error && (
<div className="mb-4 p-4 rounded-md bg-destructive/10 border border-destructive/50 flex gap-3">
<AlertCircle className="w-5 h-5 text-destructive flex-shrink-0 mt-0.5" />
<div>
<p className="text-sm font-medium text-destructive">Erreur de connexion</p>
<p className="text-sm text-destructive/80">{error}</p>
</div>
</div>
)}
<form onSubmit={handleSubmit} className="space-y-5">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<div className="relative">
<Mail className="absolute left-3 top-3 w-5 h-5 text-muted-foreground" />
<Input
id="email"
type="email"
placeholder="votre@email.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
disabled={isLoading}
className="pl-10"
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="password">Mot de passe</Label>
<div className="relative">
<Lock className="absolute left-3 top-3 w-5 h-5 text-muted-foreground" />
<Input
id="password"
type="password"
placeholder="••••••••"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
disabled={isLoading}
className="pl-10"
/>
</div>
</div>
<div className="text-right">
<Link href="#" className="text-sm text-accent hover:underline">
Mot de passe oublié ?
</Link>
</div>
<Button
type="submit"
className="w-full"
disabled={isLoading}
>
{isLoading ? (
"Connexion en cours..."
) : (
<>
<LogIn className="w-4 h-4 mr-2" />
Se Connecter
</>
)}
</Button>
</form>
<div className="relative my-6">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-border"></div>
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 bg-background text-muted-foreground">ou</span>
</div>
</div>
<p className="text-center text-sm text-muted-foreground">
Pas encore de compte?{" "}
<Link href="/auth/register" className="text-accent font-medium hover:underline">
S'inscrire
</Link>
</p>
</CardContent>
</Card>
</div>
</div>
{/* Right side - Illustration */}
<div className="hidden md:flex items-center justify-center p-8 bg-gradient-to-br from-card to-background">
<div className="text-center space-y-8 max-w-sm">
<div className="text-7xl">🗳️</div>
<div className="space-y-4">
<h2 className="text-3xl font-bold">Bienvenue</h2>
<p className="text-muted-foreground leading-relaxed">
Votez en toute confiance sur notre plateforme sécurisée par cryptographie post-quantique
</p>
</div>
<div className="space-y-4 pt-4">
<div className="flex items-center gap-4 text-left">
<span className="text-2xl">🔒</span>
<div>
<p className="font-semibold text-foreground">Cryptographie Post-Quantique</p>
<p className="text-sm text-muted-foreground">Sécurité certifiée NIST</p>
</div>
</div>
<div className="flex items-center gap-4 text-left">
<span className="text-2xl">📊</span>
<div>
<p className="font-semibold text-foreground">Résultats Transparents</p>
<p className="text-sm text-muted-foreground">Traçabilité complète</p>
</div>
</div>
<div className="flex items-center gap-4 text-left">
<span className="text-2xl">⚡</span>
<div>
<p className="font-semibold text-foreground">Accès Instantané</p>
<p className="text-sm text-muted-foreground">De n'importe quel appareil</p>
</div>
</div>
</div>
</div>
</div>
</div>
)
}