From b81a2d85daffc40d8223d8ad0809d338ebeae912 Mon Sep 17 00:00:00 2001 From: Giuseppe Scuglia Date: Tue, 21 Jan 2025 17:33:43 +0100 Subject: [PATCH 1/4] chore: update api --- .../generated/@tanstack/react-query.gen.ts | 91 +++++++ src/api/generated/sdk.gen.ts | 59 +++++ src/api/generated/types.gen.ts | 29 +- src/api/openapi.json | 247 ++++++++++++++++-- 4 files changed, 395 insertions(+), 31 deletions(-) diff --git a/src/api/generated/@tanstack/react-query.gen.ts b/src/api/generated/@tanstack/react-query.gen.ts index 3b2b6a9f..49ec7258 100644 --- a/src/api/generated/@tanstack/react-query.gen.ts +++ b/src/api/generated/@tanstack/react-query.gen.ts @@ -14,6 +14,9 @@ import { v1ListActiveWorkspaces, v1ActivateWorkspace, v1DeleteWorkspace, + v1ListArchivedWorkspaces, + v1RecoverWorkspace, + v1HardDeleteWorkspace, v1GetWorkspaceAlerts, v1GetWorkspaceMessages, v1GetWorkspaceSystemPrompt, @@ -30,6 +33,12 @@ import type { V1DeleteWorkspaceData, V1DeleteWorkspaceError, V1DeleteWorkspaceResponse, + V1RecoverWorkspaceData, + V1RecoverWorkspaceError, + V1RecoverWorkspaceResponse, + V1HardDeleteWorkspaceData, + V1HardDeleteWorkspaceError, + V1HardDeleteWorkspaceResponse, V1GetWorkspaceAlertsData, V1GetWorkspaceMessagesData, V1GetWorkspaceSystemPromptData, @@ -312,6 +321,88 @@ export const v1DeleteWorkspaceMutation = ( return mutationOptions; }; +export const v1ListArchivedWorkspacesQueryKey = ( + options?: OptionsLegacyParser, +) => [createQueryKey("v1ListArchivedWorkspaces", options)]; + +export const v1ListArchivedWorkspacesOptions = ( + options?: OptionsLegacyParser, +) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await v1ListArchivedWorkspaces({ + ...options, + ...queryKey[0], + signal, + throwOnError: true, + }); + return data; + }, + queryKey: v1ListArchivedWorkspacesQueryKey(options), + }); +}; + +export const v1RecoverWorkspaceQueryKey = ( + options: OptionsLegacyParser, +) => [createQueryKey("v1RecoverWorkspace", options)]; + +export const v1RecoverWorkspaceOptions = ( + options: OptionsLegacyParser, +) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await v1RecoverWorkspace({ + ...options, + ...queryKey[0], + signal, + throwOnError: true, + }); + return data; + }, + queryKey: v1RecoverWorkspaceQueryKey(options), + }); +}; + +export const v1RecoverWorkspaceMutation = ( + options?: Partial>, +) => { + const mutationOptions: UseMutationOptions< + V1RecoverWorkspaceResponse, + V1RecoverWorkspaceError, + OptionsLegacyParser + > = { + mutationFn: async (localOptions) => { + const { data } = await v1RecoverWorkspace({ + ...options, + ...localOptions, + throwOnError: true, + }); + return data; + }, + }; + return mutationOptions; +}; + +export const v1HardDeleteWorkspaceMutation = ( + options?: Partial>, +) => { + const mutationOptions: UseMutationOptions< + V1HardDeleteWorkspaceResponse, + V1HardDeleteWorkspaceError, + OptionsLegacyParser + > = { + mutationFn: async (localOptions) => { + const { data } = await v1HardDeleteWorkspace({ + ...options, + ...localOptions, + throwOnError: true, + }); + return data; + }, + }; + return mutationOptions; +}; + export const v1GetWorkspaceAlertsQueryKey = ( options: OptionsLegacyParser, ) => [createQueryKey("v1GetWorkspaceAlerts", options)]; diff --git a/src/api/generated/sdk.gen.ts b/src/api/generated/sdk.gen.ts index 9df3646e..95640d12 100644 --- a/src/api/generated/sdk.gen.ts +++ b/src/api/generated/sdk.gen.ts @@ -29,6 +29,14 @@ import type { V1DeleteWorkspaceData, V1DeleteWorkspaceError, V1DeleteWorkspaceResponse, + V1ListArchivedWorkspacesError, + V1ListArchivedWorkspacesResponse, + V1RecoverWorkspaceData, + V1RecoverWorkspaceError, + V1RecoverWorkspaceResponse, + V1HardDeleteWorkspaceData, + V1HardDeleteWorkspaceError, + V1HardDeleteWorkspaceResponse, V1GetWorkspaceAlertsData, V1GetWorkspaceAlertsError, V1GetWorkspaceAlertsResponse, @@ -219,6 +227,57 @@ export const v1DeleteWorkspace = ( }); }; +/** + * List Archived Workspaces + * List all archived workspaces. + */ +export const v1ListArchivedWorkspaces = ( + options?: OptionsLegacyParser, +) => { + return (options?.client ?? client).get< + V1ListArchivedWorkspacesResponse, + V1ListArchivedWorkspacesError, + ThrowOnError + >({ + ...options, + url: "/api/v1/workspaces/archive", + }); +}; + +/** + * Recover Workspace + * Recover an archived workspace by name. + */ +export const v1RecoverWorkspace = ( + options: OptionsLegacyParser, +) => { + return (options?.client ?? client).post< + V1RecoverWorkspaceResponse, + V1RecoverWorkspaceError, + ThrowOnError + >({ + ...options, + url: "/api/v1/workspaces/archive/{workspace_name}/recover", + }); +}; + +/** + * Hard Delete Workspace + * Hard delete an archived workspace by name. + */ +export const v1HardDeleteWorkspace = ( + options: OptionsLegacyParser, +) => { + return (options?.client ?? client).delete< + V1HardDeleteWorkspaceResponse, + V1HardDeleteWorkspaceError, + ThrowOnError + >({ + ...options, + url: "/api/v1/workspaces/archive/{workspace_name}", + }); +}; + /** * Get Workspace Alerts * Get alerts for a workspace. diff --git a/src/api/generated/types.gen.ts b/src/api/generated/types.gen.ts index e34a9e1f..93cf8569 100644 --- a/src/api/generated/types.gen.ts +++ b/src/api/generated/types.gen.ts @@ -55,8 +55,9 @@ export type Conversation = { conversation_timestamp: string; }; -export type CreateWorkspaceRequest = { +export type CreateOrRenameWorkspaceRequest = { name: string; + rename_to?: string | null; }; export type HTTPValidationError = { @@ -119,7 +120,7 @@ export type V1ListWorkspacesResponse = ListWorkspacesResponse; export type V1ListWorkspacesError = unknown; export type V1CreateWorkspaceData = { - body: CreateWorkspaceRequest; + body: CreateOrRenameWorkspaceRequest; }; export type V1CreateWorkspaceResponse = Workspace; @@ -151,6 +152,30 @@ export type V1DeleteWorkspaceResponse = unknown; export type V1DeleteWorkspaceError = HTTPValidationError; +export type V1ListArchivedWorkspacesResponse = ListWorkspacesResponse; + +export type V1ListArchivedWorkspacesError = unknown; + +export type V1RecoverWorkspaceData = { + path: { + workspace_name: string; + }; +}; + +export type V1RecoverWorkspaceResponse = void; + +export type V1RecoverWorkspaceError = HTTPValidationError; + +export type V1HardDeleteWorkspaceData = { + path: { + workspace_name: string; + }; +}; + +export type V1HardDeleteWorkspaceResponse = unknown; + +export type V1HardDeleteWorkspaceError = HTTPValidationError; + export type V1GetWorkspaceAlertsData = { path: { workspace_name: string; diff --git a/src/api/openapi.json b/src/api/openapi.json index 9e0dd2ba..bac3ec12 100644 --- a/src/api/openapi.json +++ b/src/api/openapi.json @@ -8,7 +8,9 @@ "paths": { "/health": { "get": { - "tags": ["System"], + "tags": [ + "System" + ], "summary": "Health Check", "operationId": "health_check_health_get", "responses": { @@ -25,7 +27,10 @@ }, "/api/v1/dashboard/messages": { "get": { - "tags": ["CodeGate API", "Dashboard"], + "tags": [ + "CodeGate API", + "Dashboard" + ], "summary": "Get Messages", "description": "Get all the messages from the database and return them as a list of conversations.", "operationId": "v1_get_messages", @@ -49,7 +54,10 @@ }, "/api/v1/dashboard/alerts": { "get": { - "tags": ["CodeGate API", "Dashboard"], + "tags": [ + "CodeGate API", + "Dashboard" + ], "summary": "Get Alerts", "description": "Get all the messages from the database and return them as a list of conversations.", "operationId": "v1_get_alerts", @@ -80,7 +88,10 @@ }, "/api/v1/dashboard/alerts_notification": { "get": { - "tags": ["CodeGate API", "Dashboard"], + "tags": [ + "CodeGate API", + "Dashboard" + ], "summary": "Stream Sse", "description": "Send alerts event", "operationId": "v1_stream_sse", @@ -98,7 +109,10 @@ }, "/api/v1/dashboard/version": { "get": { - "tags": ["CodeGate API", "Dashboard"], + "tags": [ + "CodeGate API", + "Dashboard" + ], "summary": "Version Check", "operationId": "v1_version_check", "responses": { @@ -115,7 +129,10 @@ }, "/api/v1/workspaces": { "get": { - "tags": ["CodeGate API", "Workspaces"], + "tags": [ + "CodeGate API", + "Workspaces" + ], "summary": "List Workspaces", "description": "List all workspaces.", "operationId": "v1_list_workspaces", @@ -133,7 +150,10 @@ } }, "post": { - "tags": ["CodeGate API", "Workspaces"], + "tags": [ + "CodeGate API", + "Workspaces" + ], "summary": "Create Workspace", "description": "Create a new workspace.", "operationId": "v1_create_workspace", @@ -141,7 +161,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateWorkspaceRequest" + "$ref": "#/components/schemas/CreateOrRenameWorkspaceRequest" } } }, @@ -173,7 +193,10 @@ }, "/api/v1/workspaces/active": { "get": { - "tags": ["CodeGate API", "Workspaces"], + "tags": [ + "CodeGate API", + "Workspaces" + ], "summary": "List Active Workspaces", "description": "List all active workspaces.\n\nIn it's current form, this function will only return one workspace. That is,\nthe globally active workspace.", "operationId": "v1_list_active_workspaces", @@ -191,7 +214,10 @@ } }, "post": { - "tags": ["CodeGate API", "Workspaces"], + "tags": [ + "CodeGate API", + "Workspaces" + ], "summary": "Activate Workspace", "description": "Activate a workspace by name.", "operationId": "v1_activate_workspace", @@ -240,7 +266,10 @@ }, "/api/v1/workspaces/{workspace_name}": { "delete": { - "tags": ["CodeGate API", "Workspaces"], + "tags": [ + "CodeGate API", + "Workspaces" + ], "summary": "Delete Workspace", "description": "Delete a workspace by name.", "operationId": "v1_delete_workspace", @@ -277,9 +306,114 @@ } } }, + "/api/v1/workspaces/archive": { + "get": { + "tags": [ + "CodeGate API", + "Workspaces" + ], + "summary": "List Archived Workspaces", + "description": "List all archived workspaces.", + "operationId": "v1_list_archived_workspaces", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListWorkspacesResponse" + } + } + } + } + } + } + }, + "/api/v1/workspaces/archive/{workspace_name}/recover": { + "post": { + "tags": [ + "CodeGate API", + "Workspaces" + ], + "summary": "Recover Workspace", + "description": "Recover an archived workspace by name.", + "operationId": "v1_recover_workspace", + "parameters": [ + { + "name": "workspace_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Workspace Name" + } + } + ], + "responses": { + "204": { + "description": "Successful Response" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/workspaces/archive/{workspace_name}": { + "delete": { + "tags": [ + "CodeGate API", + "Workspaces" + ], + "summary": "Hard Delete Workspace", + "description": "Hard delete an archived workspace by name.", + "operationId": "v1_hard_delete_workspace", + "parameters": [ + { + "name": "workspace_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Workspace Name" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, "/api/v1/workspaces/{workspace_name}/alerts": { "get": { - "tags": ["CodeGate API", "Workspaces"], + "tags": [ + "CodeGate API", + "Workspaces" + ], "summary": "Get Workspace Alerts", "description": "Get alerts for a workspace.", "operationId": "v1_get_workspace_alerts", @@ -331,7 +465,10 @@ }, "/api/v1/workspaces/{workspace_name}/messages": { "get": { - "tags": ["CodeGate API", "Workspaces"], + "tags": [ + "CodeGate API", + "Workspaces" + ], "summary": "Get Workspace Messages", "description": "Get messages for a workspace.", "operationId": "v1_get_workspace_messages", @@ -376,7 +513,10 @@ }, "/api/v1/workspaces/{workspace_name}/system-prompt": { "get": { - "tags": ["CodeGate API", "Workspaces"], + "tags": [ + "CodeGate API", + "Workspaces" + ], "summary": "Get Workspace System Prompt", "description": "Get the system prompt for a workspace.", "operationId": "v1_get_workspace_system_prompt", @@ -415,7 +555,10 @@ } }, "put": { - "tags": ["CodeGate API", "Workspaces"], + "tags": [ + "CodeGate API", + "Workspaces" + ], "summary": "Set Workspace System Prompt", "operationId": "v1_set_workspace_system_prompt", "parameters": [ @@ -456,7 +599,10 @@ } }, "delete": { - "tags": ["CodeGate API", "Workspaces"], + "tags": [ + "CodeGate API", + "Workspaces" + ], "summary": "Delete Workspace System Prompt", "operationId": "v1_delete_workspace_system_prompt", "parameters": [ @@ -498,7 +644,9 @@ } }, "type": "object", - "required": ["name"], + "required": [ + "name" + ], "title": "ActivateWorkspaceRequest" }, "ActiveWorkspace": { @@ -516,7 +664,11 @@ } }, "type": "object", - "required": ["name", "is_active", "last_updated"], + "required": [ + "name", + "is_active", + "last_updated" + ], "title": "ActiveWorkspace" }, "AlertConversation": { @@ -603,7 +755,11 @@ } }, "type": "object", - "required": ["message", "timestamp", "message_id"], + "required": [ + "message", + "timestamp", + "message_id" + ], "title": "ChatMessage", "description": "Represents a chat message." }, @@ -644,7 +800,11 @@ } }, "type": "object", - "required": ["code", "language", "filepath"], + "required": [ + "code", + "language", + "filepath" + ], "title": "CodeSnippet" }, "Conversation": { @@ -692,16 +852,29 @@ "title": "Conversation", "description": "Represents a conversation." }, - "CreateWorkspaceRequest": { + "CreateOrRenameWorkspaceRequest": { "properties": { "name": { "type": "string", "title": "Name" + }, + "rename_to": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Rename To" } }, "type": "object", - "required": ["name"], - "title": "CreateWorkspaceRequest" + "required": [ + "name" + ], + "title": "CreateOrRenameWorkspaceRequest" }, "HTTPValidationError": { "properties": { @@ -727,7 +900,9 @@ } }, "type": "object", - "required": ["workspaces"], + "required": [ + "workspaces" + ], "title": "ListActiveWorkspacesResponse" }, "ListWorkspacesResponse": { @@ -741,7 +916,9 @@ } }, "type": "object", - "required": ["workspaces"], + "required": [ + "workspaces" + ], "title": "ListWorkspacesResponse" }, "QuestionAnswer": { @@ -761,7 +938,10 @@ } }, "type": "object", - "required": ["question", "answer"], + "required": [ + "question", + "answer" + ], "title": "QuestionAnswer", "description": "Represents a question and answer pair." }, @@ -773,7 +953,9 @@ } }, "type": "object", - "required": ["prompt"], + "required": [ + "prompt" + ], "title": "SystemPrompt" }, "ValidationError": { @@ -802,7 +984,11 @@ } }, "type": "object", - "required": ["loc", "msg", "type"], + "required": [ + "loc", + "msg", + "type" + ], "title": "ValidationError" }, "Workspace": { @@ -817,7 +1003,10 @@ } }, "type": "object", - "required": ["name", "is_active"], + "required": [ + "name", + "is_active" + ], "title": "Workspace" } } From 0d136937bbdcd4d86eeeeef4c16628844aaf04d3 Mon Sep 17 00:00:00 2001 From: Giuseppe Scuglia Date: Tue, 21 Jan 2025 17:34:09 +0100 Subject: [PATCH 2/4] feat: add icon --- icons/FlipBackward.svg | 3 +++ src/components/icons/FlipBackward.tsx | 17 +++++++++++++++++ src/components/icons/index.ts | 1 + 3 files changed, 21 insertions(+) create mode 100644 icons/FlipBackward.svg create mode 100644 src/components/icons/FlipBackward.tsx diff --git a/icons/FlipBackward.svg b/icons/FlipBackward.svg new file mode 100644 index 00000000..c3d42473 --- /dev/null +++ b/icons/FlipBackward.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/icons/FlipBackward.tsx b/src/components/icons/FlipBackward.tsx new file mode 100644 index 00000000..7cac9f91 --- /dev/null +++ b/src/components/icons/FlipBackward.tsx @@ -0,0 +1,17 @@ +import type { SVGProps } from "react"; +const SvgFlipBackward = (props: SVGProps) => ( + + + +); +export default SvgFlipBackward; diff --git a/src/components/icons/index.ts b/src/components/icons/index.ts index b1107dfe..2f138811 100644 --- a/src/components/icons/index.ts +++ b/src/components/icons/index.ts @@ -1,5 +1,6 @@ export { default as Continue } from "./Continue"; export { default as Copilot } from "./Copilot"; export { default as Discord } from "./Discord"; +export { default as FlipBackward } from "./FlipBackward"; export { default as Github } from "./Github"; export { default as Youtube } from "./Youtube"; From b3c2183e4e9816ff2397c74455134fd549128597 Mon Sep 17 00:00:00 2001 From: Giuseppe Scuglia Date: Tue, 21 Jan 2025 17:34:51 +0100 Subject: [PATCH 3/4] feat: add archived workspace and restore --- .../hooks/use-archived-workspaces.ts | 14 +++ .../workspace/hooks/use-restore-workspace.ts | 37 +++++++ src/routes/route-workspaces.tsx | 99 ++++++++++++++++--- 3 files changed, 136 insertions(+), 14 deletions(-) create mode 100644 src/features/workspace/hooks/use-archived-workspaces.ts create mode 100644 src/features/workspace/hooks/use-restore-workspace.ts diff --git a/src/features/workspace/hooks/use-archived-workspaces.ts b/src/features/workspace/hooks/use-archived-workspaces.ts new file mode 100644 index 00000000..f645aaf4 --- /dev/null +++ b/src/features/workspace/hooks/use-archived-workspaces.ts @@ -0,0 +1,14 @@ +import { useQuery } from "@tanstack/react-query"; +import { v1ListArchivedWorkspacesOptions } from "@/api/generated/@tanstack/react-query.gen"; + +export const useArchivedWorkspaces = () => { + return useQuery({ + ...v1ListArchivedWorkspacesOptions(), + refetchInterval: 5_000, + refetchIntervalInBackground: true, + refetchOnMount: true, + refetchOnReconnect: true, + refetchOnWindowFocus: true, + retry: false, + }); +}; diff --git a/src/features/workspace/hooks/use-restore-workspace.ts b/src/features/workspace/hooks/use-restore-workspace.ts new file mode 100644 index 00000000..8f1e8588 --- /dev/null +++ b/src/features/workspace/hooks/use-restore-workspace.ts @@ -0,0 +1,37 @@ +import { V1CreateWorkspaceData } from "@/api/generated"; +import { + v1CreateWorkspaceQueryKey, + v1ListArchivedWorkspacesQueryKey, + v1RecoverWorkspaceMutation, +} from "@/api/generated/@tanstack/react-query.gen"; +import { toast } from "@stacklok/ui-kit"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useMemo } from "react"; + +export function useRestoreWorkspace(name: string) { + const queryClient = useQueryClient(); + + const options: V1CreateWorkspaceData = useMemo( + () => ({ + body: { name }, + }), + [name], + ); + + return useMutation({ + ...v1RecoverWorkspaceMutation(), + onError: (err) => { + toast.error(err.detail ? `${err.detail}` : "Failed to restore workspace"); + }, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: v1ListArchivedWorkspacesQueryKey(), + refetchType: "all", + }); + queryClient.invalidateQueries({ + queryKey: v1CreateWorkspaceQueryKey(options), + refetchType: "all", + }); + }, + }); +} diff --git a/src/routes/route-workspaces.tsx b/src/routes/route-workspaces.tsx index 7fddcc37..00bb4371 100644 --- a/src/routes/route-workspaces.tsx +++ b/src/routes/route-workspaces.tsx @@ -2,8 +2,10 @@ import { WorkspaceHeading } from "@/features/workspace/components/workspace-head import { useListWorkspaces } from "@/features/workspace/hooks/use-list-workspaces"; import { BreadcrumbHome } from "@/components/BreadcrumbHome"; import { + Badge, Breadcrumb, Breadcrumbs, + Button, Cell, Column, LinkButton, @@ -13,10 +15,81 @@ import { TableHeader, } from "@stacklok/ui-kit"; 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"; + +function CellName({ + name, + isArchived = false, +}: { + name: string; + isArchived?: boolean; +}) { + if (isArchived) + return ( + + {name} +    + + Archived + + + ); + + return {name}; +} + +function CellConfiguration({ + name, + isArchived = false, +}: { + name: string; + isArchived?: boolean; +}) { + const { mutate, isPending } = useRestoreWorkspace(name); + + if (isArchived) { + return ( + + + + ); + } + + return ( + + + + Settings + + + ); +} export function RouteWorkspaces() { - const result = useListWorkspaces(); - const workspaces = result.data?.workspaces ?? []; + const { data: availableWorkspaces } = useListWorkspaces(); + const { data: archivedWorkspaces } = useArchivedWorkspaces(); + const workspaces: (Workspace & { isArchived?: boolean })[] = [ + ...(availableWorkspaces?.workspaces ?? []), + ...(archivedWorkspaces?.workspaces.map((item) => ({ + ...item, + isArchived: true, + })) ?? []), + ]; return ( <> @@ -34,10 +107,10 @@ export function RouteWorkspaces() { - + Name - + Configuration @@ -45,16 +118,14 @@ export function RouteWorkspaces() { {workspaces.map((workspace) => ( - {workspace.name} - - - - Settings - - + + ))} From cebecf52ba2dc7999530e660b632b617f1a37113 Mon Sep 17 00:00:00 2001 From: Giuseppe Scuglia Date: Tue, 21 Jan 2025 17:52:13 +0100 Subject: [PATCH 4/4] test: add msw for archived workspaces --- .../workspace/hooks/use-restore-workspace.ts | 15 +++------------ src/mocks/msw/handlers.ts | 13 +++++++++++++ src/routes/__tests__/route-workspaces.test.tsx | 16 ++++++++++++++++ src/routes/route-workspaces.tsx | 2 +- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/features/workspace/hooks/use-restore-workspace.ts b/src/features/workspace/hooks/use-restore-workspace.ts index 8f1e8588..b2f388a1 100644 --- a/src/features/workspace/hooks/use-restore-workspace.ts +++ b/src/features/workspace/hooks/use-restore-workspace.ts @@ -1,23 +1,14 @@ -import { V1CreateWorkspaceData } from "@/api/generated"; import { - v1CreateWorkspaceQueryKey, v1ListArchivedWorkspacesQueryKey, + v1ListWorkspacesOptions, v1RecoverWorkspaceMutation, } from "@/api/generated/@tanstack/react-query.gen"; import { toast } from "@stacklok/ui-kit"; import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { useMemo } from "react"; -export function useRestoreWorkspace(name: string) { +export function useRestoreWorkspace() { const queryClient = useQueryClient(); - const options: V1CreateWorkspaceData = useMemo( - () => ({ - body: { name }, - }), - [name], - ); - return useMutation({ ...v1RecoverWorkspaceMutation(), onError: (err) => { @@ -29,7 +20,7 @@ export function useRestoreWorkspace(name: string) { refetchType: "all", }); queryClient.invalidateQueries({ - queryKey: v1CreateWorkspaceQueryKey(options), + queryKey: v1ListWorkspacesOptions(), refetchType: "all", }); }, diff --git a/src/mocks/msw/handlers.ts b/src/mocks/msw/handlers.ts index 3aaf839b..7aad1e0d 100644 --- a/src/mocks/msw/handlers.ts +++ b/src/mocks/msw/handlers.ts @@ -33,9 +33,22 @@ export const handlers = [ http.get("*/api/v1/workspaces", () => { return HttpResponse.json(mockedWorkspaces); }), + http.get("*/api/v1/workspaces/archive", () => { + return HttpResponse.json({ + workspaces: [ + { + name: "archived_workspace", + is_active: false, + }, + ], + }); + }), http.post("*/api/v1/workspaces", () => { return HttpResponse.json(mockedWorkspaces); }), + http.post("*/api/v1/workspaces/archive/:workspace_name/recover", () => { + HttpResponse.json({ status: 204 }); + }), http.delete("*/api/v1/workspaces/:name", () => HttpResponse.json({ status: 204 }), ), diff --git a/src/routes/__tests__/route-workspaces.test.tsx b/src/routes/__tests__/route-workspaces.test.tsx index a543de28..608f2f08 100644 --- a/src/routes/__tests__/route-workspaces.test.tsx +++ b/src/routes/__tests__/route-workspaces.test.tsx @@ -50,4 +50,20 @@ describe("Workspaces page", () => { expect(firstButton).toBeVisible(); expect(firstButton).toHaveAttribute("href", "/workspace/myworkspace"); }); + + it("has archived workspace", async () => { + await waitFor(() => { + expect(screen.getAllByRole("row").length).toBeGreaterThan(1); + }); + + expect( + screen.getByRole("rowheader", { name: /archived_workspace/i }), + ).toBeVisible(); + + expect( + screen.getByRole("button", { + name: /restore configuration/i, + }), + ).toBeVisible(); + }); }); diff --git a/src/routes/route-workspaces.tsx b/src/routes/route-workspaces.tsx index 00bb4371..66ae2657 100644 --- a/src/routes/route-workspaces.tsx +++ b/src/routes/route-workspaces.tsx @@ -48,7 +48,7 @@ function CellConfiguration({ name: string; isArchived?: boolean; }) { - const { mutate, isPending } = useRestoreWorkspace(name); + const { mutate, isPending } = useRestoreWorkspace(); if (isArchived) { return (