176 lines
5.1 KiB
Vue
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>
|