All checks were successful
Build and Push App / build (push) Successful in 5m50s
220 lines
12 KiB
TypeScript
220 lines
12 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import Link from 'next/link';
|
|
import { User, Mail, Lock, ArrowRight, CheckCircle, AlertCircle } from 'lucide-react';
|
|
import { useUser } from '../../components/UserProvider';
|
|
|
|
const RegisterPage = () => {
|
|
const { settings } = useUser();
|
|
const router = useRouter();
|
|
const siteName = settings?.siteName || "Afrohub";
|
|
const [formData, setFormData] = useState({
|
|
name: '',
|
|
email: '',
|
|
password: '',
|
|
confirmPassword: ''
|
|
});
|
|
const [error, setError] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
const [success, setSuccess] = useState(false);
|
|
const [csrfToken, setCsrfToken] = useState('');
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
setFormData({ ...formData, [e.target.name]: e.target.value });
|
|
setError('');
|
|
};
|
|
|
|
// Fetch CSRF token on mount
|
|
React.useEffect(() => {
|
|
fetch('/api/auth/csrf')
|
|
.then(res => res.json())
|
|
.then(data => setCsrfToken(data.csrfToken))
|
|
.catch(err => console.error('Error fetching CSRF token:', err));
|
|
}, []);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
setError('');
|
|
|
|
if (formData.password !== formData.confirmPassword) {
|
|
setError('Les mots de passe ne correspondent pas');
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const res = await fetch('/api/auth/register', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'x-csrf-token': csrfToken
|
|
},
|
|
body: JSON.stringify({
|
|
name: formData.name,
|
|
email: formData.email,
|
|
password: formData.password
|
|
}),
|
|
});
|
|
|
|
const data = await res.json();
|
|
|
|
if (!res.ok) {
|
|
throw new Error(data.error || "Une erreur est survenue");
|
|
}
|
|
|
|
setSuccess(true);
|
|
setTimeout(() => {
|
|
router.push('/login');
|
|
}, 2000);
|
|
|
|
} catch (err: any) {
|
|
setError(err.message);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8 bg-[radial-gradient(circle_at_top_right,_var(--color-brand-100),_transparent_40%),_radial-gradient(circle_at_bottom_left,_var(--color-brand-50),_transparent_40%)]">
|
|
<div className="max-w-md w-full">
|
|
<div className="text-center mb-10">
|
|
<Link href="/" className="inline-flex items-center justify-center h-16 w-16 bg-brand-600 rounded-2xl shadow-lg shadow-brand-200 text-white font-bold text-3xl mb-6 hover:scale-105 transition-transform">
|
|
{siteName.charAt(0)}
|
|
</Link>
|
|
<h2 className="text-4xl font-extrabold text-dark-900 font-serif mb-2">Rejoignez l'aventure</h2>
|
|
<p className="text-gray-600">Créez votre compte pour propulser votre entreprise</p>
|
|
</div>
|
|
|
|
<div className="bg-white/80 backdrop-blur-xl p-8 rounded-3xl shadow-2xl border border-white shadow-brand-100/50">
|
|
{success ? (
|
|
<div className="text-center py-10 animate-in fade-in zoom-in duration-500">
|
|
<div className="inline-flex items-center justify-center h-20 w-20 bg-green-100 text-green-600 rounded-full mb-6">
|
|
<CheckCircle size={40} />
|
|
</div>
|
|
<h3 className="text-2xl font-bold text-gray-900 mb-2">Compte créé !</h3>
|
|
<p className="text-gray-600">Redirection vers la page de connexion...</p>
|
|
</div>
|
|
) : (
|
|
<form className="space-y-6" onSubmit={handleSubmit} method="POST">
|
|
<input type="hidden" name="csrf_token" value={csrfToken} />
|
|
{error && (
|
|
<div className="bg-red-50 border-l-4 border-red-500 p-4 rounded-r-lg flex items-center gap-3 text-red-700 animate-in slide-in-from-top-2 duration-300">
|
|
<AlertCircle size={20} className="shrink-0" />
|
|
<p className="text-sm font-medium">{error}</p>
|
|
</div>
|
|
)}
|
|
|
|
<div className="space-y-4">
|
|
<div className="space-y-1">
|
|
<label className="text-sm font-semibold text-gray-700 ml-1">Nom complet</label>
|
|
<div className="relative group">
|
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-gray-400 group-focus-within:text-brand-600 transition-colors">
|
|
<User size={18} />
|
|
</div>
|
|
<input
|
|
name="name"
|
|
type="text"
|
|
required
|
|
className="block w-full pl-10 pr-3 py-3 border border-gray-200 rounded-xl bg-gray-50/50 focus:bg-white focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500 transition-all text-gray-900 placeholder-gray-400"
|
|
placeholder="Ex: Jean Dupont"
|
|
value={formData.name}
|
|
onChange={handleChange}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-1">
|
|
<label className="text-sm font-semibold text-gray-700 ml-1">Adresse email</label>
|
|
<div className="relative group">
|
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-gray-400 group-focus-within:text-brand-600 transition-colors">
|
|
<Mail size={18} />
|
|
</div>
|
|
<input
|
|
name="email"
|
|
type="email"
|
|
required
|
|
className="block w-full pl-10 pr-3 py-3 border border-gray-200 rounded-xl bg-gray-50/50 focus:bg-white focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500 transition-all text-gray-900 placeholder-gray-400"
|
|
placeholder="votre@email.com"
|
|
value={formData.email}
|
|
onChange={handleChange}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-1">
|
|
<label className="text-sm font-semibold text-gray-700 ml-1">Mot de passe</label>
|
|
<div className="relative group">
|
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-gray-400 group-focus-within:text-brand-600 transition-colors">
|
|
<Lock size={18} />
|
|
</div>
|
|
<input
|
|
name="password"
|
|
type="password"
|
|
required
|
|
className="block w-full pl-10 pr-3 py-3 border border-gray-200 rounded-xl bg-gray-50/50 focus:bg-white focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500 transition-all text-gray-900 placeholder-gray-400"
|
|
placeholder="••••••••"
|
|
value={formData.password}
|
|
onChange={handleChange}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-1">
|
|
<label className="text-sm font-semibold text-gray-700 ml-1">Confirmer le mot de passe</label>
|
|
<div className="relative group">
|
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-gray-400 group-focus-within:text-brand-600 transition-colors">
|
|
<Lock size={18} />
|
|
</div>
|
|
<input
|
|
name="confirmPassword"
|
|
type="password"
|
|
required
|
|
className="block w-full pl-10 pr-3 py-3 border border-gray-200 rounded-xl bg-gray-50/50 focus:bg-white focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500 transition-all text-gray-900 placeholder-gray-400"
|
|
placeholder="••••••••"
|
|
value={formData.confirmPassword}
|
|
onChange={handleChange}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
className="group relative w-full flex justify-center items-center py-3.5 px-4 border border-transparent text-sm font-bold rounded-xl text-white bg-brand-600 hover:bg-brand-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-brand-500 transition-all shadow-lg shadow-brand-200 disabled:opacity-70 disabled:cursor-not-allowed overflow-hidden"
|
|
>
|
|
{loading ? (
|
|
<div className="h-5 w-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
|
) : (
|
|
<>
|
|
S'inscrire
|
|
<ArrowRight className="ml-2 group-hover:translate-x-1 transition-transform" size={18} />
|
|
</>
|
|
)}
|
|
</button>
|
|
|
|
<div className="text-center mt-6">
|
|
<p className="text-gray-600 text-sm">
|
|
Déjà un compte ?{' '}
|
|
<Link href="/login" className="font-bold text-brand-600 hover:text-brand-700 transition-colors underline-offset-4 hover:underline">
|
|
Se connecter
|
|
</Link>
|
|
</p>
|
|
</div>
|
|
</form>
|
|
)}
|
|
</div>
|
|
|
|
<p className="mt-8 text-center text-xs text-gray-400 uppercase tracking-widest font-semibold">
|
|
{siteName} © 2026
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default RegisterPage;
|