Rendre le dashboard actionnable (inbox, peek, carte mentale), aligner la facturation sur l’essai 7 jours, et bloquer le login e-mail tant que l’adresse n’est pas confirmée. Co-authored-by: Cursor <cursoragent@cursor.com>
93 lines
3.5 KiB
TypeScript
93 lines
3.5 KiB
TypeScript
'use client'
|
|
|
|
import { Suspense, useState } from 'react'
|
|
import Link from 'next/link'
|
|
import { useSearchParams } from 'next/navigation'
|
|
import { Mail, ArrowLeft, Sparkles } from 'lucide-react'
|
|
import { useLanguage } from '@/lib/i18n'
|
|
import { resendSignupVerification } from '@/app/actions/auth-verify'
|
|
import { toast } from 'sonner'
|
|
|
|
function CheckEmailContent() {
|
|
const { t, language } = useLanguage()
|
|
const searchParams = useSearchParams()
|
|
const initialEmail = searchParams.get('email') ?? ''
|
|
const [email, setEmail] = useState(initialEmail)
|
|
const [sending, setSending] = useState(false)
|
|
|
|
const handleResend = async () => {
|
|
const target = email.trim()
|
|
if (!target) {
|
|
toast.error(t('auth.verifyMissingEmail'))
|
|
return
|
|
}
|
|
setSending(true)
|
|
const result = await resendSignupVerification(target, language)
|
|
setSending(false)
|
|
if (result.success) {
|
|
toast.success(t('auth.verifyResent'))
|
|
} else {
|
|
toast.error(t('auth.verifyResendFailed'))
|
|
}
|
|
}
|
|
|
|
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 text-center">
|
|
<div className="w-14 h-14 mx-auto rounded-2xl bg-[var(--color-brand-accent)]/10 flex items-center justify-center">
|
|
<Mail size={28} className="text-[var(--color-brand-accent)]" />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<h1 className="text-2xl md:text-3xl font-serif font-bold">
|
|
{t('auth.checkEmailTitle')}
|
|
</h1>
|
|
<p className="text-[var(--muted-foreground)] text-sm font-light leading-relaxed">
|
|
{initialEmail
|
|
? t('auth.checkEmailDescription', { email: initialEmail })
|
|
: t('auth.checkEmailDescriptionGeneric')}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-1.5 text-start">
|
|
<label htmlFor="email" className="text-[10px] uppercase tracking-widest font-bold text-[var(--muted-foreground)] px-4">
|
|
{t('auth.email')}
|
|
</label>
|
|
<input
|
|
id="email"
|
|
type="email"
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
placeholder={t('auth.emailPlaceholder')}
|
|
className="w-full bg-slate-50 dark:bg-white/5 border border-[var(--border)] rounded-2xl py-4 px-4 text-sm outline-none focus:border-[var(--color-brand-accent)] focus:ring-4 ring-[var(--color-brand-accent)]/5 transition-all"
|
|
/>
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={handleResend}
|
|
disabled={sending}
|
|
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"
|
|
>
|
|
{sending ? <Sparkles size={16} className="animate-spin" /> : t('auth.resendVerification')}
|
|
</button>
|
|
|
|
<Link
|
|
href="/login"
|
|
className="inline-flex items-center gap-2 text-xs text-[var(--muted-foreground)] hover:text-[var(--color-brand-accent)] transition-colors"
|
|
>
|
|
<ArrowLeft size={14} />
|
|
{t('auth.backToLogin')}
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function CheckEmailPage() {
|
|
return (
|
|
<Suspense fallback={<div className="p-10 text-center text-sm text-muted-foreground">…</div>}>
|
|
<CheckEmailContent />
|
|
</Suspense>
|
|
)
|
|
}
|