Les comptes déjà créés ne sont pas inscrits. Le choix est facultatif à l’inscription et dans les paramètres. Un lien public demande confirmation avant d’arrêter les actualités, sans toucher aux messages de compte.
191 lines
7.7 KiB
TypeScript
191 lines
7.7 KiB
TypeScript
'use client';
|
|
|
|
import { useActionState } from 'react';
|
|
import { useFormStatus } from 'react-dom';
|
|
import { register } from '@/app/actions/register';
|
|
import Link from 'next/link';
|
|
import { Mail, Lock, User, ArrowRight, Sparkles } from 'lucide-react';
|
|
import { useLanguage } from '@/lib/i18n';
|
|
import { GoogleSignInButton } from '@/components/google-sign-in-button';
|
|
|
|
function RegisterButton() {
|
|
const { pending } = useFormStatus();
|
|
const { t } = useLanguage();
|
|
return (
|
|
<button
|
|
type="submit"
|
|
disabled={pending}
|
|
className="w-full bg-[var(--foreground)] text-[var(--background)] py-4 rounded-2xl font-bold uppercase tracking-[0.2em] text-[10px] flex items-center justify-center gap-3 transition-all hover:shadow-xl hover:shadow-black/10 active:scale-[0.98] disabled:opacity-50 mt-4"
|
|
>
|
|
{pending ? (
|
|
<Sparkles size={16} className="animate-spin" />
|
|
) : (
|
|
<>
|
|
{t('auth.signUp')}
|
|
<ArrowRight size={14} />
|
|
</>
|
|
)}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
function AuthDivider({ label }: { label: string }) {
|
|
return (
|
|
<div className="relative flex items-center py-2">
|
|
<div className="flex-grow border-t border-[var(--border)]" />
|
|
<span className="mx-4 text-[10px] uppercase tracking-widest text-[var(--muted-foreground)] font-bold">
|
|
{label}
|
|
</span>
|
|
<div className="flex-grow border-t border-[var(--border)]" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function RegisterForm({ googleAuthEnabled = false }: { googleAuthEnabled?: boolean }) {
|
|
const [errorMessage, dispatch] = useActionState(register, undefined);
|
|
const { t, language } = useLanguage();
|
|
|
|
return (
|
|
<div className="bg-white dark:bg-[var(--background)]/50 border border-[var(--border)] p-8 md:p-10 rounded-[48px] shadow-2xl">
|
|
<div className="space-y-8">
|
|
<div className="text-center space-y-2">
|
|
<h1 className="text-2xl md:text-3xl font-serif font-bold">
|
|
{t('auth.createYourSpace')}
|
|
</h1>
|
|
<p className="text-[var(--muted-foreground)] text-sm font-light">
|
|
{t('auth.createYourSpaceSubtitle')}
|
|
</p>
|
|
</div>
|
|
|
|
{googleAuthEnabled && (
|
|
<>
|
|
<GoogleSignInButton label={t('auth.continueWithGoogle')} />
|
|
<AuthDivider label={t('auth.orContinueWith')} />
|
|
</>
|
|
)}
|
|
|
|
<form action={dispatch} className="space-y-4">
|
|
<div className="space-y-1.5">
|
|
<label htmlFor="name" className="text-[10px] uppercase tracking-widest font-bold text-[var(--muted-foreground)] px-4">
|
|
{t('auth.name')}
|
|
</label>
|
|
<div className="relative group">
|
|
<div className="absolute left-4 top-1/2 -translate-y-1/2 text-[var(--muted-foreground)] group-focus-within:text-[var(--color-brand-accent)] transition-colors">
|
|
<User size={16} />
|
|
</div>
|
|
<input
|
|
className="w-full bg-slate-50 dark:bg-white/5 border border-[var(--border)] rounded-2xl py-4 pl-12 pr-4 text-sm outline-none focus:border-[var(--color-brand-accent)] focus:ring-4 ring-[var(--color-brand-accent)]/5 transition-all"
|
|
id="name"
|
|
type="text"
|
|
name="name"
|
|
autoComplete="name"
|
|
placeholder={t('auth.namePlaceholder')}
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-1.5">
|
|
<label htmlFor="email" className="text-[10px] uppercase tracking-widest font-bold text-[var(--muted-foreground)] px-4">
|
|
{t('auth.email')}
|
|
</label>
|
|
<div className="relative group">
|
|
<div className="absolute left-4 top-1/2 -translate-y-1/2 text-[var(--muted-foreground)] group-focus-within:text-[var(--color-brand-accent)] transition-colors">
|
|
<Mail size={16} />
|
|
</div>
|
|
<input
|
|
className="w-full bg-slate-50 dark:bg-white/5 border border-[var(--border)] rounded-2xl py-4 pl-12 pr-4 text-sm outline-none focus:border-[var(--color-brand-accent)] focus:ring-4 ring-[var(--color-brand-accent)]/5 transition-all"
|
|
id="email"
|
|
type="email"
|
|
name="email"
|
|
autoComplete="email"
|
|
placeholder={t('auth.emailPlaceholder')}
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-1.5">
|
|
<label htmlFor="password" className="text-[10px] uppercase tracking-widest font-bold text-[var(--muted-foreground)] px-4">
|
|
{t('auth.password')}
|
|
</label>
|
|
<div className="relative group">
|
|
<div className="absolute left-4 top-1/2 -translate-y-1/2 text-[var(--muted-foreground)] group-focus-within:text-[var(--color-brand-accent)] transition-colors">
|
|
<Lock size={16} />
|
|
</div>
|
|
<input
|
|
className="w-full bg-slate-50 dark:bg-white/5 border border-[var(--border)] rounded-2xl py-4 pl-12 pr-4 text-sm outline-none focus:border-[var(--color-brand-accent)] focus:ring-4 ring-[var(--color-brand-accent)]/5 transition-all"
|
|
id="password"
|
|
type="password"
|
|
name="password"
|
|
autoComplete="new-password"
|
|
placeholder={t('auth.passwordMinChars')}
|
|
required
|
|
minLength={6}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-1.5">
|
|
<label htmlFor="confirmPassword" className="text-[10px] uppercase tracking-widest font-bold text-[var(--muted-foreground)] px-4">
|
|
{t('auth.confirmPassword')}
|
|
</label>
|
|
<div className="relative group">
|
|
<div className="absolute left-4 top-1/2 -translate-y-1/2 text-[var(--muted-foreground)] group-focus-within:text-[var(--color-brand-accent)] transition-colors">
|
|
<Lock size={16} />
|
|
</div>
|
|
<input
|
|
className="w-full bg-slate-50 dark:bg-white/5 border border-[var(--border)] rounded-2xl py-4 pl-12 pr-4 text-sm outline-none focus:border-[var(--color-brand-accent)] focus:ring-4 ring-[var(--color-brand-accent)]/5 transition-all"
|
|
id="confirmPassword"
|
|
type="password"
|
|
name="confirmPassword"
|
|
autoComplete="new-password"
|
|
placeholder={t('auth.confirmPasswordPlaceholder')}
|
|
required
|
|
minLength={6}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<label className="flex items-start gap-3 px-1 pt-2 cursor-pointer">
|
|
<input
|
|
type="checkbox"
|
|
name="marketingConsent"
|
|
value="1"
|
|
className="mt-1 size-4 rounded border-[var(--border)] accent-[var(--color-brand-accent)]"
|
|
/>
|
|
<span className="text-left text-xs leading-relaxed text-[var(--muted-foreground)]">
|
|
{t('auth.marketingConsent')}
|
|
</span>
|
|
</label>
|
|
<input type="hidden" name="marketingLanguage" value={language} />
|
|
|
|
<RegisterButton />
|
|
|
|
<div
|
|
className="flex h-8 items-end space-x-1"
|
|
aria-live="polite"
|
|
aria-atomic="true"
|
|
>
|
|
{errorMessage && (
|
|
<p className="text-sm text-red-500">{errorMessage}</p>
|
|
)}
|
|
</div>
|
|
</form>
|
|
|
|
<div className="text-center pt-2">
|
|
<p className="text-xs text-[var(--muted-foreground)]">
|
|
{t('auth.hasAccount')}{' '}
|
|
<Link
|
|
href="/login"
|
|
className="text-[var(--color-brand-accent)] font-bold hover:underline"
|
|
>
|
|
{t('auth.signIn')}
|
|
</Link>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|