SolyQuiz/app/quiz/[code]/StudentJoinClient.tsx
corenthin-lebreton 28aa3b0e10 initial project
2026-02-26 20:10:14 +01:00

163 lines
6.3 KiB
TypeScript

'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { User, UserCheck } from 'lucide-react'
import { cn } from '@/lib/utils'
interface Props {
sessionCode: string
quizTitle: string
schoolName: string | null
className: string | null
sessionId: string
}
export default function StudentJoinClient({ sessionCode, quizTitle, schoolName, className }: Props) {
const router = useRouter()
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const handleJoin = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
setError(null)
try {
const res = await fetch('/api/student/join', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
short_code: sessionCode,
first_name: firstName.trim(),
last_name: lastName.trim(),
}),
})
const data = await res.json()
if (!res.ok) {
if (data.code === 'SESSION_INACTIVE') {
setError('Ce quiz est terminé. Vous ne pouvez plus rejoindre cette session.')
} else {
setError(data.error ?? 'Erreur lors de la connexion')
}
} else {
router.push(`/quiz/${sessionCode}/exam?pid=${data.participation.id}`)
}
} catch {
setError('Erreur réseau. Vérifiez votre connexion.')
} finally {
setLoading(false)
}
}
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-8 h-8 bg-primary rounded-lg flex items-center justify-center shadow-md shadow-primary/30">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
<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-lg font-bold text-text-primary">SolyQuiz</span>
</div>
</header>
<div className="flex-1 flex items-center justify-center p-4">
<div className="w-full max-w-md">
<div className="card overflow-hidden shadow-2xl shadow-black/40">
{/* Gradient header */}
<div className="relative h-28 bg-gradient-to-br from-primary/80 to-blue-700/60 flex items-center justify-center overflow-hidden">
<div className="absolute w-40 h-40 rounded-full bg-white/10 blur-xl -right-5 -top-5" />
<div className="relative z-10 text-center">
<div className="w-12 h-12 bg-white/15 backdrop-blur-sm border border-white/20 rounded-xl flex items-center justify-center mx-auto mb-1">
<UserCheck size={24} className="text-white" />
</div>
</div>
</div>
<div className="p-8">
<div className="text-center mb-6">
<h1 className="text-xl font-bold text-text-primary mb-1">{quizTitle}</h1>
<div className="flex items-center justify-center gap-3 text-xs text-text-muted">
{schoolName && <span>📍 {schoolName}</span>}
{className && <span>🎓 {className}</span>}
<span className="font-mono bg-background-elevated px-2 py-0.5 rounded text-primary font-semibold">
#{sessionCode}
</span>
</div>
</div>
<form onSubmit={handleJoin} className="space-y-4">
<div>
<label className="block text-xs font-medium text-text-secondary uppercase tracking-wider mb-1.5 ml-1">
Prénom
</label>
<div className="relative">
<User size={15} className="absolute left-3 top-1/2 -translate-y-1/2 text-text-muted" />
<input
type="text"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
placeholder="Votre prénom"
className="input-field pl-9"
required
autoFocus
/>
</div>
</div>
<div>
<label className="block text-xs font-medium text-text-secondary uppercase tracking-wider mb-1.5 ml-1">
Nom
</label>
<div className="relative">
<User size={15} className="absolute left-3 top-1/2 -translate-y-1/2 text-text-muted" />
<input
type="text"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
placeholder="Votre nom de famille"
className="input-field pl-9"
required
/>
</div>
</div>
{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>
)}
<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...
</>
) : (
'Commencer le quiz'
)}
</button>
</form>
</div>
</div>
</div>
</div>
</div>
)
}