diff --git a/ui/src/components/account/flow-viz/account-card.tsx b/ui/src/components/account/flow-viz/account-card.tsx index 7f7d2bd7..29dff515 100644 --- a/ui/src/components/account/flow-viz/account-card.tsx +++ b/ui/src/components/account/flow-viz/account-card.tsx @@ -10,9 +10,10 @@ import { getMinClaudeQuota, } from '@/lib/utils'; import { PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context'; -import { GripVertical, Loader2, Clock, Pause } from 'lucide-react'; +import { GripVertical, Loader2, Clock, Pause, Play } from 'lucide-react'; import { useAccountQuota } from '@/hooks/use-cliproxy-stats'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; +import { Button } from '@/components/ui/button'; import type { AccountData, DragOffset } from './types'; import { cleanEmail } from './utils'; @@ -34,6 +35,8 @@ interface AccountCardProps { onPointerDown: (e: React.PointerEvent) => void; onPointerMove: (e: React.PointerEvent) => void; onPointerUp: () => void; + onPauseToggle?: (accountId: string, paused: boolean) => void; + isPausingAccount?: boolean; } const BORDER_SIDE_MAP: Record = { @@ -77,6 +80,8 @@ export function AccountCard({ onPointerDown, onPointerMove, onPointerUp, + onPauseToggle, + isPausingAccount, }: AccountCardProps) { const borderSide = BORDER_SIDE_MAP[zone]; const borderColor = getBorderColorStyle(zone, account.color); @@ -109,13 +114,48 @@ export function AccountCard({ borderSide, 'select-none touch-none', isHovered && 'bg-muted/50 dark:bg-zinc-800/60', - isDragging && 'cursor-grabbing shadow-xl scale-105 z-50' + isDragging && 'cursor-grabbing shadow-xl scale-105 z-50', + account.paused && 'opacity-60' )} style={{ ...borderColor, transform: `translate(${offset.x}px, ${offset.y}px)${isDragging ? ' scale(1.05)' : ''}`, }} > + {/* Pause toggle button - visible on hover or when paused */} + {onPauseToggle && ( + + + + + + + {account.paused ? 'Resume account' : 'Pause account'} + + + + )}
(null); const svgRef = useRef(null); const [hoveredAccount, setHoveredAccount] = useState(null); @@ -112,6 +117,8 @@ export function AccountFlowViz({ providerData, onBack }: AccountFlowVizProps) { onPointerDown={(e) => handlePointerDown(account.id, e)} onPointerMove={handlePointerMove} onPointerUp={handlePointerUp} + onPauseToggle={onPauseToggle} + isPausingAccount={isPausingAccount} /> ); }); diff --git a/ui/src/components/account/flow-viz/types.ts b/ui/src/components/account/flow-viz/types.ts index b6e5ca33..423155b1 100644 --- a/ui/src/components/account/flow-viz/types.ts +++ b/ui/src/components/account/flow-viz/types.ts @@ -16,6 +16,7 @@ export interface AccountData { failureCount: number; lastUsedAt?: string; color: string; + paused?: boolean; } export interface ProviderData { @@ -28,6 +29,8 @@ export interface ProviderData { export interface AccountFlowVizProps { providerData: ProviderData; onBack?: () => void; + onPauseToggle?: (accountId: string, paused: boolean) => void; + isPausingAccount?: boolean; } export interface ConnectionEvent { diff --git a/ui/src/components/cliproxy/provider-editor/account-item.tsx b/ui/src/components/cliproxy/provider-editor/account-item.tsx index 33ee1c46..afc9ac3e 100644 --- a/ui/src/components/cliproxy/provider-editor/account-item.tsx +++ b/ui/src/components/cliproxy/provider-editor/account-item.tsx @@ -267,24 +267,6 @@ export function AccountItem({ Set as default )} - {onPauseToggle && ( - onPauseToggle(!account.paused)} - disabled={isPausingAccount} - > - {account.paused ? ( - <> - - {isPausingAccount ? 'Resuming...' : 'Resume account'} - - ) : ( - <> - - {isPausingAccount ? 'Pausing...' : 'Pause account'} - - )} - - )} (null); const [hoveredProvider, setHoveredProvider] = useState(null); + // Account control mutations for flow viz + const pauseMutation = usePauseAccount(); + const resumeMutation = useResumeAccount(); + // Get selected provider data for detail view const selectedProviderData = selectedProvider ? providerStats.find((ps) => ps.provider === selectedProvider) : null; + const handlePauseToggle = (accountId: string, paused: boolean) => { + if (!selectedProvider || pauseMutation.isPending || resumeMutation.isPending) return; + if (paused) { + pauseMutation.mutate({ provider: selectedProvider, accountId }); + } else { + resumeMutation.mutate({ provider: selectedProvider, accountId }); + } + }; + if (isLoading) { return (
@@ -121,6 +135,8 @@ export function AuthMonitor() { setSelectedProvider(null)} + onPauseToggle={handlePauseToggle} + isPausingAccount={pauseMutation.isPending || resumeMutation.isPending} /> ) : (