feat: self-serve signup Phase 2 (frontend cutover) (#162)
Co-authored-by: Michael Chihlas <michael@resolutionflow.com> Co-committed-by: Michael Chihlas <michael@resolutionflow.com>
This commit was merged in pull request #162.
This commit is contained in:
90
frontend/src/components/common/EmailVerificationWall.tsx
Normal file
90
frontend/src/components/common/EmailVerificationWall.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
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
|
||||
Reference in New Issue
Block a user