diff --git a/packages/dui3/lib/common/generated/gql/graphql.ts b/packages/dui3/lib/common/generated/gql/graphql.ts index 6fe2a46e5..41045642b 100644 --- a/packages/dui3/lib/common/generated/gql/graphql.ts +++ b/packages/dui3/lib/common/generated/gql/graphql.ts @@ -27,11 +27,23 @@ export type ActiveUserMutations = { emailMutations: UserEmailMutations; /** Mark onboarding as complete */ finishOnboarding: Scalars['Boolean']['output']; + setActiveWorkspace: Scalars['Boolean']['output']; /** Edit a user's profile */ update: User; }; +export type ActiveUserMutationsFinishOnboardingArgs = { + input?: InputMaybe; +}; + + +export type ActiveUserMutationsSetActiveWorkspaceArgs = { + isProjectsActive?: InputMaybe; + slug?: InputMaybe; +}; + + export type ActiveUserMutationsUpdateArgs = { user: UserUpdateInput; }; @@ -243,6 +255,11 @@ export type AutomateAuthCodePayloadTest = { workspaceId?: InputMaybe; }; +/** Additional resources to validate user access to. */ +export type AutomateAuthCodeResources = { + workspaceId?: InputMaybe; +}; + export type AutomateFunction = { __typename?: 'AutomateFunction'; /** Only returned if user is a part of this speckle server */ @@ -938,6 +955,18 @@ export type DiscoverableStreamsSortingInput = { type: DiscoverableStreamsSortType; }; +export type DiscoverableWorkspaceCollaborator = { + __typename?: 'DiscoverableWorkspaceCollaborator'; + avatar?: Maybe; +}; + +export type DiscoverableWorkspaceCollaboratorCollection = { + __typename?: 'DiscoverableWorkspaceCollaboratorCollection'; + cursor?: Maybe; + items: Array; + totalCount: Scalars['Int']['output']; +}; + export type EditCommentInput = { commentId: Scalars['String']['input']; content: CommentContentInput; @@ -1151,6 +1180,31 @@ export type LimitedWorkspace = { name: Scalars['String']['output']; /** Unique workspace short id. Used for navigation. */ slug: Scalars['String']['output']; + /** Workspace members visible to people with verified email domain */ + team?: Maybe; +}; + + +/** Workspace metadata visible to non-workspace members. */ +export type LimitedWorkspaceTeamArgs = { + cursor?: InputMaybe; + limit?: Scalars['Int']['input']; +}; + +export type LimitedWorkspaceJoinRequest = { + __typename?: 'LimitedWorkspaceJoinRequest'; + createdAt: Scalars['DateTime']['output']; + id: Scalars['String']['output']; + status: WorkspaceJoinRequestStatus; + user: LimitedUser; + workspace: LimitedWorkspace; +}; + +export type LimitedWorkspaceJoinRequestCollection = { + __typename?: 'LimitedWorkspaceJoinRequestCollection'; + cursor?: Maybe; + items: Array; + totalCount: Scalars['Int']['output']; }; export type MarkCommentViewedInput = { @@ -1811,10 +1865,18 @@ export type ObjectCreateInput = { streamId: Scalars['String']['input']; }; +export type OnboardingCompletionInput = { + plans?: InputMaybe>; + role?: InputMaybe; + source?: InputMaybe; +}; + export enum PaidWorkspacePlans { Business = 'business', Plus = 'plus', - Starter = 'starter' + Pro = 'pro', + Starter = 'starter', + Team = 'team' } export type PasswordStrengthCheckFeedback = { @@ -1889,6 +1951,13 @@ export type PendingWorkspaceCollaboratorsFilter = { search?: InputMaybe; }; +export type Price = { + __typename?: 'Price'; + amount: Scalars['Float']['output']; + currency: Scalars['String']['output']; + currencySymbol: Scalars['String']['output']; +}; + export type Project = { __typename?: 'Project'; allowPublicComments: Scalars['Boolean']['output']; @@ -2621,7 +2690,6 @@ export type Query = { * Either token or workspaceId must be specified, or both */ workspaceInvite?: Maybe; - workspacePricingPlans: Scalars['JSONObject']['output']; /** Find workspaces a given user email can use SSO to sign with */ workspaceSsoByEmail: Array; }; @@ -2662,6 +2730,7 @@ export type QueryAutomateFunctionsArgs = { export type QueryAutomateValidateAuthCodeArgs = { payload: AutomateAuthCodePayloadTest; + resources?: InputMaybe; }; @@ -2866,6 +2935,8 @@ export type ServerAutomateInfo = { export type ServerConfiguration = { __typename?: 'ServerConfiguration'; blobSizeLimitBytes: Scalars['Int']['output']; + /** Whether the email feature is enabled on this server */ + isEmailEnabled: Scalars['Boolean']['output']; objectMultipartUploadSizeLimitBytes: Scalars['Int']['output']; objectSizeLimitBytes: Scalars['Int']['output']; }; @@ -3016,6 +3087,8 @@ export type ServerStats = { export type ServerWorkspacesInfo = { __typename?: 'ServerWorkspacesInfo'; + /** Up-to-date prices for paid & non-invoiced Workspace plans */ + planPrices: Array; /** * This is a backend control variable for the workspaces feature set. * Since workspaces need a backend logic to be enabled, this is not enough as a feature flag. @@ -3624,6 +3697,8 @@ export type UpgradePlanInput = { */ export type User = { __typename?: 'User'; + /** The last-visited workspace for the given user */ + activeWorkspace?: Maybe; /** * All the recent activity from this user in chronological order * @deprecated Part of the old API surface and will be removed in the future. @@ -3671,6 +3746,8 @@ export type User = { id: Scalars['ID']['output']; /** Whether post-sign up onboarding has been finished or skipped entirely */ isOnboardingFinished?: Maybe; + /** Returns `true` if last visited project was "legacy" "personal project" outside of a workspace */ + isProjectsActive?: Maybe; name: Scalars['String']['output']; notificationPreferences: Scalars['JSONObject']['output']; profiles?: Maybe; @@ -3707,6 +3784,7 @@ export type User = { versions: CountOnlyCollection; /** Get all invitations to workspaces that the active user has */ workspaceInvites: Array; + workspaceJoinRequests?: Maybe; /** Get the workspaces for the user */ workspaces: WorkspaceCollection; }; @@ -3808,6 +3886,17 @@ export type UserVersionsArgs = { }; +/** + * Full user type, should only be used in the context of admin operations or + * when a user is reading/writing info about himself + */ +export type UserWorkspaceJoinRequestsArgs = { + cursor?: InputMaybe; + filter?: InputMaybe; + limit?: Scalars['Int']['input']; +}; + + /** * Full user type, should only be used in the context of admin operations or * when a user is reading/writing info about himself @@ -4420,6 +4509,10 @@ export type WorkspaceJoinRequestCollection = { totalCount: Scalars['Int']['output']; }; +export type WorkspaceJoinRequestFilter = { + status?: InputMaybe; +}; + export type WorkspaceJoinRequestMutations = { __typename?: 'WorkspaceJoinRequestMutations'; approve: Scalars['Boolean']['output']; @@ -4544,6 +4637,13 @@ export type WorkspacePlan = { status: WorkspacePlanStatuses; }; +export type WorkspacePlanPrice = { + __typename?: 'WorkspacePlanPrice'; + id: Scalars['String']['output']; + monthly?: Maybe; + yearly?: Maybe; +}; + export enum WorkspacePlanStatuses { CancelationScheduled = 'cancelationScheduled', Canceled = 'canceled', @@ -4560,8 +4660,10 @@ export enum WorkspacePlans { Free = 'free', Plus = 'plus', PlusInvoiced = 'plusInvoiced', + Pro = 'pro', Starter = 'starter', StarterInvoiced = 'starterInvoiced', + Team = 'team', Unlimited = 'unlimited' } @@ -4589,10 +4691,12 @@ export type WorkspaceProjectMutations = { __typename?: 'WorkspaceProjectMutations'; create: Project; /** - * Update project region and move all regional data to new db. - * TODO: Currently performs all operations synchronously in request, should probably be scheduled. + * Schedule a job that will: + * - Move all regional data to target region + * - Update project region key + * - TODO: Eventually delete data in previous region */ - moveToRegion: Project; + moveToRegion: Scalars['String']['output']; moveToWorkspace: Project; updateRole: Project; }; diff --git a/packages/frontend-2/components/common/Card.vue b/packages/frontend-2/components/common/Card.vue index 0980c9475..49473c375 100644 --- a/packages/frontend-2/components/common/Card.vue +++ b/packages/frontend-2/components/common/Card.vue @@ -19,7 +19,7 @@

Quickstart

-
+
-

Recently updated projects

@@ -57,6 +53,19 @@ />
+
+

Highlighted workflows

+
+ +
+
@@ -86,24 +95,20 @@ import { } from '~~/lib/dashboard/graphql/queries' import type { QuickStartItem } from '~~/lib/dashboard/helpers/types' import { useQuery } from '@vue/apollo-composable' -// import { useMixpanel } from '~~/lib/core/composables/mp' +import { useMixpanel } from '~~/lib/core/composables/mp' import { - docsPageUrl, - forumPageUrl, homeRoute, - connectorsRoute, projectsRoute, - tutorialsRoute + tutorialsRoute, + connectorsRoute, + forumPageUrl } from '~~/lib/common/helpers/route' -// import type { ManagerExtension } from '~~/lib/common/utils/downloadManager' -// import { downloadManager } from '~~/lib/common/utils/downloadManager' -// import { ToastNotificationType, useGlobalToast } from '~~/lib/common/composables/toast' import type { LayoutDialogButton } from '@speckle/ui-components' import type { PromoBanner } from '~/lib/promo-banners/types' import { tutorialItems } from '~/lib/dashboard/helpers/tutorials' import { useUserProjectsUpdatedTracking } from '~~/lib/user/composables/projectUpdates' -// const mixpanel = useMixpanel() +const mixpanel = useMixpanel() const isWorkspacesEnabled = useIsWorkspacesEnabled() const { result: projectsResult } = useQuery(dashboardProjectsPageQuery) const { result: workspacesResult } = useQuery( @@ -113,14 +118,82 @@ const { result: workspacesResult } = useQuery( enabled: isWorkspacesEnabled.value }) ) -// const { triggerNotification } = useGlobalToast() + const { isGuest } = useActiveUser() const router = useRouter() useUserProjectsUpdatedTracking() const promoBanners = ref() const openNewProject = ref(false) -const quickStartItems = shallowRef([ + +const workflowItems = shallowRef([ + { + title: 'Design Coordination', + description: + "The smoothest design coordination for AEC! Ditch files. Share only what's needed and catch changes instantly.", + buttons: [ + { + text: 'View workflows', + props: { to: 'https://www.speckle.systems/use-cases/design-coordination' }, + onClick: () => { + mixpanel.track('Workflow Card Clicked', { + title: 'Design Coordination' + }) + } + } + ] + }, + { + title: 'Business Intelligence', + description: + 'Get from boring BIM data to insightful dashboards! Swap guesswork for informed decisions.', + buttons: [ + { + text: 'View workflows', + props: { to: 'https://www.speckle.systems/use-cases/business-intelligence' }, + onClick: () => { + mixpanel.track('Workflow Card Clicked', { + title: 'Business Intelligence' + }) + } + } + ] + }, + { + title: 'Online Collaboration', + description: + 'View, share, and brainstorm on 3D models online! Share with anyone—no desktop apps, no licenses, no hassle.', + buttons: [ + { + text: 'View workflows', + props: { to: 'https://www.speckle.systems/use-cases/online-collaboration' }, + onClick: () => { + mixpanel.track('Workflow Card Clicked', { + title: 'Online Collaboration' + }) + } + } + ] + }, + { + title: 'Automation', + description: + 'Goodbye, repetitive tasks! Kick into high gear with pre-built automations for your workflows.', + buttons: [ + { + text: 'View workflows', + props: { to: 'https://www.speckle.systems/use-cases/automate' }, + onClick: () => { + mixpanel.track('Workflow Card Clicked', { + title: 'Automation' + }) + } + } + ] + } +]) + +const useCaseItems = shallowRef([ { title: 'Install Connectors', description: @@ -133,16 +206,6 @@ const quickStartItems = shallowRef([ ], isExternalRoute: false }, - { - title: "Don't know where to start?", - description: "We'll walk you through some of most common usage scenarios.", - buttons: [ - { - text: 'Open documentation', - props: { to: docsPageUrl } - } - ] - }, { title: 'Have a question you need answered?', description: 'Submit your question on the forum and get help from the community.', diff --git a/packages/frontend-2/lib/common/utils/downloadManager.ts b/packages/frontend-2/lib/common/utils/downloadManager.ts deleted file mode 100644 index adae70a73..000000000 --- a/packages/frontend-2/lib/common/utils/downloadManager.ts +++ /dev/null @@ -1,15 +0,0 @@ -export type ManagerExtension = 'exe' | 'dmg' - -/* Util to download the Manager file */ -export const downloadManager = (extension: ManagerExtension) => { - const fileName = `manager.${extension}` - const downloadLink = `https://releases.speckle.dev/manager2/installer/${fileName}` - - const a = document.createElement('a') - a.style.display = 'none' - a.href = downloadLink - a.download = fileName - document.body.appendChild(a) - a.click() - document.body.removeChild(a) -}