diff --git a/packages/frontend-2/components/pricingTable/Plan.vue b/packages/frontend-2/components/pricingTable/Plan.vue
index 8184bca5f..1c7cf3864 100644
--- a/packages/frontend-2/components/pricingTable/Plan.vue
+++ b/packages/frontend-2/components/pricingTable/Plan.vue
@@ -56,29 +56,45 @@
-
+
+
+
+
+
+
-
-
-
- {{ featureMetadata.displayName }}
-
-
+ :is-included="planFeatures.includes(feature)"
+ :display-name="featureMetadata.displayName"
+ :description="featureMetadata.description"
+ />
@@ -96,7 +112,6 @@ import {
WorkspacePlanStatuses,
BillingInterval
} from '~/lib/common/generated/gql/graphql'
-import { XMarkIcon } from '@heroicons/vue/24/outline'
import { useWorkspacePlanPrices } from '~/lib/billing/composables/prices'
import { formatPrice, formatName } from '~/lib/billing/helpers/plan'
import { useBillingActions } from '~/lib/billing/composables/actions'
@@ -122,6 +137,7 @@ const { upgradePlan, redirectToCheckout } = useBillingActions()
const isYearlyIntervalSelected = ref(props.yearlyIntervalSelected)
+const planLimits = computed(() => WorkspacePlanConfigs[props.plan].limits)
const planFeatures = computed(() => WorkspacePlanConfigs[props.plan].features)
const planPrice = computed(() => {
if (props.plan === WorkspacePlans.Team || props.plan === WorkspacePlans.Pro) {
diff --git a/packages/frontend-2/components/pricingTable/PlanFeature.vue b/packages/frontend-2/components/pricingTable/PlanFeature.vue
new file mode 100644
index 000000000..64d9e3749
--- /dev/null
+++ b/packages/frontend-2/components/pricingTable/PlanFeature.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+ {{ displayName }}
+
+
+
+
+
diff --git a/packages/frontend-2/components/settings/workspaces/billing/Page.vue b/packages/frontend-2/components/settings/workspaces/billing/Page.vue
index c677411d1..5466afa7c 100644
--- a/packages/frontend-2/components/settings/workspaces/billing/Page.vue
+++ b/packages/frontend-2/components/settings/workspaces/billing/Page.vue
@@ -4,12 +4,6 @@
title="Billing and plans"
text="Update your payment information or switch plans according to your needs"
/>
-
- You are on an old plan
-
- If you are a server admin use the buttons below to upgrade
-
-
@@ -88,7 +80,7 @@ const route = useRoute()
const slug = computed(() => (route.params.slug as string) || '')
const { isAdmin: isServerAdmin } = useActiveUser()
const isBillingIntegrationEnabled = useIsBillingIntegrationEnabled()
-const { isPurchasablePlan, isNewPlan } = useWorkspacePlan(slug.value)
+const { isPurchasablePlan } = useWorkspacePlan(slug.value)
const { mutate: mutateWorkspacePlan } = useMutation(adminUpdateWorkspacePlanMutation)
const { result: workspaceResult } = useQuery(
settingsWorkspaceBillingQuery,
diff --git a/packages/frontend-2/components/settings/workspaces/billing/UpgradeDialog.vue b/packages/frontend-2/components/settings/workspaces/billing/UpgradeDialog.vue
index c1edf0836..a30d01e23 100644
--- a/packages/frontend-2/components/settings/workspaces/billing/UpgradeDialog.vue
+++ b/packages/frontend-2/components/settings/workspaces/billing/UpgradeDialog.vue
@@ -27,8 +27,7 @@ import {
} from '~/lib/common/generated/gql/graphql'
import { useBillingActions } from '~/lib/billing/composables/actions'
import { startCase } from 'lodash'
-import type { PaidWorkspacePlansOld } from '@speckle/shared'
-import { isPaidPlan } from '~/lib/billing/helpers/types'
+import { type PaidWorkspacePlansOld, isSelfServeAvailablePlan } from '@speckle/shared'
import { useWorkspacePlanPrices } from '~/lib/billing/composables/prices'
import { formatPrice } from '~/lib/billing/helpers/plan'
@@ -43,7 +42,7 @@ const { upgradePlan } = useBillingActions()
const { prices } = useWorkspacePlanPrices()
const seatPrice = computed(() => {
- if (isPaidPlan(props.plan)) {
+ if (isSelfServeAvailablePlan(props.plan)) {
const planPrices = prices.value?.[props.plan]
const price = planPrices?.[props.billingInterval]
diff --git a/packages/frontend-2/lib/billing/helpers/constants.ts b/packages/frontend-2/lib/billing/helpers/constants.ts
deleted file mode 100644
index 2754d67de..000000000
--- a/packages/frontend-2/lib/billing/helpers/constants.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import {
- PaidWorkspacePlansOld,
- Roles,
- WorkspacePlanBillingIntervals,
- type WorkspacePlanPriceStructure
-} from '@speckle/shared'
-
-// TODO: Read these from API, especially for new plans
-export const WorkspaceOldPaidPlanPrices: {
- [plan in PaidWorkspacePlansOld]: WorkspacePlanPriceStructure
-} = {
- [PaidWorkspacePlansOld.Starter]: {
- [WorkspacePlanBillingIntervals.Monthly]: {
- [Roles.Workspace.Guest]: 15,
- [Roles.Workspace.Member]: 15,
- [Roles.Workspace.Admin]: 15
- },
- [WorkspacePlanBillingIntervals.Yearly]: {
- [Roles.Workspace.Guest]: 12,
- [Roles.Workspace.Member]: 12,
- [Roles.Workspace.Admin]: 12
- }
- },
- [PaidWorkspacePlansOld.Plus]: {
- [WorkspacePlanBillingIntervals.Monthly]: {
- [Roles.Workspace.Guest]: 15,
- [Roles.Workspace.Member]: 50,
- [Roles.Workspace.Admin]: 50
- },
- [WorkspacePlanBillingIntervals.Yearly]: {
- [Roles.Workspace.Guest]: 12,
- [Roles.Workspace.Member]: 40,
- [Roles.Workspace.Admin]: 40
- }
- },
- [PaidWorkspacePlansOld.Business]: {
- [WorkspacePlanBillingIntervals.Monthly]: {
- [Roles.Workspace.Guest]: 15,
- [Roles.Workspace.Member]: 75,
- [Roles.Workspace.Admin]: 75
- },
- [WorkspacePlanBillingIntervals.Yearly]: {
- [Roles.Workspace.Guest]: 12,
- [Roles.Workspace.Member]: 60,
- [Roles.Workspace.Admin]: 60
- }
- }
-}
diff --git a/packages/frontend-2/lib/billing/helpers/types.ts b/packages/frontend-2/lib/billing/helpers/types.ts
deleted file mode 100644
index 26a3432c9..000000000
--- a/packages/frontend-2/lib/billing/helpers/types.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import type { WorkspacePlans } from '@speckle/shared'
-import { PaidWorkspacePlans } from '@speckle/shared'
-
-// Check if the plan matches PaidWorkspacePlans
-export const isPaidPlan = (plan?: WorkspacePlans): boolean =>
- plan ? (Object.values(PaidWorkspacePlans) as string[]).includes(plan) : false
diff --git a/packages/frontend-2/lib/common/generated/gql/gql.ts b/packages/frontend-2/lib/common/generated/gql/gql.ts
index b718dfa07..73681ed84 100644
--- a/packages/frontend-2/lib/common/generated/gql/gql.ts
+++ b/packages/frontend-2/lib/common/generated/gql/gql.ts
@@ -45,14 +45,14 @@ type Documents = {
"\n fragment FormSelectModels_Model on Model {\n id\n name\n }\n": typeof types.FormSelectModels_ModelFragmentDoc,
"\n fragment FormSelectProjects_Project on Project {\n id\n name\n }\n": typeof types.FormSelectProjects_ProjectFragmentDoc,
"\n fragment FormUsersSelectItem on LimitedUser {\n id\n name\n avatar\n }\n": typeof types.FormUsersSelectItemFragmentDoc,
+ "\n fragment HeaderNavShare_Project on Project {\n id\n visibility\n ...ProjectsModelPageEmbed_Project\n }\n": typeof types.HeaderNavShare_ProjectFragmentDoc,
+ "\n fragment HeaderNavNotificationsProjectInvite_PendingStreamCollaborator on PendingStreamCollaborator {\n id\n invitedBy {\n ...LimitedUserAvatar\n }\n projectId\n projectName\n token\n user {\n id\n }\n }\n": typeof types.HeaderNavNotificationsProjectInvite_PendingStreamCollaboratorFragmentDoc,
+ "\n fragment HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n workspaceId\n workspaceName\n token\n user {\n id\n }\n ...UseWorkspaceInviteManager_PendingWorkspaceCollaborator\n }\n": typeof types.HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment HeaderWorkspaceSwitcherActiveWorkspace_Workspace on Workspace {\n id\n name\n logo\n ...HeaderWorkspaceSwitcherHeaderWorkspace_Workspace\n }\n": typeof types.HeaderWorkspaceSwitcherActiveWorkspace_WorkspaceFragmentDoc,
"\n fragment HeaderWorkspaceSwitcherWorkspaceList_Workspace on Workspace {\n id\n name\n logo\n role\n slug\n creationState {\n completed\n }\n plan {\n name\n }\n }\n": typeof types.HeaderWorkspaceSwitcherWorkspaceList_WorkspaceFragmentDoc,
"\n fragment HeaderWorkspaceSwitcherWorkspaceList_User on User {\n id\n expiredSsoSessions {\n id\n ...HeaderWorkspaceSwitcherHeaderExpiredSso_LimitedWorkspace\n }\n workspaces {\n items {\n id\n ...HeaderWorkspaceSwitcherWorkspaceList_Workspace\n }\n }\n }\n": typeof types.HeaderWorkspaceSwitcherWorkspaceList_UserFragmentDoc,
"\n fragment HeaderWorkspaceSwitcherHeaderExpiredSso_LimitedWorkspace on LimitedWorkspace {\n id\n slug\n name\n logo\n }\n": typeof types.HeaderWorkspaceSwitcherHeaderExpiredSso_LimitedWorkspaceFragmentDoc,
"\n fragment HeaderWorkspaceSwitcherHeaderWorkspace_Workspace on Workspace {\n ...InviteDialogWorkspace_Workspace\n id\n name\n logo\n role\n plan {\n name\n }\n team {\n totalCount\n }\n }\n": typeof types.HeaderWorkspaceSwitcherHeaderWorkspace_WorkspaceFragmentDoc,
- "\n fragment HeaderNavShare_Project on Project {\n id\n visibility\n ...ProjectsModelPageEmbed_Project\n }\n": typeof types.HeaderNavShare_ProjectFragmentDoc,
- "\n fragment HeaderNavNotificationsProjectInvite_PendingStreamCollaborator on PendingStreamCollaborator {\n id\n invitedBy {\n ...LimitedUserAvatar\n }\n projectId\n projectName\n token\n user {\n id\n }\n }\n": typeof types.HeaderNavNotificationsProjectInvite_PendingStreamCollaboratorFragmentDoc,
- "\n fragment HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n workspaceId\n workspaceName\n token\n user {\n id\n }\n ...UseWorkspaceInviteManager_PendingWorkspaceCollaborator\n }\n": typeof types.HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment InviteDialogWorkspace_Workspace on Workspace {\n id\n name\n domainBasedMembershipProtectionEnabled\n domains {\n domain\n id\n }\n }\n": typeof types.InviteDialogWorkspace_WorkspaceFragmentDoc,
"\n fragment InviteDialogProject_Project on Project {\n id\n name\n ...InviteDialogProjectWorkspaceMembers_Project\n workspace {\n id\n name\n role\n domainBasedMembershipProtectionEnabled\n domains {\n domain\n id\n }\n }\n }\n": typeof types.InviteDialogProject_ProjectFragmentDoc,
"\n fragment InviteDialogProjectWorkspaceMembersRow_WorkspaceCollaborator on WorkspaceCollaborator {\n role\n id\n user {\n id\n name\n bio\n company\n avatar\n verified\n role\n }\n }\n": typeof types.InviteDialogProjectWorkspaceMembersRow_WorkspaceCollaboratorFragmentDoc,
@@ -454,14 +454,14 @@ const documents: Documents = {
"\n fragment FormSelectModels_Model on Model {\n id\n name\n }\n": types.FormSelectModels_ModelFragmentDoc,
"\n fragment FormSelectProjects_Project on Project {\n id\n name\n }\n": types.FormSelectProjects_ProjectFragmentDoc,
"\n fragment FormUsersSelectItem on LimitedUser {\n id\n name\n avatar\n }\n": types.FormUsersSelectItemFragmentDoc,
+ "\n fragment HeaderNavShare_Project on Project {\n id\n visibility\n ...ProjectsModelPageEmbed_Project\n }\n": types.HeaderNavShare_ProjectFragmentDoc,
+ "\n fragment HeaderNavNotificationsProjectInvite_PendingStreamCollaborator on PendingStreamCollaborator {\n id\n invitedBy {\n ...LimitedUserAvatar\n }\n projectId\n projectName\n token\n user {\n id\n }\n }\n": types.HeaderNavNotificationsProjectInvite_PendingStreamCollaboratorFragmentDoc,
+ "\n fragment HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n workspaceId\n workspaceName\n token\n user {\n id\n }\n ...UseWorkspaceInviteManager_PendingWorkspaceCollaborator\n }\n": types.HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment HeaderWorkspaceSwitcherActiveWorkspace_Workspace on Workspace {\n id\n name\n logo\n ...HeaderWorkspaceSwitcherHeaderWorkspace_Workspace\n }\n": types.HeaderWorkspaceSwitcherActiveWorkspace_WorkspaceFragmentDoc,
"\n fragment HeaderWorkspaceSwitcherWorkspaceList_Workspace on Workspace {\n id\n name\n logo\n role\n slug\n creationState {\n completed\n }\n plan {\n name\n }\n }\n": types.HeaderWorkspaceSwitcherWorkspaceList_WorkspaceFragmentDoc,
"\n fragment HeaderWorkspaceSwitcherWorkspaceList_User on User {\n id\n expiredSsoSessions {\n id\n ...HeaderWorkspaceSwitcherHeaderExpiredSso_LimitedWorkspace\n }\n workspaces {\n items {\n id\n ...HeaderWorkspaceSwitcherWorkspaceList_Workspace\n }\n }\n }\n": types.HeaderWorkspaceSwitcherWorkspaceList_UserFragmentDoc,
"\n fragment HeaderWorkspaceSwitcherHeaderExpiredSso_LimitedWorkspace on LimitedWorkspace {\n id\n slug\n name\n logo\n }\n": types.HeaderWorkspaceSwitcherHeaderExpiredSso_LimitedWorkspaceFragmentDoc,
"\n fragment HeaderWorkspaceSwitcherHeaderWorkspace_Workspace on Workspace {\n ...InviteDialogWorkspace_Workspace\n id\n name\n logo\n role\n plan {\n name\n }\n team {\n totalCount\n }\n }\n": types.HeaderWorkspaceSwitcherHeaderWorkspace_WorkspaceFragmentDoc,
- "\n fragment HeaderNavShare_Project on Project {\n id\n visibility\n ...ProjectsModelPageEmbed_Project\n }\n": types.HeaderNavShare_ProjectFragmentDoc,
- "\n fragment HeaderNavNotificationsProjectInvite_PendingStreamCollaborator on PendingStreamCollaborator {\n id\n invitedBy {\n ...LimitedUserAvatar\n }\n projectId\n projectName\n token\n user {\n id\n }\n }\n": types.HeaderNavNotificationsProjectInvite_PendingStreamCollaboratorFragmentDoc,
- "\n fragment HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n workspaceId\n workspaceName\n token\n user {\n id\n }\n ...UseWorkspaceInviteManager_PendingWorkspaceCollaborator\n }\n": types.HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment InviteDialogWorkspace_Workspace on Workspace {\n id\n name\n domainBasedMembershipProtectionEnabled\n domains {\n domain\n id\n }\n }\n": types.InviteDialogWorkspace_WorkspaceFragmentDoc,
"\n fragment InviteDialogProject_Project on Project {\n id\n name\n ...InviteDialogProjectWorkspaceMembers_Project\n workspace {\n id\n name\n role\n domainBasedMembershipProtectionEnabled\n domains {\n domain\n id\n }\n }\n }\n": types.InviteDialogProject_ProjectFragmentDoc,
"\n fragment InviteDialogProjectWorkspaceMembersRow_WorkspaceCollaborator on WorkspaceCollaborator {\n role\n id\n user {\n id\n name\n bio\n company\n avatar\n verified\n role\n }\n }\n": types.InviteDialogProjectWorkspaceMembersRow_WorkspaceCollaboratorFragmentDoc,
@@ -970,6 +970,18 @@ export function graphql(source: "\n fragment FormSelectProjects_Project on Proj
* 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 FormUsersSelectItem on LimitedUser {\n id\n name\n avatar\n }\n"): (typeof documents)["\n fragment FormUsersSelectItem on LimitedUser {\n id\n name\n avatar\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 HeaderNavShare_Project on Project {\n id\n visibility\n ...ProjectsModelPageEmbed_Project\n }\n"): (typeof documents)["\n fragment HeaderNavShare_Project on Project {\n id\n visibility\n ...ProjectsModelPageEmbed_Project\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 HeaderNavNotificationsProjectInvite_PendingStreamCollaborator on PendingStreamCollaborator {\n id\n invitedBy {\n ...LimitedUserAvatar\n }\n projectId\n projectName\n token\n user {\n id\n }\n }\n"): (typeof documents)["\n fragment HeaderNavNotificationsProjectInvite_PendingStreamCollaborator on PendingStreamCollaborator {\n id\n invitedBy {\n ...LimitedUserAvatar\n }\n projectId\n projectName\n token\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 HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n workspaceId\n workspaceName\n token\n user {\n id\n }\n ...UseWorkspaceInviteManager_PendingWorkspaceCollaborator\n }\n"): (typeof documents)["\n fragment HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n workspaceId\n workspaceName\n token\n user {\n id\n }\n ...UseWorkspaceInviteManager_PendingWorkspaceCollaborator\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -990,18 +1002,6 @@ export function graphql(source: "\n fragment HeaderWorkspaceSwitcherHeaderExpir
* 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 HeaderWorkspaceSwitcherHeaderWorkspace_Workspace on Workspace {\n ...InviteDialogWorkspace_Workspace\n id\n name\n logo\n role\n plan {\n name\n }\n team {\n totalCount\n }\n }\n"): (typeof documents)["\n fragment HeaderWorkspaceSwitcherHeaderWorkspace_Workspace on Workspace {\n ...InviteDialogWorkspace_Workspace\n id\n name\n logo\n role\n plan {\n name\n }\n team {\n totalCount\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 HeaderNavShare_Project on Project {\n id\n visibility\n ...ProjectsModelPageEmbed_Project\n }\n"): (typeof documents)["\n fragment HeaderNavShare_Project on Project {\n id\n visibility\n ...ProjectsModelPageEmbed_Project\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 HeaderNavNotificationsProjectInvite_PendingStreamCollaborator on PendingStreamCollaborator {\n id\n invitedBy {\n ...LimitedUserAvatar\n }\n projectId\n projectName\n token\n user {\n id\n }\n }\n"): (typeof documents)["\n fragment HeaderNavNotificationsProjectInvite_PendingStreamCollaborator on PendingStreamCollaborator {\n id\n invitedBy {\n ...LimitedUserAvatar\n }\n projectId\n projectName\n token\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 HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n workspaceId\n workspaceName\n token\n user {\n id\n }\n ...UseWorkspaceInviteManager_PendingWorkspaceCollaborator\n }\n"): (typeof documents)["\n fragment HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n workspaceId\n workspaceName\n token\n user {\n id\n }\n ...UseWorkspaceInviteManager_PendingWorkspaceCollaborator\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
diff --git a/packages/frontend-2/lib/common/generated/gql/graphql.ts b/packages/frontend-2/lib/common/generated/gql/graphql.ts
index 09f46c01f..40097b20b 100644
--- a/packages/frontend-2/lib/common/generated/gql/graphql.ts
+++ b/packages/frontend-2/lib/common/generated/gql/graphql.ts
@@ -5098,6 +5098,12 @@ export type FormSelectProjects_ProjectFragment = { __typename?: 'Project', id: s
export type FormUsersSelectItemFragment = { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null };
+export type HeaderNavShare_ProjectFragment = { __typename?: 'Project', id: string, visibility: SimpleProjectVisibility, role?: string | null };
+
+export type HeaderNavNotificationsProjectInvite_PendingStreamCollaboratorFragment = { __typename?: 'PendingStreamCollaborator', id: string, projectId: string, projectName: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }, user?: { __typename?: 'LimitedUser', id: string } | null };
+
+export type HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaboratorFragment = { __typename?: 'PendingWorkspaceCollaborator', id: string, workspaceId: string, workspaceName: string, token?: string | null, workspaceSlug: string, invitedBy: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }, user?: { __typename?: 'LimitedUser', id: string } | null };
+
export type HeaderWorkspaceSwitcherActiveWorkspace_WorkspaceFragment = { __typename?: 'Workspace', id: string, name: string, logo?: string | null, role?: string | null, domainBasedMembershipProtectionEnabled: boolean, plan?: { __typename?: 'WorkspacePlan', name: WorkspacePlans } | null, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number }, domains?: Array<{ __typename?: 'WorkspaceDomain', domain: string, id: string }> | null };
export type HeaderWorkspaceSwitcherWorkspaceList_WorkspaceFragment = { __typename?: 'Workspace', id: string, name: string, logo?: string | null, role?: string | null, slug: string, creationState?: { __typename?: 'WorkspaceCreationState', completed: boolean } | null, plan?: { __typename?: 'WorkspacePlan', name: WorkspacePlans } | null };
@@ -5108,12 +5114,6 @@ export type HeaderWorkspaceSwitcherHeaderExpiredSso_LimitedWorkspaceFragment = {
export type HeaderWorkspaceSwitcherHeaderWorkspace_WorkspaceFragment = { __typename?: 'Workspace', id: string, name: string, logo?: string | null, role?: string | null, domainBasedMembershipProtectionEnabled: boolean, plan?: { __typename?: 'WorkspacePlan', name: WorkspacePlans } | null, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number }, domains?: Array<{ __typename?: 'WorkspaceDomain', domain: string, id: string }> | null };
-export type HeaderNavShare_ProjectFragment = { __typename?: 'Project', id: string, visibility: SimpleProjectVisibility, role?: string | null };
-
-export type HeaderNavNotificationsProjectInvite_PendingStreamCollaboratorFragment = { __typename?: 'PendingStreamCollaborator', id: string, projectId: string, projectName: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }, user?: { __typename?: 'LimitedUser', id: string } | null };
-
-export type HeaderNavNotificationsWorkspaceInvite_PendingWorkspaceCollaboratorFragment = { __typename?: 'PendingWorkspaceCollaborator', id: string, workspaceId: string, workspaceName: string, token?: string | null, workspaceSlug: string, invitedBy: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }, user?: { __typename?: 'LimitedUser', id: string } | null };
-
export type InviteDialogWorkspace_WorkspaceFragment = { __typename?: 'Workspace', id: string, name: string, domainBasedMembershipProtectionEnabled: boolean, domains?: Array<{ __typename?: 'WorkspaceDomain', domain: string, id: string }> | null };
export type InviteDialogProject_ProjectFragment = { __typename?: 'Project', id: string, name: string, role?: string | null, workspace?: { __typename?: 'Workspace', id: string, name: string, role?: string | null, domainBasedMembershipProtectionEnabled: boolean, domains?: Array<{ __typename?: 'WorkspaceDomain', domain: string, id: string }> | null, team: { __typename?: 'WorkspaceCollaboratorCollection', items: Array<{ __typename?: 'WorkspaceCollaborator', role: string, id: string, user: { __typename?: 'LimitedUser', id: string, name: string, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, role?: string | null } }> } } | null, invitedTeam?: Array<{ __typename?: 'PendingStreamCollaborator', id: string, title: string, role: string, inviteId: string, user?: { __typename?: 'LimitedUser', role?: string | null, id: string, name: string, avatar?: string | null } | null }> | null, team: Array<{ __typename?: 'ProjectCollaborator', role: string, seatType?: WorkspaceSeatType | null, user: { __typename?: 'LimitedUser', id: string, role?: string | null, name: string, avatar?: string | null } }> };
diff --git a/packages/frontend-2/lib/workspaces/composables/plan.ts b/packages/frontend-2/lib/workspaces/composables/plan.ts
index 2ee2bc885..97200dfd2 100644
--- a/packages/frontend-2/lib/workspaces/composables/plan.ts
+++ b/packages/frontend-2/lib/workspaces/composables/plan.ts
@@ -2,7 +2,6 @@ import { graphql } from '~~/lib/common/generated/gql'
import { workspacePlanQuery } from '~~/lib/workspaces/graphql/queries'
import { useQuery } from '@vue/apollo-composable'
import {
- isNewWorkspacePlan,
PaidWorkspacePlansNew,
UnpaidWorkspacePlans,
WorkspacePlans,
@@ -62,10 +61,6 @@ export const useWorkspacePlan = (slug: string) => {
const subscription = computed(() => result.value?.workspaceBySlug?.subscription)
const plan = computed(() => result.value?.workspaceBySlug?.plan)
- // Plan type information
- const isNewPlan = computed(() =>
- isNewWorkspacePlan(result.value?.workspaceBySlug?.plan?.name)
- )
const isFreePlan = computed(() => plan.value?.name === UnpaidWorkspacePlans.Free)
const isUnlimitedPlan = computed(
() => plan.value?.name === UnpaidWorkspacePlans.Unlimited
@@ -117,7 +112,6 @@ export const useWorkspacePlan = (slug: string) => {
return {
plan,
- isNewPlan,
statusIsExpired,
statusIsCanceled,
isPurchasablePlan,
diff --git a/packages/frontend-2/middleware/requireDiscoverableWorkspaces.ts b/packages/frontend-2/middleware/requireDiscoverableWorkspaces.ts
index 35836ca16..0b7b4c4e0 100644
--- a/packages/frontend-2/middleware/requireDiscoverableWorkspaces.ts
+++ b/packages/frontend-2/middleware/requireDiscoverableWorkspaces.ts
@@ -8,7 +8,6 @@ import { homeRoute, workspaceCreateRoute } from '~~/lib/common/helpers/route'
*/
export default defineNuxtRouteMiddleware(async (to) => {
const isWorkspacesEnabled = useIsWorkspacesEnabled()
- const isNewPlansEnabled = useWorkspaceNewPlansEnabled()
if (!isWorkspacesEnabled.value) return
@@ -31,7 +30,7 @@ export default defineNuxtRouteMiddleware(async (to) => {
return navigateTo(workspaceCreateRoute())
}
- if (isNewPlansEnabled && isMemberOfWorkspace) {
+ if (isMemberOfWorkspace) {
return navigateTo(homeRoute)
}
})
diff --git a/packages/frontend-2/pages/settings/workspaces/[slug]/general.vue b/packages/frontend-2/pages/settings/workspaces/[slug]/general.vue
index 95f07f223..9a0449a99 100644
--- a/packages/frontend-2/pages/settings/workspaces/[slug]/general.vue
+++ b/packages/frontend-2/pages/settings/workspaces/[slug]/general.vue
@@ -149,7 +149,6 @@ import { Roles } from '@speckle/shared'
import { workspaceRoute } from '~/lib/common/helpers/route'
import { useRoute } from 'vue-router'
import { WorkspacePlanStatuses } from '~/lib/common/generated/gql/graphql'
-import { isPaidPlan } from '~/lib/billing/helpers/types'
import { useWorkspaceSsoStatus } from '~/lib/workspaces/composables/sso'
graphql(`
@@ -217,15 +216,13 @@ const canDeleteWorkspace = computed(
!needsSsoLogin.value &&
(!isBillingIntegrationEnabled ||
!(
- (
- [
- WorkspacePlanStatuses.Valid,
- WorkspacePlanStatuses.PaymentFailed,
- WorkspacePlanStatuses.CancelationScheduled
- ] as string[]
- ).includes(
- workspaceResult.value?.workspaceBySlug?.plan?.status as WorkspacePlanStatuses
- ) && isPaidPlan(workspaceResult.value?.workspaceBySlug?.plan?.name)
+ [
+ WorkspacePlanStatuses.Valid,
+ WorkspacePlanStatuses.PaymentFailed,
+ WorkspacePlanStatuses.CancelationScheduled
+ ] as string[]
+ ).includes(
+ workspaceResult.value?.workspaceBySlug?.plan?.status as WorkspacePlanStatuses
))
)
const deleteWorkspaceTooltip = computed(() => {
diff --git a/packages/server/modules/core/tests/unit/services/versions.spec.ts b/packages/server/modules/core/tests/unit/services/versions.spec.ts
index 827a9e913..316a2ead1 100644
--- a/packages/server/modules/core/tests/unit/services/versions.spec.ts
+++ b/packages/server/modules/core/tests/unit/services/versions.spec.ts
@@ -22,7 +22,7 @@ describe('Module @core', () => {
})
it('should return null if version is outside of workspace limit', async () => {
const getWorkspaceLimits = (() => ({
- versionsHistory: { value: 1, unit: 'week' }
+ versionsHistory: { value: 7, unit: 'day' }
})) as unknown as GetWorkspaceLimits
const project = { workspaceId: createRandomString() }
const tenDaysAgo = new Date()
@@ -40,7 +40,7 @@ describe('Module @core', () => {
})
it('should return version referencedObject if version is inside of workspace limit', async () => {
const getWorkspaceLimits = (() => ({
- versionsHistory: { value: 1, unit: 'week' }
+ versionsHistory: { value: 7, unit: 'day' }
})) as unknown as GetWorkspaceLimits
const project = { workspaceId: createRandomString() }
const twoDaysAgo = new Date()
diff --git a/packages/shared/src/workspaces/helpers/features.ts b/packages/shared/src/workspaces/helpers/features.ts
index 06727f38d..f61799b6a 100644
--- a/packages/shared/src/workspaces/helpers/features.ts
+++ b/packages/shared/src/workspaces/helpers/features.ts
@@ -7,21 +7,16 @@ import {
WorkspacePlans
} from './plans.js'
-type StringTemplate = (data: Data) => string
-
/**
* WORKSPACE FEATURES
*/
export const WorkspacePlanFeatures = {
// Core features pretty much available to everyone
- Workspace: 'workspace',
- RoleManagement: 'roleManagement',
- GuestUsers: 'guestUsers',
- PrivateAutomateFunctions: 'privateAutomateFunctions',
+ AutomateBeta: 'automateBeta',
+ DomainDiscoverability: 'domainDiscoverability',
// Optional/plan specific
DomainSecurity: 'domainBasedSecurityPolicies',
- PrioritySupport: 'prioritySupport',
SSO: 'oidcSso',
CustomDataRegion: 'workspaceDataRegionSpecificity'
}
@@ -30,24 +25,13 @@ export type WorkspacePlanFeatures =
(typeof WorkspacePlanFeatures)[keyof typeof WorkspacePlanFeatures]
export const WorkspacePlanFeaturesMetadata = ({
- // Old
- [WorkspacePlanFeatures.Workspace]: {
- displayName: 'Workspace',
- description: 'A shared space for your team and projects'
+ [WorkspacePlanFeatures.AutomateBeta]: {
+ displayName: 'Automate beta access',
+ description: 'Some automate text'
},
- [WorkspacePlanFeatures.RoleManagement]: {
- displayName: 'Role management',
- description: "Control individual members' access and edit rights"
- },
- [WorkspacePlanFeatures.GuestUsers]: {
- displayName: 'Guest users',
- description: (params: { price: number | string }) =>
- `Give guests access to specific projects in the workspace at ${params.price}/month/guest`
- },
- [WorkspacePlanFeatures.PrivateAutomateFunctions]: {
- displayName: 'Private automate functions',
- description:
- 'Create and manage private automation functions securely within your workspace'
+ [WorkspacePlanFeatures.DomainDiscoverability]: {
+ displayName: 'Domain discoverability',
+ description: 'Some domain discoverability text'
},
[WorkspacePlanFeatures.DomainSecurity]: {
displayName: 'Domain security',
@@ -60,17 +44,12 @@ export const WorkspacePlanFeaturesMetadata = ({
[WorkspacePlanFeatures.CustomDataRegion]: {
displayName: 'Custom data residency',
description: 'Store the workspace data in a custom region'
- },
- [WorkspacePlanFeatures.PrioritySupport]: {
- displayName: 'Priority support',
- description: 'Personal and fast support'
}
}) satisfies Record<
WorkspacePlanFeatures,
{
displayName: string
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- description: string | StringTemplate
+ description: string
}
>
@@ -97,63 +76,46 @@ export type WorkspacePlanConfig =
}
const baseFeatures = [
- WorkspacePlanFeatures.Workspace,
- WorkspacePlanFeatures.RoleManagement,
- WorkspacePlanFeatures.GuestUsers,
- WorkspacePlanFeatures.PrivateAutomateFunctions
+ WorkspacePlanFeatures.AutomateBeta,
+ WorkspacePlanFeatures.DomainDiscoverability
] as const
-const teamFeatures = [...baseFeatures]
-
-const proFeatures = [
- ...teamFeatures,
- WorkspacePlanFeatures.DomainSecurity,
- WorkspacePlanFeatures.SSO,
- WorkspacePlanFeatures.CustomDataRegion,
- WorkspacePlanFeatures.PrioritySupport
-]
-
export const WorkspacePaidPlanConfigs: {
[plan in PaidWorkspacePlans]: WorkspacePlanConfig
} = {
// Old
[PaidWorkspacePlans.Starter]: {
plan: PaidWorkspacePlans.Starter,
- features: [...baseFeatures, WorkspacePlanFeatures.DomainSecurity],
+ features: [...baseFeatures],
limits: unlimited
},
[PaidWorkspacePlans.Plus]: {
plan: PaidWorkspacePlans.Plus,
- features: [
- ...baseFeatures,
- WorkspacePlanFeatures.DomainSecurity,
- WorkspacePlanFeatures.SSO
- ],
+ features: [...baseFeatures, WorkspacePlanFeatures.SSO],
limits: unlimited
},
[PaidWorkspacePlans.Business]: {
plan: PaidWorkspacePlans.Business,
features: [
...baseFeatures,
- WorkspacePlanFeatures.DomainSecurity,
WorkspacePlanFeatures.SSO,
- WorkspacePlanFeatures.CustomDataRegion,
- WorkspacePlanFeatures.PrioritySupport
+ WorkspacePlanFeatures.CustomDataRegion
],
limits: unlimited
},
[PaidWorkspacePlans.Team]: {
plan: PaidWorkspacePlans.Team,
- features: teamFeatures,
+ features: [...baseFeatures],
limits: {
projectCount: 5,
modelCount: 25,
versionsHistory: { value: 30, unit: 'day' }
}
},
+ // New
[PaidWorkspacePlans.TeamUnlimited]: {
plan: PaidWorkspacePlans.TeamUnlimited,
- features: teamFeatures,
+ features: [...baseFeatures],
limits: {
projectCount: null,
modelCount: null,
@@ -162,7 +124,12 @@ export const WorkspacePaidPlanConfigs: {
},
[PaidWorkspacePlans.Pro]: {
plan: PaidWorkspacePlans.Pro,
- features: proFeatures,
+ features: [
+ ...baseFeatures,
+ WorkspacePlanFeatures.DomainSecurity,
+ WorkspacePlanFeatures.SSO,
+ WorkspacePlanFeatures.CustomDataRegion
+ ],
limits: {
projectCount: 10,
modelCount: 50,
@@ -171,7 +138,12 @@ export const WorkspacePaidPlanConfigs: {
},
[PaidWorkspacePlans.ProUnlimited]: {
plan: PaidWorkspacePlans.ProUnlimited,
- features: proFeatures,
+ features: [
+ ...baseFeatures,
+ WorkspacePlanFeatures.DomainSecurity,
+ WorkspacePlanFeatures.SSO,
+ WorkspacePlanFeatures.CustomDataRegion
+ ],
limits: {
projectCount: null,
modelCount: null,
@@ -190,8 +162,7 @@ export const WorkspaceUnpaidPlanConfigs: {
...baseFeatures,
WorkspacePlanFeatures.DomainSecurity,
WorkspacePlanFeatures.SSO,
- WorkspacePlanFeatures.CustomDataRegion,
- WorkspacePlanFeatures.PrioritySupport
+ WorkspacePlanFeatures.CustomDataRegion
],
limits: unlimited
},
@@ -201,8 +172,7 @@ export const WorkspaceUnpaidPlanConfigs: {
...baseFeatures,
WorkspacePlanFeatures.DomainSecurity,
WorkspacePlanFeatures.SSO,
- WorkspacePlanFeatures.CustomDataRegion,
- WorkspacePlanFeatures.PrioritySupport
+ WorkspacePlanFeatures.CustomDataRegion
],
limits: unlimited
},
@@ -233,7 +203,7 @@ export const WorkspaceUnpaidPlanConfigs: {
limits: {
projectCount: 1,
modelCount: 5,
- versionsHistory: { value: 1, unit: 'week' }
+ versionsHistory: { value: 7, unit: 'day' }
}
}
}