"use client"; import { CheckIcon, MailIcon } from "lucide-react"; import { AnimatePresence, motion, useAnimationControls, useReducedMotion } from "motion/react"; import { useEffect, useId, useState } from "react"; import { Button } from "@/components/ui/button"; import { OTPField, OTPFieldInput, OTPFieldSeparator } from "@/components/ui/otp-field"; import { cn } from "@/lib/utils"; export type VerifyResult = { ok: true } | { ok: false; error: string }; export interface VerifyEmailProps { email?: string; length?: number; resendAfter?: number; onVerify?: (code: string) => Promise; onResend?: () => void; onChangeEmail?: () => void; onContinue?: () => void; className?: string; } async function demoVerify(code: string): Promise { await new Promise((resolve) => setTimeout(resolve, 800)); if (/^(\d)\1+$/.test(code)) return { ok: false, error: "That code didn’t work. Check the email and try again." }; return { ok: true }; } function Tile({ done }: { done: boolean }) { return ( {done ? : } ); } export function VerifyEmail({ email = "ada@ridgeline.app", length = 6, resendAfter = 30, onVerify = demoVerify, onResend, onChangeEmail, onContinue, className, }: VerifyEmailProps) { const id = useId(); const reduce = useReducedMotion(); const shake = useAnimationControls(); const [code, setCode] = useState(""); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const [done, setDone] = useState(false); const [left, setLeft] = useState(resendAfter); useEffect(() => { if (left <= 0 || done) return; const timer = setTimeout(() => setLeft((value) => value - 1), 1000); return () => clearTimeout(timer); }, [left, done]); const verify = async (value: string) => { if (busy || done || value.length < length) return; setBusy(true); setError(null); const result = await onVerify(value); setBusy(false); if (result.ok) { setDone(true); return; } setError(result.error); if (!reduce) await shake.start({ x: [0, -6, 6, -4, 4, 0], transition: { duration: 0.36 } }); }; const half = Math.ceil(length / 2); return (

{done ? "Email verified" : "Check your email"}

{done ? ( <> {email} is confirmed. You’re all set. ) : ( <> Enter the {length}-digit code we sent to {email} )}

{ event.preventDefault(); if (done) onContinue?.(); else verify(code); }} > { setCode(value); setError(null); }} onValueComplete={verify} size="lg" value={code} > {Array.from({ length }, (_, i) => ( {i === half && } ))} {error && ( )}

{done ? null : left > 0 ? ( <> Didn’t get a code? Resend in 0:{String(left).padStart(2, "0")} ) : ( <> Didn’t get a code?{" "} )}

Wrong email?{" "}

); }