diff --git a/apps/builder/components/dashboard/WorkspaceSettingsModal/WorkspaceSettingsForm.tsx b/apps/builder/components/dashboard/WorkspaceSettingsModal/WorkspaceSettingsForm.tsx index 480407580..801bc05b1 100644 --- a/apps/builder/components/dashboard/WorkspaceSettingsModal/WorkspaceSettingsForm.tsx +++ b/apps/builder/components/dashboard/WorkspaceSettingsModal/WorkspaceSettingsForm.tsx @@ -1,11 +1,21 @@ -import { Stack, FormControl, FormLabel, Flex } from '@chakra-ui/react' +import { + Stack, + FormControl, + FormLabel, + Flex, + Button, + useDisclosure, + Text, +} from '@chakra-ui/react' +import { ConfirmModal } from 'components/modals/ConfirmModal' import { EditableEmojiOrImageIcon } from 'components/shared/EditableEmojiOrImageIcon' import { Input } from 'components/shared/Textbox' import { useWorkspace } from 'contexts/WorkspaceContext' import React from 'react' -export const WorkspaceSettingsForm = () => { - const { workspace, updateWorkspace } = useWorkspace() +export const WorkspaceSettingsForm = ({ onClose }: { onClose: () => void }) => { + const { workspace, workspaces, updateWorkspace, deleteCurrentWorkspace } = + useWorkspace() const handleNameChange = (name: string) => { if (!workspace?.id) return @@ -17,6 +27,11 @@ export const WorkspaceSettingsForm = () => { updateWorkspace(workspace?.id, { icon }) } + const handleDeleteClick = async () => { + await deleteCurrentWorkspace() + onClose() + } + return ( @@ -40,6 +55,41 @@ export const WorkspaceSettingsForm = () => { /> )} + {workspace && workspaces && workspaces.length > 1 && ( + + )} ) } + +const DeleteWorkspaceButton = ({ + workspaceName, + onConfirm, +}: { + workspaceName: string + onConfirm: () => Promise +}) => { + const { isOpen, onOpen, onClose } = useDisclosure() + return ( + <> + + + Are you sure you want to delete {workspaceName} workspace? All its + folders, typebots and results will be deleted forever.' + + } + confirmButtonLabel="Delete" + /> + + ) +} diff --git a/apps/builder/components/dashboard/WorkspaceSettingsModal/WorkspaceSettingsModal.tsx b/apps/builder/components/dashboard/WorkspaceSettingsModal/WorkspaceSettingsModal.tsx index 54158680e..fa784d479 100644 --- a/apps/builder/components/dashboard/WorkspaceSettingsModal/WorkspaceSettingsModal.tsx +++ b/apps/builder/components/dashboard/WorkspaceSettingsModal/WorkspaceSettingsModal.tsx @@ -132,21 +132,27 @@ export const WorkspaceSettingsModal = ({ - + ) } -const SettingsContent = ({ tab }: { tab: SettingsTab }) => { +const SettingsContent = ({ + tab, + onClose, +}: { + tab: SettingsTab + onClose: () => void +}) => { switch (tab) { case 'my-account': return case 'user-settings': return case 'workspace-settings': - return + return case 'members': return case 'billing': diff --git a/apps/builder/contexts/WorkspaceContext.tsx b/apps/builder/contexts/WorkspaceContext.tsx index 9ffa349be..4dabd2de5 100644 --- a/apps/builder/contexts/WorkspaceContext.tsx +++ b/apps/builder/contexts/WorkspaceContext.tsx @@ -11,6 +11,7 @@ import { createNewWorkspace, useWorkspaces, updateWorkspace as patchWorkspace, + deleteWorkspace, } from 'services/workspace/workspace' import { useUser } from './UserContext' import { useTypebot } from './TypebotContext' @@ -29,6 +30,7 @@ const workspaceContext = createContext<{ workspaceId: string, updates: Partial ) => Promise + deleteCurrentWorkspace: () => Promise // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore }>({}) @@ -122,6 +124,20 @@ export const WorkspaceContext = ({ children }: { children: ReactNode }) => { }) } + const deleteCurrentWorkspace = async () => { + if (!currentWorkspace || !workspaces || workspaces.length < 2) return + const { data } = await deleteWorkspace(currentWorkspace.id) + if (!data || !currentWorkspace) return + setCurrentWorkspace(workspaces[0]) + mutate({ + workspaces: (workspaces ?? []).filter((w) => + w.id === currentWorkspace.id + ? { ...data.workspace, members: w.members } + : w + ), + }) + } + return ( { switchWorkspace, createWorkspace, updateWorkspace, + deleteCurrentWorkspace, }} > {children} diff --git a/apps/builder/pages/api/workspaces/[workspaceId].ts b/apps/builder/pages/api/workspaces/[workspaceId].ts index 6b828ce0c..2848ad172 100644 --- a/apps/builder/pages/api/workspaces/[workspaceId].ts +++ b/apps/builder/pages/api/workspaces/[workspaceId].ts @@ -22,6 +22,18 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { workspace: updatedWorkspace, }) } + if (req.method === 'DELETE') { + const id = req.query.workspaceId as string + await prisma.workspace.deleteMany({ + where: { + id, + members: { some: { userId: user.id, role: WorkspaceRole.ADMIN } }, + }, + }) + return res.status(200).json({ + message: 'success', + }) + } methodNotAllowed(res) } diff --git a/apps/builder/playwright/tests/workspaces.spec.ts b/apps/builder/playwright/tests/workspaces.spec.ts index 7299c49a3..3df72294d 100644 --- a/apps/builder/playwright/tests/workspaces.spec.ts +++ b/apps/builder/playwright/tests/workspaces.spec.ts @@ -41,7 +41,7 @@ test('can switch between workspaces and access typebot', async ({ page }) => { await expect(page.locator('text="Hey there"')).toBeVisible() }) -test('can create a new workspace', async ({ page }) => { +test('can create and delete a new workspace', async ({ page }) => { await page.goto('/typebots') await page.click("text=Pro user's workspace") await expect( @@ -53,6 +53,20 @@ test('can create a new workspace', async ({ page }) => { await expect( page.locator('text="Pro user\'s workspace" >> nth=1') ).toBeVisible() + await page.click('text=Settings & Members') + await page.click('text="Settings"') + await page.click('text="Delete workspace"') + await expect( + page.locator( + "text=Are you sure you want to delete Pro user's workspace workspace?" + ) + ).toBeVisible() + await page.click('text="Delete"') + await expect(page.locator('text=Pro typebot')).toBeVisible() + await page.click("text=Pro user's workspace") + await expect( + page.locator('text="Pro user\'s workspace" >> nth=1') + ).toBeHidden() }) test('can update workspace info', async ({ page }) => { diff --git a/apps/builder/services/workspace/workspace.ts b/apps/builder/services/workspace/workspace.ts index e78ee664d..6d8964227 100644 --- a/apps/builder/services/workspace/workspace.ts +++ b/apps/builder/services/workspace/workspace.ts @@ -38,6 +38,14 @@ export const updateWorkspace = async (updates: Partial) => body: updates, }) +export const deleteWorkspace = (workspaceId: string) => + sendRequest<{ + workspace: Workspace + }>({ + url: `/api/workspaces/${workspaceId}`, + method: 'DELETE', + }) + export const planToReadable = (plan?: Plan) => { if (!plan) return switch (plan) {