257 lines
9.5 KiB
TypeScript

'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import Link from 'next/link'
import {
Mail,
Lock,
User,
Shield,
Eye,
EyeOff,
Loader2,
ArrowLeft,
RefreshCw,
Check,
} from 'lucide-react'
import { cn } from '@/lib/utils'
function generatePassword() {
const chars = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789!@#$'
return Array.from({ length: 12 }, () => chars[Math.floor(Math.random() * chars.length)]).join('')
}
export default function CreateUserPage() {
const router = useRouter()
const [email, setEmail] = useState('')
const [username, setUsername] = useState('')
const [password, setPassword] = useState(generatePassword())
const [role, setRole] = useState<'formateur' | 'admin'>('formateur')
const [showPassword, setShowPassword] = useState(false)
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [success, setSuccess] = useState(false)
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
setError(null)
try {
const res = await fetch('/api/admin/users/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, username, password, role }),
})
const data = await res.json()
if (!res.ok) {
setError(data.error)
} else {
setSuccess(true)
setTimeout(() => router.push('/dashboard/admin/users'), 1500)
}
} catch {
setError('Erreur réseau')
} finally {
setLoading(false)
}
}
if (success) {
return (
<div className="p-8 flex items-center justify-center min-h-[60vh]">
<div className="text-center">
<div className="w-16 h-16 bg-green-500/20 rounded-full flex items-center justify-center mx-auto mb-4">
<Check size={32} className="text-green-400" />
</div>
<h2 className="text-xl font-bold text-text-primary mb-2">Compte créé !</h2>
<p className="text-text-secondary">Redirection en cours...</p>
</div>
</div>
)
}
return (
<div className="p-4 md:p-8">
<div className="max-w-xl mx-auto">
{/* Header */}
<div className="mb-8">
<Link
href="/dashboard/admin/users"
className="flex items-center gap-1.5 text-sm text-text-muted hover:text-text-primary transition-colors mb-4"
>
<ArrowLeft size={15} />
Retour à la liste
</Link>
<h1 className="text-3xl font-bold text-text-primary mb-2">Créer un compte</h1>
<p className="text-text-secondary">
Le nouveau membre pourra se connecter immédiatement avec ses identifiants.
</p>
</div>
<form onSubmit={handleSubmit}>
<div className="card overflow-hidden">
{/* Identité */}
<div className="p-6 border-b border-border">
<div className="flex items-center gap-2 mb-5">
<User size={18} className="text-primary" />
<h2 className="font-semibold text-text-primary">Identité</h2>
</div>
<div className="space-y-4">
<div>
<label className="block text-sm text-text-secondary mb-1.5">
Nom d&apos;utilisateur *
</label>
<div className="relative">
<User size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-text-muted" />
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Ex: jean.dupont"
className="input-field pl-9"
required
/>
</div>
</div>
<div>
<label className="block text-sm text-text-secondary mb-1.5">
Adresse email *
</label>
<div className="relative">
<Mail size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-text-muted" />
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Ex: jean.dupont@solyti.fr"
className="input-field pl-9"
required
/>
</div>
</div>
</div>
</div>
{/* Rôle */}
<div className="p-6 border-b border-border">
<div className="flex items-center gap-2 mb-5">
<Shield size={18} className="text-primary" />
<h2 className="font-semibold text-text-primary">Rôle</h2>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{([
{
value: 'formateur',
label: 'Formateur',
desc: 'Gère ses quiz et sessions',
icon: '👨‍🏫',
},
{
value: 'admin',
label: 'Administrateur',
desc: 'Accès complet + gestion des comptes',
icon: '🛡️',
},
] as const).map((r) => (
<button
key={r.value}
type="button"
onClick={() => setRole(r.value)}
className={cn(
'text-left p-4 rounded-xl border-2 transition-all',
role === r.value
? 'border-primary bg-primary/10'
: 'border-border bg-background-elevated/30 hover:border-border-light'
)}
>
<span className="text-2xl mb-2 block">{r.icon}</span>
<p className={cn(
'font-semibold text-sm',
role === r.value ? 'text-primary' : 'text-text-primary'
)}>
{r.label}
</p>
<p className="text-xs text-text-muted mt-0.5">{r.desc}</p>
</button>
))}
</div>
</div>
{/* Mot de passe */}
<div className="p-6">
<div className="flex items-center gap-2 mb-5">
<Lock size={18} className="text-primary" />
<h2 className="font-semibold text-text-primary">Mot de passe temporaire</h2>
</div>
<div>
<label className="block text-sm text-text-secondary mb-1.5">
Mot de passe *{' '}
<span className="text-text-muted">(min. 8 caractères)</span>
</label>
<div className="relative">
<Lock size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-text-muted" />
<input
type={showPassword ? 'text' : 'password'}
value={password}
onChange={(e) => setPassword(e.target.value)}
className="input-field pl-9 pr-20"
required
minLength={8}
/>
<div className="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-1">
<button
type="button"
onClick={() => setPassword(generatePassword())}
className="p-1.5 hover:bg-background-elevated rounded-md text-text-muted hover:text-text-primary transition-colors"
title="Générer un nouveau mot de passe"
>
<RefreshCw size={14} />
</button>
<button
type="button"
onClick={() => setShowPassword((v) => !v)}
className="p-1.5 hover:bg-background-elevated rounded-md text-text-muted hover:text-text-primary transition-colors"
>
{showPassword ? <EyeOff size={14} /> : <Eye size={14} />}
</button>
</div>
</div>
<p className="text-xs text-text-muted mt-1.5">
Communiquez ce mot de passe à l&apos;utilisateur. Il pourra le modifier depuis ses paramètres.
</p>
</div>
</div>
{/* Footer */}
<div className="px-6 py-4 border-t border-border bg-background-secondary/50 flex items-center justify-between">
<Link href="/dashboard/admin/users" className="btn-secondary">
Annuler
</Link>
<div className="flex items-center gap-3">
{error && <p className="text-sm text-red-400 max-w-xs text-right">{error}</p>}
<button
type="submit"
disabled={loading}
className={cn('btn-primary', loading && 'opacity-70 cursor-not-allowed')}
>
{loading ? (
<><Loader2 size={16} className="animate-spin" /> Création...</>
) : (
'Créer le compte'
)}
</button>
</div>
</div>
</div>
</form>
</div>
</div>
)
}