fix(ui): replace browser confirm dialogs with ConfirmModal component (#1060)
Replace 10 instances of native browser confirm() dialogs with the existing ConfirmModal component for consistent UX across the dashboard. Changes: - Add ConfirmModal to 5 files (combos, endpoints, proxy pools, providers, connections) - Maintain same confirmation flow with improved styling - Use 'danger' variant for destructive actions - Preserve all existing functionality Affected areas: - Combo deletion (combos page) - API key deletion/pausing (EndpointPageClient) - Proxy pool management (single/bulk delete, disable dead proxies) - Provider operations (disable all models, delete connection, delete compatible node) - Connection management (ConnectionsCard) All changes manually tested and verified.
This commit is contained in:
1 parent
5cab23d92e
commit
3c2503c4b4
5 files changed
+225
-114
No files matched your search
@@ -4,7 +4,7 @@ import { useState, useEffect, useCallback } from "react";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { Card, Button, Badge, Input, Modal, CardSkeleton, OAuthModal, KiroOAuthWrapper, CursorAuthModal, IFlowCookieModal, GitLabAuthModal, Toggle, Select, EditConnectionModal, NoAuthProxyCard } from "@/shared/components";
|
||||
import { Card, Button, Badge, Input, Modal, CardSkeleton, OAuthModal, KiroOAuthWrapper, CursorAuthModal, IFlowCookieModal, GitLabAuthModal, Toggle, Select, EditConnectionModal, NoAuthProxyCard, ConfirmModal } from "@/shared/components";
|
||||
import { OAUTH_PROVIDERS, APIKEY_PROVIDERS, FREE_PROVIDERS, FREE_TIER_PROVIDERS, WEB_COOKIE_PROVIDERS, getProviderAlias, isOpenAICompatibleProvider, isAnthropicCompatibleProvider, AI_PROVIDERS, THINKING_CONFIG } from "@/shared/constants/providers";
|
||||
import { getModelsByProviderId } from "@/shared/constants/models";
|
||||
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
|
||||
@@ -42,12 +42,13 @@ export default function ProviderDetailPage() {
|
||||
const [selectedConnectionIds, setSelectedConnectionIds] = useState([]);
|
||||
const [bulkProxyPoolId, setBulkProxyPoolId] = useState("__none__");
|
||||
const [bulkUpdatingProxy, setBulkUpdatingProxy] = useState(false);
|
||||
const [providerStrategy, setProviderStrategy] = useState(null); // null = use global, "round-robin" = override
|
||||
const [providerStrategy, setProviderStrategy] = useState(null);
|
||||
const [providerStickyLimit, setProviderStickyLimit] = useState("");
|
||||
const [thinkingMode, setThinkingMode] = useState("auto");
|
||||
const [suggestedModels, setSuggestedModels] = useState([]);
|
||||
const [kiloFreeModels, setKiloFreeModels] = useState([]);
|
||||
const [disabledModelIds, setDisabledModelIds] = useState([]);
|
||||
const [confirmState, setConfirmState] = useState(null);
|
||||
const { copied, copy } = useCopyToClipboard();
|
||||
|
||||
const providerInfo = providerNode
|
||||
@@ -110,17 +111,23 @@ export default function ProviderDetailPage() {
|
||||
|
||||
const handleDisableAll = async (ids) => {
|
||||
if (!ids.length) return;
|
||||
if (!confirm(`Disable all ${ids.length} model(s)?`)) return;
|
||||
try {
|
||||
const res = await fetch("/api/models/disabled", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ providerAlias: providerStorageAlias, ids }),
|
||||
});
|
||||
if (res.ok) await fetchDisabledModels();
|
||||
} catch (error) {
|
||||
console.log("Error disabling all models:", error);
|
||||
}
|
||||
setConfirmState({
|
||||
title: "Disable All Models",
|
||||
message: `Disable all ${ids.length} model(s)?`,
|
||||
onConfirm: async () => {
|
||||
setConfirmState(null);
|
||||
try {
|
||||
const res = await fetch("/api/models/disabled", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ providerAlias: providerStorageAlias, ids }),
|
||||
});
|
||||
if (res.ok) await fetchDisabledModels();
|
||||
} catch (error) {
|
||||
console.log("Error disabling all models:", error);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleEnableAll = async () => {
|
||||
@@ -338,15 +345,21 @@ export default function ProviderDetailPage() {
|
||||
};
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
if (!confirm("Delete this connection?")) return;
|
||||
try {
|
||||
const res = await fetch(`/api/providers/${id}`, { method: "DELETE" });
|
||||
if (res.ok) {
|
||||
setConnections(connections.filter(c => c.id !== id));
|
||||
setConfirmState({
|
||||
title: "Delete Connection",
|
||||
message: "Delete this connection?",
|
||||
onConfirm: async () => {
|
||||
setConfirmState(null);
|
||||
try {
|
||||
const res = await fetch(`/api/providers/${id}`, { method: "DELETE" });
|
||||
if (res.ok) {
|
||||
setConnections(connections.filter(c => c.id !== id));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error deleting connection:", error);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error deleting connection:", error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleOAuthSuccess = () => {
|
||||
@@ -954,15 +967,21 @@ export default function ProviderDetailPage() {
|
||||
variant="secondary"
|
||||
icon="delete"
|
||||
onClick={async () => {
|
||||
if (!confirm(`Delete this ${isAnthropicCompatible ? "Anthropic" : "OpenAI"} Compatible node?`)) return;
|
||||
try {
|
||||
const res = await fetch(`/api/provider-nodes/${providerId}`, { method: "DELETE" });
|
||||
if (res.ok) {
|
||||
router.push("/dashboard/providers");
|
||||
setConfirmState({
|
||||
title: "Delete Compatible Node",
|
||||
message: `Delete this ${isAnthropicCompatible ? "Anthropic" : "OpenAI"} Compatible node?`,
|
||||
onConfirm: async () => {
|
||||
setConfirmState(null);
|
||||
try {
|
||||
const res = await fetch(`/api/provider-nodes/${providerId}`, { method: "DELETE" });
|
||||
if (res.ok) {
|
||||
router.push("/dashboard/providers");
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error deleting provider node:", error);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error deleting provider node:", error);
|
||||
}
|
||||
});
|
||||
}}
|
||||
className="w-full sm:w-auto"
|
||||
>
|
||||
@@ -1222,6 +1241,16 @@ export default function ProviderDetailPage() {
|
||||
onClose={() => setShowAddCustomModel(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Confirm Modal */}
|
||||
<ConfirmModal
|
||||
isOpen={!!confirmState}
|
||||
onClose={() => setConfirmState(null)}
|
||||
onConfirm={confirmState?.onConfirm}
|
||||
title={confirmState?.title || "Confirm"}
|
||||
message={confirmState?.message}
|
||||
variant="danger"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Card, Badge, Button, Modal, Select, Toggle, EditConnectionModal } from "@/shared/components";
|
||||
import { Card, Badge, Button, Modal, Select, Toggle, EditConnectionModal, ConfirmModal } from "@/shared/components";
|
||||
|
||||
// ── CooldownTimer ──────────────────────────────────────────────
|
||||
function CooldownTimer({ until }) {
|
||||
@@ -308,6 +308,7 @@ export default function ConnectionsCard({ providerId, isOAuth }) {
|
||||
const [selectedConnection, setSelectedConnection] = useState(null);
|
||||
const [providerStrategy, setProviderStrategy] = useState(null);
|
||||
const [providerStickyLimit, setProviderStickyLimit] = useState("1");
|
||||
const [confirmState, setConfirmState] = useState(null);
|
||||
|
||||
const fetch_ = useCallback(async () => {
|
||||
try {
|
||||
@@ -358,11 +359,17 @@ export default function ConnectionsCard({ providerId, isOAuth }) {
|
||||
};
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
if (!confirm("Delete this connection?")) return;
|
||||
try {
|
||||
const res = await fetch(`/api/providers/${id}`, { method: "DELETE" });
|
||||
if (res.ok) setConnections((prev) => prev.filter((c) => c.id !== id));
|
||||
} catch (e) { console.log("delete error:", e); }
|
||||
setConfirmState({
|
||||
title: "Delete Connection",
|
||||
message: "Delete this connection?",
|
||||
onConfirm: async () => {
|
||||
setConfirmState(null);
|
||||
try {
|
||||
const res = await fetch(`/api/providers/${id}`, { method: "DELETE" });
|
||||
if (res.ok) setConnections((prev) => prev.filter((c) => c.id !== id));
|
||||
} catch (e) { console.log("delete error:", e); }
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleToggleActive = async (id, isActive) => {
|
||||
@@ -470,6 +477,16 @@ export default function ConnectionsCard({ providerId, isOAuth }) {
|
||||
onSave={handleUpdateConnection}
|
||||
onClose={() => setShowEditModal(false)}
|
||||
/>
|
||||
|
||||
{/* Confirm Modal */}
|
||||
<ConfirmModal
|
||||
isOpen={!!confirmState}
|
||||
onClose={() => setConfirmState(null)}
|
||||
onConfirm={confirmState?.onConfirm}
|
||||
title={confirmState?.title || "Confirm"}
|
||||
message={confirmState?.message}
|
||||
variant="danger"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user