"use client";
import { useState } from "react";
import {
Shield,
Copy,
Check,
Download,
Smartphone,
Key,
AlertTriangle,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { cn } from "@/lib/utils";
type Step = "scan" | "verify" | "backup" | "complete";
const SECRET_KEY = "JBSWY3DPEHPK3PXP";
const BACKUP_CODES = [
"A1B2-C3D4",
"E5F6-G7H8",
"I9J0-K1L2",
"M3N4-O5P6",
"Q7R8-S9T0",
"U1V2-W3X4",
"Y5Z6-A7B8",
"C9D0-E1F2",
];
export const title = "React Dialog Block Two-Factor Setup";
export default function DialogTwoFactorSetup() {
const [open, setOpen] = useState(false);
const [step, setStep] = useState<Step>("scan");
const [otp, setOtp] = useState("");
const [copied, setCopied] = useState(false);
const [showSecret, setShowSecret] = useState(false);
const [verifyError, setVerifyError] = useState(false);
const handleCopySecret = () => {
navigator.clipboard.writeText(SECRET_KEY);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
const handleVerify = () => {
if (otp === "123456" || otp.length === 6) {
setVerifyError(false);
setStep("backup");
} else {
setVerifyError(true);
}
};
const handleDownloadCodes = () => {
const content = `Two-Factor Authentication Backup Codes\n${"=".repeat(40)}\n\nKeep these codes safe. Each code can only be used once.\n\n${BACKUP_CODES.join("\n")}\n\nGenerated: ${new Date().toLocaleDateString()}`;
const blob = new Blob([content], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "backup-codes.txt";
a.click();
URL.revokeObjectURL(url);
};
const handleClose = () => {
setOpen(false);
setTimeout(() => {
setStep("scan");
setOtp("");
setCopied(false);
setShowSecret(false);
setVerifyError(false);
}, 200);
};
return (
<Dialog open={open} onOpenChange={setOpen}>
<div className="flex min-h-[350px] items-center justify-center">
<DialogTrigger asChild>
<Button variant="outline">
<Shield className="mr-2 h-4 w-4" />
Enable Two-Factor Auth
</Button>
</DialogTrigger>
</div>
<DialogContent className="sm:max-w-md">
{step === "scan" && (
<>
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Smartphone className="h-5 w-5" />
Set Up Authenticator
</DialogTitle>
<DialogDescription>
Scan the QR code with your authenticator app or enter the key manually.
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="flex justify-center">
<div className="rounded-lg border bg-white p-4">
<div className="h-[140px] w-[140px] bg-muted flex items-center justify-center">
<div className="text-center text-xs text-muted-foreground">
<div className="grid grid-cols-5 gap-1">
{Array.from({ length: 25 }).map((_, i) => (
<div
key={i}
className={cn(
"h-5 w-5 rounded-sm",
Math.random() > 0.5 ? "bg-foreground" : "bg-white"
)}
/>
))}
</div>
</div>
</div>
</div>
</div>
<div className="space-y-2">
<button
onClick={() => setShowSecret(!showSecret)}
className="text-sm text-primary hover:underline w-full text-center"
>
{showSecret ? "Hide" : "Can't scan? Enter key manually"}
</button>
{showSecret && (
<div className="flex items-center gap-2 p-3 rounded-lg border bg-muted/50">
<Key className="h-4 w-4 text-muted-foreground flex-shrink-0" />
<code className="flex-1 text-sm font-mono tracking-wider">
{SECRET_KEY}
</code>
<Button
variant="ghost"
size="sm"
className="h-8 w-8 p-0"
onClick={handleCopySecret}
>
{copied ? (
<Check className="h-4 w-4 text-primary" />
) : (
<Copy className="h-4 w-4" />
)}
</Button>
</div>
)}
</div>
<Alert>
<AlertTriangle className="h-4 w-4" />
<AlertDescription>
Use Google Authenticator, Authy, or any TOTP-compatible app.
</AlertDescription>
</Alert>
</div>
<DialogFooter className="gap-2 sm:gap-0">
<Button variant="outline" onClick={handleClose}>
Cancel
</Button>
<Button onClick={() => setStep("verify")}>
Continue
</Button>
</DialogFooter>
</>
)}
{step === "verify" && (
<>
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Shield className="h-5 w-5" />
Verify Setup
</DialogTitle>
<DialogDescription>
Enter the 6-digit code from your authenticator app to verify setup.
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-6">
<div className="flex justify-center">
<InputOTP
maxLength={6}
value={otp}
onChange={(value) => {
setOtp(value);
setVerifyError(false);
}}
>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
</div>
{verifyError && (
<p className="text-sm text-destructive text-center">
Invalid code. Please try again.
</p>
)}
<p className="text-sm text-muted-foreground text-center">
Code refreshes every 30 seconds
</p>
</div>
<DialogFooter className="gap-2 sm:gap-0">
<Button variant="outline" onClick={() => setStep("scan")}>
Back
</Button>
<Button onClick={handleVerify} disabled={otp.length !== 6}>
Verify
</Button>
</DialogFooter>
</>
)}
{step === "backup" && (
<>
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Key className="h-5 w-5" />
Save Backup Codes
</DialogTitle>
<DialogDescription>
Store these codes safely. Use them to access your account if you lose your authenticator.
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="grid grid-cols-2 gap-2 p-4 rounded-lg border bg-muted/30">
{BACKUP_CODES.map((code) => (
<code
key={code}
className="text-sm font-mono text-center py-1"
>
{code}
</code>
))}
</div>
<Button
variant="outline"
className="w-full"
onClick={handleDownloadCodes}
>
<Download className="mr-2 h-4 w-4" />
Download Backup Codes
</Button>
<Alert>
<AlertTriangle className="h-4 w-4" />
<AlertDescription>
Each code can only be used once. Keep them somewhere safe.
</AlertDescription>
</Alert>
</div>
<DialogFooter>
<Button onClick={() => setStep("complete")} className="w-full">
I've Saved My Codes
</Button>
</DialogFooter>
</>
)}
{step === "complete" && (
<>
<DialogHeader className="text-center">
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 mb-4">
<Check className="h-6 w-6 text-primary" />
</div>
<DialogTitle>Two-Factor Enabled</DialogTitle>
<DialogDescription>
Your account is now protected with two-factor authentication.
</DialogDescription>
</DialogHeader>
<div className="py-4">
<div className="rounded-lg border p-4 space-y-2">
<div className="flex items-center gap-2 text-sm">
<Check className="h-4 w-4 text-primary" />
<span>Authenticator app configured</span>
</div>
<div className="flex items-center gap-2 text-sm">
<Check className="h-4 w-4 text-primary" />
<span>8 backup codes saved</span>
</div>
<div className="flex items-center gap-2 text-sm">
<Check className="h-4 w-4 text-primary" />
<span>Account security enhanced</span>
</div>
</div>
</div>
<DialogFooter>
<Button onClick={handleClose} className="w-full">
Done
</Button>
</DialogFooter>
</>
)}
</DialogContent>
</Dialog>
);
}