SolyQuiz/app/login/page.tsx
corenthin-lebreton 28aa3b0e10 initial project
2026-02-26 20:10:14 +01:00

212 lines
9.2 KiB
TypeScript

'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { createClient } from '@/lib/supabase/client'
import { Eye, EyeOff, HelpCircle, Shield } from 'lucide-react'
import { cn } from '@/lib/utils'
export default function LoginPage() {
const router = useRouter()
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [showPassword, setShowPassword] = useState(false)
const [rememberMe, setRememberMe] = useState(false)
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
setError(null)
const supabase = createClient()
// Supabase Auth utilise l'email — on convertit le username en email fictif
// ou on utilise l'email directement selon la configuration
const email = username.includes('@') ? username : `${username}@solyquiz.local`
const { error: authError } = await supabase.auth.signInWithPassword({
email,
password,
})
if (authError) {
setError('Identifiants incorrects. Vérifiez votre nom d\'utilisateur et mot de passe.')
setLoading(false)
return
}
router.push('/dashboard')
router.refresh()
}
return (
<div className="min-h-screen bg-background flex flex-col">
{/* Header */}
<header className="flex items-center justify-between px-10 py-5 border-b border-border">
<div className="flex items-center gap-3">
<div className="w-9 h-9 bg-primary rounded-lg flex items-center justify-center shadow-lg shadow-primary/30">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 14H9V8h2v8zm4 0h-2V8h2v8z" fill="white"/>
</svg>
</div>
<span className="text-xl font-bold text-text-primary">SolyQuiz</span>
</div>
<button className="text-text-secondary hover:text-text-primary transition-colors">
<HelpCircle size={20} />
</button>
</header>
{/* Background decorations */}
<div className="flex-1 relative overflow-hidden flex items-center justify-center">
<div className="absolute w-96 h-96 rounded-full bg-primary/5 blur-3xl -left-20 top-20" />
<div className="absolute w-96 h-96 rounded-full bg-blue-500/5 blur-3xl right-20 bottom-20" />
{/* Login Card */}
<div className="relative z-10 w-full max-w-md mx-4">
<div className="card overflow-hidden shadow-2xl shadow-black/40">
{/* Card header gradient */}
<div className="relative h-32 bg-gradient-to-br from-primary/80 to-blue-600/60 flex items-center justify-center overflow-hidden">
<div className="absolute w-48 h-48 rounded-full bg-white/10 blur-2xl -right-10 -top-10" />
<div className="absolute w-40 h-40 rounded-full bg-white/5 blur-xl -left-10 bottom-0" />
<div className="relative z-10 w-16 h-18 bg-white/10 backdrop-blur-sm border border-white/20 rounded-xl flex items-center justify-center p-3">
<svg width="36" height="44" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 14H9V8h2v8zm4 0h-2V8h2v8z" fill="white"/>
</svg>
</div>
</div>
{/* Card body */}
<div className="p-8">
<div className="text-center mb-8">
<h1 className="text-2xl font-bold text-text-primary mb-1">Espace Formateur</h1>
<p className="text-text-secondary text-sm">
Connectez-vous pour gérer vos quiz et évaluations.
</p>
</div>
<form onSubmit={handleLogin} className="space-y-5">
{/* Username */}
<div>
<label className="block text-xs font-medium text-text-secondary uppercase tracking-wider mb-1.5 ml-1">
Nom d&apos;utilisateur
</label>
<div className="relative">
<div className="absolute left-3 top-1/2 -translate-y-1/2 text-text-muted">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
<circle cx="12" cy="7" r="4" />
</svg>
</div>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="ex: formateur.dupont"
className="input-field pl-10"
required
/>
</div>
</div>
{/* Password */}
<div>
<label className="block text-xs font-medium text-text-secondary uppercase tracking-wider mb-1.5 ml-1">
Mot de passe
</label>
<div className="relative">
<div className="absolute left-3 top-1/2 -translate-y-1/2 text-text-muted">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
</svg>
</div>
<input
type={showPassword ? 'text' : 'password'}
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="••••••••"
className="input-field pl-10 pr-10"
required
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-text-muted hover:text-text-secondary transition-colors"
>
{showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
</button>
</div>
</div>
{/* Remember me + forgot password */}
<div className="flex items-center justify-between">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={rememberMe}
onChange={(e) => setRememberMe(e.target.checked)}
className="w-4 h-4 rounded border-border bg-background-elevated text-primary"
/>
<span className="text-sm text-text-secondary">Rester connecté</span>
</label>
<button type="button" className="text-sm text-primary hover:text-primary-light transition-colors">
Mot de passe oublié ?
</button>
</div>
{/* Error message */}
{error && (
<div className="bg-red-500/10 border border-red-500/30 text-red-400 text-sm px-4 py-3 rounded-lg">
{error}
</div>
)}
{/* Submit */}
<button
type="submit"
disabled={loading}
className={cn(
'btn-primary w-full justify-center py-3',
loading && 'opacity-70 cursor-not-allowed'
)}
>
{loading ? (
<>
<svg className="animate-spin w-4 h-4" viewBox="0 0 24 24" fill="none">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
Connexion...
</>
) : (
'Se connecter'
)}
</button>
</form>
</div>
{/* Card footer */}
<div className="border-t border-border px-8 py-4">
<p className="text-center text-xs text-text-muted">
En continuant, vous acceptez les{' '}
<button className="text-primary hover:text-primary-light transition-colors">
Conditions d&apos;utilisation
</button>{' '}
de SolyQuiz.
</p>
</div>
</div>
{/* Secure connection note */}
<div className="flex items-center justify-center gap-1.5 mt-4">
<Shield size={14} className="text-text-muted" />
<span className="text-xs text-text-muted">Connexion sécurisée par SSL</span>
</div>
</div>
</div>
</div>
)
}