authentification nocodebackend ok
This commit is contained in:
202
components/AuthPage.tsx
Normal file
202
components/AuthPage.tsx
Normal file
@@ -0,0 +1,202 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Mail, Lock, User, ArrowRight, Loader2, BookOpen, ShieldCheck } from 'lucide-react';
|
||||
import { useAuth } from '../hooks';
|
||||
|
||||
interface AuthPageProps {
|
||||
onBack: () => void;
|
||||
onSuccess: () => void;
|
||||
initialMode?: 'signin' | 'signup' | 'forgot';
|
||||
}
|
||||
|
||||
const AuthPage: React.FC<AuthPageProps> = ({ onBack, onSuccess, initialMode = 'signin' }) => {
|
||||
const [mode, setMode] = useState<'signin' | 'signup' | 'forgot'>(initialMode);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [formData, setFormData] = useState({ name: '', email: '', password: '' });
|
||||
const [error, setError] = useState('');
|
||||
|
||||
// On récupère les fonctions de connexion directement du hook
|
||||
const { user, login, signup } = useAuth();
|
||||
|
||||
// Redirection automatique dès que l'utilisateur est détecté dans l'état global
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
onSuccess();
|
||||
}
|
||||
}, [user, onSuccess]);
|
||||
|
||||
const handleAdminLogin = async () => {
|
||||
const adminData = { email: 'streaper2@gmail.com', password: 'Kency1313' };
|
||||
setFormData({ name: 'Admin Plume', ...adminData });
|
||||
setLoading(true);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
const result = await login(adminData);
|
||||
if (result?.error) setError(result.error);
|
||||
} catch (e) {
|
||||
setError('Erreur de connexion au service.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
let result;
|
||||
if (mode === 'signup') {
|
||||
result = await signup(formData);
|
||||
} else {
|
||||
result = await login({ email: formData.email, password: formData.password });
|
||||
}
|
||||
|
||||
if (result?.error) {
|
||||
setError(result.error);
|
||||
}
|
||||
} catch (e) {
|
||||
setError('Une erreur technique est survenue.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50 flex overflow-hidden font-sans text-slate-900">
|
||||
{/* Panneau Latéral (Visible sur Desktop) */}
|
||||
<div className="hidden lg:flex w-1/2 bg-slate-900 relative p-12 flex-col justify-between overflow-hidden">
|
||||
<div className="absolute top-0 right-0 w-full h-full opacity-20 pointer-events-none">
|
||||
<div className="absolute top-10 right-10 w-64 h-64 bg-blue-500 rounded-full blur-[120px]" />
|
||||
<div className="absolute bottom-10 left-10 w-96 h-96 bg-indigo-500 rounded-full blur-[150px]" />
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex items-center gap-2 text-white text-2xl font-black">
|
||||
<BookOpen className="text-blue-500" /> PlumeIA
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 max-w-lg">
|
||||
<h2 className="text-5xl font-black text-white leading-tight mb-6">
|
||||
L'endroit où vos <span className="text-blue-400">histoires</span> prennent vie.
|
||||
</h2>
|
||||
<p className="text-slate-400 text-lg leading-relaxed">
|
||||
Rejoignez une communauté d'auteurs qui utilisent l'IA pour briser la page blanche.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 text-slate-500 text-sm">
|
||||
© 2024 PlumeIA Ecosystem.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Formulaire */}
|
||||
<div className="w-full lg:w-1/2 flex items-center justify-center p-8 bg-white overflow-y-auto">
|
||||
<div className="w-full max-w-md animate-in fade-in slide-in-from-right-10 duration-500 py-8">
|
||||
<div className="text-center mb-10">
|
||||
<h1 className="text-3xl font-black text-slate-900 mb-2">
|
||||
{mode === 'signin' ? 'Content de vous revoir' : mode === 'signup' ? "Commencer l'aventure" : 'Récupération'}
|
||||
</h1>
|
||||
<p className="text-slate-500">
|
||||
{mode === 'signin' ? 'Entrez vos identifiants pour continuer.' : 'Créez votre compte gratuit en quelques secondes.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="mb-6 p-4 bg-red-50 border border-red-100 text-red-600 text-sm font-medium rounded-xl animate-in shake duration-300">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
{mode === 'signup' && (
|
||||
<div className="space-y-1">
|
||||
<label className="text-xs font-black text-slate-500 uppercase tracking-widest ml-1">Nom complet</label>
|
||||
<div className="relative">
|
||||
<User className="absolute left-4 top-3.5 text-slate-400" size={18} />
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
placeholder="Marc Dupré"
|
||||
className="w-full pl-12 pr-4 py-3 bg-slate-50 border border-slate-200 rounded-xl outline-none focus:ring-2 focus:ring-blue-500 font-medium"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-1">
|
||||
<label className="text-xs font-black text-slate-500 uppercase tracking-widest ml-1">Email</label>
|
||||
<div className="relative">
|
||||
<Mail className="absolute left-4 top-3.5 text-slate-400" size={18} />
|
||||
<input
|
||||
type="email"
|
||||
required
|
||||
value={formData.email}
|
||||
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||
placeholder="votre@email.com"
|
||||
className="w-full pl-12 pr-4 py-3 bg-slate-50 border border-slate-200 rounded-xl outline-none focus:ring-2 focus:ring-blue-500 font-medium"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{mode !== 'forgot' && (
|
||||
<div className="space-y-1">
|
||||
<label className="text-xs font-black text-slate-500 uppercase tracking-widest ml-1">Mot de passe</label>
|
||||
<div className="relative">
|
||||
<Lock className="absolute left-4 top-3.5 text-slate-400" size={18} />
|
||||
<input
|
||||
type="password"
|
||||
required
|
||||
value={formData.password}
|
||||
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
||||
placeholder="••••••••"
|
||||
className="w-full pl-12 pr-4 py-3 bg-slate-50 border border-slate-200 rounded-xl outline-none focus:ring-2 focus:ring-blue-500 font-medium"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full bg-slate-900 text-white py-4 rounded-xl font-bold flex items-center justify-center gap-2 hover:bg-blue-600 transition-all shadow-xl disabled:opacity-50 mt-4"
|
||||
>
|
||||
{loading ? <Loader2 className="animate-spin" /> : (
|
||||
<>{mode === 'signin' ? 'Se connecter' : mode === 'signup' ? 'Créer mon compte' : 'Envoyer'} <ArrowRight size={18} /></>
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{mode === 'signin' && (
|
||||
<button
|
||||
onClick={handleAdminLogin}
|
||||
className="w-full mt-4 bg-amber-50 border border-amber-200 text-amber-800 py-3 rounded-xl font-bold flex items-center justify-center gap-2 hover:bg-amber-100 transition-all"
|
||||
>
|
||||
<ShieldCheck size={18} /> Connexion démo (Admin)
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div className="mt-10 text-center">
|
||||
<p className="text-sm text-slate-500">
|
||||
{mode === 'signin' ? "Pas de compte ?" : "Déjà membre ?"}
|
||||
<button
|
||||
onClick={() => setMode(mode === 'signin' ? 'signup' : 'signin')}
|
||||
className="ml-2 font-bold text-blue-600"
|
||||
>
|
||||
{mode === 'signin' ? "S'inscrire" : "Se connecter"}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button onClick={onBack} className="mt-8 text-xs text-slate-300 w-full text-center hover:text-slate-500 transition-colors">
|
||||
← Revenir au site
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AuthPage;
|
||||
Reference in New Issue
Block a user