| import { useState } from 'react'; |
| import { Button } from "@/components/ui/button"; |
| import { Input } from "@/components/ui/input"; |
| import { Label } from "@/components/ui/label"; |
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; |
| import { AlertCircle, CheckCircle2, Loader2, QrCode } from 'lucide-react'; |
| import authApi from '@/api/auth.api'; |
|
|
| interface TwoFactorSetupProps { |
| onComplete?: () => void; |
| } |
|
|
| export default function TwoFactorSetup({ onComplete }: TwoFactorSetupProps) { |
| const [step, setStep] = useState<'initial' | 'setup' | 'verify' | 'success'>('initial'); |
| const [qrCode, setQrCode] = useState<string>(''); |
| const [otp, setOtp] = useState<string>(''); |
| const [isLoading, setIsLoading] = useState(false); |
| const [error, setError] = useState<string | null>(null); |
|
|
| const startSetup = async () => { |
| setIsLoading(true); |
| setError(null); |
| try { |
| const data = await authApi.setup2FA(); |
| setQrCode(data.qrCode); |
| setStep('setup'); |
| } catch (err: any) { |
| setError(err.response?.data?.message || "Failed to start 2FA setup."); |
| } finally { |
| setIsLoading(false); |
| } |
| }; |
|
|
| const verifySetup = async () => { |
| if (!otp) return; |
| setIsLoading(true); |
| setError(null); |
| try { |
| await authApi.verify2FA(otp); |
| setStep('success'); |
| } catch (err: any) { |
| setError(err.response?.data?.message || "Invalid OTP. Please try again."); |
| } finally { |
| setIsLoading(false); |
| } |
| }; |
|
|
| const handleComplete = () => { |
| if (onComplete) { |
| onComplete(); |
| } else { |
| setStep('initial'); |
| } |
| }; |
|
|
| return ( |
| <Card className="w-full max-w-md mx-auto"> |
| <CardHeader> |
| <CardTitle className="flex items-center gap-2"> |
| <QrCode className="w-5 h-5" /> |
| Two-Factor Authentication |
| </CardTitle> |
| <CardDescription> |
| Secure your account with Google Authenticator. |
| </CardDescription> |
| </CardHeader> |
| <CardContent> |
| {step === 'initial' && ( |
| <div className="space-y-4"> |
| <p className="text-sm text-muted-foreground"> |
| Two-factor authentication adds an extra layer of security to your account. |
| Once enabled, you'll need to enter a code from your Google Authenticator app when logging in. |
| </p> |
| <Button onClick={startSetup} disabled={isLoading} className="w-full"> |
| {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />} |
| Setup / Reset 2FA |
| </Button> |
| {error && <p className="text-sm text-red-500 mt-2">{error}</p>} |
| </div> |
| )} |
| |
| {step === 'setup' && ( |
| <div className="space-y-6"> |
| <div className="flex flex-col items-center justify-center p-4 border rounded-lg bg-white"> |
| <img src={qrCode} alt="2FA QR Code" className="w-48 h-48" /> |
| </div> |
| <div className="space-y-2"> |
| <Label>1. Scan QR Code</Label> |
| <p className="text-xs text-muted-foreground"> |
| Open Google Authenticator and scan the QR code above. |
| </p> |
| </div> |
| <div className="space-y-2"> |
| <Label>2. Enter Code</Label> |
| <Input |
| placeholder="Enter 6-digit code" |
| value={otp} |
| onChange={(e) => setOtp(e.target.value)} |
| maxLength={6} |
| /> |
| </div> |
| {error && ( |
| <div className="flex items-center gap-2 text-sm text-red-500"> |
| <AlertCircle className="w-4 h-4" /> |
| {error} |
| </div> |
| )} |
| <Button onClick={verifySetup} disabled={isLoading || otp.length !== 6} className="w-full"> |
| {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />} |
| Verify & Enable |
| </Button> |
| </div> |
| )} |
| |
| {step === 'success' && ( |
| <div className="flex flex-col items-center justify-center py-6 space-y-4 text-center"> |
| <div className="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center text-green-600"> |
| <CheckCircle2 className="w-6 h-6" /> |
| </div> |
| <div className="space-y-1"> |
| <h3 className="font-medium">2FA Enabled Successfully</h3> |
| <p className="text-sm text-muted-foreground"> |
| Your account is now secured with two-factor authentication. |
| </p> |
| </div> |
| <Button variant="outline" onClick={handleComplete} className="w-full"> |
| Done |
| </Button> |
| </div> |
| )} |
| </CardContent> |
| </Card> |
| ); |
| } |
|
|