Co-authored-by: Michael Chihlas <michael@resolutionflow.com> Co-committed-by: Michael Chihlas <michael@resolutionflow.com>
91 lines
3.0 KiB
TypeScript
91 lines
3.0 KiB
TypeScript
import { useState } from 'react'
|
|
import { Loader2, MailCheck } from 'lucide-react'
|
|
import { authApi } from '@/api/auth'
|
|
import { useAuthStore } from '@/store/authStore'
|
|
import { toast } from '@/lib/toast'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
interface EmailVerificationWallProps {
|
|
className?: string
|
|
}
|
|
|
|
/**
|
|
* Hard wall shown after the email-verification grace period expires.
|
|
*
|
|
* Minimal v1 — Task 37 will refine copy, layout, and add the
|
|
* `/verify-email?token=...` route handling. Until then this gives
|
|
* Day 7+ unverified users a way to re-send the verification email
|
|
* or sign out.
|
|
*/
|
|
export function EmailVerificationWall({ className }: EmailVerificationWallProps) {
|
|
const user = useAuthStore((s) => s.user)
|
|
const logout = useAuthStore((s) => s.logout)
|
|
const [isSending, setIsSending] = useState(false)
|
|
|
|
const handleResend = async () => {
|
|
setIsSending(true)
|
|
try {
|
|
await authApi.sendVerificationEmail()
|
|
toast.success('Verification email sent')
|
|
} catch {
|
|
toast.error('Failed to send verification email')
|
|
} finally {
|
|
setIsSending(false)
|
|
}
|
|
}
|
|
|
|
const handleLogout = async () => {
|
|
try {
|
|
await logout()
|
|
} catch {
|
|
// logout swallows API errors internally
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'flex min-h-[60vh] items-center justify-center px-4 py-12',
|
|
className,
|
|
)}
|
|
data-testid="email-verification-wall"
|
|
>
|
|
<div className="w-full max-w-md rounded-lg border border-default bg-card p-6 text-center">
|
|
<div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full border border-default bg-elevated text-muted-foreground">
|
|
<MailCheck className="h-5 w-5" aria-hidden="true" />
|
|
</div>
|
|
<h2 className="text-lg font-semibold text-heading">
|
|
Verify your email to continue
|
|
</h2>
|
|
<p className="mt-2 text-sm text-muted-foreground">
|
|
{user?.email
|
|
? `We sent a verification link to ${user.email}. Click it to unlock your account.`
|
|
: 'Check your inbox for the verification link we sent when you signed up.'}
|
|
</p>
|
|
<div className="mt-6 flex flex-col gap-2">
|
|
<button
|
|
type="button"
|
|
onClick={handleResend}
|
|
disabled={isSending}
|
|
data-testid="resend-button"
|
|
className="inline-flex items-center justify-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-50"
|
|
>
|
|
{isSending && <Loader2 className="h-4 w-4 animate-spin" />}
|
|
Resend verification email
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={handleLogout}
|
|
data-testid="sign-out-button"
|
|
className="rounded-md border border-default bg-elevated px-4 py-2 text-sm font-medium text-primary transition-colors hover:bg-white/[0.06]"
|
|
>
|
|
Sign out
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default EmailVerificationWall
|