tripoli.cloud/app/src/components/auth/AuthPanel.tsx
Adam Belkacemi 4cf882144c
All checks were successful
CI / build-and-test (push) Successful in 2m14s
CI / deploy (push) Successful in 3m16s
Refonte complète du thème : éditorial clair, inspiré de studiocart.co
À la demande explicite de l'utilisateur (site jugé "trop moche"),
inspiration visuelle prise sur https://www.studiocart.co/ : grands
titres serif (Fraunces) avec italique, fond papier clair plutôt que le
coffre-fort sombre d'origine, un seul accent vif (citron vert) utilisé
avec parcimonie pour les surlignages, cartes blanches nettes à bordure
fine, petites touches dessinées à la main (squiggle, flèche) sur l'écran
de connexion.

- Jetons de thème (index.css, tailwind.config.js) : le thème clair
  devient la palette par défaut. Le thème sombre existant reste
  disponible via le bouton de bascule, inchangé.
- ThemeContext : thème par défaut passé à 'light'.
- Écran de connexion entièrement repensé (fond génératif pastel qui
  dérive lentement, carte blanche, surlignage citron vert via la
  nouvelle classe utilitaire .tc-highlight, doodles SVG).
- Reste de l'application : cascade automatique via les tokens CSS déjà
  utilisés partout (bg-panel, text-ink, ...), sans avoir à toucher
  chaque composant individuellement.

Inspiration de studiocart.co pour le ton visuel uniquement (typographie,
palette, motifs) - aucun contenu, logo ni image de ce site n'est repris.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 14:37:16 +02:00

168 lines
6.5 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,
error,
} = useAuthForm();
const isSignup = mode === 'signup';
return (
<div className="w-full max-w-[430px] rounded-[26px] border border-ink/12 bg-white p-6 shadow-[0_28px_64px_rgba(23,20,14,0.14)] sm:p-[34px]">
<h2 className="m-0 mb-1.5 font-display text-[24px] font-semibold tracking-tight text-ink">
{isSignup ? 'Créer votre espace' : 'Bon retour'}
</h2>
<p className="m-0 mb-6 text-[13.5px] text-ink-soft">
{isSignup ? 'Créez votre compte en quelques secondes.' : 'Déverrouillez votre coffre-fort.'}
</p>
<div className="mb-6 flex gap-1 rounded-xl border border-ink/10 bg-field 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 text-ink shadow-sm' : 'bg-transparent text-ink-soft'
}`}
>
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 text-ink shadow-sm' : 'bg-transparent text-ink-soft'
}`}
>
Créer un compte
</button>
</div>
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
{error && (
<div className="flex items-center gap-2 rounded-xl border border-danger/40 bg-danger/10 px-3.5 py-3 text-[13px] font-semibold text-danger">
<AlertCircle size={15} />
{error}
</div>
)}
{isSignup && (
<div className="flex flex-col gap-1.5">
<label htmlFor="tc-name" className="text-[12.5px] font-semibold text-ink-soft">
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-ink/14 bg-field px-[15px] py-3.5 text-[14.5px] text-ink outline-none placeholder:text-ink-soft/70 focus:border-ink/30"
/>
</div>
)}
<div className="flex flex-col gap-1.5">
<label htmlFor="tc-email" className="text-[12.5px] font-semibold text-ink-soft">
Adresse e-mail
</label>
<div className="relative flex items-center">
<Mail size={16} className="absolute left-3.5 text-ink-soft/70" 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-field py-3.5 pl-10 pr-[15px] text-[14.5px] text-ink outline-none placeholder:text-ink-soft/70 focus:border-ink/30 ${
emailError ? 'border-danger' : 'border-ink/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-ink-soft">
Mot de passe
</label>
<a href="#tc-pass" className="text-xs font-semibold text-brand hover:text-brand-soft">
Oublié ?
</a>
</div>
<div className="relative flex items-center">
<Lock size={16} className="absolute left-3.5 text-ink-soft/70" 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-field py-3.5 pl-10 pr-[15px] text-[14.5px] text-ink outline-none placeholder:text-ink-soft/70 focus:border-ink/30 ${
passwordError ? 'border-danger' : 'border-ink/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-ink/16 bg-white px-[18px] py-[13px] text-[13.5px] font-semibold text-ink transition-colors hover:bg-field"
>
<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-ink/10 pt-4 text-xs text-ink-soft">
<ShieldCheck size={14} className="text-success" strokeWidth={2} />
Connexion chiffrée (HTTPS), mots de passe hachés (Argon2) — jamais stockés en clair.
</div>
</form>
</div>
);
}