diff --git a/src/components/icons/FlipBackward.tsx b/src/components/icons/FlipBackward.tsx index 7cac9f91..b96b865e 100644 --- a/src/components/icons/FlipBackward.tsx +++ b/src/components/icons/FlipBackward.tsx @@ -7,7 +7,7 @@ const SvgFlipBackward = (props: SVGProps) => ( {...props} > { }); const renderComponent = () => - render(); + render(); test("can update system prompt", async () => { server.use( http.get("*/api/v1/workspaces/:name/system-prompt", () => { return HttpResponse.json({ prompt: "initial prompt from server" }); - }), + }) ); const { getByRole } = renderComponent(); @@ -48,7 +48,7 @@ test("can update system prompt", async () => { server.use( http.get("*/api/v1/workspaces/:name/system-prompt", () => { return HttpResponse.json({ prompt: "new prompt from test" }); - }), + }) ); await waitFor(() => { diff --git a/src/features/workspace-system-prompt/components/system-prompt-editor.tsx b/src/features/workspace-system-prompt/components/system-prompt-editor.tsx index 1ae7d08a..d0fd9fd0 100644 --- a/src/features/workspace-system-prompt/components/system-prompt-editor.tsx +++ b/src/features/workspace-system-prompt/components/system-prompt-editor.tsx @@ -132,9 +132,11 @@ function usePromptValue({ export function SystemPromptEditor({ className, workspaceName, + isArchived, }: { className?: string; workspaceName: string; + isArchived: boolean | undefined; }) { const context = useContext(DarkModeContext); const theme: Theme = inferDarkMode(context); @@ -194,13 +196,14 @@ export function SystemPromptEditor({ setValue(v ?? "")} height="20rem" defaultLanguage="Markdown" theme={theme} - className="bg-base" + className={twMerge("bg-base", isArchived ? "opacity-25" : "")} /> )} @@ -208,7 +211,7 @@ export function SystemPromptEditor({ + diff --git a/src/features/workspace/hooks/use-archive-workspace-button.tsx b/src/features/workspace/hooks/use-archive-workspace-button.tsx new file mode 100644 index 00000000..d7139eb6 --- /dev/null +++ b/src/features/workspace/hooks/use-archive-workspace-button.tsx @@ -0,0 +1,19 @@ +import { Button } from "@stacklok/ui-kit"; +import { ComponentProps } from "react"; +import { useArchiveWorkspace } from "@/features/workspace-system-prompt/hooks/use-archive-workspace"; + +export function useArchiveWorkspaceButton({ + workspaceName, +}: { + workspaceName: string; +}): ComponentProps { + const { mutate, isPending } = useArchiveWorkspace(); + + return { + isPending, + isDisabled: isPending, + onPress: () => mutate({ path: { workspace_name: workspaceName } }), + isDestructive: true, + children: "Archive", + }; +} diff --git a/src/features/workspace/hooks/use-archived-workspaces.ts b/src/features/workspace/hooks/use-archived-workspaces.ts index f645aaf4..2cc2d3fe 100644 --- a/src/features/workspace/hooks/use-archived-workspaces.ts +++ b/src/features/workspace/hooks/use-archived-workspaces.ts @@ -1,14 +1,20 @@ import { useQuery } from "@tanstack/react-query"; import { v1ListArchivedWorkspacesOptions } from "@/api/generated/@tanstack/react-query.gen"; +import { V1ListArchivedWorkspacesResponse } from "@/api/generated"; -export const useArchivedWorkspaces = () => { +export function useArchivedWorkspaces({ + select, +}: { + select?: (data: V1ListArchivedWorkspacesResponse) => T; +} = {}) { return useQuery({ ...v1ListArchivedWorkspacesOptions(), - refetchInterval: 5_000, + refetchInterval: 5000, refetchIntervalInBackground: true, refetchOnMount: true, refetchOnReconnect: true, refetchOnWindowFocus: true, retry: false, + select, }); -}; +} diff --git a/src/features/workspace/hooks/use-list-workspaces.ts b/src/features/workspace/hooks/use-list-workspaces.ts index 0b0b28b9..47b7ffca 100644 --- a/src/features/workspace/hooks/use-list-workspaces.ts +++ b/src/features/workspace/hooks/use-list-workspaces.ts @@ -1,14 +1,20 @@ import { useQuery } from "@tanstack/react-query"; import { v1ListWorkspacesOptions } from "@/api/generated/@tanstack/react-query.gen"; +import { V1ListWorkspacesResponse } from "@/api/generated"; -export const useListWorkspaces = () => { +export function useListWorkspaces({ + select, +}: { + select?: (data: V1ListWorkspacesResponse) => T; +} = {}) { return useQuery({ ...v1ListWorkspacesOptions(), - refetchInterval: 5_000, + refetchInterval: 5000, refetchIntervalInBackground: true, refetchOnMount: true, refetchOnReconnect: true, refetchOnWindowFocus: true, retry: false, + select, }); -}; +} diff --git a/src/features/workspace/hooks/use-restore-workspace-button.tsx b/src/features/workspace/hooks/use-restore-workspace-button.tsx new file mode 100644 index 00000000..ce619b34 --- /dev/null +++ b/src/features/workspace/hooks/use-restore-workspace-button.tsx @@ -0,0 +1,18 @@ +import { Button } from "@stacklok/ui-kit"; +import { ComponentProps } from "react"; +import { useRestoreWorkspace } from "./use-restore-workspace"; + +export function useRestoreWorkspaceButton({ + workspaceName, +}: { + workspaceName: string; +}): ComponentProps { + const { mutate, isPending } = useRestoreWorkspace(); + + return { + isPending, + isDisabled: isPending, + onPress: () => mutate({ path: { workspace_name: workspaceName } }), + children: "Restore", + }; +} diff --git a/src/routes/route-workspace.tsx b/src/routes/route-workspace.tsx index 48d7c3e4..816e1684 100644 --- a/src/routes/route-workspace.tsx +++ b/src/routes/route-workspace.tsx @@ -3,14 +3,37 @@ import { ArchiveWorkspace } from "@/features/workspace/components/archive-worksp import { SystemPromptEditor } from "@/features/workspace-system-prompt/components/system-prompt-editor"; import { WorkspaceHeading } from "@/features/workspace/components/workspace-heading"; import { WorkspaceName } from "@/features/workspace/components/workspace-name"; -import { Breadcrumb, Breadcrumbs } from "@stacklok/ui-kit"; +import { Alert, Breadcrumb, Breadcrumbs } from "@stacklok/ui-kit"; import { useParams } from "react-router-dom"; +import { useArchivedWorkspaces } from "@/features/workspace/hooks/use-archived-workspaces"; +import { useRestoreWorkspaceButton } from "@/features/workspace/hooks/use-restore-workspace-button"; + +function WorkspaceArchivedBanner({ name }: { name: string }) { + const restoreButtonProps = useRestoreWorkspaceButton({ workspaceName: name }); + + return ( + + You can still view this workspace's configuration. To begin using it + again, you must restore it. + + ); +} export function RouteWorkspace() { const { name } = useParams(); if (!name) throw Error("Workspace name is required"); + const { data: isArchived } = useArchivedWorkspaces({ + select: (data) => + data?.workspaces.find((w) => w.name === name) !== undefined, + }); + return ( <> @@ -20,9 +43,20 @@ export function RouteWorkspace() { - - - + + {isArchived ? : null} + + + + ); } diff --git a/src/routes/route-workspaces.tsx b/src/routes/route-workspaces.tsx index 66ae2657..22b2dda1 100644 --- a/src/routes/route-workspaces.tsx +++ b/src/routes/route-workspaces.tsx @@ -18,7 +18,7 @@ import { Settings, SquarePlus } from "lucide-react"; import { useArchivedWorkspaces } from "@/features/workspace/hooks/use-archived-workspaces"; import { Workspace } from "@/api/generated"; import SvgFlipBackward from "@/components/icons/FlipBackward"; -import { useRestoreWorkspace } from "@/features/workspace/hooks/use-restore-workspace"; +import { useRestoreWorkspaceButton } from "@/features/workspace/hooks/use-restore-workspace-button"; function CellName({ name, @@ -48,17 +48,15 @@ function CellConfiguration({ name: string; isArchived?: boolean; }) { - const { mutate, isPending } = useRestoreWorkspace(); + const restoreButtonProps = useRestoreWorkspaceButton({ workspaceName: name }); if (isArchived) { return (