Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add Mixpanel event for workspaces #2798

Merged
merged 1 commit into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion packages/frontend-2/components/dashboard/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
:plus-click="
isUserAdmin
? () => {
showWorkspaceCreateDialog = true
openWorkspaceCreateDialog
}
: undefined
"
Expand Down Expand Up @@ -152,6 +152,7 @@
<WorkspaceCreateDialog
v-model:open="showWorkspaceCreateDialog"
navigate-on-success
event-source="sidebar"
/>
</div>
</template>
Expand All @@ -175,11 +176,13 @@ import {
import { useRoute } from 'vue-router'
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
import { HomeIcon } from '@heroicons/vue/24/outline'
import { useMixpanel } from '~~/lib/core/composables/mp'

const { isLoggedIn } = useActiveUser()
const isWorkspacesEnabled = useIsWorkspacesEnabled()
const route = useRoute()
const { activeUser: user } = useActiveUser()
const mixpanel = useMixpanel()

const isOpenMobile = ref(false)
const showWorkspaceCreateDialog = ref(false)
Expand All @@ -205,4 +208,11 @@ const workspacesItems = computed(() =>
}))
: []
)

const openWorkspaceCreateDialog = () => {
showWorkspaceCreateDialog.value = true
mixpanel.track('Create Workspace Button Clicked', {
source: 'sidebar'
})
}
</script>
6 changes: 5 additions & 1 deletion packages/frontend-2/components/projects/AddDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ const onSubmit = handleSubmit(async (values) => {
workspaceId: props.workspaceId
})
emit('created')
mp.track('Stream Action', { type: 'action', name: 'create' })
mp.track('Stream Action', {
type: 'action',
name: 'create',
workspaceId: props.workspaceId
})
open.value = false
})

Expand Down
17 changes: 14 additions & 3 deletions packages/frontend-2/components/settings/Dialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
<LayoutSidebarMenuGroupItem
v-if="isAdmin"
label="Add workspace"
@click="showWorkspaceCreateDialog = true"
@click="openWorkspaceCreateDialog"
>
<template #icon>
<PlusIcon class="h-4 w-4 text-foreground-2" />
Expand All @@ -109,7 +109,10 @@
/>
</div>

<WorkspaceCreateDialog v-model:open="showWorkspaceCreateDialog" />
<WorkspaceCreateDialog
v-model:open="showWorkspaceCreateDialog"
event-source="settings"
/>
</LayoutDialog>
</template>

Expand All @@ -131,6 +134,7 @@ import {
} from '@speckle/ui-components'
import { graphql } from '~~/lib/common/generated/gql'
import type { WorkspaceRoles } from '@speckle/shared'
import { useMixpanel } from '~~/lib/core/composables/mp'

