Merge branch 'main' into iain/ratelimiter-should-respect-configuration
This commit is contained in:
@@ -38,4 +38,7 @@ const dialogButtons = computed((): LayoutDialogButton[] => [
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
// testing deployments in prod, ignore this
|
||||
markUsed('a')
|
||||
</script>
|
||||
|
||||
@@ -26,11 +26,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useForm } from 'vee-validate'
|
||||
import type {
|
||||
OnboardingRole,
|
||||
OnboardingPlan,
|
||||
OnboardingSource
|
||||
} from '~/lib/auth/helpers/onboarding'
|
||||
import type { OnboardingRole, OnboardingPlan, OnboardingSource } from '@speckle/shared'
|
||||
import { useProcessOnboarding } from '~~/lib/auth/composables/onboarding'
|
||||
import { homeRoute } from '~/lib/common/helpers/route'
|
||||
|
||||
@@ -50,7 +46,11 @@ const onSubmit = handleSubmit(async () => {
|
||||
if (values.role) {
|
||||
setMixpanelSegments({ role: values.role })
|
||||
}
|
||||
await setUserOnboardingComplete()
|
||||
await setUserOnboardingComplete({
|
||||
role: values.role,
|
||||
plans: values.plan,
|
||||
source: values.source
|
||||
})
|
||||
navigateTo(homeRoute)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFormSelectChildInternals } from '@speckle/ui-components'
|
||||
import { OnboardingPlan, PlanTitleMap } from '~/lib/auth/helpers/onboarding'
|
||||
import { PlanTitleMap } from '~/lib/auth/helpers/onboarding'
|
||||
import { OnboardingPlan } from '@speckle/shared'
|
||||
import { isRequired } from '~~/lib/common/helpers/validation'
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFormSelectChildInternals } from '@speckle/ui-components'
|
||||
import { OnboardingRole, RoleTitleMap } from '~/lib/auth/helpers/onboarding'
|
||||
import { RoleTitleMap } from '~/lib/auth/helpers/onboarding'
|
||||
import { OnboardingRole } from '@speckle/shared'
|
||||
import { isRequired } from '~~/lib/common/helpers/validation'
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFormSelectChildInternals } from '@speckle/ui-components'
|
||||
import { OnboardingSource, SourceTitleMap } from '~/lib/auth/helpers/onboarding'
|
||||
import { SourceTitleMap } from '~/lib/auth/helpers/onboarding'
|
||||
import { OnboardingSource } from '@speckle/shared'
|
||||
import { isRequired } from '~~/lib/common/helpers/validation'
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { useApolloClient } from '@vue/apollo-composable'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { UnsupportedEnvironmentError } from '~~/lib/core/errors/base'
|
||||
import type { OnboardingState } from '~~/lib/auth/helpers/onboarding'
|
||||
import type {
|
||||
OnboardingPlan,
|
||||
OnboardingRole,
|
||||
OnboardingSource,
|
||||
OnboardingState
|
||||
} from '@speckle/shared'
|
||||
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
|
||||
import { OnboardingError } from '~~/lib/auth/errors/errors'
|
||||
import { finishOnboardingMutation } from '~~/lib/auth/graphql/mutations'
|
||||
@@ -86,14 +91,22 @@ export const useProcessOnboarding = () => {
|
||||
|
||||
/**
|
||||
* Marks the current user as having completed the onboarding - we're using this as a flag to
|
||||
* know that we've set up the sample project once.
|
||||
* know that we've set up the sample project once. Also updates their Mailchimp tags.
|
||||
*/
|
||||
const setUserOnboardingComplete = async () => {
|
||||
const setUserOnboardingComplete = async (onboardingData?: {
|
||||
role?: OnboardingRole
|
||||
plans?: OnboardingPlan[]
|
||||
source?: OnboardingSource
|
||||
}) => {
|
||||
const user = activeUser.value
|
||||
if (!user) throw new OnboardingError('Attempting to onboard unidentified user')
|
||||
|
||||
await apollo
|
||||
.mutate({
|
||||
mutation: finishOnboardingMutation,
|
||||
variables: {
|
||||
input: onboardingData
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
if (!data?.activeUserMutations.finishOnboarding) return
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { graphql } from '~~/lib/common/generated/gql'
|
||||
|
||||
export const finishOnboardingMutation = graphql(`
|
||||
mutation FinishOnboarding {
|
||||
mutation FinishOnboarding($input: OnboardingCompletionInput) {
|
||||
activeUserMutations {
|
||||
finishOnboarding
|
||||
finishOnboarding(input: $input)
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
@@ -1,32 +1,4 @@
|
||||
export enum OnboardingRole {
|
||||
ComputationalDesign = 'computational-design',
|
||||
BIM = 'bim',
|
||||
ArchitecturePlanning = 'architecture-planning',
|
||||
EngineeringAEC = 'engineering-aec',
|
||||
EngineeringSoftware = 'engineering-software',
|
||||
Education = 'education',
|
||||
Management = 'management',
|
||||
Other = 'other'
|
||||
}
|
||||
|
||||
export enum OnboardingPlan {
|
||||
Exploring = 'exploring',
|
||||
DataExchange = 'data-exchange',
|
||||
Analytics = 'analytics',
|
||||
Collaboration = 'collaboration',
|
||||
DataWarehouse = 'data-warehouse',
|
||||
Development = 'development',
|
||||
Other = 'other'
|
||||
}
|
||||
|
||||
export enum OnboardingSource {
|
||||
SocialMedia = 'social-media',
|
||||
Search = 'internet-search',
|
||||
Referral = 'friend-or-colleague',
|
||||
Event = 'event-conference',
|
||||
Education = 'university-course',
|
||||
Other = 'other'
|
||||
}
|
||||
import { OnboardingRole, OnboardingPlan, OnboardingSource } from '@speckle/shared'
|
||||
|
||||
export const RoleTitleMap: Record<OnboardingRole, string> = {
|
||||
[OnboardingRole.ComputationalDesign]: 'Computational Design',
|
||||
@@ -58,9 +30,3 @@ export const SourceTitleMap: Record<OnboardingSource, string> = {
|
||||
[OnboardingSource.Education]: 'University or course',
|
||||
[OnboardingSource.Other]: 'Other'
|
||||
}
|
||||
|
||||
export type OnboardingState = {
|
||||
role?: OnboardingRole
|
||||
plans?: OnboardingPlan[]
|
||||
source?: OnboardingSource
|
||||
}
|
||||
|
||||
@@ -122,10 +122,10 @@ type Documents = {
|
||||
"\n fragment SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n }\n projectRoles {\n role\n project {\n id\n name\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersGuestsTable_WorkspaceCollaboratorFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesMembersGuestsTable_Workspace on Workspace {\n id\n ...SettingsWorkspacesMembersTableHeader_Workspace\n ...SettingsSharedDeleteUserDialog_Workspace\n ...SettingsWorkspacesMembersChangeRoleDialog_Workspace\n team {\n items {\n id\n ...SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator\n }\n }\n }\n": typeof 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": typeof types.SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaboratorFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesMembersInvitesTable_Workspace on Workspace {\n id\n ...SettingsWorkspacesMembersTableHeader_Workspace\n invitedTeam(filter: $invitesFilter) {\n ...SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator\n }\n }\n": typeof types.SettingsWorkspacesMembersInvitesTable_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesMembersRequestsTable_Workspace on Workspace {\n ...SettingsWorkspacesMembersTableHeader_Workspace\n id\n adminWorkspacesJoinRequests(filter: $joinRequestsFilter) {\n totalCount\n items {\n ...WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest\n id\n createdAt\n status\n user {\n id\n avatar\n name\n }\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersRequestsTable_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n workspaceDomainPolicyCompliant\n }\n }\n": typeof types.SettingsWorkspacesMembersMembersTable_WorkspaceCollaboratorFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesMembersMembersTable_Workspace on Workspace {\n id\n name\n ...SettingsSharedDeleteUserDialog_Workspace\n ...SettingsWorkspacesMembersTableHeader_Workspace\n ...SettingsWorkspacesMembersChangeRoleDialog_Workspace\n team {\n items {\n id\n ...SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersMembersTable_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesMembersInvitesTable_Workspace on Workspace {\n id\n ...SettingsWorkspacesMembersTableHeader_Workspace\n invitedTeam {\n ...SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator\n }\n }\n": typeof types.SettingsWorkspacesMembersInvitesTable_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesMembersRequestsTable_Workspace on Workspace {\n ...SettingsWorkspacesMembersTableHeader_Workspace\n id\n adminWorkspacesJoinRequests {\n totalCount\n items {\n ...WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest\n id\n createdAt\n status\n user {\n id\n avatar\n name\n }\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersRequestsTable_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n workspaceDomainPolicyCompliant\n }\n }\n": typeof types.SettingsWorkspacesMembersTable_WorkspaceCollaboratorFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesMembersTable_Workspace on Workspace {\n id\n name\n ...SettingsSharedDeleteUserDialog_Workspace\n ...SettingsWorkspacesMembersTableHeader_Workspace\n ...SettingsWorkspacesMembersChangeRoleDialog_Workspace\n team {\n items {\n id\n ...SettingsWorkspacesMembersTable_WorkspaceCollaborator\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersTable_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesMembersTableHeader_Workspace on Workspace {\n id\n role\n ...InviteDialogWorkspace_Workspace\n }\n": typeof types.SettingsWorkspacesMembersTableHeader_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesRegionsSelect_ServerRegionItem on ServerRegionItem {\n id\n key\n name\n description\n }\n": typeof types.SettingsWorkspacesRegionsSelect_ServerRegionItemFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain on WorkspaceDomain {\n id\n domain\n }\n": typeof types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomainFragmentDoc,
|
||||
@@ -151,7 +151,7 @@ type Documents = {
|
||||
"\n fragment WorkspaceWizardStepRegion_ServerInfo on ServerInfo {\n multiRegion {\n regions {\n id\n ...SettingsWorkspacesRegionsSelect_ServerRegionItem\n }\n }\n }\n": typeof types.WorkspaceWizardStepRegion_ServerInfoFragmentDoc,
|
||||
"\n query ActiveUserMainMetadata {\n activeUser {\n id\n email\n emails {\n id\n verified\n }\n company\n bio\n name\n role\n avatar\n isOnboardingFinished\n createdAt\n verified\n notificationPreferences\n versions(limit: 0) {\n totalCount\n }\n }\n }\n": typeof types.ActiveUserMainMetadataDocument,
|
||||
"\n mutation CreateOnboardingProject {\n projectMutations {\n createForOnboarding {\n ...ProjectPageProject\n ...ProjectDashboardItem\n }\n }\n }\n ": typeof types.CreateOnboardingProjectDocument,
|
||||
"\n mutation FinishOnboarding {\n activeUserMutations {\n finishOnboarding\n }\n }\n": typeof types.FinishOnboardingDocument,
|
||||
"\n mutation FinishOnboarding($input: OnboardingCompletionInput) {\n activeUserMutations {\n finishOnboarding(input: $input)\n }\n }\n": typeof types.FinishOnboardingDocument,
|
||||
"\n mutation RequestVerificationByEmail($email: String!) {\n requestVerificationByEmail(email: $email)\n }\n": typeof types.RequestVerificationByEmailDocument,
|
||||
"\n query AuthLoginPanel {\n serverInfo {\n authStrategies {\n id\n }\n ...AuthStategiesServerInfoFragment\n }\n }\n": typeof types.AuthLoginPanelDocument,
|
||||
"\n query AuthRegisterPanel($token: String) {\n serverInfo {\n inviteOnly\n authStrategies {\n id\n }\n ...AuthStategiesServerInfoFragment\n ...ServerTermsOfServicePrivacyPolicyFragment\n }\n serverInviteByToken(token: $token) {\n id\n email\n }\n }\n": typeof types.AuthRegisterPanelDocument,
|
||||
@@ -307,9 +307,12 @@ type Documents = {
|
||||
"\n query SettingsWorkspaceBilling($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n ...SettingsWorkspacesBilling_Workspace\n }\n }\n": typeof types.SettingsWorkspaceBillingDocument,
|
||||
"\n query SettingsWorkspaceBillingCustomerPortal($workspaceId: String!) {\n workspace(id: $workspaceId) {\n customerPortalUrl\n }\n }\n": typeof types.SettingsWorkspaceBillingCustomerPortalDocument,
|
||||
"\n query SettingsWorkspaceRegions($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n ...SettingsWorkspacesRegions_Workspace\n }\n serverInfo {\n ...SettingsWorkspacesRegions_ServerInfo\n }\n }\n": typeof types.SettingsWorkspaceRegionsDocument,
|
||||
"\n query SettingsWorkspacesMembers(\n $slug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n $joinRequestsFilter: AdminWorkspaceJoinRequestFilter\n ) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembers_Workspace\n ...SettingsWorkspacesMembersMembersTable_Workspace\n ...SettingsWorkspacesMembersGuestsTable_Workspace\n ...SettingsWorkspacesMembersInvitesTable_Workspace\n ...SettingsWorkspacesMembersRequestsTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersDocument,
|
||||
"\n query SettingsWorkspacesMembersSearch($slug: String!, $filter: WorkspaceTeamFilter) {\n workspaceBySlug(slug: $slug) {\n id\n team(filter: $filter) {\n items {\n id\n ...SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator\n }\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersSearchDocument,
|
||||
"\n query SettingsWorkspacesJoinRequestsSearch(\n $slug: String!\n $joinRequestsFilter: AdminWorkspaceJoinRequestFilter\n ) {\n workspaceBySlug(slug: $slug) {\n id\n ...SettingsWorkspacesMembersRequestsTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesJoinRequestsSearchDocument,
|
||||
"\n query SettingsWorkspacesMembers($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembers_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersDocument,
|
||||
"\n query SettingsWorkspacesMembersTable($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersTableDocument,
|
||||
"\n query SettingsWorkspacesMembersGuests($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersGuestsTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersGuestsDocument,
|
||||
"\n query SettingsWorkspacesMembersInvites($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersInvitesTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersInvitesDocument,
|
||||
"\n query SettingsWorkspacesMembersRequests($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersRequestsTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersRequestsDocument,
|
||||
"\n query SettingsWorkspacesMembersSearch($slug: String!, $filter: WorkspaceTeamFilter) {\n workspaceBySlug(slug: $slug) {\n id\n team(filter: $filter) {\n items {\n id\n ...SettingsWorkspacesMembersTable_WorkspaceCollaborator\n }\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersSearchDocument,
|
||||
"\n query SettingsWorkspacesInvitesSearch(\n $slug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersInvitesTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesInvitesSearchDocument,
|
||||
"\n query SettingsWorkspacesProjects(\n $slug: String!\n $limit: Int!\n $cursor: String\n $filter: WorkspaceProjectsFilter\n ) {\n workspaceBySlug(slug: $slug) {\n id\n slug\n readOnly\n projects(limit: $limit, cursor: $cursor, filter: $filter) {\n cursor\n ...SettingsWorkspacesProjects_ProjectCollection\n }\n }\n }\n": typeof types.SettingsWorkspacesProjectsDocument,
|
||||
"\n query SettingsWorkspaceSecurity($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesSecurity_Workspace\n }\n activeUser {\n ...SettingsWorkspacesSecurity_User\n }\n }\n": typeof types.SettingsWorkspaceSecurityDocument,
|
||||
@@ -391,7 +394,7 @@ type Documents = {
|
||||
"\n query SettingsServerRegions {\n serverInfo {\n multiRegion {\n regions {\n id\n ...SettingsServerRegionsTable_ServerRegionItem\n }\n availableKeys\n }\n }\n }\n": typeof types.SettingsServerRegionsDocument,
|
||||
"\n fragment SettingsWorkspacesBilling_Workspace on Workspace {\n ...BillingAlert_Workspace\n id\n role\n plan {\n name\n status\n createdAt\n paymentMethod\n }\n subscription {\n billingInterval\n currentBillingCycleEnd\n seats {\n guest\n plan\n }\n }\n team {\n items {\n id\n role\n }\n }\n }\n": typeof types.SettingsWorkspacesBilling_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesGeneral_Workspace on Workspace {\n ...SettingsWorkspacesGeneralEditAvatar_Workspace\n ...SettingsWorkspaceGeneralDeleteDialog_Workspace\n ...SettingsWorkspacesGeneralEditSlugDialog_Workspace\n id\n name\n slug\n description\n logo\n role\n defaultProjectRole\n plan {\n status\n name\n }\n }\n": typeof types.SettingsWorkspacesGeneral_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesMembers_Workspace on Workspace {\n id\n role\n team {\n items {\n id\n }\n }\n invitedTeam(filter: $invitesFilter) {\n user {\n id\n }\n }\n adminWorkspacesJoinRequests(filter: $joinRequestsFilter) {\n totalCount\n }\n }\n": typeof types.SettingsWorkspacesMembers_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesMembers_Workspace on Workspace {\n id\n role\n team {\n items {\n id\n role\n }\n }\n invitedTeam {\n user {\n id\n }\n }\n adminWorkspacesJoinRequests {\n items {\n id\n status\n }\n totalCount\n }\n }\n": typeof types.SettingsWorkspacesMembers_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesProjects_ProjectCollection on ProjectCollection {\n totalCount\n items {\n ...SettingsSharedProjects_Project\n }\n }\n": typeof types.SettingsWorkspacesProjects_ProjectCollectionFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesRegions_Workspace on Workspace {\n id\n role\n defaultRegion {\n id\n ...SettingsWorkspacesRegionsSelect_ServerRegionItem\n }\n hasAccessToMultiRegion: hasAccessToFeature(\n featureName: workspaceDataRegionSpecificity\n )\n hasProjects: projects(limit: 0) {\n totalCount\n }\n }\n": typeof types.SettingsWorkspacesRegions_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesRegions_ServerInfo on ServerInfo {\n multiRegion {\n regions {\n id\n ...SettingsWorkspacesRegionsSelect_ServerRegionItem\n }\n }\n }\n": typeof types.SettingsWorkspacesRegions_ServerInfoFragmentDoc,
|
||||
@@ -535,7 +538,7 @@ const documents: Documents = {
|
||||
"\n fragment WorkspaceWizardStepRegion_ServerInfo on ServerInfo {\n multiRegion {\n regions {\n id\n ...SettingsWorkspacesRegionsSelect_ServerRegionItem\n }\n }\n }\n": types.WorkspaceWizardStepRegion_ServerInfoFragmentDoc,
|
||||
"\n query ActiveUserMainMetadata {\n activeUser {\n id\n email\n emails {\n id\n verified\n }\n company\n bio\n name\n role\n avatar\n isOnboardingFinished\n createdAt\n verified\n notificationPreferences\n versions(limit: 0) {\n totalCount\n }\n }\n }\n": types.ActiveUserMainMetadataDocument,
|
||||
"\n mutation CreateOnboardingProject {\n projectMutations {\n createForOnboarding {\n ...ProjectPageProject\n ...ProjectDashboardItem\n }\n }\n }\n ": types.CreateOnboardingProjectDocument,
|
||||
"\n mutation FinishOnboarding {\n activeUserMutations {\n finishOnboarding\n }\n }\n": types.FinishOnboardingDocument,
|
||||
"\n mutation FinishOnboarding($input: OnboardingCompletionInput) {\n activeUserMutations {\n finishOnboarding(input: $input)\n }\n }\n": types.FinishOnboardingDocument,
|
||||
"\n mutation RequestVerificationByEmail($email: String!) {\n requestVerificationByEmail(email: $email)\n }\n": types.RequestVerificationByEmailDocument,
|
||||
"\n query AuthLoginPanel {\n serverInfo {\n authStrategies {\n id\n }\n ...AuthStategiesServerInfoFragment\n }\n }\n": types.AuthLoginPanelDocument,
|
||||
"\n query AuthRegisterPanel($token: String) {\n serverInfo {\n inviteOnly\n authStrategies {\n id\n }\n ...AuthStategiesServerInfoFragment\n ...ServerTermsOfServicePrivacyPolicyFragment\n }\n serverInviteByToken(token: $token) {\n id\n email\n }\n }\n": types.AuthRegisterPanelDocument,
|
||||
@@ -1350,7 +1353,7 @@ export function graphql(source: "\n mutation CreateOnboardingProject {\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 mutation FinishOnboarding {\n activeUserMutations {\n finishOnboarding\n }\n }\n"): (typeof documents)["\n mutation FinishOnboarding {\n activeUserMutations {\n finishOnboarding\n }\n }\n"];
|
||||
export function graphql(source: "\n mutation FinishOnboarding($input: OnboardingCompletionInput) {\n activeUserMutations {\n finishOnboarding(input: $input)\n }\n }\n"): (typeof documents)["\n mutation FinishOnboarding($input: OnboardingCompletionInput) {\n activeUserMutations {\n finishOnboarding(input: $input)\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
@@ -173,11 +173,17 @@ type UserSearchResultCollection {
|
||||
items: [LimitedUser!]!
|
||||
}
|
||||
|
||||
input OnboardingCompletionInput {
|
||||
role: String
|
||||
plans: [String!]
|
||||
source: String
|
||||
}
|
||||
|
||||
type ActiveUserMutations {
|
||||
"""
|
||||
Mark onboarding as complete
|
||||
"""
|
||||
finishOnboarding: Boolean!
|
||||
finishOnboarding(input: OnboardingCompletionInput): Boolean!
|
||||
|
||||
"""
|
||||
Edit a user's profile
|
||||
|
||||
@@ -4,6 +4,7 @@ import { md5 } from '@/modules/shared/helpers/cryptoHelper'
|
||||
import { getMailchimpConfig } from '@/modules/shared/helpers/envHelper'
|
||||
import { UserRecord } from '@/modules/core/helpers/types'
|
||||
import { MisconfiguredEnvironmentError } from '@/modules/shared/errors'
|
||||
import { OnboardingCompletionInput } from '@/modules/core/graph/generated/graphql'
|
||||
|
||||
let mailchimpInitialized = false
|
||||
|
||||
@@ -63,4 +64,55 @@ async function triggerMailchimpCustomerJourney(
|
||||
})
|
||||
}
|
||||
|
||||
export { addToMailchimpAudience, triggerMailchimpCustomerJourney }
|
||||
async function updateMailchimpMemberTags(
|
||||
user: UserRecord,
|
||||
listId: string,
|
||||
onboardingData: OnboardingCompletionInput
|
||||
) {
|
||||
initializeMailchimp()
|
||||
const subscriberHash = md5(user.email.toLowerCase())
|
||||
|
||||
// Check if user is already in audience (meaning they consented to marketing emails)
|
||||
try {
|
||||
await mailchimp.lists.getListMember(listId, subscriberHash)
|
||||
} catch {
|
||||
throw new Error(
|
||||
`User ${user.email} not found in Mailchimp audience. They should have been added during registration.`
|
||||
)
|
||||
}
|
||||
|
||||
const tags: { name: string; status: 'active' | 'inactive' }[] = []
|
||||
|
||||
if (onboardingData.role) {
|
||||
tags.push({
|
||||
name: `Role: ${onboardingData.role}`,
|
||||
status: 'active'
|
||||
})
|
||||
}
|
||||
|
||||
if (onboardingData.plans?.length) {
|
||||
onboardingData.plans.forEach((plan) => {
|
||||
tags.push({
|
||||
name: `Use case: ${plan}`,
|
||||
status: 'active'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (onboardingData.source) {
|
||||
tags.push({
|
||||
name: `Source: ${onboardingData.source}`,
|
||||
status: 'active'
|
||||
})
|
||||
}
|
||||
|
||||
await mailchimp.lists.updateListMemberTags(listId, subscriberHash, {
|
||||
tags
|
||||
})
|
||||
}
|
||||
|
||||
export {
|
||||
addToMailchimpAudience,
|
||||
triggerMailchimpCustomerJourney,
|
||||
updateMailchimpMemberTags
|
||||
}
|
||||
|
||||
@@ -49,6 +49,11 @@ export type ActiveUserMutations = {
|
||||
};
|
||||
|
||||
|
||||
export type ActiveUserMutationsFinishOnboardingArgs = {
|
||||
input?: InputMaybe<OnboardingCompletionInput>;
|
||||
};
|
||||
|
||||
|
||||
export type ActiveUserMutationsUpdateArgs = {
|
||||
user: UserUpdateInput;
|
||||
};
|
||||
@@ -1875,6 +1880,12 @@ export type ObjectCreateInput = {
|
||||
streamId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type OnboardingCompletionInput = {
|
||||
plans?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
role?: InputMaybe<Scalars['String']['input']>;
|
||||
source?: InputMaybe<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
export const PaidWorkspacePlans = {
|
||||
Business: 'business',
|
||||
Plus: 'plus',
|
||||
@@ -5031,6 +5042,7 @@ export type ResolversTypes = {
|
||||
Object: ResolverTypeWrapper<ObjectGraphQLReturn>;
|
||||
ObjectCollection: ResolverTypeWrapper<Omit<ObjectCollection, 'objects'> & { objects: Array<ResolversTypes['Object']> }>;
|
||||
ObjectCreateInput: ObjectCreateInput;
|
||||
OnboardingCompletionInput: OnboardingCompletionInput;
|
||||
PaidWorkspacePlans: PaidWorkspacePlans;
|
||||
PasswordStrengthCheckFeedback: ResolverTypeWrapper<PasswordStrengthCheckFeedback>;
|
||||
PasswordStrengthCheckResults: ResolverTypeWrapper<PasswordStrengthCheckResults>;
|
||||
@@ -5338,6 +5350,7 @@ export type ResolversParentTypes = {
|
||||
Object: ObjectGraphQLReturn;
|
||||
ObjectCollection: Omit<ObjectCollection, 'objects'> & { objects: Array<ResolversParentTypes['Object']> };
|
||||
ObjectCreateInput: ObjectCreateInput;
|
||||
OnboardingCompletionInput: OnboardingCompletionInput;
|
||||
PasswordStrengthCheckFeedback: PasswordStrengthCheckFeedback;
|
||||
PasswordStrengthCheckResults: PasswordStrengthCheckResults;
|
||||
PendingStreamCollaborator: PendingStreamCollaboratorGraphQLReturn;
|
||||
@@ -5531,7 +5544,7 @@ export type IsOwnerDirectiveResolver<Result, Parent, ContextType = GraphQLContex
|
||||
|
||||
export type ActiveUserMutationsResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['ActiveUserMutations'] = ResolversParentTypes['ActiveUserMutations']> = {
|
||||
emailMutations?: Resolver<ResolversTypes['UserEmailMutations'], ParentType, ContextType>;
|
||||
finishOnboarding?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
||||
finishOnboarding?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, Partial<ActiveUserMutationsFinishOnboardingArgs>>;
|
||||
update?: Resolver<ResolversTypes['User'], ParentType, ContextType, RequireFields<ActiveUserMutationsUpdateArgs, 'user'>>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
@@ -40,6 +40,11 @@ import { getAdminUsersListCollectionFactory } from '@/modules/core/services/user
|
||||
import { Resolvers } from '@/modules/core/graph/generated/graphql'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import {
|
||||
getMailchimpStatus,
|
||||
getMailchimpOnboardingIds
|
||||
} from '@/modules/shared/helpers/envHelper'
|
||||
import { updateMailchimpMemberTags } from '@/modules/auth/services/mailchimp'
|
||||
|
||||
const getUser = legacyGetUserFactory({ db })
|
||||
const getUserByEmail = legacyGetUserByEmailFactory({ db })
|
||||
@@ -248,8 +253,30 @@ export = {
|
||||
activeUserMutations: () => ({})
|
||||
},
|
||||
ActiveUserMutations: {
|
||||
async finishOnboarding(_parent, _args, ctx) {
|
||||
return await markOnboardingComplete(ctx.userId || '')
|
||||
async finishOnboarding(_parent, args, ctx) {
|
||||
const userId = ctx.userId
|
||||
if (!userId) return false
|
||||
|
||||
const success = await markOnboardingComplete(userId)
|
||||
|
||||
// If onboarding was marked complete successfully and we have onboarding data
|
||||
if (success && args.input && getMailchimpStatus()) {
|
||||
try {
|
||||
const user = await getUser(userId)
|
||||
const { listId } = getMailchimpOnboardingIds()
|
||||
|
||||
await updateMailchimpMemberTags(user, listId, {
|
||||
role: args.input?.role || undefined,
|
||||
plans: args.input?.plans || undefined,
|
||||
source: args.input?.source || undefined
|
||||
})
|
||||
} catch (error) {
|
||||
// Log but don't fail the request
|
||||
ctx.log.warn({ err: error }, 'Failed to update Mailchimp tags')
|
||||
}
|
||||
}
|
||||
|
||||
return success
|
||||
},
|
||||
async update(_parent, args, context) {
|
||||
const newUser = await updateUserAndNotify(context.userId!, args.user)
|
||||
|
||||
@@ -30,6 +30,11 @@ export type ActiveUserMutations = {
|
||||
};
|
||||
|
||||
|
||||
export type ActiveUserMutationsFinishOnboardingArgs = {
|
||||
input?: InputMaybe<OnboardingCompletionInput>;
|
||||
};
|
||||
|
||||
|
||||
export type ActiveUserMutationsUpdateArgs = {
|
||||
user: UserUpdateInput;
|
||||
};
|
||||
@@ -1856,6 +1861,12 @@ export type ObjectCreateInput = {
|
||||
streamId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type OnboardingCompletionInput = {
|
||||
plans?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
role?: InputMaybe<Scalars['String']['input']>;
|
||||
source?: InputMaybe<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
export const PaidWorkspacePlans = {
|
||||
Business: 'business',
|
||||
Plus: 'plus',
|
||||
|
||||
@@ -31,6 +31,11 @@ export type ActiveUserMutations = {
|
||||
};
|
||||
|
||||
|
||||
export type ActiveUserMutationsFinishOnboardingArgs = {
|
||||
input?: InputMaybe<OnboardingCompletionInput>;
|
||||
};
|
||||
|
||||
|
||||
export type ActiveUserMutationsUpdateArgs = {
|
||||
user: UserUpdateInput;
|
||||
};
|
||||
@@ -1857,6 +1862,12 @@ export type ObjectCreateInput = {
|
||||
streamId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type OnboardingCompletionInput = {
|
||||
plans?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
role?: InputMaybe<Scalars['String']['input']>;
|
||||
source?: InputMaybe<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
export const PaidWorkspacePlans = {
|
||||
Business: 'business',
|
||||
Plus: 'plus',
|
||||
|
||||
@@ -5,3 +5,4 @@ export * as SpeckleViewer from './viewer/index.js'
|
||||
export * as Automate from './automate/index.js'
|
||||
export * from './core/index.js'
|
||||
export * from './workspaces/index.js'
|
||||
export * from './onboarding/index.js'
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
export enum OnboardingRole {
|
||||
ComputationalDesign = 'computational-design',
|
||||
BIM = 'bim',
|
||||
ArchitecturePlanning = 'architecture-planning',
|
||||
EngineeringAEC = 'engineering-aec',
|
||||
EngineeringSoftware = 'engineering-software',
|
||||
Education = 'education',
|
||||
Management = 'management',
|
||||
Other = 'other'
|
||||
}
|
||||
|
||||
export enum OnboardingPlan {
|
||||
Exploring = 'exploring',
|
||||
DataExchange = 'data-exchange',
|
||||
Analytics = 'analytics',
|
||||
Collaboration = 'collaboration',
|
||||
DataWarehouse = 'data-warehouse',
|
||||
Development = 'development',
|
||||
Other = 'other'
|
||||
}
|
||||
|
||||
export enum OnboardingSource {
|
||||
SocialMedia = 'social-media',
|
||||
Search = 'internet-search',
|
||||
Referral = 'friend-or-colleague',
|
||||
Event = 'event-conference',
|
||||
Education = 'university-course',
|
||||
Other = 'other'
|
||||
}
|
||||
|
||||
export type OnboardingState = {
|
||||
role?: OnboardingRole
|
||||
plans?: OnboardingPlan[]
|
||||
source?: OnboardingSource
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './helpers/index.js'
|
||||
Reference in New Issue
Block a user