import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { getTotpSetup, enableTotp } from "@/api/auth"; import { useAuthStore } from "@/store/authStore"; import { useQuery, useMutation } from "@tanstack/react-query"; import { ShieldCheck, Copy, CheckCircle, Loader2 } from "lucide-react"; import { useDemoMode } from "@/hooks/useDemoMode"; const schema = z.object({ code: z.string().length(6, "6-digit code required") }); type Form = z.infer; export default function TwoFactorSetupPage() { const navigate = useNavigate(); const { setTotpEnabled } = useAuthStore(); const isDemo = useDemoMode(); useEffect(() => { if (isDemo) navigate("/settings", { replace: true }); }, [isDemo, navigate]); const [copied, setCopied] = useState(false); const [secret, setSecret] = useState(null); const [error, setError] = useState(null); const { data, isLoading } = useQuery({ queryKey: ["totp-setup"], queryFn: async () => { const res = await getTotpSetup(); setSecret(res.secret); return res; }, enabled: !isDemo, }); const { register, handleSubmit, formState } = useForm
({ resolver: zodResolver(schema), }); const enableMutation = useMutation({ mutationFn: ({ code }: { code: string }) => enableTotp(secret!, code), onSuccess: () => { setTotpEnabled(true); navigate("/settings"); }, onError: () => setError("Invalid code — try again"), }); function copySecret() { if (data?.secret) { navigator.clipboard.writeText(data.secret); setCopied(true); setTimeout(() => setCopied(false), 2000); } } if (isLoading) { return (
); } return (

Set up two-factor authentication

{/* QR code */}
{data?.qr_code_png_b64 && ( TOTP QR code )}

Scan with your authenticator app (Authy, Google Authenticator, etc.)

{/* Manual secret */}

Or enter the secret manually:

{data?.secret}
{/* Backup codes */} {data?.backup_codes && (

Save these backup codes — you can only see them once:

{data.backup_codes.map((code) => ( {code} ))}
)} {/* Verify */} enableMutation.mutate({ code }))} className="space-y-3" >
{formState.errors.code && (

{formState.errors.code.message}

)}
{error && (

{error}

)}
); }