Files
speckle-server/packages/frontend-2/components/settings/workspaces/General/DeleteDialog.vue
T

176 lines
5.1 KiB
Vue

<template>
<LayoutDialog
v-model:open="isOpen"
title="Delete workspace"
max-width="sm"
:buttons="dialogButtons"
>
<p class="text-body-xs text-foreground mb-2">
Are you sure you want to permanently delete
<span class="font-medium">{{ workspace?.name }}?</span>
This action cannot be undone.
</p>
<FormTextInput
v-model="workspaceNameInput"
name="workspaceNameConfirm"
label="To confirm deletion, type the workspace name below."
placeholder="Type the workspace name here..."
full-width
show-label
hide-error-message
class="text-sm mb-2"
color="foundation"
/>
<FormTextArea
v-model="feedback"
name="reasonForDeletion"
label="Why did you delete this workspace?"
placeholder="We want to improve so we're curious about your honest feedback"
show-label
show-optional
full-width
class="text-sm mb-2"
color="foundation"
/>
</LayoutDialog>
</template>
<script setup lang="ts">
import { graphql } from '~~/lib/common/generated/gql'
import type {
SettingsWorkspaceGeneralDeleteDialog_WorkspaceFragment,
UserWorkspacesArgs,
User
} from '~/lib/common/generated/gql/graphql'
import { FormTextInput, type LayoutDialogButton } from '@speckle/ui-components'
import { useMutation, useApolloClient } from '@vue/apollo-composable'
import { deleteWorkspaceMutation } from '~/lib/settings/graphql/mutations'
import {
convertThrowIntoFetchResult,
getFirstErrorMessage,
getCacheId,
modifyObjectFields
} from '~~/lib/common/helpers/graphql'
import { ToastNotificationType, useGlobalToast } from '~~/lib/common/composables/toast'
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
import { isUndefined } from 'lodash-es'
import { homeRoute, defaultZapierWebhookUrl } from '~/lib/common/helpers/route'
import { useZapier } from '~/lib/core/composables/zapier'
import { useForm } from 'vee-validate'
import type { MaybeNullOrUndefined } from '@speckle/shared'
import { useNavigation } from '~/lib/navigation/composables/navigation'
graphql(`
fragment SettingsWorkspaceGeneralDeleteDialog_Workspace on Workspace {
id
name
}
`)
const props = defineProps<{
workspace: MaybeNullOrUndefined<SettingsWorkspaceGeneralDeleteDialog_WorkspaceFragment>
}>()
const isOpen = defineModel<boolean>('open', { required: true })
const { mutate: deleteWorkspace } = useMutation(deleteWorkspaceMutation)
const { triggerNotification } = useGlobalToast()
const { activeUser } = useActiveUser()
const router = useRouter()
const apollo = useApolloClient().client
const { sendWebhook } = useZapier()
const { resetForm } = useForm<{ feedback: string }>()
const { mutateActiveWorkspaceSlug } = useNavigation()
const workspaceNameInput = ref('')
const feedback = ref('')
const onDelete = async () => {
if (!props.workspace) return
if (workspaceNameInput.value !== props.workspace.name) return
// Create a copy of the workspace name and ID before deletion to avoid errors after deletion/cache update
const { name: workspaceName, id: workspaceId } = props.workspace
const cache = apollo.cache
const result = await deleteWorkspace({
workspaceId: props.workspace.id
}).catch(convertThrowIntoFetchResult)
if (result?.data) {
if (activeUser.value) {
cache.evict({
id: getCacheId('Workspace', props.workspace.id)
})
modifyObjectFields<UserWorkspacesArgs, User['workspaces']>(
cache,
activeUser.value.id,
(_fieldName, variables, value, { DELETE }) => {
if (variables?.filter?.search?.length) return DELETE
const newTotalCount = isUndefined(value?.totalCount)
? undefined
: Math.max(0, (value?.totalCount || 0) - 1)
return {
...value,
...(isUndefined(newTotalCount) ? {} : { totalCount: newTotalCount })
}
},
{ fieldNameWhitelist: ['workspaces'] }
)
}
if (feedback.value) {
await sendWebhook(defaultZapierWebhookUrl, {
feedback: [
`**Action:** Workspace Deleted`,
`**Workspace:** ${workspaceName}`,
`**User ID:** ${activeUser.value?.id}`,
`**Workspace ID:** ${workspaceId}`,
`**Feedback:** ${feedback.value}`
].join('\n')
})
}
triggerNotification({
type: ToastNotificationType.Success,
title: `${workspaceName} workspace deleted`
})
mutateActiveWorkspaceSlug(null)
router.push(homeRoute)
isOpen.value = false
} else {
const errorMessage = getFirstErrorMessage(result?.errors)
triggerNotification({
type: ToastNotificationType.Danger,
title: 'Failed to delete workspace',
description: errorMessage
})
}
}
const dialogButtons = computed((): LayoutDialogButton[] => [
{
text: 'Cancel',
props: { color: 'outline' },
onClick: () => {
isOpen.value = false
}
},
{
text: 'Delete',
props: {
color: 'danger',
disabled: workspaceNameInput.value !== props.workspace?.name
},
onClick: onDelete
}
])
watch(isOpen, () => {
resetForm()
})
</script>