feat: enable saved views for all workspace plans (#5343)
* feat: enable saved views for all workspace plans * more test fixes
This commit is contained in:
committed by
GitHub
parent
fdf3b93e95
commit
a074aedd65
Binary file not shown.
|
Before Width: | Height: | Size: 8.4 KiB |
@@ -97,7 +97,6 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<ViewerSavedViewsPlanUpsell v-else />
|
|
||||||
<ViewerSavedViewsPanelGroupsCreateDialog
|
<ViewerSavedViewsPanelGroupsCreateDialog
|
||||||
v-model:open="showCreateGroupDialog"
|
v-model:open="showCreateGroupDialog"
|
||||||
@success="onAddGroup"
|
@success="onAddGroup"
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col gap-4 p-4">
|
|
||||||
<img src="~/assets/images/viewer/saved-views/plan_upsell.webp" alt="Saved Views" />
|
|
||||||
<div>
|
|
||||||
<div class="text-foreground text-body font-semibold">Save custom views</div>
|
|
||||||
<div class="text-body-2xs font-medium text-foreground-2">
|
|
||||||
<p class="pb-3">Upgrade to a business plan to save, organise and present</p>
|
|
||||||
<ul class="flex flex-col gap-2 list-disc list-inside">
|
|
||||||
<li>It's cool</li>
|
|
||||||
<li>It's nice</li>
|
|
||||||
<li>It's got enough spice</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<FormButton size="sm">Upgrade</FormButton>
|
|
||||||
<FormButton size="sm" color="outline">Learn more</FormButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -74,7 +74,7 @@ import { Roles, WorkspacePlans } from '@speckle/shared'
|
|||||||
import {
|
import {
|
||||||
ProjectNotEnoughPermissionsError,
|
ProjectNotEnoughPermissionsError,
|
||||||
SavedViewNoAccessError,
|
SavedViewNoAccessError,
|
||||||
WorkspacePlanNoFeatureAccessError
|
WorkspaceNoAccessError
|
||||||
} from '@speckle/shared/authz'
|
} from '@speckle/shared/authz'
|
||||||
import * as ViewerRoute from '@speckle/shared/viewer/route'
|
import * as ViewerRoute from '@speckle/shared/viewer/route'
|
||||||
import { resourceBuilder } from '@speckle/shared/viewer/route'
|
import { resourceBuilder } from '@speckle/shared/viewer/route'
|
||||||
@@ -121,7 +121,6 @@ const fakeViewerState = (overrides?: PartialDeep<ViewerState.SerializedViewerSta
|
|||||||
let otherGuy: BasicTestUser
|
let otherGuy: BasicTestUser
|
||||||
let myProject: BasicTestStream
|
let myProject: BasicTestStream
|
||||||
let myProjectWorkspace: BasicTestWorkspace
|
let myProjectWorkspace: BasicTestWorkspace
|
||||||
let myLackingProjectWorkspace: BasicTestWorkspace
|
|
||||||
let myLackingProject: BasicTestStream
|
let myLackingProject: BasicTestStream
|
||||||
let myModel1: BasicTestBranch
|
let myModel1: BasicTestBranch
|
||||||
let myModel2: BasicTestBranch
|
let myModel2: BasicTestBranch
|
||||||
@@ -269,13 +268,13 @@ const fakeViewerState = (overrides?: PartialDeep<ViewerState.SerializedViewerSta
|
|||||||
addPlan: WorkspacePlans.Pro
|
addPlan: WorkspacePlans.Pro
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
myLackingProjectWorkspace = workspaceCreate[0]
|
|
||||||
myProjectWorkspace = workspaceCreate[1]
|
myProjectWorkspace = workspaceCreate[1]
|
||||||
|
|
||||||
const projectCreate = await Promise.all([
|
const projectCreate = await Promise.all([
|
||||||
createTestStream(
|
createTestStream(
|
||||||
buildBasicTestProject({
|
buildBasicTestProject({
|
||||||
workspaceId: myLackingProjectWorkspace.id
|
// non-workspaced project
|
||||||
|
workspaceId: undefined
|
||||||
}),
|
}),
|
||||||
me
|
me
|
||||||
),
|
),
|
||||||
@@ -355,61 +354,6 @@ const fakeViewerState = (overrides?: PartialDeep<ViewerState.SerializedViewerSta
|
|||||||
expect(res.data?.projectMutations.savedViewMutations.createView).to.not.be.ok
|
expect(res.data?.projectMutations.savedViewMutations.createView).to.not.be.ok
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should fail with ForbiddenError if workspace plan does not include SavedViews', async () => {
|
|
||||||
const res = await createSavedView(
|
|
||||||
buildCreateInput({
|
|
||||||
projectId: myLackingProject.id,
|
|
||||||
resourceIdString: 'abc'
|
|
||||||
})
|
|
||||||
)
|
|
||||||
expect(res).to.haveGraphQLErrors({ code: ForbiddenError.code })
|
|
||||||
expect(res.data?.projectMutations.savedViewMutations.createView).to.not.be.ok
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should fail with ForbiddenError to create a saved view group if user lacks access (free plan)', async () => {
|
|
||||||
const resourceIds = ViewerRoute.resourceBuilder().addModel(
|
|
||||||
myLackingProject.id
|
|
||||||
)
|
|
||||||
const resourceIdString = resourceIds.toString()
|
|
||||||
|
|
||||||
const res = await createSavedViewGroup({
|
|
||||||
input: {
|
|
||||||
projectId: myLackingProject.id,
|
|
||||||
resourceIdString,
|
|
||||||
groupName: 'Should Not Work'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(res).to.haveGraphQLErrors({ code: ForbiddenError.code })
|
|
||||||
expect(res.data?.projectMutations.savedViewMutations.createGroup).to.not.be.ok
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should fail with ForbiddenError to create a saved view if user lacks access (free plan)', async () => {
|
|
||||||
const resourceIds = ViewerRoute.resourceBuilder().addModel(
|
|
||||||
myLackingProject.id
|
|
||||||
)
|
|
||||||
const resourceIdString = resourceIds.toString()
|
|
||||||
const viewerState = fakeViewerState({
|
|
||||||
projectId: myLackingProject.id,
|
|
||||||
resources: {
|
|
||||||
request: {
|
|
||||||
resourceIdString
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const res = await createSavedView(
|
|
||||||
buildCreateInput({
|
|
||||||
projectId: myLackingProject.id,
|
|
||||||
resourceIdString,
|
|
||||||
viewerState
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(res).to.haveGraphQLErrors({ code: ForbiddenError.code })
|
|
||||||
expect(res.data?.projectMutations.savedViewMutations.createView).to.not.be.ok
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should support dedicated auth policy check', async () => {
|
it('should support dedicated auth policy check', async () => {
|
||||||
const res = await canCreateSavedView({
|
const res = await canCreateSavedView({
|
||||||
projectId: myLackingProject.id
|
projectId: myLackingProject.id
|
||||||
@@ -419,7 +363,7 @@ const fakeViewerState = (overrides?: PartialDeep<ViewerState.SerializedViewerSta
|
|||||||
|
|
||||||
const data = res.data?.project.permissions.canCreateSavedView
|
const data = res.data?.project.permissions.canCreateSavedView
|
||||||
expect(data?.authorized).to.be.false
|
expect(data?.authorized).to.be.false
|
||||||
expect(data?.code).to.equal(WorkspacePlanNoFeatureAccessError.code)
|
expect(data?.code).to.equal(WorkspaceNoAccessError.code)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -977,7 +977,7 @@ describe('ensureCanUseProjectWorkspacePlanFeatureFragment', () => {
|
|||||||
|
|
||||||
const result = await sut({
|
const result = await sut({
|
||||||
projectId: 'project-id',
|
projectId: 'project-id',
|
||||||
feature: WorkspacePlanFeatures.SavedViews
|
feature: WorkspacePlanFeatures.HideSpeckleBranding
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toBeAuthOKResult()
|
expect(result).toBeAuthOKResult()
|
||||||
@@ -990,7 +990,7 @@ describe('ensureCanUseProjectWorkspacePlanFeatureFragment', () => {
|
|||||||
|
|
||||||
const result = await sut({
|
const result = await sut({
|
||||||
projectId: 'project-id',
|
projectId: 'project-id',
|
||||||
feature: WorkspacePlanFeatures.SavedViews
|
feature: WorkspacePlanFeatures.HideSpeckleBranding
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toBeAuthErrorResult({
|
expect(result).toBeAuthErrorResult({
|
||||||
@@ -1008,7 +1008,7 @@ describe('ensureCanUseProjectWorkspacePlanFeatureFragment', () => {
|
|||||||
|
|
||||||
const result = await sut({
|
const result = await sut({
|
||||||
projectId: 'project-id',
|
projectId: 'project-id',
|
||||||
feature: WorkspacePlanFeatures.SavedViews
|
feature: WorkspacePlanFeatures.HideSpeckleBranding
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toBeAuthErrorResult({
|
expect(result).toBeAuthErrorResult({
|
||||||
@@ -1026,7 +1026,7 @@ describe('ensureCanUseProjectWorkspacePlanFeatureFragment', () => {
|
|||||||
|
|
||||||
const result = await sut({
|
const result = await sut({
|
||||||
projectId: 'project-id',
|
projectId: 'project-id',
|
||||||
feature: WorkspacePlanFeatures.SavedViews,
|
feature: WorkspacePlanFeatures.HideSpeckleBranding,
|
||||||
allowUnworkspaced: true
|
allowUnworkspaced: true
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1044,7 +1044,7 @@ describe('ensureCanUseProjectWorkspacePlanFeatureFragment', () => {
|
|||||||
|
|
||||||
const result = await sut({
|
const result = await sut({
|
||||||
projectId: 'project-id',
|
projectId: 'project-id',
|
||||||
feature: WorkspacePlanFeatures.SavedViews
|
feature: WorkspacePlanFeatures.HideSpeckleBranding
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toBeAuthErrorResult({
|
expect(result).toBeAuthErrorResult({
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ import {
|
|||||||
SavedViewNoAccessError,
|
SavedViewNoAccessError,
|
||||||
SavedViewNotFoundError,
|
SavedViewNotFoundError,
|
||||||
UngroupedSavedViewGroupLockError,
|
UngroupedSavedViewGroupLockError,
|
||||||
WorkspaceNoAccessError,
|
WorkspaceNoAccessError
|
||||||
WorkspacePlanNoFeatureAccessError
|
|
||||||
} from '../domain/authErrors.js'
|
} from '../domain/authErrors.js'
|
||||||
import { nanoid } from 'nanoid'
|
import { nanoid } from 'nanoid'
|
||||||
|
|
||||||
@@ -191,11 +190,17 @@ describe('ensureCanAccessSavedViewFragment', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
it.each(<const>['read', 'write'])(
|
it.each(<const>['read', 'write'])(
|
||||||
'fails when workspace plan is too cheap (%s)',
|
'succeeds to %s even on free plan',
|
||||||
async (access) => {
|
async (access) => {
|
||||||
const sut = buildWorkspaceSUT({
|
const sut = buildWorkspaceSUT({
|
||||||
getWorkspacePlan: getWorkspacePlanFake({
|
getWorkspacePlan: getWorkspacePlanFake({
|
||||||
name: 'team'
|
name: 'free'
|
||||||
|
}),
|
||||||
|
getSavedView: getSavedViewFake({
|
||||||
|
id: savedViewId,
|
||||||
|
projectId,
|
||||||
|
visibility: SavedViewVisibility.public,
|
||||||
|
authorId: userId
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -205,9 +210,7 @@ describe('ensureCanAccessSavedViewFragment', () => {
|
|||||||
savedViewId,
|
savedViewId,
|
||||||
access
|
access
|
||||||
})
|
})
|
||||||
expect(result).toBeAuthErrorResult({
|
expect(result).toBeAuthOKResult()
|
||||||
code: WorkspacePlanNoFeatureAccessError.code
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -413,27 +416,6 @@ describe('ensureCanAccessSavedViewGroupFragment', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it.each(<const>['read', 'write'])(
|
|
||||||
'fails when workspace plan is too cheap (%s)',
|
|
||||||
async (access) => {
|
|
||||||
const sut = buildWorkspaceSUT({
|
|
||||||
getWorkspacePlan: getWorkspacePlanFake({
|
|
||||||
name: 'team'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const result = await sut({
|
|
||||||
userId,
|
|
||||||
projectId,
|
|
||||||
savedViewGroupId,
|
|
||||||
access
|
|
||||||
})
|
|
||||||
expect(result).toBeAuthErrorResult({
|
|
||||||
code: WorkspacePlanNoFeatureAccessError.code
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
it.each(<const>['read', 'write'])(
|
it.each(<const>['read', 'write'])(
|
||||||
'fails if view doesnt exist (%s)',
|
'fails if view doesnt exist (%s)',
|
||||||
async (access) => {
|
async (access) => {
|
||||||
|
|||||||
@@ -347,7 +347,7 @@ describe('ensureCanUseWorkspacePlanFeatureFragment', () => {
|
|||||||
|
|
||||||
const result = await sut({
|
const result = await sut({
|
||||||
workspaceId: cryptoRandomString({ length: 10 }),
|
workspaceId: cryptoRandomString({ length: 10 }),
|
||||||
feature: WorkspacePlanFeatures.SavedViews
|
feature: WorkspacePlanFeatures.HideSpeckleBranding
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toBeOKResult()
|
expect(result).toBeOKResult()
|
||||||
@@ -362,7 +362,7 @@ describe('ensureCanUseWorkspacePlanFeatureFragment', () => {
|
|||||||
|
|
||||||
const result = await sut({
|
const result = await sut({
|
||||||
workspaceId: cryptoRandomString({ length: 10 }),
|
workspaceId: cryptoRandomString({ length: 10 }),
|
||||||
feature: WorkspacePlanFeatures.SavedViews
|
feature: WorkspacePlanFeatures.HideSpeckleBranding
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toBeAuthErrorResult({
|
expect(result).toBeAuthErrorResult({
|
||||||
@@ -380,7 +380,7 @@ describe('ensureCanUseWorkspacePlanFeatureFragment', () => {
|
|||||||
|
|
||||||
const result = await sut({
|
const result = await sut({
|
||||||
workspaceId: cryptoRandomString({ length: 10 }),
|
workspaceId: cryptoRandomString({ length: 10 }),
|
||||||
feature: WorkspacePlanFeatures.SavedViews
|
feature: WorkspacePlanFeatures.HideSpeckleBranding
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toBeAuthErrorResult({
|
expect(result).toBeAuthErrorResult({
|
||||||
@@ -395,7 +395,7 @@ describe('ensureCanUseWorkspacePlanFeatureFragment', () => {
|
|||||||
|
|
||||||
const result = await sut({
|
const result = await sut({
|
||||||
workspaceId: cryptoRandomString({ length: 10 }),
|
workspaceId: cryptoRandomString({ length: 10 }),
|
||||||
feature: WorkspacePlanFeatures.SavedViews
|
feature: WorkspacePlanFeatures.HideSpeckleBranding
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toBeAuthErrorResult({
|
expect(result).toBeAuthErrorResult({
|
||||||
@@ -413,7 +413,7 @@ describe('ensureCanUseWorkspacePlanFeatureFragment', () => {
|
|||||||
|
|
||||||
const result = await sut({
|
const result = await sut({
|
||||||
workspaceId: cryptoRandomString({ length: 10 }),
|
workspaceId: cryptoRandomString({ length: 10 }),
|
||||||
feature: WorkspacePlanFeatures.SavedViews
|
feature: WorkspacePlanFeatures.HideSpeckleBranding
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toBeAuthErrorResult({
|
expect(result).toBeAuthErrorResult({
|
||||||
|
|||||||
@@ -14,10 +14,9 @@ import {
|
|||||||
ProjectNotEnoughPermissionsError,
|
ProjectNotEnoughPermissionsError,
|
||||||
ServerNoAccessError,
|
ServerNoAccessError,
|
||||||
WorkspaceNoAccessError,
|
WorkspaceNoAccessError,
|
||||||
WorkspacePlanNoFeatureAccessError,
|
|
||||||
WorkspaceReadOnlyError
|
WorkspaceReadOnlyError
|
||||||
} from '../../../domain/authErrors.js'
|
} from '../../../domain/authErrors.js'
|
||||||
import { PaidWorkspacePlans } from '../../../../workspaces/index.js'
|
import { WorkspacePlans } from '../../../../workspaces/index.js'
|
||||||
|
|
||||||
const buildSUT = (overrides?: OverridesOf<typeof canCreateSavedViewPolicy>) =>
|
const buildSUT = (overrides?: OverridesOf<typeof canCreateSavedViewPolicy>) =>
|
||||||
canCreateSavedViewPolicy({
|
canCreateSavedViewPolicy({
|
||||||
@@ -71,7 +70,7 @@ describe('canCreateSavedViewPolicy', () => {
|
|||||||
id: 'workspace-id'
|
id: 'workspace-id'
|
||||||
}),
|
}),
|
||||||
getWorkspacePlan: getWorkspacePlanFake({
|
getWorkspacePlan: getWorkspacePlanFake({
|
||||||
name: PaidWorkspacePlans.Pro
|
name: WorkspacePlans.Pro
|
||||||
}),
|
}),
|
||||||
getWorkspaceSsoProvider: async () => ({
|
getWorkspaceSsoProvider: async () => ({
|
||||||
providerId: 'provider-id'
|
providerId: 'provider-id'
|
||||||
@@ -153,10 +152,10 @@ describe('canCreateSavedViewPolicy', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fails if not on pro/business plan', async () => {
|
it('succeeds even on free plan', async () => {
|
||||||
const canCreate = buildWorkspaceSUT({
|
const canCreate = buildWorkspaceSUT({
|
||||||
getWorkspacePlan: getWorkspacePlanFake({
|
getWorkspacePlan: getWorkspacePlanFake({
|
||||||
name: PaidWorkspacePlans.Team
|
name: WorkspacePlans.Free
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -164,15 +163,13 @@ describe('canCreateSavedViewPolicy', () => {
|
|||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
projectId: 'project-id'
|
projectId: 'project-id'
|
||||||
})
|
})
|
||||||
expect(result).toBeAuthErrorResult({
|
expect(result).toBeAuthOKResult()
|
||||||
code: WorkspacePlanNoFeatureAccessError.code
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fails if workspace readonly', async () => {
|
it('fails if workspace readonly', async () => {
|
||||||
const canCreate = buildWorkspaceSUT({
|
const canCreate = buildWorkspaceSUT({
|
||||||
getWorkspacePlan: getWorkspacePlanFake({
|
getWorkspacePlan: getWorkspacePlanFake({
|
||||||
name: PaidWorkspacePlans.Pro,
|
name: WorkspacePlans.Pro,
|
||||||
status: 'canceled'
|
status: 'canceled'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -134,135 +134,137 @@ export const WorkspacePaidPlanConfigs: (params: {
|
|||||||
featureFlags: Partial<FeatureFlags> | undefined
|
featureFlags: Partial<FeatureFlags> | undefined
|
||||||
}) => {
|
}) => {
|
||||||
[plan in PaidWorkspacePlans]: WorkspacePlanConfig<plan>
|
[plan in PaidWorkspacePlans]: WorkspacePlanConfig<plan>
|
||||||
} = (params) => ({
|
} = (params) => {
|
||||||
[PaidWorkspacePlans.Team]: {
|
const finalBaseFeatures = [
|
||||||
plan: PaidWorkspacePlans.Team,
|
...baseFeatures,
|
||||||
features: [...baseFeatures],
|
...(params.featureFlags?.FF_SAVED_VIEWS_ENABLED
|
||||||
limits: {
|
? [WorkspacePlanFeatures.SavedViews]
|
||||||
projectCount: 5,
|
: [])
|
||||||
modelCount: 25,
|
]
|
||||||
versionsHistory: { value: 30, unit: 'day' },
|
|
||||||
commentHistory: { value: 30, unit: 'day' }
|
return {
|
||||||
}
|
[PaidWorkspacePlans.Team]: {
|
||||||
},
|
plan: PaidWorkspacePlans.Team,
|
||||||
[PaidWorkspacePlans.TeamUnlimited]: {
|
features: [...finalBaseFeatures],
|
||||||
plan: PaidWorkspacePlans.TeamUnlimited,
|
limits: {
|
||||||
features: [...baseFeatures],
|
projectCount: 5,
|
||||||
limits: {
|
modelCount: 25,
|
||||||
projectCount: null,
|
versionsHistory: { value: 30, unit: 'day' },
|
||||||
modelCount: null,
|
commentHistory: { value: 30, unit: 'day' }
|
||||||
versionsHistory: { value: 30, unit: 'day' },
|
}
|
||||||
commentHistory: { value: 30, unit: 'day' }
|
},
|
||||||
}
|
[PaidWorkspacePlans.TeamUnlimited]: {
|
||||||
},
|
plan: PaidWorkspacePlans.TeamUnlimited,
|
||||||
[PaidWorkspacePlans.Pro]: {
|
features: [...finalBaseFeatures],
|
||||||
plan: PaidWorkspacePlans.Pro,
|
limits: {
|
||||||
features: [
|
projectCount: null,
|
||||||
...baseFeatures,
|
modelCount: null,
|
||||||
WorkspacePlanFeatures.DomainSecurity,
|
versionsHistory: { value: 30, unit: 'day' },
|
||||||
WorkspacePlanFeatures.SSO,
|
commentHistory: { value: 30, unit: 'day' }
|
||||||
WorkspacePlanFeatures.CustomDataRegion,
|
}
|
||||||
WorkspacePlanFeatures.HideSpeckleBranding,
|
},
|
||||||
...(params.featureFlags?.FF_SAVED_VIEWS_ENABLED
|
[PaidWorkspacePlans.Pro]: {
|
||||||
? [WorkspacePlanFeatures.SavedViews]
|
plan: PaidWorkspacePlans.Pro,
|
||||||
: [])
|
features: [
|
||||||
],
|
...finalBaseFeatures,
|
||||||
limits: {
|
WorkspacePlanFeatures.DomainSecurity,
|
||||||
projectCount: 10,
|
WorkspacePlanFeatures.SSO,
|
||||||
modelCount: 50,
|
WorkspacePlanFeatures.CustomDataRegion,
|
||||||
versionsHistory: null,
|
WorkspacePlanFeatures.HideSpeckleBranding
|
||||||
commentHistory: null
|
],
|
||||||
}
|
limits: {
|
||||||
},
|
projectCount: 10,
|
||||||
[PaidWorkspacePlans.ProUnlimited]: {
|
modelCount: 50,
|
||||||
plan: PaidWorkspacePlans.ProUnlimited,
|
versionsHistory: null,
|
||||||
features: [
|
commentHistory: null
|
||||||
...baseFeatures,
|
}
|
||||||
WorkspacePlanFeatures.DomainSecurity,
|
},
|
||||||
WorkspacePlanFeatures.SSO,
|
[PaidWorkspacePlans.ProUnlimited]: {
|
||||||
WorkspacePlanFeatures.CustomDataRegion,
|
plan: PaidWorkspacePlans.ProUnlimited,
|
||||||
WorkspacePlanFeatures.HideSpeckleBranding,
|
features: [
|
||||||
...(params.featureFlags?.FF_SAVED_VIEWS_ENABLED
|
...finalBaseFeatures,
|
||||||
? [WorkspacePlanFeatures.SavedViews]
|
WorkspacePlanFeatures.DomainSecurity,
|
||||||
: [])
|
WorkspacePlanFeatures.SSO,
|
||||||
],
|
WorkspacePlanFeatures.CustomDataRegion,
|
||||||
limits: {
|
WorkspacePlanFeatures.HideSpeckleBranding
|
||||||
projectCount: null,
|
],
|
||||||
modelCount: null,
|
limits: {
|
||||||
versionsHistory: null,
|
projectCount: null,
|
||||||
commentHistory: null
|
modelCount: null,
|
||||||
|
versionsHistory: null,
|
||||||
|
commentHistory: null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
export const WorkspaceUnpaidPlanConfigs: (params: {
|
export const WorkspaceUnpaidPlanConfigs: (params: {
|
||||||
featureFlags: Partial<FeatureFlags> | undefined
|
featureFlags: Partial<FeatureFlags> | undefined
|
||||||
}) => {
|
}) => {
|
||||||
[plan in UnpaidWorkspacePlans]: WorkspacePlanConfig<plan>
|
[plan in UnpaidWorkspacePlans]: WorkspacePlanConfig<plan>
|
||||||
} = (params) => ({
|
} = (params) => {
|
||||||
[UnpaidWorkspacePlans.Enterprise]: {
|
const finalBaseFeatures = [
|
||||||
plan: UnpaidWorkspacePlans.Enterprise,
|
...baseFeatures,
|
||||||
features: [
|
...(params.featureFlags?.FF_SAVED_VIEWS_ENABLED
|
||||||
...baseFeatures,
|
? [WorkspacePlanFeatures.SavedViews]
|
||||||
WorkspacePlanFeatures.DomainSecurity,
|
: [])
|
||||||
WorkspacePlanFeatures.SSO,
|
]
|
||||||
WorkspacePlanFeatures.CustomDataRegion,
|
return {
|
||||||
WorkspacePlanFeatures.HideSpeckleBranding,
|
[UnpaidWorkspacePlans.Enterprise]: {
|
||||||
WorkspacePlanFeatures.ExclusiveMembership,
|
plan: UnpaidWorkspacePlans.Enterprise,
|
||||||
...(params.featureFlags?.FF_SAVED_VIEWS_ENABLED
|
features: [
|
||||||
? [WorkspacePlanFeatures.SavedViews]
|
...finalBaseFeatures,
|
||||||
: [])
|
WorkspacePlanFeatures.DomainSecurity,
|
||||||
],
|
WorkspacePlanFeatures.SSO,
|
||||||
limits: unlimited
|
WorkspacePlanFeatures.CustomDataRegion,
|
||||||
},
|
WorkspacePlanFeatures.HideSpeckleBranding,
|
||||||
[UnpaidWorkspacePlans.Unlimited]: {
|
WorkspacePlanFeatures.ExclusiveMembership
|
||||||
plan: UnpaidWorkspacePlans.Unlimited,
|
],
|
||||||
features: [
|
limits: unlimited
|
||||||
...baseFeatures,
|
},
|
||||||
WorkspacePlanFeatures.DomainSecurity,
|
[UnpaidWorkspacePlans.Unlimited]: {
|
||||||
WorkspacePlanFeatures.SSO,
|
plan: UnpaidWorkspacePlans.Unlimited,
|
||||||
WorkspacePlanFeatures.CustomDataRegion,
|
features: [
|
||||||
WorkspacePlanFeatures.HideSpeckleBranding,
|
...finalBaseFeatures,
|
||||||
WorkspacePlanFeatures.ExclusiveMembership,
|
WorkspacePlanFeatures.DomainSecurity,
|
||||||
...(params.featureFlags?.FF_SAVED_VIEWS_ENABLED
|
WorkspacePlanFeatures.SSO,
|
||||||
? [WorkspacePlanFeatures.SavedViews]
|
WorkspacePlanFeatures.CustomDataRegion,
|
||||||
: [])
|
WorkspacePlanFeatures.HideSpeckleBranding,
|
||||||
],
|
WorkspacePlanFeatures.ExclusiveMembership
|
||||||
limits: unlimited
|
],
|
||||||
},
|
limits: unlimited
|
||||||
[UnpaidWorkspacePlans.Academia]: {
|
},
|
||||||
plan: UnpaidWorkspacePlans.Academia,
|
[UnpaidWorkspacePlans.Academia]: {
|
||||||
features: [
|
plan: UnpaidWorkspacePlans.Academia,
|
||||||
...baseFeatures,
|
features: [
|
||||||
WorkspacePlanFeatures.DomainSecurity,
|
...finalBaseFeatures,
|
||||||
WorkspacePlanFeatures.SSO,
|
WorkspacePlanFeatures.DomainSecurity,
|
||||||
WorkspacePlanFeatures.CustomDataRegion,
|
WorkspacePlanFeatures.SSO,
|
||||||
WorkspacePlanFeatures.HideSpeckleBranding,
|
WorkspacePlanFeatures.CustomDataRegion,
|
||||||
...(params.featureFlags?.FF_SAVED_VIEWS_ENABLED
|
WorkspacePlanFeatures.HideSpeckleBranding
|
||||||
? [WorkspacePlanFeatures.SavedViews]
|
],
|
||||||
: [])
|
limits: unlimited
|
||||||
],
|
},
|
||||||
limits: unlimited
|
[UnpaidWorkspacePlans.TeamUnlimitedInvoiced]: {
|
||||||
},
|
...WorkspacePaidPlanConfigs(params).teamUnlimited,
|
||||||
[UnpaidWorkspacePlans.TeamUnlimitedInvoiced]: {
|
plan: UnpaidWorkspacePlans.TeamUnlimitedInvoiced
|
||||||
...WorkspacePaidPlanConfigs(params).teamUnlimited,
|
},
|
||||||
plan: UnpaidWorkspacePlans.TeamUnlimitedInvoiced
|
[UnpaidWorkspacePlans.ProUnlimitedInvoiced]: {
|
||||||
},
|
...WorkspacePaidPlanConfigs(params).proUnlimited,
|
||||||
[UnpaidWorkspacePlans.ProUnlimitedInvoiced]: {
|
plan: UnpaidWorkspacePlans.ProUnlimitedInvoiced
|
||||||
...WorkspacePaidPlanConfigs(params).proUnlimited,
|
},
|
||||||
plan: UnpaidWorkspacePlans.ProUnlimitedInvoiced
|
[UnpaidWorkspacePlans.Free]: {
|
||||||
},
|
plan: UnpaidWorkspacePlans.Free,
|
||||||
[UnpaidWorkspacePlans.Free]: {
|
features: finalBaseFeatures,
|
||||||
plan: UnpaidWorkspacePlans.Free,
|
limits: {
|
||||||
features: baseFeatures,
|
projectCount: 1,
|
||||||
limits: {
|
modelCount: 5,
|
||||||
projectCount: 1,
|
versionsHistory: { value: 7, unit: 'day' },
|
||||||
modelCount: 5,
|
commentHistory: { value: 7, unit: 'day' }
|
||||||
versionsHistory: { value: 7, unit: 'day' },
|
}
|
||||||
commentHistory: { value: 7, unit: 'day' }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
export const WorkspacePlanConfigs = (params: {
|
export const WorkspacePlanConfigs = (params: {
|
||||||
featureFlags: Partial<FeatureFlags> | undefined
|
featureFlags: Partial<FeatureFlags> | undefined
|
||||||
|
|||||||
+1
-21
@@ -1,23 +1,3 @@
|
|||||||
{
|
{
|
||||||
/* load each package separately, rather than as one giant progream */
|
"files": []
|
||||||
"files": [],
|
|
||||||
"references": [
|
|
||||||
{ "path": "packages/fileimport-service" },
|
|
||||||
{ "path": "packages/frontend-2" },
|
|
||||||
{ "path": "packages/monitor-deployment" },
|
|
||||||
{ "path": "packages/objectloader" },
|
|
||||||
{ "path": "packages/objectloader2" },
|
|
||||||
{ "path": "packages/objectsender" },
|
|
||||||
{ "path": "packages/preview-frontend" },
|
|
||||||
{ "path": "packages/preview-service" },
|
|
||||||
{ "path": "packages/server" },
|
|
||||||
{ "path": "packages/shared" },
|
|
||||||
{ "path": "packages/tailwind-theme" },
|
|
||||||
{ "path": "packages/ui-components" },
|
|
||||||
{ "path": "packages/ui-components-nuxt" },
|
|
||||||
{ "path": "packages/viewer" },
|
|
||||||
{ "path": "packages/viewer-sandbox" },
|
|
||||||
{ "path": "packages/webhook-service" }
|
|
||||||
/* …add all other packages listed in workspace.code-workspace */
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,7 @@
|
|||||||
"Prorotation"
|
"Prorotation"
|
||||||
],
|
],
|
||||||
"typescript.tsserver.maxTsServerMemory": 8192,
|
"typescript.tsserver.maxTsServerMemory": 8192,
|
||||||
|
"typescript.disableAutomaticTypeAcquisition": true,
|
||||||
"tailwindCSS.experimental.configFile": {
|
"tailwindCSS.experimental.configFile": {
|
||||||
"packages/frontend-2/tailwind.config.cjs": "packages/frontend-2/**"
|
"packages/frontend-2/tailwind.config.cjs": "packages/frontend-2/**"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user