import React, { useState } from "react"; import { Modal, Stack, Text, Button, Paper, Group, List, Code, CopyButton, Alert, PasswordInput, } from "@mantine/core"; import { IconRefresh, IconCopy, IconCheck, IconAlertCircle, } from "@tabler/icons-react"; import { useMutation } from "@tanstack/react-query"; import { notifications } from "@mantine/notifications"; import { useTranslation } from "react-i18next"; import { regenerateBackupCodes } from "@/ee/mfa"; import { useForm } from "@mantine/form"; import { zodResolver } from "mantine-form-zod-resolver"; import { z } from "zod"; interface MfaBackupCodesModalProps { opened: boolean; onClose: () => void; } const formSchema = z.object({ confirmPassword: z.string().min(1, { message: "Password is required" }), }); export function MfaBackupCodesModal({ opened, onClose, }: MfaBackupCodesModalProps) { const { t } = useTranslation(); const [backupCodes, setBackupCodes] = useState([]); const [showNewCodes, setShowNewCodes] = useState(false); const form = useForm({ validate: zodResolver(formSchema), initialValues: { confirmPassword: "", }, }); const regenerateMutation = useMutation({ mutationFn: (data: { confirmPassword: string }) => regenerateBackupCodes(data), onSuccess: (data) => { setBackupCodes(data.backupCodes); setShowNewCodes(true); form.reset(); notifications.show({ title: t("Success"), message: t("New backup codes have been generated"), }); }, onError: (error: any) => { notifications.show({ title: t("Error"), message: error.response?.data?.message || t("Failed to regenerate backup codes"), color: "red", }); }, }); const handleRegenerate = (values: { confirmPassword: string }) => { regenerateMutation.mutate(values); }; const handleClose = () => { setShowNewCodes(false); setBackupCodes([]); form.reset(); onClose(); }; return ( {!showNewCodes ? (
} title={t("About backup codes")} color="blue" variant="light" > {t( "Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.", )} {t( "You can regenerate new backup codes at any time. This will invalidate all existing codes.", )}
) : ( <> } title={t("Save your new backup codes")} color="yellow" > {t( "Make sure to save these codes in a secure place. Your old backup codes are no longer valid.", )} {t("Your new backup codes")} {({ copied, copy }) => ( )} {backupCodes.map((code, index) => ( {code} ))} )}
); }