From 0d7ff9ff5472aee6e9e376aada2518d57d115568 Mon Sep 17 00:00:00 2001 From: Giuseppe Scuglia Date: Fri, 14 Feb 2025 19:46:50 +0100 Subject: [PATCH] feat: add activate workspace button into detail page --- .../components/workspace-activate-button.tsx | 77 +++++++++++++++++++ src/routes/__tests__/route-workspace.test.tsx | 61 +++++++++++++++ src/routes/route-workspace.tsx | 23 +++--- 3 files changed, 151 insertions(+), 10 deletions(-) create mode 100644 src/features/workspace/components/workspace-activate-button.tsx diff --git a/src/features/workspace/components/workspace-activate-button.tsx b/src/features/workspace/components/workspace-activate-button.tsx new file mode 100644 index 00000000..2531cd00 --- /dev/null +++ b/src/features/workspace/components/workspace-activate-button.tsx @@ -0,0 +1,77 @@ +import { useMutationActivateWorkspace } from '@/hooks/use-mutation-activate-workspace' +import { useQueryActiveWorkspaceName } from '@/hooks/use-query-active-workspace-name' +import { + Button, + Tooltip, + TooltipInfoButton, + TooltipTrigger, +} from '@stacklok/ui-kit' +import { Check } from '@untitled-ui/icons-react' + +function getTooltipText({ + isActivated, + isArchived, +}: { + isArchived: boolean + isActivated: boolean +}) { + if (isArchived) { + return 'Cannot activate an archived workspace' + } + + if (isActivated) { + return 'Workspace already active' + } + + return null +} + +function TooltipActivateBtn({ + isActivated, + isArchived, +}: { + isActivated: boolean + isArchived: boolean +}) { + const text = getTooltipText({ isActivated, isArchived }) + + if (!text) return null + return ( + + + {text} + + ) +} + +export function WorkspaceActivateButton({ + workspaceName, + isArchived, +}: { + workspaceName: string + isArchived: boolean | undefined +}) { + const { data: activeWorkspaceName, isPending: isPendingWsName } = + useQueryActiveWorkspaceName() + const { mutateAsync: activateWorkspace, isPending: isPendingMutation } = + useMutationActivateWorkspace() + const isActivated = activeWorkspaceName === workspaceName + const isPending = isPendingWsName || isPendingMutation + + return ( +
+ + +
+ ) +} diff --git a/src/routes/__tests__/route-workspace.test.tsx b/src/routes/__tests__/route-workspace.test.tsx index 4ecf1b7a..b451dc1b 100644 --- a/src/routes/__tests__/route-workspace.test.tsx +++ b/src/routes/__tests__/route-workspace.test.tsx @@ -3,6 +3,9 @@ import { test, expect, vi } from 'vitest' import userEvent from '@testing-library/user-event' import { RouteWorkspace } from '../route-workspace' import { useParams } from 'react-router-dom' +import { server } from '@/mocks/msw/node' +import { http, HttpResponse } from 'msw' +import { mswEndpoint } from '@/test/msw-endpoint' const mockNavigate = vi.fn() @@ -148,3 +151,61 @@ test('revert changes button', async () => { }) ).toHaveValue('foo') }) + +test('disable activate workspace button', async () => { + server.use( + http.get(mswEndpoint('/api/v1/workspaces/active'), async () => { + return HttpResponse.json({ + workspaces: [ + { + name: 'foo', + is_active: true, + last_updated: new Date(Date.now()).toISOString(), + }, + ], + }) + }) + ) + + const { getByTestId } = renderComponent() + const activateSection = getByTestId(/workspace-activate/i) + await waitFor(() => { + expect( + within(activateSection).getByRole('button', { name: /activate/i }) + ).toBeDisabled() + }) + + expect( + within(activateSection).getByRole('button', { + name: /context active button/i, + }) + ).toBeVisible() +}) + +test('activate workspace', async () => { + server.use( + http.get(mswEndpoint('/api/v1/workspaces/active'), async () => { + return HttpResponse.json({ + workspaces: [ + { + name: 'bar', + is_active: true, + last_updated: new Date(Date.now()).toISOString(), + }, + ], + }) + }) + ) + const { getByTestId, getByText } = renderComponent() + const activateSection = getByTestId(/workspace-activate/i) + const activateButton = await within(activateSection).findByRole('button', { + name: /activate/i, + }) + expect(activateButton).not.toBeDisabled() + + await userEvent.click(activateButton) + + await waitFor(() => { + expect(getByText(/Activated "foo" workspace/i)).toBeVisible() + }) +}) diff --git a/src/routes/route-workspace.tsx b/src/routes/route-workspace.tsx index 3de4e14f..d5df90ed 100644 --- a/src/routes/route-workspace.tsx +++ b/src/routes/route-workspace.tsx @@ -1,14 +1,15 @@ import { BreadcrumbHome } from '@/components/BreadcrumbHome' import { ArchiveWorkspace } from '@/features/workspace/components/archive-workspace' -import { PageHeading } from "@/components/heading"; -import { WorkspaceName } from "@/features/workspace/components/workspace-name"; -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"; -import { WorkspaceCustomInstructions } from "@/features/workspace/components/workspace-custom-instructions"; -import { WorkspaceMuxingModel } from "@/features/workspace/components/workspace-muxing-model"; -import { PageContainer } from "@/components/page-container"; +import { PageHeading } from '@/components/heading' +import { WorkspaceName } from '@/features/workspace/components/workspace-name' +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' +import { WorkspaceCustomInstructions } from '@/features/workspace/components/workspace-custom-instructions' +import { WorkspaceMuxingModel } from '@/features/workspace/components/workspace-muxing-model' +import { PageContainer } from '@/components/page-container' +import { WorkspaceActivateButton } from '@/features/workspace/components/workspace-activate-button' function WorkspaceArchivedBanner({ name }: { name: string }) { const restoreButtonProps = useRestoreWorkspaceButton({ workspaceName: name }) @@ -44,7 +45,9 @@ export function RouteWorkspace() { Workspace Settings - + + + {isArchived ? : null}