/* Copyright (C) 2023-2026 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { useState, useEffect, useCallback } from 'react' import { Loader2 } from 'lucide-react' import { QRCodeSVG } from 'qrcode.react' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' import { setup2FA, enable2FA } from '@/lib/api' import { Alert, AlertDescription } from '@/components/ui/alert' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { CopyButton } from '@/components/copy-button' import { Dialog } from '@/components/dialog' import type { TwoFASetupData } from '../../types' // ============================================================================ // Two-FA Setup Dialog Component // ============================================================================ interface TwoFASetupDialogProps { open: boolean onOpenChange: (open: boolean) => void onSuccess: () => void } export function TwoFASetupDialog({ open, onOpenChange, onSuccess, }: TwoFASetupDialogProps) { const { t } = useTranslation() const [loading, setLoading] = useState(false) const [initializing, setInitializing] = useState(false) const [step, setStep] = useState(0) const [setupData, setSetupData] = useState(null) const [code, setCode] = useState('') const stepLabels = [ t('Scan QR Code'), t('Save Backup Codes'), t('Verify Setup'), ] const handleSetup = useCallback(async () => { try { setInitializing(true) const response = await setup2FA() if (response.success && response.data) { setSetupData(response.data) setStep(0) } else { toast.error(response.message || t('Failed to setup 2FA')) onOpenChange(false) } } catch (error) { // eslint-disable-next-line no-console console.error('Setup 2FA error:', error) toast.error(t('Failed to setup 2FA')) onOpenChange(false) } finally { setInitializing(false) } }, [onOpenChange, t]) const handleEnable = async () => { if (!code) { toast.error(t('Please enter the verification code')) return } try { setLoading(true) const response = await enable2FA(code) if (response.success) { toast.success(t('Two-factor authentication enabled successfully!')) onOpenChange(false) onSuccess() // Reset setStep(0) setCode('') setSetupData(null) } else { toast.error(response.message || t('Failed to enable 2FA')) } } catch (_error) { toast.error(t('Failed to enable 2FA')) } finally { setLoading(false) } } const handleOpenChange = (open: boolean) => { if (!loading && !initializing) { if (open && !setupData) { handleSetup() } if (!open) { setStep(0) setCode('') setSetupData(null) } onOpenChange(open) } } // Initialize when dialog opens useEffect(() => { if (open && !setupData && !initializing) { handleSetup() } }, [open, setupData, initializing, handleSetup]) return ( {t('Step')} {step + 1} {t('of 3:')} {stepLabels[step]} } contentClassName='sm:max-w-lg' contentHeight='auto' bodyClassName='space-y-4' footer={ <> {step > 0 && ( )} {step < 2 ? ( ) : ( )} } >
{initializing ? (
{t('Setting up 2FA...')}
) : !setupData ? (
{t('Failed to load setup data')}
) : ( <> {/* Step 0: QR Code */} {step === 0 && (

{t( 'Scan this QR code with your authenticator app (Google Authenticator, Microsoft Authenticator, etc.)' )}

{t('Or enter this key manually:')}

{setupData.secret}
)} {/* Step 1: Backup Codes */} {step === 1 && (
{t( 'Save these backup codes in a safe place. Each code can only be used once.' )}
{setupData.backup_codes.map((code, index) => (
{code}
))}
{t('Copy All Codes')}
)} {/* Step 2: Verify */} {step === 2 && (
setCode(e.target.value)} placeholder={t('Enter 6-digit code')} maxLength={6} disabled={loading} />

{t('Enter the 6-digit code from your authenticator app')}

)} )}
) }