import InputError from '@/components/input-error';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from '@/components/ui/input-otp';
import { useClipboard } from '@/hooks/use-clipboard';
import { OTP_MAX_LENGTH } from '@/hooks/use-two-factor-auth';
import { confirm } from '@/routes/two-factor';
import { Form } from '@inertiajs/react';
import { REGEXP_ONLY_DIGITS } from 'input-otp';
import { Check, Copy, Loader2, ScanLine } from 'lucide-react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import AlertError from './alert-error';
function GridScanIcon() {
return (
{Array.from({ length: 5 }, (_, i) => (
))}
{Array.from({ length: 5 }, (_, i) => (
))}
);
}
function TwoFactorSetupStep({
qrCodeSvg,
manualSetupKey,
buttonText,
onNextStep,
errors,
}: {
qrCodeSvg: string | null;
manualSetupKey: string | null;
buttonText: string;
onNextStep: () => void;
errors: string[];
}) {
const [copiedText, copy] = useClipboard();
const IconComponent = copiedText === manualSetupKey ? Check : Copy;
return (
<>
{errors?.length ? (
) : (
<>
or, enter the code manually
{!manualSetupKey ? (
) : (
<>
>
)}
>
)}
>
);
}
function TwoFactorVerificationStep({
onClose,
onBack,
}: {
onClose: () => void;
onBack: () => void;
}) {
const [code, setCode] = useState('');
const pinInputContainerRef = useRef(null);
useEffect(() => {
setTimeout(() => {
pinInputContainerRef.current?.querySelector('input')?.focus();
}, 0);
}, []);
return (
);
}
interface TwoFactorSetupModalProps {
isOpen: boolean;
onClose: () => void;
requiresConfirmation: boolean;
twoFactorEnabled: boolean;
qrCodeSvg: string | null;
manualSetupKey: string | null;
clearSetupData: () => void;
fetchSetupData: () => Promise;
errors: string[];
}
export default function TwoFactorSetupModal({
isOpen,
onClose,
requiresConfirmation,
twoFactorEnabled,
qrCodeSvg,
manualSetupKey,
clearSetupData,
fetchSetupData,
errors,
}: TwoFactorSetupModalProps) {
const [showVerificationStep, setShowVerificationStep] =
useState(false);
const modalConfig = useMemo<{
title: string;
description: string;
buttonText: string;
}>(() => {
if (twoFactorEnabled) {
return {
title: 'Two-Factor Authentication Enabled',
description:
'Two-factor authentication is now enabled. Scan the QR code or enter the setup key in your authenticator app.',
buttonText: 'Close',
};
}
if (showVerificationStep) {
return {
title: 'Verify Authentication Code',
description:
'Enter the 6-digit code from your authenticator app',
buttonText: 'Continue',
};
}
return {
title: 'Enable Two-Factor Authentication',
description:
'To finish enabling two-factor authentication, scan the QR code or enter the setup key in your authenticator app',
buttonText: 'Continue',
};
}, [twoFactorEnabled, showVerificationStep]);
const handleModalNextStep = useCallback(() => {
if (requiresConfirmation) {
setShowVerificationStep(true);
return;
}
clearSetupData();
onClose();
}, [requiresConfirmation, clearSetupData, onClose]);
const resetModalState = useCallback(() => {
setShowVerificationStep(false);
if (twoFactorEnabled) {
clearSetupData();
}
}, [twoFactorEnabled, clearSetupData]);
useEffect(() => {
if (!isOpen) {
resetModalState();
return;
}
if (!qrCodeSvg) {
fetchSetupData();
}
}, [isOpen, qrCodeSvg, fetchSetupData, resetModalState]);
return (
);
}