This commit is contained in:
gpt-engineer-app[bot]
2026-01-12 06:28:36 +00:00
parent b82abaa423
commit 872e720b9a
4 changed files with 256 additions and 71 deletions
+80 -31
View File
@@ -1,6 +1,8 @@
import { useState } from "react";
import { AlertTriangle, Loader2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
AlertDialog,
AlertDialogContent,
@@ -9,6 +11,7 @@ import {
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { useToast } from "@/hooks/use-toast";
import { api, ApiError } from "@/lib/api";
interface TotpRemoveDialogProps {
open: boolean;
@@ -24,16 +27,34 @@ export function TotpRemoveDialog({
isRequired = false,
}: TotpRemoveDialogProps) {
const [isLoading, setIsLoading] = useState(false);
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const { toast } = useToast();
const resetDialog = () => {
setPassword("");
setError(null);
setIsLoading(false);
};
const handleClose = (isOpen: boolean) => {
if (!isOpen) {
resetDialog();
}
onOpenChange(isOpen);
};
const handleRemove = async () => {
if (!password) {
setError("Password is required to disable TOTP");
return;
}
setIsLoading(true);
setError(null);
try {
// TODO: Call actual API to remove TOTP
// await api.users.removeTotpEnrollment();
await new Promise((resolve) => setTimeout(resolve, 1000));
await api.totp.disable(password);
toast({
title: "Two-factor authentication disabled",
@@ -41,21 +62,31 @@ export function TotpRemoveDialog({
});
onSuccess();
onOpenChange(false);
handleClose(false);
} catch (err) {
console.error("Failed to remove TOTP:", err);
toast({
title: "Failed to remove TOTP",
description: "An error occurred. Please try again.",
variant: "destructive",
});
if (err instanceof ApiError) {
if (err.type === "INVALID_CREDENTIALS" || err.code === 401) {
setError("Incorrect password. Please try again.");
} else {
setError(err.message);
}
} else {
setError("An error occurred. Please try again.");
}
} finally {
setIsLoading(false);
}
};
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Enter" && password) {
handleRemove();
}
};
return (
<AlertDialog open={open} onOpenChange={onOpenChange}>
<AlertDialog open={open} onOpenChange={handleClose}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle className="flex items-center gap-2">
@@ -74,29 +105,47 @@ export function TotpRemoveDialog({
You may lose access to certain features if you disable it.
</div>
)}
<p className="text-sm">
Are you sure you want to continue?
</p>
</AlertDialogDescription>
</AlertDialogHeader>
<div className="flex justify-end gap-2 mt-4">
<Button
variant="outline"
onClick={() => onOpenChange(false)}
disabled={isLoading}
>
Cancel
</Button>
<Button
variant="destructive"
onClick={handleRemove}
disabled={isLoading}
>
{isLoading && <Loader2 className="w-4 h-4 mr-2 animate-spin" />}
Remove TOTP
</Button>
<div className="space-y-4 mt-4">
<div className="space-y-2">
<Label htmlFor="password-confirm">Enter your password to confirm</Label>
<Input
id="password-confirm"
type="password"
placeholder="Your current password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
setError(null);
}}
onKeyDown={handleKeyDown}
disabled={isLoading}
autoFocus
/>
{error && (
<p className="text-sm text-destructive">{error}</p>
)}
</div>
<div className="flex justify-end gap-2">
<Button
variant="outline"
onClick={() => handleClose(false)}
disabled={isLoading}
>
Cancel
</Button>
<Button
variant="destructive"
onClick={handleRemove}
disabled={isLoading || !password}
>
{isLoading && <Loader2 className="w-4 h-4 mr-2 animate-spin" />}
Remove TOTP
</Button>
</div>
</div>
</AlertDialogContent>
</AlertDialog>