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:
Zanuar Tri Romadon authored and GitHub committed 2026-05-13 15:32:17 +07:00
1 parent 5cab23d92e
commit 3c2503c4b4
5 files changed
+225 -114

No files matched your search

@@ -1,7 +1,7 @@
"use client";
import { useCallback, useEffect, useMemo, useState } from "react";
import { Badge, Button, Card, CardSkeleton, Input, Modal, Toggle } from "@/shared/components";
import { Badge, Button, Card, CardSkeleton, Input, Modal, Toggle, ConfirmModal } from "@/shared/components";
import { useNotificationStore } from "@/store/notificationStore";
function getStatusVariant(status) {
@@ -45,6 +45,7 @@ export default function ProxyPoolsPage() {
const [healthChecking, setHealthChecking] = useState(false);
const [healthProgress, setHealthProgress] = useState({ current: 0, total: 0 });
const [bulkBusy, setBulkBusy] = useState(false);
const [confirmState, setConfirmState] = useState(null);
const notify = useNotificationStore();
const fetchProxyPools = useCallback(async () => {
@@ -122,27 +123,31 @@ export default function ProxyPoolsPage() {
};
const handleDelete = async (proxyPool) => {
const deleting = confirm(`Delete proxy pool \"${proxyPool.name}\"?`);
if (!deleting) return;
setConfirmState({
title: "Delete Proxy Pool",
message: `Delete proxy pool "${proxyPool.name}"?`,
onConfirm: async () => {
setConfirmState(null);
try {
const res = await fetch(`/api/proxy-pools/${proxyPool.id}`, { method: "DELETE" });
if (res.ok) {
setProxyPools((prev) => prev.filter((item) => item.id !== proxyPool.id));
notify.success("Proxy pool deleted");
return;
}
try {
const res = await fetch(`/api/proxy-pools/${proxyPool.id}`, { method: "DELETE" });
if (res.ok) {
setProxyPools((prev) => prev.filter((item) => item.id !== proxyPool.id));
notify.success("Proxy pool deleted");
return;
const data = await res.json();
if (res.status === 409) {
notify.warning(`Cannot delete: ${data.boundConnectionCount || 0} connection(s) are still using this pool.`);
} else {
notify.error(data.error || "Failed to delete proxy pool");
}
} catch (error) {
console.log("Error deleting proxy pool:", error);
notify.error("Failed to delete proxy pool");
}
}
const data = await res.json();
if (res.status === 409) {
notify.warning(`Cannot delete: ${data.boundConnectionCount || 0} connection(s) are still using this pool.`);
} else {
notify.error(data.error || "Failed to delete proxy pool");
}
} catch (error) {
console.log("Error deleting proxy pool:", error);
notify.error("Failed to delete proxy pool");
}
});
};
const handleTest = async (proxyPoolId) => {
@@ -215,24 +220,30 @@ export default function ProxyPoolsPage() {
const bulkDelete = async () => {
if (selectedIds.length === 0) return;
if (!confirm(`Delete ${selectedIds.length} proxy pool(s)?`)) return;
setBulkBusy(true);
try {
let ok = 0; let blocked = 0; let failed = 0;
for (const id of selectedIds) {
setConfirmState({
title: "Delete Proxy Pools",
message: `Delete ${selectedIds.length} proxy pool(s)?`,
onConfirm: async () => {
setConfirmState(null);
setBulkBusy(true);
try {
const res = await fetch(`/api/proxy-pools/${id}`, { method: "DELETE" });
if (res.ok) ok += 1;
else if (res.status === 409) blocked += 1;
else failed += 1;
} catch { failed += 1; }
let ok = 0; let blocked = 0; let failed = 0;
for (const id of selectedIds) {
try {
const res = await fetch(`/api/proxy-pools/${id}`, { method: "DELETE" });
if (res.ok) ok += 1;
else if (res.status === 409) blocked += 1;
else failed += 1;
} catch { failed += 1; }
}
await fetchProxyPools();
clearSelection();
notify.success(`Deleted ${ok}${blocked ? `, ${blocked} bound` : ""}${failed ? `, ${failed} failed` : ""}`);
} finally {
setBulkBusy(false);
}
}
await fetchProxyPools();
clearSelection();
notify.success(`Deleted ${ok}${blocked ? `, ${blocked} bound` : ""}${failed ? `, ${failed} failed` : ""}`);
} finally {
setBulkBusy(false);
}
});
};
const handleHealthCheck = async () => {
@@ -269,23 +280,30 @@ export default function ProxyPoolsPage() {
setHealthChecking(false);
setHealthProgress({ current: 0, total: 0 });
if (deadIds.length > 0 && confirm(`Alive: ${alive}, Dead: ${deadIds.length}.\n\nDisable ${deadIds.length} dead proxies?`)) {
setBulkBusy(true);
try {
for (const id of deadIds) {
if (deadIds.length > 0) {
setConfirmState({
title: "Disable Dead Proxies",
message: `Alive: ${alive}, Dead: ${deadIds.length}.\n\nDisable ${deadIds.length} dead proxies?`,
onConfirm: async () => {
setConfirmState(null);
setBulkBusy(true);
try {
await fetch(`/api/proxy-pools/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ isActive: false }),
});
} catch {}
for (const id of deadIds) {
try {
await fetch(`/api/proxy-pools/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ isActive: false }),
});
} catch {}
}
await fetchProxyPools();
notify.success(`Disabled ${deadIds.length} dead proxies`);
} finally {
setBulkBusy(false);
}
}
await fetchProxyPools();
notify.success(`Disabled ${deadIds.length} dead proxies`);
} finally {
setBulkBusy(false);
}
});
} else {
notify.success(`Health check done. Alive: ${alive}, Dead: ${deadIds.length}`);
}
@@ -768,6 +786,16 @@ export default function ProxyPoolsPage() {
</div>
</div>
</Modal>
{/* Confirm Modal */}
<ConfirmModal
isOpen={!!confirmState}
onClose={() => setConfirmState(null)}
onConfirm={confirmState?.onConfirm}
title={confirmState?.title || "Confirm"}
message={confirmState?.message}
variant="danger"
/>
</div>
);
}