Feat: Mixpanel workspace groups add properties (#2904)

This commit is contained in:
Mike
2024-09-10 14:18:29 +02:00
committed by GitHub
parent cf3a948cf1
commit 7c00a1de80
8 changed files with 128 additions and 13 deletions
@@ -146,18 +146,19 @@ const save = handleSubmit(async () => {
const result = await updateMutation({ input }).catch(convertThrowIntoFetchResult)
if (result?.data) {
triggerNotification({
type: ToastNotificationType.Success,
title: 'Workspace updated'
})
mixpanel.track('Workspace General Settings Updated', {
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'
})
} else {
const errorMessage = getFirstErrorMessage(result?.errors)
triggerNotification({
@@ -99,6 +99,7 @@ import type {
} from '~~/lib/common/generated/gql/graphql'
import { workspaceRoute } from '~/lib/common/helpers/route'
import { Roles } from '@speckle/shared'
import { useWorkspacesMixpanel } from '~/lib/workspaces/composables/mixpanel'
import {
SettingMenuKeys,
type AvailableSettingsMenuKeys
@@ -117,6 +118,7 @@ graphql(`
const selectedRoles = ref(undefined as Optional<StreamRoles[]>)
const openNewProject = ref(false)
const { workspaceMixpanelUpdateGroup } = useWorkspacesMixpanel()
const areQueriesLoading = useQueryLoading()
const route = useRoute()
const {
@@ -141,7 +143,7 @@ const token = computed(() => route.query.token as Optional<string>)
const pageFetchPolicy = usePageQueryStandardFetchPolicy()
const { result: initialQueryResult } = useQuery(
const { result: initialQueryResult, onResult } = useQuery(
workspacePageQuery,
() => ({
workspaceId: props.workspaceId,
@@ -239,4 +241,10 @@ const onShowSettingsDialog = (target: AvailableSettingsMenuKeys) => {
showSettingsDialog.value = true
settingsDialogTarget.value = target
}
onResult((queryResult) => {
if (queryResult.data?.workspace) {
workspaceMixpanelUpdateGroup(queryResult.data.workspace)
}
})
</script>
@@ -299,12 +299,14 @@ const documents = {
"\n subscription OnViewerCommentsUpdated($target: ViewerUpdateTrackingTarget!) {\n projectCommentsUpdated(target: $target) {\n id\n type\n comment {\n id\n parent {\n id\n }\n ...ViewerCommentThread\n }\n }\n }\n": types.OnViewerCommentsUpdatedDocument,
"\n fragment LinkableComment on Comment {\n id\n viewerResources {\n modelId\n versionId\n objectId\n }\n }\n": types.LinkableCommentFragmentDoc,
"\n fragment UseWorkspaceInviteManager_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n token\n workspaceId\n user {\n id\n }\n }\n": types.UseWorkspaceInviteManager_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment WorkspaceMixpanelUpdateGroup_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n }\n": types.WorkspaceMixpanelUpdateGroup_WorkspaceCollaboratorFragmentDoc,
"\n fragment WorkspaceMixpanelUpdateGroup_Workspace on Workspace {\n id\n name\n description\n domainBasedMembershipProtectionEnabled\n discoverabilityEnabled\n billing {\n cost {\n total\n }\n versionsCount {\n current\n max\n }\n }\n team {\n totalCount\n items {\n ...WorkspaceMixpanelUpdateGroup_WorkspaceCollaborator\n }\n }\n }\n": types.WorkspaceMixpanelUpdateGroup_WorkspaceFragmentDoc,
"\n mutation UpdateRole($input: WorkspaceRoleUpdateInput!) {\n workspaceMutations {\n updateRole(input: $input) {\n team {\n items {\n id\n role\n }\n }\n }\n }\n }\n": types.UpdateRoleDocument,
"\n mutation InviteToWorkspace(\n $workspaceId: String!\n $input: [WorkspaceInviteCreateInput!]!\n ) {\n workspaceMutations {\n invites {\n batchCreate(workspaceId: $workspaceId, input: $input) {\n id\n invitedTeam {\n ...SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator\n }\n }\n }\n }\n }\n": types.InviteToWorkspaceDocument,
"\n mutation CreateWorkspace($input: WorkspaceCreateInput!) {\n workspaceMutations {\n create(input: $input) {\n id\n ...SettingsDialog_Workspace\n }\n }\n }\n": types.CreateWorkspaceDocument,
"\n mutation ProcessWorkspaceInvite($input: WorkspaceInviteUseInput!) {\n workspaceMutations {\n invites {\n use(input: $input)\n }\n }\n }\n": types.ProcessWorkspaceInviteDocument,
"\n query WorkspaceAccessCheck($id: String!) {\n workspace(id: $id) {\n id\n }\n }\n": types.WorkspaceAccessCheckDocument,
"\n query WorkspacePageQuery(\n $workspaceId: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n $token: String\n ) {\n workspace(id: $workspaceId) {\n id\n ...WorkspaceHeader_Workspace\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceProjectList_ProjectCollection\n }\n }\n workspaceInvite(workspaceId: $workspaceId, token: $token) {\n id\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n ...WorkspaceInviteBlock_PendingWorkspaceCollaborator\n }\n }\n": types.WorkspacePageQueryDocument,
"\n query WorkspacePageQuery(\n $workspaceId: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n $token: String\n ) {\n workspace(id: $workspaceId) {\n id\n ...WorkspaceHeader_Workspace\n ...WorkspaceMixpanelUpdateGroup_Workspace\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceProjectList_ProjectCollection\n }\n }\n workspaceInvite(workspaceId: $workspaceId, token: $token) {\n id\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n ...WorkspaceInviteBlock_PendingWorkspaceCollaborator\n }\n }\n": types.WorkspacePageQueryDocument,
"\n query WorkspaceProjectsQuery(\n $workspaceId: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n ) {\n workspace(id: $workspaceId) {\n id\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceProjectList_ProjectCollection\n }\n }\n }\n": types.WorkspaceProjectsQueryDocument,
"\n query WorkspaceInvite($workspaceId: String, $token: String) {\n workspaceInvite(workspaceId: $workspaceId, token: $token) {\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n ...WorkspaceInviteBlock_PendingWorkspaceCollaborator\n }\n }\n": types.WorkspaceInviteDocument,
"\n query LegacyBranchRedirectMetadata($streamId: String!, $branchName: String!) {\n project(id: $streamId) {\n modelByName(name: $branchName) {\n id\n }\n }\n }\n": types.LegacyBranchRedirectMetadataDocument,
@@ -1479,6 +1481,14 @@ export function graphql(source: "\n fragment LinkableComment on Comment {\n
* 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 UseWorkspaceInviteManager_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n token\n workspaceId\n user {\n id\n }\n }\n"): (typeof documents)["\n fragment UseWorkspaceInviteManager_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n token\n workspaceId\n user {\n id\n }\n }\n"];
/**
* 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 WorkspaceMixpanelUpdateGroup_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n }\n"): (typeof documents)["\n fragment WorkspaceMixpanelUpdateGroup_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n }\n"];
/**
* 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 WorkspaceMixpanelUpdateGroup_Workspace on Workspace {\n id\n name\n description\n domainBasedMembershipProtectionEnabled\n discoverabilityEnabled\n billing {\n cost {\n total\n }\n versionsCount {\n current\n max\n }\n }\n team {\n totalCount\n items {\n ...WorkspaceMixpanelUpdateGroup_WorkspaceCollaborator\n }\n }\n }\n"): (typeof documents)["\n fragment WorkspaceMixpanelUpdateGroup_Workspace on Workspace {\n id\n name\n description\n domainBasedMembershipProtectionEnabled\n discoverabilityEnabled\n billing {\n cost {\n total\n }\n versionsCount {\n current\n max\n }\n }\n team {\n totalCount\n items {\n ...WorkspaceMixpanelUpdateGroup_WorkspaceCollaborator\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1502,7 +1512,7 @@ export function graphql(source: "\n query WorkspaceAccessCheck($id: String!) {\
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query WorkspacePageQuery(\n $workspaceId: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n $token: String\n ) {\n workspace(id: $workspaceId) {\n id\n ...WorkspaceHeader_Workspace\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceProjectList_ProjectCollection\n }\n }\n workspaceInvite(workspaceId: $workspaceId, token: $token) {\n id\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n ...WorkspaceInviteBlock_PendingWorkspaceCollaborator\n }\n }\n"): (typeof documents)["\n query WorkspacePageQuery(\n $workspaceId: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n $token: String\n ) {\n workspace(id: $workspaceId) {\n id\n ...WorkspaceHeader_Workspace\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceProjectList_ProjectCollection\n }\n }\n workspaceInvite(workspaceId: $workspaceId, token: $token) {\n id\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n ...WorkspaceInviteBlock_PendingWorkspaceCollaborator\n }\n }\n"];
export function graphql(source: "\n query WorkspacePageQuery(\n $workspaceId: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n $token: String\n ) {\n workspace(id: $workspaceId) {\n id\n ...WorkspaceHeader_Workspace\n ...WorkspaceMixpanelUpdateGroup_Workspace\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceProjectList_ProjectCollection\n }\n }\n workspaceInvite(workspaceId: $workspaceId, token: $token) {\n id\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n ...WorkspaceInviteBlock_PendingWorkspaceCollaborator\n }\n }\n"): (typeof documents)["\n query WorkspacePageQuery(\n $workspaceId: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n $token: String\n ) {\n workspace(id: $workspaceId) {\n id\n ...WorkspaceHeader_Workspace\n ...WorkspaceMixpanelUpdateGroup_Workspace\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceProjectList_ProjectCollection\n }\n }\n workspaceInvite(workspaceId: $workspaceId, token: $token) {\n id\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n ...WorkspaceInviteBlock_PendingWorkspaceCollaborator\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
File diff suppressed because one or more lines are too long
+10 -2
View File
@@ -5,7 +5,14 @@ import type { Merge } from 'type-fest'
export type MixpanelClient = Merge<
Pick<
OverridedMixpanel,
'track' | 'init' | 'reset' | 'register' | 'identify' | 'people' | 'add_group'
| 'track'
| 'init'
| 'reset'
| 'register'
| 'identify'
| 'people'
| 'add_group'
| 'get_group'
>,
{
people: Pick<OverridedMixpanel['people'], 'set' | 'set_once'>
@@ -25,5 +32,6 @@ export const fakeMixpanelClient = (): MixpanelClient => ({
set: noop,
set_once: noop
},
add_group: noop
add_group: noop,
get_group: noop as MixpanelClient['get_group']
})
@@ -0,0 +1,82 @@
import { useMixpanel } from '~/lib/core/composables/mp'
import { graphql } from '~~/lib/common/generated/gql'
import type {
WorkspaceMixpanelUpdateGroup_WorkspaceFragment,
WorkspaceMixpanelUpdateGroup_WorkspaceCollaboratorFragment
} from '~/lib/common/generated/gql/graphql'
import { Roles, type WorkspaceRoles } from '@speckle/shared'
graphql(`
fragment WorkspaceMixpanelUpdateGroup_WorkspaceCollaborator on WorkspaceCollaborator {
id
role
}
`)
graphql(`
fragment WorkspaceMixpanelUpdateGroup_Workspace on Workspace {
id
name
description
domainBasedMembershipProtectionEnabled
discoverabilityEnabled
billing {
cost {
total
}
versionsCount {
current
max
}
}
team {
totalCount
items {
...WorkspaceMixpanelUpdateGroup_WorkspaceCollaborator
}
}
}
`)
export const useWorkspacesMixpanel = () => {
const mixpanel = useMixpanel()
const workspaceMixpanelUpdateGroup = (
workspace: WorkspaceMixpanelUpdateGroup_WorkspaceFragment
) => {
if (!workspace.id) return
const roleCount = {
[Roles.Workspace.Admin]: 0,
[Roles.Workspace.Member]: 0,
[Roles.Workspace.Guest]: 0
}
workspace.team.items.forEach(
(item: WorkspaceMixpanelUpdateGroup_WorkspaceCollaboratorFragment) => {
roleCount[item.role as WorkspaceRoles] =
(roleCount[item.role as WorkspaceRoles] ?? 0) + 1
}
)
const input = {
name: workspace.name,
description: workspace.description,
domainBasedMembershipProtectionEnabled:
workspace.domainBasedMembershipProtectionEnabled,
discoverabilityEnabled: workspace.discoverabilityEnabled,
costTotal: workspace.billing?.cost.total,
versionsCountCurrent: workspace.billing?.versionsCount.current,
versionsCountMax: workspace.billing?.versionsCount.max,
teamTotalCount: workspace.team.totalCount,
teamAdminCount: roleCount[Roles.Workspace.Admin],
teamMemberCount: roleCount[Roles.Workspace.Member],
teamGuestCount: roleCount[Roles.Workspace.Guest]
}
mixpanel.get_group('workspace_id', workspace.id).set(input)
}
return {
workspaceMixpanelUpdateGroup
}
}
@@ -19,6 +19,7 @@ export const workspacePageQuery = graphql(`
workspace(id: $workspaceId) {
id
...WorkspaceHeader_Workspace
...WorkspaceMixpanelUpdateGroup_Workspace
projects(filter: $filter, cursor: $cursor, limit: 10) {
...WorkspaceProjectList_ProjectCollection
}
@@ -8,7 +8,6 @@ import { useRoute } from 'vue-router'
const route = useRoute()
const workspaceId = computed(() => route.params.id as string)
definePageMeta({
middleware: ['requires-workspaces-enabled', 'require-valid-workspace']
})