refactor(fe2): Change workspace creation triggers (#3211)

* Tasks from ticket

* Update copy

* Add confirm dialog to cancel button

* Show Workspace create dialog when on explainer page

* Reorder

* Add new mixpanel event

* Use 1 confirm modal
This commit is contained in:
andrewwallacespeckle
2024-10-10 16:10:21 +01:00
committed by GitHub
parent 1866d3369b
commit d956fbf79d
4 changed files with 135 additions and 57 deletions
@@ -0,0 +1,37 @@
<template>
<LayoutDialog v-model:open="open" max-width="xs" :buttons="dialogButtons">
<template #header>Discard changes?</template>
<p v-if="text" class="mb-2">{{ text }}</p>
<p v-else class="mb-2">You have unsaved changes. Are you sure you want to leave?</p>
</LayoutDialog>
</template>
<script setup lang="ts">
import type { LayoutDialogButton } from '@speckle/ui-components'
defineProps<{
text?: string
}>()
const emit = defineEmits(['confirm'])
const open = defineModel<boolean>('open', { required: true })
const dialogButtons = computed((): LayoutDialogButton[] => {
return [
{
text: 'Cancel',
props: { color: 'outline' },
onClick: () => {
open.value = false
}
},
{
text: 'Continue',
onClick: () => {
open.value = false
emit('confirm')
}
}
]
})
</script>
@@ -58,16 +58,10 @@
v-if="isWorkspacesEnabled"
collapsible
title="Workspaces"
:plus-click="
isNotGuest
? () => {
openWorkspaceCreateDialog()
}
: undefined
"
:plus-click="isNotGuest ? handlePlusClick : undefined"
plus-text="Create workspace"
>
<NuxtLink :to="workspacesRoute" @click="isOpenMobile = false">
<NuxtLink :to="workspacesRoute" @click="handleIntroducingWorkspacesClick">
<LayoutSidebarMenuGroupItem
label="Introducing workspaces"
:active="isActive(workspacesRoute)"
@@ -206,6 +200,7 @@ import { Roles } from '@speckle/shared'
const { isLoggedIn } = useActiveUser()
const isWorkspacesEnabled = useIsWorkspacesEnabled()
const route = useRoute()
const router = useRouter()
const { activeUser: user } = useActiveUser()
const mixpanel = useMixpanel()
@@ -241,13 +236,6 @@ const workspacesItems = computed(() =>
: []
)
const openWorkspaceCreateDialog = () => {
showWorkspaceCreateDialog.value = true
mixpanel.track('Create Workspace Button Clicked', {
source: 'sidebar'
})
}
onWorkspaceResult((result) => {
if (result.data?.activeUser) {
const workspaceIds = result.data.activeUser.workspaces.items.map(
@@ -273,4 +261,29 @@ const openFeedbackDialog = () => {
showFeedbackDialog.value = true
isOpenMobile.value = false
}
const openWorkspaceCreateDialog = () => {
showWorkspaceCreateDialog.value = true
mixpanel.track('Create Workspace Button Clicked', {
source: 'sidebar'
})
}
const handlePlusClick = () => {
if (route.path === workspacesRoute) {
openWorkspaceCreateDialog()
} else {
mixpanel.track('Clicked Link to Workspace Explainer', {
source: 'sidebar'
})
router.push(workspacesRoute)
}
}
const handleIntroducingWorkspacesClick = () => {
isOpenMobile.value = false
mixpanel.track('Clicked Link to Workspace Explainer', {
source: 'sidebar'
})
}
</script>
@@ -1,5 +1,11 @@
<template>
<LayoutDialog v-model:open="open" max-width="sm" :buttons="dialogButtons">
<LayoutDialog
v-model:open="open"
max-width="sm"
:buttons="dialogButtons"
hide-closer
prevent-close-on-click-outside
>
<template #header>Create a new project</template>
<form class="flex flex-col text-foreground" @submit="onSubmit">
<div class="flex flex-col gap-y-4 mb-2">
@@ -28,7 +34,7 @@
<ProjectVisibilitySelect v-model="visibility" mount-menu-on-body />
</div>
<template v-if="isWorkspacesEnabled && !workspaceId">
<div v-if="!isCreatingWorkspace" class="flex gap-y-2 flex-col">
<div class="flex gap-y-2 flex-col">
<p class="text-body-xs text-foreground font-medium">Workspace</p>
<div v-if="hasWorkspaces">
<div class="flex gap-x-2 items-center">
@@ -39,31 +45,31 @@
disabled-item-tooltip="You dont have rights to create projects in this workspace"
class="flex-1"
/>
<FormButton
:icon-left="PlusIcon"
hide-text
class="flex"
color="outline"
@click="isCreatingWorkspace = true"
/>
<div v-tippy="'Create workspace'" class="flex">
<FormButton
:icon-left="PlusIcon"
hide-text
class="flex"
color="outline"
@click="navigateToWorkspaceExplainer"
/>
</div>
</div>
</div>
<FormButton v-else color="outline" @click="isCreatingWorkspace = true">
<FormButton v-else color="outline" @click="navigateToWorkspaceExplainer">
New workspace
</FormButton>
<p class="text-foreground-2 text-body-2xs">
Workspace offers better project management and higher data security.
Workspaces offer better project management and higher data security.
</p>
</div>
<ProjectsNewWorkspace
v-if="isCreatingWorkspace"
mixpanel-event-source="create-project-modal"
@cancel="isCreatingWorkspace = false"
@workspace-created="onWorkspaceCreated"
/>
</template>
</div>
</form>
<CommonConfirmDialog
v-model:open="showConfirmDialog"
@confirm="handleConfirmAction"
/>
</LayoutDialog>
</template>
<script setup lang="ts">
@@ -80,6 +86,7 @@ import { graphql } from '~~/lib/common/generated/gql'
import { projectWorkspaceSelectQuery } from '~/lib/projects/graphql/queries'
import { useQuery } from '@vue/apollo-composable'
import { Roles } from '@speckle/shared'
import { workspacesRoute } from '~/lib/common/helpers/route'
graphql(`
fragment ProjectsAddDialog_Workspace on Workspace {
@@ -114,24 +121,21 @@ const emit = defineEmits<{
const isWorkspacesEnabled = useIsWorkspacesEnabled()
const createProject = useCreateProject()
const { handleSubmit } = useForm<FormValues>()
const router = useRouter()
const { handleSubmit, meta } = useForm<FormValues>()
const { result: workspaceResult } = useQuery(projectWorkspaceSelectQuery, null, () => ({
enabled: isWorkspacesEnabled.value
}))
const visibility = ref(ProjectVisibility.Unlisted)
const selectedWorkspace = ref<ProjectsAddDialog_WorkspaceFragment>()
const isCreatingWorkspace = ref<boolean>(false)
const showConfirmDialog = ref(false)
const confirmActionType = ref<'navigate' | 'close' | null>(null)
const open = defineModel<boolean>('open', { required: true })
const mp = useMixpanel()
const onWorkspaceCreated = (workspace: ProjectsAddDialog_WorkspaceFragment) => {
isCreatingWorkspace.value = false
selectedWorkspace.value = workspace
}
const onSubmit = handleSubmit(async (values) => {
await createProject({
name: values.name,
@@ -154,14 +158,11 @@ const workspaces = computed(
)
const hasWorkspaces = computed(() => workspaces.value.length > 0)
const dialogButtons = computed((): LayoutDialogButton[] => {
if (isCreatingWorkspace.value) return []
return [
{
text: 'Cancel',
props: { color: 'outline' },
onClick: () => {
open.value = false
}
onClick: confirmCancel
},
{
text: 'Create',
@@ -173,6 +174,37 @@ const dialogButtons = computed((): LayoutDialogButton[] => {
]
})
const formIsDirty = computed(() => {
return meta.value.dirty
})
const navigateToWorkspaceExplainer = () => {
if (formIsDirty.value) {
confirmActionType.value = 'navigate'
showConfirmDialog.value = true
} else {
router.push(workspacesRoute)
}
}
const confirmCancel = () => {
if (formIsDirty.value) {
confirmActionType.value = 'close'
showConfirmDialog.value = true
} else {
open.value = false
}
}
const handleConfirmAction = () => {
if (confirmActionType.value === 'navigate') {
router.push(workspacesRoute)
} else if (confirmActionType.value === 'close') {
open.value = false
}
confirmActionType.value = null
}
watch(open, (newVal, oldVal) => {
if (newVal && !oldVal) {
selectedWorkspace.value = undefined
@@ -85,15 +85,17 @@
/>
</template>
</LayoutSidebarMenuGroup>
<LayoutSidebarMenuGroupItem
<NuxtLink
v-if="canCreateWorkspace"
label="Add workspace"
@click="openWorkspaceCreateDialog"
:to="workspacesRoute"
@click="isOpen = false"
>
<template #icon>
<PlusIcon class="h-4 w-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
<LayoutSidebarMenuGroupItem label="Create workspace">
<template #icon>
<PlusIcon class="h-4 w-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
</LayoutSidebarMenuGroup>
</LayoutSidebarMenu>
</LayoutSidebar>
@@ -136,6 +138,7 @@ import {
import { graphql } from '~~/lib/common/generated/gql'
import type { WorkspaceRoles } from '@speckle/shared'
import { useMixpanel } from '~~/lib/core/composables/mp'
import { workspacesRoute } from '~/lib/common/helpers/route'
graphql(`
fragment SettingsDialog_Workspace on Workspace {
@@ -209,13 +212,6 @@ const onWorkspaceMenuItemClick = (id: string, target: string, disabled?: boolean
})
}
const openWorkspaceCreateDialog = () => {
showWorkspaceCreateDialog.value = true
mixpanel.track('Create Workspace Button Clicked', {
source: 'settings'
})
}
const workspaceMenuItemClasses = (
itemKey: string | number,
workspaceId: string,