diff --git a/.changeset/age-2471-assistant-status-toggle.md b/.changeset/age-2471-assistant-status-toggle.md new file mode 100644 index 0000000000..b21b6aea4b --- /dev/null +++ b/.changeset/age-2471-assistant-status-toggle.md @@ -0,0 +1,5 @@ +--- +"dashboard": patch +--- + +The active/paused indicator on each assistant card is now an interactive switch — you can pause or resume an assistant directly from the assistants list without opening it. diff --git a/client/dashboard/src/pages/assistants/Assistants.tsx b/client/dashboard/src/pages/assistants/Assistants.tsx index 95b41160bc..3eb2255ac9 100644 --- a/client/dashboard/src/pages/assistants/Assistants.tsx +++ b/client/dashboard/src/pages/assistants/Assistants.tsx @@ -3,33 +3,82 @@ import { Page } from "@/components/page-layout"; import { RequireScope } from "@/components/require-scope"; import { Card, Cards } from "@/components/ui/card"; import { MoreActions } from "@/components/ui/more-actions"; -import { Badge } from "@/components/ui/badge"; import { Skeleton } from "@/components/ui/skeleton"; +import { Switch } from "@/components/ui/switch"; import { SimpleTooltip } from "@/components/ui/tooltip"; import { Type } from "@/components/ui/type"; import { UpdatedAt } from "@/components/updated-at"; import { useProductTier } from "@/hooks/useProductTier"; +import { useRBAC } from "@/hooks/useRBAC"; import { useRoutes } from "@/routes"; -import { Assistant } from "@gram/client/models/components/assistant.js"; +import { + Assistant, + AssistantStatus, +} from "@gram/client/models/components/assistant.js"; import { invalidateAllAssistantsList, useAssistantsDeleteMutation, useAssistantsList, + useAssistantsUpdateMutation, useGetPeriodUsage, } from "@gram/client/react-query/index.js"; import { Button, Icon, Stack } from "@speakeasy-api/moonshine"; import { useQueryClient } from "@tanstack/react-query"; import { Info, Plus } from "lucide-react"; +import { MouseEvent } from "react"; import { Outlet } from "react-router"; +import { toast } from "sonner"; + +function stopLinkNavigation(e: MouseEvent) { + e.preventDefault(); + e.stopPropagation(); +} export function AssistantsRoot() { return ; } -function StatusBadge({ status }: { status: string }) { - if (status === "active") return Active; - if (status === "paused") return Paused; - return {status}; +function StatusToggle({ assistant }: { assistant: Assistant }) { + const queryClient = useQueryClient(); + const { hasScope } = useRBAC(); + const canWrite = hasScope("project:write"); + const isActive = assistant.status === AssistantStatus.Active; + + const updateAssistant = useAssistantsUpdateMutation({ + onSuccess: () => { + invalidateAllAssistantsList(queryClient); + }, + onError: () => { + toast.error("Failed to update assistant status"); + }, + }); + + const handleToggle = () => { + updateAssistant.mutate({ + request: { + updateAssistantForm: { + id: assistant.id, + status: isActive ? AssistantStatus.Paused : AssistantStatus.Active, + }, + }, + }); + }; + + return ( + +
+ +
+ + {isActive ? "Active" : "Paused"} + +
+ ); } function AssistantsEmptyState({ onCreate }: { onCreate: () => void }) { @@ -215,8 +264,8 @@ function AssistantCard({ assistant }: { assistant: Assistant }) { - - + + {assistant.model}