graphql(`
fragment SettingsDialog_User on User {
Expand All @@ -152,7 +156,7 @@ const targetWorkspaceId = defineModel<string | null>('targetWorkspaceId')
const { activeUser: user } = useActiveUser()
const { userMenuItems, serverMenuItems, workspaceMenuItems } = useSettingsMenu()
const breakpoints = useBreakpoints(TailwindBreakpoints)

const mixpanel = useMixpanel()
const isWorkspacesEnabled = useIsWorkspacesEnabled()
const { result: workspaceResult } = useQuery(settingsSidebarQuery, null, {
enabled: isWorkspacesEnabled.value
Expand Down Expand Up @@ -186,6 +190,13 @@ const onWorkspaceMenuItemClick = (id: string, target: string, disabled?: boolean
targetMenuItem.value = target
}

const openWorkspaceCreateDialog = () => {
showWorkspaceCreateDialog.value = true
mixpanel.track('Create Workspace Button Clicked', {
source: 'settings'
})
}

const workspaceMenuItemClasses = (
itemKey: string | number,
workspaceId: string,
Expand Down
10 changes: 10 additions & 0 deletions packages/frontend-2/components/settings/workspaces/General.vue
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ import {
convertThrowIntoFetchResult
} from '~~/lib/common/helpers/graphql'
import { isRequired, isStringOfLength } from '~~/lib/common/helpers/validation'
import { useMixpanel } from '~/lib/core/composables/mp'

graphql(`
fragment SettingsWorkspacesGeneral_Workspace on Workspace {
Expand All @@ -115,6 +116,7 @@ const props = defineProps<{
workspaceId: string
}>()

const mixpanel = useMixpanel()
const { handleSubmit } = useForm<FormValues>()
const { triggerNotification } = useGlobalToast()
const { mutate: updateMutation } = useMutation(settingsUpdateWorkspaceMutation)
Expand Down Expand Up @@ -144,6 +146,14 @@ const save = handleSubmit(async () => {
const result = await updateMutation({ input }).catch(convertThrowIntoFetchResult)

if (result?.data) {
mixpanel.track('Workspace Domain Protection Toggled', {
fields: (Object.keys(input) as Array<keyof WorkspaceUpdateInput>).filter(
(key) => key !== 'id'
),
// eslint-disable-next-line camelcase
workspace_id: props.workspaceId
})

triggerNotification({
type: ToastNotificationType.Success,
title: 'Workspace updated'
Expand Down
18 changes: 16 additions & 2 deletions packages/frontend-2/components/settings/workspaces/Security.vue
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ import { SettingsUpdateWorkspaceSecurityDocument } from '~/lib/common/generated/
import { getCacheId, getFirstErrorMessage } from '~/lib/common/helpers/graphql'
import { settingsWorkspacesSecurityQuery } from '~/lib/settings/graphql/queries'
import { useAddWorkspaceDomain } from '~/lib/settings/composables/management'
import { useMixpanel } from '~/lib/core/composables/mp'

graphql(`
fragment SettingsWorkspacesSecurity_Workspace on Workspace {
Expand Down Expand Up @@ -140,6 +141,7 @@ const props = defineProps<{
const addWorkspaceDomain = useAddWorkspaceDomain()
const { triggerNotification } = useGlobalToast()
const apollo = useApolloClient().client
const mixpanel = useMixpanel()

const selectedDomain = ref<string>()
const showRemoveDomainDialog = ref(false)
Expand Down Expand Up @@ -206,7 +208,13 @@ const isDomainProtectionEnabled = computed({
})
.catch(convertThrowIntoFetchResult)

if (!mutationResult?.data) {
if (mutationResult?.data) {
mixpanel.track('Workspace Domain Protection Toggled', {
value: newVal,
// eslint-disable-next-line camelcase
workspace_id: props.workspaceId
})
} else {
triggerNotification({
type: ToastNotificationType.Danger,
title: 'Failed to update',
Expand Down Expand Up @@ -252,7 +260,13 @@ const isDomainDiscoverabilityEnabled = computed({
}
})

if (!mutationResult?.data) {
if (mutationResult?.data) {
mixpanel.track('Workspace Discoverability Toggled', {
value: newVal,
// eslint-disable-next-line camelcase
workspace_id: props.workspaceId
})
} else {
triggerNotification({
type: ToastNotificationType.Danger,
title: 'Failed to update',
Expand Down
5 changes: 5 additions & 0 deletions packages/frontend-2/components/workspace/CreateDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ type FormValues = { name: string; description: string }

const props = defineProps<{
navigateOnSuccess?: boolean
// Used to send to Mixpanel to know where the modal was triggered from
eventSource: string
}>()

const isOpen = defineModel<boolean>('open', { required: true })
Expand Down Expand Up @@ -94,6 +96,9 @@ const handleCreateWorkspace = handleSubmit(async () => {
},
{
navigateOnSuccess: props.navigateOnSuccess === true
},
{
source: props.eventSource
}
)

Expand Down
17 changes: 12 additions & 5 deletions packages/frontend-2/components/workspaces/Promo/Page.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
<template>
<div class="flex flex-col gap-12">
<WorkspacesPromoBanner
:is-admin="isAdmin"
@create="showWorkspaceCreateDialog = true"
/>
<WorkspacesPromoBanner :is-admin="isAdmin" @create="openWorkspaceCreateDialog" />

<section>
<div class="flex justify-between mb-2">
<h4 class="text-foreground-2 text-heading-sm">In a nutshell</h4>
<FormButton :disabled="!isAdmin" @click="showWorkspaceCreateDialog = true">
<FormButton :disabled="!isAdmin" @click="openWorkspaceCreateDialog">
Create workspace
</FormButton>
</div>
Expand Down Expand Up @@ -65,10 +62,12 @@
<WorkspaceCreateDialog
v-model:open="showWorkspaceCreateDialog"
navigate-on-success
event-source="promo-page"
/>
</div>
</template>
<script setup lang="ts">
import { useMixpanel } from '~~/lib/core/composables/mp'
import { Roles } from '@speckle/shared'
import {
UserGroupIcon,
Expand All @@ -81,6 +80,14 @@ import {
const showWorkspaceCreateDialog = ref(false)

const { activeUser: user } = useActiveUser()
const mixpanel = useMixpanel()

const isAdmin = computed(() => user.value?.role === Roles.Server.Admin)

const openWorkspaceCreateDialog = () => {
showWorkspaceCreateDialog.value = true
mixpanel.track('Create Workspace Button Clicked', {
source: 'promo-page'
})
}
</script>
4 changes: 2 additions & 2 deletions packages/frontend-2/lib/common/generated/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ const documents = {
"\n fragment SettingsWorkspacesGeneralEditAvatar_Workspace on Workspace {\n id\n logo\n name\n defaultLogoIndex\n }\n": types.SettingsWorkspacesGeneralEditAvatar_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembers_Workspace on Workspace {\n id\n role\n }\n": types.SettingsWorkspacesMembers_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesProjects_ProjectCollection on ProjectCollection {\n totalCount\n items {\n ...SettingsSharedProjects_Project\n }\n }\n": types.SettingsWorkspacesProjects_ProjectCollectionFragmentDoc,
"\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n id\n domains {\n id\n domain\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n domainBasedMembershipProtectionEnabled\n discoverabilityEnabled\n role\n }\n\n fragment SettingsWorkspacesSecurity_User on User {\n id\n emails {\n id\n email\n verified\n }\n }\n": types.SettingsWorkspacesSecurity_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n id\n domains {\n id\n domain\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n domainBasedMembershipProtectionEnabled\n discoverabilityEnabled\n }\n\n fragment SettingsWorkspacesSecurity_User on User {\n id\n emails {\n id\n email\n verified\n }\n }\n": types.SettingsWorkspacesSecurity_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n verified\n }\n }\n": types.SettingsWorkspacesMembersGuestsTable_WorkspaceCollaboratorFragmentDoc,
"\n fragment SettingsWorkspacesMembersGuestsTable_Workspace on Workspace {\n id\n ...SettingsWorkspacesMembersTableHeader_Workspace\n team {\n id\n ...SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator\n }\n }\n": types.SettingsWorkspacesMembersGuestsTable_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n inviteId\n role\n title\n updatedAt\n user {\n id\n ...LimitedUserAvatar\n }\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n }\n": types.SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaboratorFragmentDoc,
Expand Down Expand Up @@ -697,7 +697,7 @@ export function graphql(source: "\n fragment SettingsWorkspacesProjects_Project
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n id\n domains {\n id\n domain\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n domainBasedMembershipProtectionEnabled\n discoverabilityEnabled\n role\n }\n\n fragment SettingsWorkspacesSecurity_User on User {\n id\n emails {\n id\n email\n verified\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n id\n domains {\n id\n domain\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n domainBasedMembershipProtectionEnabled\n discoverabilityEnabled\n role\n }\n\n fragment SettingsWorkspacesSecurity_User on User {\n id\n emails {\n id\n email\n verified\n }\n }\n"];
export function graphql(source: "\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n id\n domains {\n id\n domain\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n domainBasedMembershipProtectionEnabled\n discoverabilityEnabled\n }\n\n fragment SettingsWorkspacesSecurity_User on User {\n id\n emails {\n id\n email\n verified\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n id\n domains {\n id\n domain\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n domainBasedMembershipProtectionEnabled\n discoverabilityEnabled\n }\n\n fragment SettingsWorkspacesSecurity_User on User {\n id\n emails {\n id\n email\n verified\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
Loading