164 lines
6.3 KiB
TypeScript
164 lines
6.3 KiB
TypeScript
|
|
import { AlertCircle, ArrowRight, Eye, Lock, Mail, ShieldCheck } from 'lucide-react';
|
||
|
|
import { useAuthForm } from '../../hooks/useAuthForm';
|
||
|
|
|
||
|
|
export function AuthPanel() {
|
||
|
|
const {
|
||
|
|
mode,
|
||
|
|
setMode,
|
||
|
|
name,
|
||
|
|
setName,
|
||
|
|
email,
|
||
|
|
setEmail,
|
||
|
|
password,
|
||
|
|
setPassword,
|
||
|
|
emailError,
|
||
|
|
passwordError,
|
||
|
|
handleSubmit,
|
||
|
|
continueAsGuest,
|
||
|
|
loading,
|
||
|
|
} = useAuthForm();
|
||
|
|
|
||
|
|
const isSignup = mode === 'signup';
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
className="w-full max-w-[430px] rounded-[22px] border border-white/16 bg-white/[0.075] p-6 shadow-[0_32px_80px_rgba(0,0,0,0.5),inset_0_1px_0_rgba(255,255,255,0.18)] backdrop-blur-2xl backdrop-saturate-150 sm:p-[34px]"
|
||
|
|
style={{ colorScheme: 'dark' }}
|
||
|
|
>
|
||
|
|
<h2 className="m-0 mb-1.5 text-[23px] font-bold tracking-tight text-white">
|
||
|
|
{isSignup ? 'Créer votre espace' : 'Bon retour'}
|
||
|
|
</h2>
|
||
|
|
<p className="m-0 mb-6 text-[13.5px] text-white/55">
|
||
|
|
{isSignup ? 'Un coffre-fort chiffré, une seule clé : la vôtre.' : 'Déverrouillez votre coffre-fort.'}
|
||
|
|
</p>
|
||
|
|
|
||
|
|
<div className="mb-6 flex gap-1 rounded-xl border border-white/8 bg-black/28 p-1">
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={() => setMode('login')}
|
||
|
|
className={`flex-1 rounded-[9px] py-2.5 text-[13px] font-semibold transition-colors ${
|
||
|
|
mode === 'login' ? 'bg-white/14 text-white' : 'bg-transparent text-white/55'
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
Connexion
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={() => setMode('signup')}
|
||
|
|
className={`flex-1 rounded-[9px] py-2.5 text-[13px] font-semibold transition-colors ${
|
||
|
|
mode === 'signup' ? 'bg-white/14 text-white' : 'bg-transparent text-white/55'
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
Créer un compte
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
||
|
|
{isSignup && (
|
||
|
|
<div className="flex flex-col gap-1.5">
|
||
|
|
<label htmlFor="tc-name" className="text-[12.5px] font-semibold text-white/72">
|
||
|
|
Nom complet
|
||
|
|
</label>
|
||
|
|
<input
|
||
|
|
id="tc-name"
|
||
|
|
name="name"
|
||
|
|
type="text"
|
||
|
|
autoComplete="name"
|
||
|
|
value={name}
|
||
|
|
onChange={(e) => setName(e.target.value)}
|
||
|
|
placeholder="Leïla Barghouti"
|
||
|
|
className="w-full rounded-xl border border-white/14 bg-black/26 px-[15px] py-3.5 text-[14.5px] text-white outline-none placeholder:text-white/34"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
<div className="flex flex-col gap-1.5">
|
||
|
|
<label htmlFor="tc-email" className="text-[12.5px] font-semibold text-white/72">
|
||
|
|
Adresse e-mail
|
||
|
|
</label>
|
||
|
|
<div className="relative flex items-center">
|
||
|
|
<Mail size={16} className="absolute left-3.5 text-white/40" strokeWidth={1.8} />
|
||
|
|
<input
|
||
|
|
id="tc-email"
|
||
|
|
name="email"
|
||
|
|
type="email"
|
||
|
|
autoComplete="email"
|
||
|
|
value={email}
|
||
|
|
onChange={(e) => setEmail(e.target.value)}
|
||
|
|
aria-invalid={emailError}
|
||
|
|
aria-describedby="tc-email-err"
|
||
|
|
placeholder="vous@domaine.tn"
|
||
|
|
className={`w-full rounded-xl border bg-black/26 py-3.5 pl-10 pr-[15px] text-[14.5px] text-white outline-none placeholder:text-white/34 ${
|
||
|
|
emailError ? 'border-danger' : 'border-white/14'
|
||
|
|
}`}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
{emailError && (
|
||
|
|
<span id="tc-email-err" className="flex items-center gap-1.5 text-xs text-danger">
|
||
|
|
<AlertCircle size={13} />
|
||
|
|
Saisissez une adresse e-mail valide.
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex flex-col gap-1.5">
|
||
|
|
<div className="flex items-baseline justify-between gap-3">
|
||
|
|
<label htmlFor="tc-pass" className="text-[12.5px] font-semibold text-white/72">
|
||
|
|
Mot de passe
|
||
|
|
</label>
|
||
|
|
<a href="#tc-pass" className="text-xs text-brand-soft hover:text-[#a8c2ff]">
|
||
|
|
Oublié ?
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
<div className="relative flex items-center">
|
||
|
|
<Lock size={16} className="absolute left-3.5 text-white/40" strokeWidth={1.8} />
|
||
|
|
<input
|
||
|
|
id="tc-pass"
|
||
|
|
name="password"
|
||
|
|
type="password"
|
||
|
|
autoComplete="current-password"
|
||
|
|
value={password}
|
||
|
|
onChange={(e) => setPassword(e.target.value)}
|
||
|
|
aria-invalid={passwordError}
|
||
|
|
aria-describedby="tc-pass-err"
|
||
|
|
placeholder="••••••••••"
|
||
|
|
className={`w-full rounded-xl border bg-black/26 py-3.5 pl-10 pr-[15px] text-[14.5px] text-white outline-none placeholder:text-white/34 ${
|
||
|
|
passwordError ? 'border-danger' : 'border-white/14'
|
||
|
|
}`}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
{passwordError && (
|
||
|
|
<span id="tc-pass-err" className="flex items-center gap-1.5 text-xs text-danger">
|
||
|
|
<AlertCircle size={13} />
|
||
|
|
Huit caractères minimum.
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<button
|
||
|
|
type="submit"
|
||
|
|
disabled={loading}
|
||
|
|
className="mt-1 flex w-full items-center justify-center gap-2.5 rounded-xl bg-brand-gradient px-[18px] py-3.5 text-[14.5px] font-bold text-white shadow-[0_12px_30px_rgba(91,140,255,0.34)] transition-[filter] hover:brightness-110 disabled:opacity-70"
|
||
|
|
>
|
||
|
|
<span>{isSignup ? 'Créer mon compte' : 'Se connecter'}</span>
|
||
|
|
<ArrowRight size={17} />
|
||
|
|
</button>
|
||
|
|
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={() => void continueAsGuest()}
|
||
|
|
className="flex w-full items-center justify-center gap-2 rounded-xl border border-white/18 bg-white/6 px-[18px] py-[13px] text-[13.5px] font-semibold text-white transition-colors hover:bg-white/12"
|
||
|
|
>
|
||
|
|
<Eye size={16} strokeWidth={1.8} />
|
||
|
|
Visiter en tant qu'invité
|
||
|
|
</button>
|
||
|
|
|
||
|
|
<div className="mt-1.5 flex items-center gap-2.5 border-t border-white/10 pt-4 text-xs text-white/48">
|
||
|
|
<ShieldCheck size={14} className="text-success" strokeWidth={2} />
|
||
|
|
Chiffrement AES-256 côté client — aucune donnée en clair sur le serveur.
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|