feat: add bulk delete for provider connections

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
teddytkz
2026-06-26 10:23:27 +07:00
committed by decolua
co-authored by Cursor
parent c22f11de38
commit 644bff4cdd
@@ -637,7 +637,7 @@ export default function ProviderDetailPage() {
try { try {
const res = await fetch(`/api/providers/${id}`, { method: "DELETE" }); const res = await fetch(`/api/providers/${id}`, { method: "DELETE" });
if (res.ok) { if (res.ok) {
setConnections(connections.filter(c => c.id !== id)); setConnections(prev => prev.filter(c => c.id !== id));
} }
} catch (error) { } catch (error) {
console.log("Error deleting connection:", error); console.log("Error deleting connection:", error);
@@ -646,6 +646,32 @@ export default function ProviderDetailPage() {
}); });
}; };
const handleBulkDelete = () => {
const count = selectedConnectionIds.length;
if (count === 0) return;
setConfirmState({
title: `Delete ${count} Connection${count > 1 ? "s" : ""}`,
message: `Delete ${count} connection${count > 1 ? "s" : ""}? This cannot be undone.`,
onConfirm: async () => {
setConfirmState(null);
let failed = 0;
const idsToDelete = [...selectedConnectionIds];
for (const id of idsToDelete) {
try {
const res = await fetch(`/api/providers/${id}`, { method: "DELETE" });
if (!res.ok) failed += 1;
} catch (error) {
console.log("Error deleting connection:", error);
failed += 1;
}
}
setConnections(prev => prev.filter(c => !idsToDelete.includes(c.id)));
setSelectedConnectionIds([]);
if (failed > 0) alert(`Deleted ${idsToDelete.length - failed} connection(s), ${failed} failed.`);
}
});
};
const handleOAuthSuccess = () => { const handleOAuthSuccess = () => {
fetchConnections(); fetchConnections();
setShowOAuthModal(false); setShowOAuthModal(false);
@@ -844,6 +870,14 @@ export default function ProviderDetailPage() {
{connections {connections
.map((conn, index) => ( .map((conn, index) => (
<div key={conn.id} className="flex min-w-0 items-stretch"> <div key={conn.id} className="flex min-w-0 items-stretch">
<div className="flex shrink-0 items-center pl-1 sm:pl-2">
<input
type="checkbox"
checked={isSelected(conn.id)}
onChange={() => toggleSelectConnection(conn.id)}
className="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"
/>
</div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<ConnectionRow <ConnectionRow
connection={conn} connection={conn}
@@ -1319,6 +1353,16 @@ export default function ProviderDetailPage() {
)} )}
{connections.length > 0 && ( {connections.length > 0 && (
<> <>
{selectedConnectionIds.length > 0 && (
<Button
size="sm"
variant="danger"
icon="delete"
onClick={handleBulkDelete}
>
Delete Selected ({selectedConnectionIds.length})
</Button>
)}
<Button <Button
size="sm" size="sm"
variant="secondary" variant="secondary"
@@ -1446,6 +1490,19 @@ export default function ProviderDetailPage() {
</div> </div>
</div> </div>
)} )}
{connections.length > 0 && (
<div className="mb-3 flex items-center gap-2 border-b border-black/[0.03] pb-2 dark:border-white/[0.03]">
<label className="flex cursor-pointer items-center gap-1.5 text-xs text-text-muted hover:text-primary">
<input
type="checkbox"
checked={allSelected}
onChange={toggleSelectAllConnections}
className="h-3.5 w-3.5 rounded border-gray-300 text-primary focus:ring-primary"
/>
Select All
</label>
</div>
)}
{connectionsList} {connectionsList}
{!isCompatible && ( {!isCompatible && (
<div className="mt-4 grid grid-cols-1 gap-2 sm:flex"> <div className="mt-4 grid grid-cols-1 gap-2 sm:flex">