feat: show failover toggle independently on main page with confirm dialog

Add enableFailoverToggle setting to control failover toggle visibility
on the main page, decoupled from proxy takeover state. First-time
enable shows a ConfirmDialog (same pattern as proxy toggle). The toggle
row is placed in the Auto Failover accordion section in settings.
This commit is contained in:
Jason
2026-03-08 22:25:48 +08:00
parent 6d078e7f33
commit 032a8203fd
7 changed files with 79 additions and 13 deletions
+40 -1
View File
@@ -1,5 +1,5 @@
import { useState } from "react";
import { Server, Activity, Zap, Globe } from "lucide-react";
import { Server, Activity, Zap, Globe, ShieldAlert } from "lucide-react";
import { motion } from "framer-motion";
import { useTranslation } from "react-i18next";
import {
@@ -16,6 +16,7 @@ import { FailoverQueueManager } from "@/components/proxy/FailoverQueueManager";
import { RectifierConfigPanel } from "@/components/settings/RectifierConfigPanel";
import { GlobalProxySettings } from "@/components/settings/GlobalProxySettings";
import { ConfirmDialog } from "@/components/ConfirmDialog";
import { ToggleRow } from "@/components/ui/toggle-row";
import { useProxyStatus } from "@/hooks/useProxyStatus";
import type { SettingsFormState } from "@/hooks/useSettings";
@@ -30,6 +31,7 @@ export function ProxyTabContent({
}: ProxyTabContentProps) {
const { t } = useTranslation();
const [showProxyConfirm, setShowProxyConfirm] = useState(false);
const [showFailoverConfirm, setShowFailoverConfirm] = useState(false);
const {
isRunning,
@@ -62,6 +64,23 @@ export function ProxyTabContent({
}
};
const handleFailoverToggleChange = (checked: boolean) => {
if (checked && !settings?.failoverConfirmed) {
setShowFailoverConfirm(true);
} else {
void onAutoSave({ enableFailoverToggle: checked });
}
};
const handleFailoverConfirm = async () => {
setShowFailoverConfirm(false);
try {
await onAutoSave({ failoverConfirmed: true, enableFailoverToggle: true });
} catch (error) {
console.error("Failover confirm failed:", error);
}
};
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
@@ -131,6 +150,16 @@ export function ProxyTabContent({
</AccordionTrigger>
<AccordionContent className="px-6 pb-6 pt-4 border-t border-border/50">
<div className="space-y-6">
<ToggleRow
icon={<ShieldAlert className="h-4 w-4 text-orange-500" />}
title={t("settings.advanced.proxy.enableFailoverToggle")}
description={t(
"settings.advanced.proxy.enableFailoverToggleDescription",
)}
checked={settings?.enableFailoverToggle ?? false}
onCheckedChange={handleFailoverToggleChange}
/>
{!isRunning && (
<div className="p-4 rounded-lg bg-yellow-500/10 border border-yellow-500/20">
<p className="text-sm text-yellow-600 dark:text-yellow-400">
@@ -274,6 +303,16 @@ export function ProxyTabContent({
onConfirm={() => void handleProxyConfirm()}
onCancel={() => setShowProxyConfirm(false)}
/>
<ConfirmDialog
isOpen={showFailoverConfirm}
variant="info"
title={t("confirm.failover.title")}
message={t("confirm.failover.message")}
confirmText={t("confirm.failover.confirm")}
onConfirm={() => void handleFailoverConfirm()}
onCancel={() => setShowFailoverConfirm(false)}
/>
</motion.div>
);
}