From 4e37027e1f88fcf2ac0a4e0ceb151c0190ce1109 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Fri, 20 Feb 2026 02:22:47 -0300 Subject: [PATCH] feat: add enable/disable toggle to provider cards on main page Toggle appears on hover next to the chevron. Clicking it batch-toggles all connections for that provider (enable/disable). Uses optimistic UI update with parallel API calls. stopPropagation prevents navigation. --- .../(dashboard)/dashboard/providers/page.tsx | 92 +++++++++++++++++-- 1 file changed, 83 insertions(+), 9 deletions(-) diff --git a/src/app/(dashboard)/dashboard/providers/page.tsx b/src/app/(dashboard)/dashboard/providers/page.tsx index fabe1b5193..4f5b68f994 100644 --- a/src/app/(dashboard)/dashboard/providers/page.tsx +++ b/src/app/(dashboard)/dashboard/providers/page.tsx @@ -3,7 +3,16 @@ import { useState, useEffect } from "react"; import Image from "next/image"; import PropTypes from "prop-types"; -import { Card, CardSkeleton, Badge, Button, Input, Modal, Select } from "@/shared/components"; +import { + Card, + CardSkeleton, + Badge, + Button, + Input, + Modal, + Select, + Toggle, +} from "@/shared/components"; import { OAUTH_PROVIDERS, APIKEY_PROVIDERS } from "@/shared/constants/config"; import { FREE_PROVIDERS, @@ -148,6 +157,29 @@ export default function ProvidersPage() { return { connected, error, total, errorCode, errorTime, allDisabled }; }; + // Toggle all connections for a provider on/off + const handleToggleProvider = async (providerId: string, authType: string, newActive: boolean) => { + const providerConns = connections.filter( + (c) => c.provider === providerId && c.authType === authType + ); + // Optimistically update UI + setConnections((prev) => + prev.map((c) => + c.provider === providerId && c.authType === authType ? { ...c, isActive: newActive } : c + ) + ); + // Fire API calls in parallel + await Promise.allSettled( + providerConns.map((c) => + fetch(`/api/providers/${c.id}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ isActive: newActive }), + }) + ) + ); + }; + const handleBatchTest = async (mode, providerId = null) => { if (testingMode) return; setTestingMode(mode === "provider" ? providerId : mode); @@ -237,6 +269,7 @@ export default function ProvidersPage() { provider={info} stats={getProviderStats(key, "oauth")} authType="oauth" + onToggle={(active) => handleToggleProvider(key, "oauth", active)} /> ))} @@ -273,6 +306,7 @@ export default function ProvidersPage() { provider={info} stats={getProviderStats(key, "oauth")} authType="free" + onToggle={(active) => handleToggleProvider(key, "oauth", active)} /> ))} @@ -310,6 +344,7 @@ export default function ProvidersPage() { provider={info} stats={getProviderStats(key, "apikey")} authType="apikey" + onToggle={(active) => handleToggleProvider(key, "apikey", active)} /> ))} @@ -373,6 +408,7 @@ export default function ProvidersPage() { provider={info} stats={getProviderStats(info.id, "apikey")} authType="compatible" + onToggle={(active) => handleToggleProvider(info.id, "apikey", active)} /> ))} @@ -425,7 +461,7 @@ export default function ProvidersPage() { ); } -function ProviderCard({ providerId, provider, stats, authType }) { +function ProviderCard({ providerId, provider, stats, authType, onToggle }) { const { connected, error, errorCode, errorTime, allDisabled } = stats; const [imgError, setImgError] = useState(false); @@ -490,9 +526,28 @@ function ProviderCard({ providerId, provider, stats, authType }) { - - chevron_right - +
+ {stats.total > 0 && ( +
{ + e.preventDefault(); + e.stopPropagation(); + onToggle(!allDisabled ? false : true); + }} + className="opacity-0 group-hover:opacity-100 transition-opacity" + > + {}} + title={allDisabled ? "Enable provider" : "Disable provider"} + /> +
+ )} + + chevron_right + +
@@ -517,7 +572,7 @@ ProviderCard.propTypes = { }; // API Key providers - use image with textIcon fallback (same as OAuth providers) -function ApiKeyProviderCard({ providerId, provider, stats, authType }) { +function ApiKeyProviderCard({ providerId, provider, stats, authType, onToggle }) { const { connected, error, errorCode, errorTime, allDisabled } = stats; const isCompatible = providerId.startsWith(OPENAI_COMPATIBLE_PREFIX); const isAnthropicCompatible = providerId.startsWith(ANTHROPIC_COMPATIBLE_PREFIX); @@ -605,9 +660,28 @@ function ApiKeyProviderCard({ providerId, provider, stats, authType }) { - - chevron_right - +
+ {stats.total > 0 && ( +
{ + e.preventDefault(); + e.stopPropagation(); + onToggle(!allDisabled ? false : true); + }} + className="opacity-0 group-hover:opacity-100 transition-opacity" + > + {}} + title={allDisabled ? "Enable provider" : "Disable provider"} + /> +
+ )} + + chevron_right + +