Merge pull request #4328 from specklesystems/andrew/usage-limits-plan-composables

refactor(fe): usage/plan/limits composables
This commit is contained in:
andrewwallacespeckle
2025-04-04 17:34:06 +02:00
committed by GitHub
10 changed files with 296 additions and 247 deletions
@@ -1,64 +1,68 @@
<template>
<LayoutDialog v-model:open="open" max-width="sm" :buttons="dialogButtons">
<template #header>Move project to workspace</template>
<div class="flex flex-col space-y-4">
<ProjectsWorkspaceSelect
v-if="hasWorkspaces"
v-model="selectedWorkspace"
:items="workspaces"
:disabled-roles="[Roles.Workspace.Member, Roles.Workspace.Guest]"
disabled-item-tooltip="Only workspace admins can move projects into a workspace."
label="Select a workspace"
help="Once a project is moved to a workspace, it cannot be moved out from it."
show-label
/>
<div v-else class="flex flex-col gap-y-2">
<p class="text-body-xs text-foreground font-medium">
You're not a member of any workspaces.
</p>
<FormButton :to="workspaceCreateRoute">Create a workspace</FormButton>
</div>
<div>
<LayoutDialog v-model:open="open" max-width="sm" :buttons="dialogButtons">
<template #header>Move project to workspace</template>
<div class="flex flex-col space-y-4">
<template v-if="!workspace">
<ProjectsWorkspaceSelect
v-if="hasWorkspaces"
v-model="selectedWorkspace"
:items="workspaces"
:disabled-roles="[Roles.Workspace.Member, Roles.Workspace.Guest]"
disabled-item-tooltip="Only workspace admins can move projects into a workspace."
label="Select a workspace"
help="Once a project is moved to a workspace, it cannot be moved out from it."
show-label
/>
<div v-else class="flex flex-col gap-y-2">
<p class="text-body-xs text-foreground font-medium">
You're not a member of any workspaces.
</p>
<FormButton :to="workspaceCreateRoute">Learn about workspaces</FormButton>
</div>
</template>
<div v-if="project && selectedWorkspace" class="text-body-xs">
<div class="text-body-xs text-foreground flex flex-col gap-y-4">
<div class="rounded border bg-foundation-2 border-outline-3 py-2 px-4">
<p>
Move
<span class="font-medium">{{ project.name }}</span>
to
<span class="font-medium">
{{ selectedWorkspace?.name }}
<div v-if="project && (selectedWorkspace || workspace)" class="text-body-xs">
<div class="text-body-xs text-foreground flex flex-col gap-y-4">
<div class="rounded border bg-foundation-2 border-outline-3 py-2 px-4">
<p>
Move
<span class="font-medium">{{ project.name }}</span>
to
<span class="font-medium">
{{ selectedWorkspace?.name ?? workspace?.name }}
</span>
</p>
<p class="text-foreground-3">
{{ project.modelCount.totalCount }} {{ modelText }},
{{ project.versions.totalCount }} {{ versionsText }}
</p>
</div>
<p class="text-body-2xs text-foreground-2">
The project, including models and versions, will be moved to the
workspace, where all existing members and admins will have access.
<span class="pt-2 block">
The project's collaborators will become workspace members and keep their
project roles.
</span>
</p>
<p class="text-foreground-3">
{{ project.modelCount.totalCount }} {{ modelText }},
{{ project.versions.totalCount }} {{ versionsText }}
</p>
</div>
<p class="text-body-2xs text-foreground-2">
The project, including models and versions, will be moved to the workspace,
where all existing members and admins will have access.
<span class="pt-2 block">
The project's collaborators will become workspace members and keep their
project roles.
</span>
</p>
</div>
</div>
</div>
<WorkspaceRegionStaticDataDisclaimer
v-if="showRegionStaticDataDisclaimer"
v-model:open="showRegionStaticDataDisclaimer"
:variant="RegionStaticDataDisclaimerVariant.MoveProjectIntoWorkspace"
@confirm="onConfirmHandler"
/>
<WorkspaceRegionStaticDataDisclaimer
v-if="showRegionStaticDataDisclaimer"
v-model:open="showRegionStaticDataDisclaimer"
:variant="RegionStaticDataDisclaimerVariant.MoveProjectIntoWorkspace"
@confirm="onConfirmHandler"
/>
</LayoutDialog>
<WorkspacePlanLimitReachedDialog
v-if="activeLimit"
v-model:open="showLimitReachedDialog"
:limit="activeLimit"
:limit-type="limitType"
/>
</LayoutDialog>
</div>
</template>
<script setup lang="ts">
@@ -76,17 +80,15 @@ import {
useWorkspaceCustomDataResidencyDisclaimer,
RegionStaticDataDisclaimerVariant
} from '~/lib/workspaces/composables/region'
import { useWorkspacePlanLimits } from '~/lib/workspaces/composables/plan'
import { useWorkspaceLimits } from '~/lib/workspaces/composables/limits'
graphql(`
fragment ProjectsMoveToWorkspaceDialog_Workspace on Workspace {
id
role
projectCount: projects(limit: 0) {
totalCount
}
name
logo
slug
...WorkspaceHasCustomDataResidency_Workspace
...ProjectsWorkspaceSelect_Workspace
}
@@ -126,8 +128,10 @@ const query = graphql(`
const props = defineProps<{
project: ProjectsMoveToWorkspaceDialog_ProjectFragment
workspace?: ProjectsMoveToWorkspaceDialog_WorkspaceFragment
eventSource?: string // Used for mixpanel tracking
}>()
const open = defineModel<boolean>('open', { required: true })
const isWorkspacesEnabled = useIsWorkspacesEnabled()
@@ -138,6 +142,16 @@ const loading = useMutationLoading()
const moveProject = useMoveProjectToWorkspace()
const selectedWorkspace = ref<ProjectsMoveToWorkspaceDialog_WorkspaceFragment>()
const activeWorkspaceSlug = computed(
() => selectedWorkspace.value?.slug || props.workspace?.slug || ''
)
// Get workspace limits
const { canAddProject, canAddModels, limits } = useWorkspaceLimits(
activeWorkspaceSlug.value
)
const showLimitReachedDialog = ref(false)
const workspaces = computed(() => result.value?.activeUser?.workspaces.items ?? [])
@@ -149,16 +163,23 @@ const versionsText = computed(() =>
props.project.versions.totalCount === 1 ? 'version' : 'versions'
)
const modelCount = computed(() => {
return props.project.modelCount.totalCount
// Determine which limit type is hit
const limitType = computed((): 'project' | 'model' | null => {
if (!canAddProject.value) return 'project'
const projectModelCount = props.project.modelCount.totalCount
if (!canAddModels(projectModelCount)) return 'model'
return null
})
const projectCount = computed(() => {
return selectedWorkspace.value?.projectCount.totalCount ?? 0
// Get the value of the limit that's hit
const activeLimit = computed(() => {
if (limitType.value === 'project') return limits.value.projectCount ?? 0
if (limitType.value === 'model') return limits.value.modelCount ?? 0
return 0
})
const { limitType, activeLimit } = useWorkspacePlanLimits(projectCount, modelCount)
const dialogButtons = computed<LayoutDialogButton[]>(() => {
return hasWorkspaces.value
? [
@@ -173,7 +194,7 @@ const dialogButtons = computed<LayoutDialogButton[]>(() => {
text: 'Move',
props: {
color: 'primary',
disabled: !selectedWorkspace.value || loading.value
disabled: (!selectedWorkspace.value && !props.workspace) || loading.value
},
onClick: () => onMoveClick()
}
@@ -190,8 +211,8 @@ const dialogButtons = computed<LayoutDialogButton[]>(() => {
})
const onMoveProject = async () => {
const workspaceId = selectedWorkspace.value?.id
const workspaceName = selectedWorkspace.value?.name
const workspaceId = selectedWorkspace.value?.id ?? props.workspace?.id
const workspaceName = selectedWorkspace.value?.name ?? props.workspace?.name
if (!workspaceId || !workspaceName) return
const res = await moveProject({
@@ -207,7 +228,7 @@ const onMoveProject = async () => {
const { showRegionStaticDataDisclaimer, triggerAction, onConfirmHandler } =
useWorkspaceCustomDataResidencyDisclaimer({
workspace: computed(() => selectedWorkspace.value),
workspace: computed(() => selectedWorkspace.value ?? props.workspace),
onConfirmAction: onMoveProject
})
@@ -222,7 +243,11 @@ watch(
)
const onMoveClick = () => {
if (limitType.value) {
const projectModelCount = props.project.modelCount.totalCount
// Check if we can add this project to the workspace
if (!canAddProject.value || !canAddModels(projectModelCount)) {
open.value = false
showLimitReachedDialog.value = true
} else {
triggerAction()
@@ -18,9 +18,15 @@
/>
<div class="text-body-2xs py-2">
You can move up to
<span class="font-medium">{{ remainingProjects }} projects</span>
<span class="font-medium">
{{ Math.max(0, remainingProjectCount) }}
{{ remainingProjectCount === 1 ? 'project' : 'projects' }}
</span>
and
<span class="font-medium">{{ remainingModels }} models</span>
<span class="font-medium">
{{ Math.max(0, remainingModelCount) }}
{{ remainingModelCount === 1 ? 'model' : 'models' }}
</span>
in total.
</div>
<div
@@ -67,12 +73,6 @@
:project="selectedProject"
event-source="move-projects-dialog"
/>
<WorkspacePlanLimitReachedDialog
v-if="activeLimit"
v-model:open="showLimitReachedDialog"
:limit="activeLimit"
:limit-type="limitType"
/>
</LayoutDialog>
</template>
<script setup lang="ts">
@@ -89,10 +89,7 @@ import type {
import { usePaginatedQuery } from '~/lib/common/composables/graphql'
import { moveProjectsDialogQuery } from '~~/lib/workspaces/graphql/queries'
import { Roles } from '@speckle/shared'
import {
useWorkspacePlanLimits,
useGetWorkspacePlanUsage
} from '~/lib/workspaces/composables/plan'
import { useWorkspaceLimits } from '~/lib/workspaces/composables/limits'
graphql(`
fragment MoveProjectsDialog_Workspace on Workspace {
@@ -101,12 +98,6 @@ graphql(`
projects {
items {
id
modelCount: models(limit: 0) {
totalCount
}
versions(limit: 0) {
totalCount
}
}
}
}
@@ -161,12 +152,10 @@ const {
const selectedProject = ref<ProjectsMoveToWorkspaceDialog_ProjectFragment | null>(null)
const showMoveToWorkspaceDialog = ref(false)
const showLimitReachedDialog = ref(false)
const { projectCount, modelCount } = useGetWorkspacePlanUsage(props.workspace.slug)
const { remainingProjects, remainingModels, limitType, activeLimit } =
useWorkspacePlanLimits(projectCount, modelCount)
const { remainingModelCount, remainingProjectCount } = useWorkspaceLimits(
props.workspace.slug
)
const workspaceProjects = computed(() =>
props.workspace.projects.items.map((project) => project.id)
@@ -177,6 +166,7 @@ const moveableProjects = computed(() =>
userProjects.value.filter((project) => !workspaceProjects.value.includes(project.id))
)
const hasMoveableProjects = computed(() => moveableProjects.value.length > 0)
const buttons = computed((): LayoutDialogButton[] => [
{
text: 'Done',
@@ -189,10 +179,6 @@ const buttons = computed((): LayoutDialogButton[] => [
const onMoveClick = (project: ProjectsMoveToWorkspaceDialog_ProjectFragment) => {
selectedProject.value = project
if (!limitType.value) {
showMoveToWorkspaceDialog.value = true
} else {
showLimitReachedDialog.value = true
}
showMoveToWorkspaceDialog.value = true
}
</script>
@@ -105,7 +105,6 @@ graphql(`
...WorkspaceTeam_Workspace
...WorkspaceSecurity_Workspace
...BillingAlert_Workspace
...MoveProjectsDialog_Workspace
...InviteDialogWorkspace_Workspace
projects {
...WorkspaceProjectList_ProjectCollection
@@ -14,7 +14,8 @@
<div class="flex flex-col justify-between h-full px-5 py-4">
<NuxtImg src="/images/logo.png" alt="Speckle logo" class="h-8 w-8" />
<h3 class="text-white limit-reached-text-shadow text-base">
Plan limit reached.
<span class="capitalize">{{ props.limitType }}</span>
limit reached.
</h3>
</div>
</div>
@@ -46,7 +47,7 @@ import { settingsWorkspaceRoutes } from '~/lib/common/helpers/route'
const isOpen = defineModel<boolean>('open', { required: true })
const props = defineProps<{
limit: number
limit: Nullable<number>
limitType: Nullable<'project' | 'model'>
}>()
</script>
@@ -101,7 +101,7 @@ type Documents = {
"\n fragment ProjectsDashboardFilledUser on UserProjectCollection {\n items {\n ...ProjectDashboardItem\n }\n }\n": typeof types.ProjectsDashboardFilledUserFragmentDoc,
"\n fragment ProjectsDeleteDialog_Project on Project {\n id\n name\n role\n models(limit: 0) {\n totalCount\n }\n workspace {\n slug\n id\n }\n versions(limit: 0) {\n totalCount\n }\n }\n": typeof types.ProjectsDeleteDialog_ProjectFragmentDoc,
"\n fragment ProjectsHiddenProjectWarning_User on User {\n id\n expiredSsoSessions {\n id\n slug\n name\n logo\n }\n }\n": typeof types.ProjectsHiddenProjectWarning_UserFragmentDoc,
"\n fragment ProjectsMoveToWorkspaceDialog_Workspace on Workspace {\n id\n role\n projectCount: projects(limit: 0) {\n totalCount\n }\n name\n logo\n ...WorkspaceHasCustomDataResidency_Workspace\n ...ProjectsWorkspaceSelect_Workspace\n }\n": typeof types.ProjectsMoveToWorkspaceDialog_WorkspaceFragmentDoc,
"\n fragment ProjectsMoveToWorkspaceDialog_Workspace on Workspace {\n id\n role\n name\n logo\n slug\n ...WorkspaceHasCustomDataResidency_Workspace\n ...ProjectsWorkspaceSelect_Workspace\n }\n": typeof types.ProjectsMoveToWorkspaceDialog_WorkspaceFragmentDoc,
"\n fragment ProjectsMoveToWorkspaceDialog_User on User {\n workspaces {\n items {\n ...ProjectsMoveToWorkspaceDialog_Workspace\n }\n }\n }\n": typeof types.ProjectsMoveToWorkspaceDialog_UserFragmentDoc,
"\n fragment ProjectsMoveToWorkspaceDialog_Project on Project {\n id\n name\n modelCount: models(limit: 0) {\n totalCount\n }\n versions(limit: 0) {\n totalCount\n }\n }\n": typeof types.ProjectsMoveToWorkspaceDialog_ProjectFragmentDoc,
"\n query ProjectsMoveToWorkspaceDialog {\n activeUser {\n id\n ...ProjectsMoveToWorkspaceDialog_User\n }\n }\n": typeof types.ProjectsMoveToWorkspaceDialogDocument,
@@ -136,9 +136,9 @@ type Documents = {
"\n fragment ThreadCommentAttachment on Comment {\n text {\n attachments {\n id\n fileName\n fileType\n fileSize\n }\n }\n }\n": typeof types.ThreadCommentAttachmentFragmentDoc,
"\n fragment ViewerCommentsListItem on Comment {\n id\n rawText\n archived\n author {\n ...LimitedUserAvatar\n }\n createdAt\n viewedAt\n replies {\n totalCount\n cursor\n items {\n ...ViewerCommentsReplyItem\n }\n }\n replyAuthors(limit: 4) {\n totalCount\n items {\n ...FormUsersSelectItem\n }\n }\n resources {\n resourceId\n resourceType\n }\n }\n": typeof types.ViewerCommentsListItemFragmentDoc,
"\n fragment ViewerModelVersionCardItem on Version {\n id\n message\n referencedObject\n sourceApplication\n createdAt\n previewUrl\n authorUser {\n ...LimitedUserAvatar\n }\n }\n": typeof types.ViewerModelVersionCardItemFragmentDoc,
"\n fragment MoveProjectsDialog_Workspace on Workspace {\n id\n ...ProjectsMoveToWorkspaceDialog_Workspace\n projects {\n items {\n id\n modelCount: models(limit: 0) {\n totalCount\n }\n versions(limit: 0) {\n totalCount\n }\n }\n }\n }\n": typeof types.MoveProjectsDialog_WorkspaceFragmentDoc,
"\n fragment MoveProjectsDialog_Workspace on Workspace {\n id\n ...ProjectsMoveToWorkspaceDialog_Workspace\n projects {\n items {\n id\n }\n }\n }\n": typeof types.MoveProjectsDialog_WorkspaceFragmentDoc,
"\n fragment MoveProjectsDialog_User on User {\n projects(cursor: $cursor, filter: $filter, limit: 10) {\n totalCount\n cursor\n items {\n ...ProjectsMoveToWorkspaceDialog_Project\n role\n workspace {\n id\n }\n }\n }\n }\n": typeof types.MoveProjectsDialog_UserFragmentDoc,
"\n fragment WorkspaceProjectList_Workspace on Workspace {\n id\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n ...BillingAlert_Workspace\n ...MoveProjectsDialog_Workspace\n ...InviteDialogWorkspace_Workspace\n projects {\n ...WorkspaceProjectList_ProjectCollection\n }\n creationState {\n completed\n state\n }\n readOnly\n }\n": typeof types.WorkspaceProjectList_WorkspaceFragmentDoc,
"\n fragment WorkspaceProjectList_Workspace on Workspace {\n id\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n ...BillingAlert_Workspace\n ...InviteDialogWorkspace_Workspace\n projects {\n ...WorkspaceProjectList_ProjectCollection\n }\n creationState {\n completed\n state\n }\n readOnly\n }\n": typeof types.WorkspaceProjectList_WorkspaceFragmentDoc,
"\n fragment WorkspaceProjectList_ProjectCollection on ProjectCollection {\n totalCount\n items {\n ...ProjectDashboardItem\n }\n cursor\n }\n": typeof types.WorkspaceProjectList_ProjectCollectionFragmentDoc,
"\n fragment WorkspaceHeader_Workspace on Workspace {\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...BillingAlert_Workspace\n slug\n readOnly\n }\n": typeof types.WorkspaceHeader_WorkspaceFragmentDoc,
"\n fragment WorkspaceInviteBanner_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.WorkspaceInviteBanner_PendingWorkspaceCollaboratorFragmentDoc,
@@ -349,14 +349,15 @@ type Documents = {
"\n fragment LinkableComment on Comment {\n id\n viewerResources {\n modelId\n versionId\n objectId\n }\n }\n": typeof types.LinkableCommentFragmentDoc,
"\n fragment DiscoverableList_Discoverable on User {\n discoverableWorkspaces {\n id\n name\n logo\n description\n slug\n team {\n totalCount\n items {\n avatar\n }\n }\n }\n }\n": typeof types.DiscoverableList_DiscoverableFragmentDoc,
"\n fragment DiscoverableList_Requests on User {\n workspaceJoinRequests {\n items {\n id\n status\n workspace {\n id\n name\n logo\n slug\n team {\n totalCount\n items {\n avatar\n }\n }\n }\n }\n }\n }\n": typeof types.DiscoverableList_RequestsFragmentDoc,
"\n fragment WorkspacePlanLimits_Workspace on Workspace {\n id\n plan {\n name\n }\n }\n": typeof types.WorkspacePlanLimits_WorkspaceFragmentDoc,
"\n fragment UseWorkspaceInviteManager_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n token\n workspaceId\n workspaceSlug\n user {\n id\n }\n }\n": typeof types.UseWorkspaceInviteManager_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment WorkspacesPlan_Workspace on Workspace {\n id\n plan {\n status\n createdAt\n name\n paymentMethod\n usage {\n projectCount\n modelCount\n }\n }\n subscription {\n billingInterval\n currentBillingCycleEnd\n seats {\n editors {\n assigned\n available\n }\n viewers {\n assigned\n available\n }\n }\n }\n }\n": typeof types.WorkspacesPlan_WorkspaceFragmentDoc,
"\n fragment WorkspacePlanLimits_Workspace on Workspace {\n id\n projects(limit: 0) {\n totalCount\n items {\n id\n models(limit: 0) {\n totalCount\n }\n }\n }\n plan {\n name\n }\n }\n": typeof types.WorkspacePlanLimits_WorkspaceFragmentDoc,
"\n fragment WorkspacesPlan_Workspace on Workspace {\n id\n plan {\n status\n createdAt\n name\n paymentMethod\n usage {\n projectCount\n modelCount\n }\n }\n subscription {\n billingInterval\n currentBillingCycleEnd\n seats {\n editors {\n available\n assigned\n }\n viewers {\n available\n assigned\n }\n }\n }\n }\n": typeof types.WorkspacesPlan_WorkspaceFragmentDoc,
"\n subscription OnWorkspaceProjectsUpdate($slug: String!) {\n workspaceProjectsUpdated(workspaceId: null, workspaceSlug: $slug) {\n projectId\n workspaceId\n type\n project {\n ...ProjectDashboardItem\n }\n }\n }\n ": typeof types.OnWorkspaceProjectsUpdateDocument,
"\n fragment WorkspaceHasCustomDataResidency_Workspace on Workspace {\n id\n defaultRegion {\n id\n name\n }\n }\n": typeof types.WorkspaceHasCustomDataResidency_WorkspaceFragmentDoc,
"\n query CheckProjectWorkspaceDataResidency($projectId: String!) {\n project(id: $projectId) {\n id\n workspace {\n ...WorkspaceHasCustomDataResidency_Workspace\n }\n }\n }\n": typeof types.CheckProjectWorkspaceDataResidencyDocument,
"\n fragment WorkspaceSsoStatus_Workspace on Workspace {\n id\n sso {\n provider {\n id\n name\n clientId\n issuerUrl\n }\n session {\n validUntil\n }\n }\n }\n ": typeof types.WorkspaceSsoStatus_WorkspaceFragmentDoc,
"\n fragment WorkspaceSsoStatus_User on User {\n expiredSsoSessions {\n id\n slug\n }\n }\n ": typeof types.WorkspaceSsoStatus_UserFragmentDoc,
"\n fragment WorkspaceUsage_Workspace on Workspace {\n id\n plan {\n usage {\n projectCount\n modelCount\n }\n }\n }\n": typeof types.WorkspaceUsage_WorkspaceFragmentDoc,
"\n fragment WorkspaceBase_Workspace on Workspace {\n id\n name\n slug\n role\n description\n logo\n plan {\n status\n createdAt\n }\n }\n": typeof types.WorkspaceBase_WorkspaceFragmentDoc,
"\n fragment WorkspaceDashboardAbout_Workspace on Workspace {\n id\n name\n description\n }\n": typeof types.WorkspaceDashboardAbout_WorkspaceFragmentDoc,
"\n fragment WorkspaceInvitedTeam_Workspace on Workspace {\n id\n invitedTeam(filter: $invitesFilter) {\n id\n role\n email\n }\n }\n": typeof types.WorkspaceInvitedTeam_WorkspaceFragmentDoc,
@@ -392,7 +393,8 @@ type Documents = {
"\n query DiscoverableWorkspacesRequests {\n activeUser {\n id\n ...DiscoverableList_Requests\n }\n }\n": typeof types.DiscoverableWorkspacesRequestsDocument,
"\n query WorkspacePlan($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspacesPlan_Workspace\n }\n }\n": typeof types.WorkspacePlanDocument,
"\n query WorkspaceLastAdminCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspaceLastAdminCheck_Workspace\n }\n }\n": typeof types.WorkspaceLastAdminCheckDocument,
"\n query WorkspacePlanLimits($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspacePlanLimits_Workspace\n }\n }\n": typeof types.WorkspacePlanLimitsDocument,
"\n query WorkspaceLimits($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspacePlanLimits_Workspace\n }\n }\n": typeof types.WorkspaceLimitsDocument,
"\n query WorkspaceUsage($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspaceUsage_Workspace\n }\n }\n": typeof types.WorkspaceUsageDocument,
"\n subscription onWorkspaceUpdated(\n $workspaceId: String\n $workspaceSlug: String\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceUpdated(workspaceId: $workspaceId, workspaceSlug: $workspaceSlug) {\n id\n workspace {\n id\n ...WorkspaceProjectList_Workspace\n }\n }\n }\n": typeof types.OnWorkspaceUpdatedDocument,
"\n query LegacyBranchRedirectMetadata($streamId: String!, $branchName: String!) {\n project(id: $streamId) {\n modelByName(name: $branchName) {\n id\n }\n }\n }\n": typeof types.LegacyBranchRedirectMetadataDocument,
"\n query LegacyViewerCommitRedirectMetadata($streamId: String!, $commitId: String!) {\n project(id: $streamId) {\n version(id: $commitId) {\n id\n model {\n id\n }\n }\n }\n }\n": typeof types.LegacyViewerCommitRedirectMetadataDocument,
@@ -503,7 +505,7 @@ const documents: Documents = {
"\n fragment ProjectsDashboardFilledUser on UserProjectCollection {\n items {\n ...ProjectDashboardItem\n }\n }\n": types.ProjectsDashboardFilledUserFragmentDoc,
"\n fragment ProjectsDeleteDialog_Project on Project {\n id\n name\n role\n models(limit: 0) {\n totalCount\n }\n workspace {\n slug\n id\n }\n versions(limit: 0) {\n totalCount\n }\n }\n": types.ProjectsDeleteDialog_ProjectFragmentDoc,
"\n fragment ProjectsHiddenProjectWarning_User on User {\n id\n expiredSsoSessions {\n id\n slug\n name\n logo\n }\n }\n": types.ProjectsHiddenProjectWarning_UserFragmentDoc,
"\n fragment ProjectsMoveToWorkspaceDialog_Workspace on Workspace {\n id\n role\n projectCount: projects(limit: 0) {\n totalCount\n }\n name\n logo\n ...WorkspaceHasCustomDataResidency_Workspace\n ...ProjectsWorkspaceSelect_Workspace\n }\n": types.ProjectsMoveToWorkspaceDialog_WorkspaceFragmentDoc,
"\n fragment ProjectsMoveToWorkspaceDialog_Workspace on Workspace {\n id\n role\n name\n logo\n slug\n ...WorkspaceHasCustomDataResidency_Workspace\n ...ProjectsWorkspaceSelect_Workspace\n }\n": types.ProjectsMoveToWorkspaceDialog_WorkspaceFragmentDoc,
"\n fragment ProjectsMoveToWorkspaceDialog_User on User {\n workspaces {\n items {\n ...ProjectsMoveToWorkspaceDialog_Workspace\n }\n }\n }\n": types.ProjectsMoveToWorkspaceDialog_UserFragmentDoc,
"\n fragment ProjectsMoveToWorkspaceDialog_Project on Project {\n id\n name\n modelCount: models(limit: 0) {\n totalCount\n }\n versions(limit: 0) {\n totalCount\n }\n }\n": types.ProjectsMoveToWorkspaceDialog_ProjectFragmentDoc,
"\n query ProjectsMoveToWorkspaceDialog {\n activeUser {\n id\n ...ProjectsMoveToWorkspaceDialog_User\n }\n }\n": types.ProjectsMoveToWorkspaceDialogDocument,
@@ -538,9 +540,9 @@ const documents: Documents = {
"\n fragment ThreadCommentAttachment on Comment {\n text {\n attachments {\n id\n fileName\n fileType\n fileSize\n }\n }\n }\n": types.ThreadCommentAttachmentFragmentDoc,
"\n fragment ViewerCommentsListItem on Comment {\n id\n rawText\n archived\n author {\n ...LimitedUserAvatar\n }\n createdAt\n viewedAt\n replies {\n totalCount\n cursor\n items {\n ...ViewerCommentsReplyItem\n }\n }\n replyAuthors(limit: 4) {\n totalCount\n items {\n ...FormUsersSelectItem\n }\n }\n resources {\n resourceId\n resourceType\n }\n }\n": types.ViewerCommentsListItemFragmentDoc,
"\n fragment ViewerModelVersionCardItem on Version {\n id\n message\n referencedObject\n sourceApplication\n createdAt\n previewUrl\n authorUser {\n ...LimitedUserAvatar\n }\n }\n": types.ViewerModelVersionCardItemFragmentDoc,
"\n fragment MoveProjectsDialog_Workspace on Workspace {\n id\n ...ProjectsMoveToWorkspaceDialog_Workspace\n projects {\n items {\n id\n modelCount: models(limit: 0) {\n totalCount\n }\n versions(limit: 0) {\n totalCount\n }\n }\n }\n }\n": types.MoveProjectsDialog_WorkspaceFragmentDoc,
"\n fragment MoveProjectsDialog_Workspace on Workspace {\n id\n ...ProjectsMoveToWorkspaceDialog_Workspace\n projects {\n items {\n id\n }\n }\n }\n": types.MoveProjectsDialog_WorkspaceFragmentDoc,
"\n fragment MoveProjectsDialog_User on User {\n projects(cursor: $cursor, filter: $filter, limit: 10) {\n totalCount\n cursor\n items {\n ...ProjectsMoveToWorkspaceDialog_Project\n role\n workspace {\n id\n }\n }\n }\n }\n": types.MoveProjectsDialog_UserFragmentDoc,
"\n fragment WorkspaceProjectList_Workspace on Workspace {\n id\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n ...BillingAlert_Workspace\n ...MoveProjectsDialog_Workspace\n ...InviteDialogWorkspace_Workspace\n projects {\n ...WorkspaceProjectList_ProjectCollection\n }\n creationState {\n completed\n state\n }\n readOnly\n }\n": types.WorkspaceProjectList_WorkspaceFragmentDoc,
"\n fragment WorkspaceProjectList_Workspace on Workspace {\n id\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n ...BillingAlert_Workspace\n ...InviteDialogWorkspace_Workspace\n projects {\n ...WorkspaceProjectList_ProjectCollection\n }\n creationState {\n completed\n state\n }\n readOnly\n }\n": types.WorkspaceProjectList_WorkspaceFragmentDoc,
"\n fragment WorkspaceProjectList_ProjectCollection on ProjectCollection {\n totalCount\n items {\n ...ProjectDashboardItem\n }\n cursor\n }\n": types.WorkspaceProjectList_ProjectCollectionFragmentDoc,
"\n fragment WorkspaceHeader_Workspace on Workspace {\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...BillingAlert_Workspace\n slug\n readOnly\n }\n": types.WorkspaceHeader_WorkspaceFragmentDoc,
"\n fragment WorkspaceInviteBanner_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.WorkspaceInviteBanner_PendingWorkspaceCollaboratorFragmentDoc,
@@ -751,14 +753,15 @@ const documents: Documents = {
"\n fragment LinkableComment on Comment {\n id\n viewerResources {\n modelId\n versionId\n objectId\n }\n }\n": types.LinkableCommentFragmentDoc,
"\n fragment DiscoverableList_Discoverable on User {\n discoverableWorkspaces {\n id\n name\n logo\n description\n slug\n team {\n totalCount\n items {\n avatar\n }\n }\n }\n }\n": types.DiscoverableList_DiscoverableFragmentDoc,
"\n fragment DiscoverableList_Requests on User {\n workspaceJoinRequests {\n items {\n id\n status\n workspace {\n id\n name\n logo\n slug\n team {\n totalCount\n items {\n avatar\n }\n }\n }\n }\n }\n }\n": types.DiscoverableList_RequestsFragmentDoc,
"\n fragment WorkspacePlanLimits_Workspace on Workspace {\n id\n plan {\n name\n }\n }\n": types.WorkspacePlanLimits_WorkspaceFragmentDoc,
"\n fragment UseWorkspaceInviteManager_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n token\n workspaceId\n workspaceSlug\n user {\n id\n }\n }\n": types.UseWorkspaceInviteManager_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment WorkspacesPlan_Workspace on Workspace {\n id\n plan {\n status\n createdAt\n name\n paymentMethod\n usage {\n projectCount\n modelCount\n }\n }\n subscription {\n billingInterval\n currentBillingCycleEnd\n seats {\n editors {\n assigned\n available\n }\n viewers {\n assigned\n available\n }\n }\n }\n }\n": types.WorkspacesPlan_WorkspaceFragmentDoc,
"\n fragment WorkspacePlanLimits_Workspace on Workspace {\n id\n projects(limit: 0) {\n totalCount\n items {\n id\n models(limit: 0) {\n totalCount\n }\n }\n }\n plan {\n name\n }\n }\n": types.WorkspacePlanLimits_WorkspaceFragmentDoc,
"\n fragment WorkspacesPlan_Workspace on Workspace {\n id\n plan {\n status\n createdAt\n name\n paymentMethod\n usage {\n projectCount\n modelCount\n }\n }\n subscription {\n billingInterval\n currentBillingCycleEnd\n seats {\n editors {\n available\n assigned\n }\n viewers {\n available\n assigned\n }\n }\n }\n }\n": types.WorkspacesPlan_WorkspaceFragmentDoc,
"\n subscription OnWorkspaceProjectsUpdate($slug: String!) {\n workspaceProjectsUpdated(workspaceId: null, workspaceSlug: $slug) {\n projectId\n workspaceId\n type\n project {\n ...ProjectDashboardItem\n }\n }\n }\n ": types.OnWorkspaceProjectsUpdateDocument,
"\n fragment WorkspaceHasCustomDataResidency_Workspace on Workspace {\n id\n defaultRegion {\n id\n name\n }\n }\n": types.WorkspaceHasCustomDataResidency_WorkspaceFragmentDoc,
"\n query CheckProjectWorkspaceDataResidency($projectId: String!) {\n project(id: $projectId) {\n id\n workspace {\n ...WorkspaceHasCustomDataResidency_Workspace\n }\n }\n }\n": types.CheckProjectWorkspaceDataResidencyDocument,
"\n fragment WorkspaceSsoStatus_Workspace on Workspace {\n id\n sso {\n provider {\n id\n name\n clientId\n issuerUrl\n }\n session {\n validUntil\n }\n }\n }\n ": types.WorkspaceSsoStatus_WorkspaceFragmentDoc,
"\n fragment WorkspaceSsoStatus_User on User {\n expiredSsoSessions {\n id\n slug\n }\n }\n ": types.WorkspaceSsoStatus_UserFragmentDoc,
"\n fragment WorkspaceUsage_Workspace on Workspace {\n id\n plan {\n usage {\n projectCount\n modelCount\n }\n }\n }\n": types.WorkspaceUsage_WorkspaceFragmentDoc,
"\n fragment WorkspaceBase_Workspace on Workspace {\n id\n name\n slug\n role\n description\n logo\n plan {\n status\n createdAt\n }\n }\n": types.WorkspaceBase_WorkspaceFragmentDoc,
"\n fragment WorkspaceDashboardAbout_Workspace on Workspace {\n id\n name\n description\n }\n": types.WorkspaceDashboardAbout_WorkspaceFragmentDoc,
"\n fragment WorkspaceInvitedTeam_Workspace on Workspace {\n id\n invitedTeam(filter: $invitesFilter) {\n id\n role\n email\n }\n }\n": types.WorkspaceInvitedTeam_WorkspaceFragmentDoc,
@@ -794,7 +797,8 @@ const documents: Documents = {
"\n query DiscoverableWorkspacesRequests {\n activeUser {\n id\n ...DiscoverableList_Requests\n }\n }\n": types.DiscoverableWorkspacesRequestsDocument,
"\n query WorkspacePlan($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspacesPlan_Workspace\n }\n }\n": types.WorkspacePlanDocument,
"\n query WorkspaceLastAdminCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspaceLastAdminCheck_Workspace\n }\n }\n": types.WorkspaceLastAdminCheckDocument,
"\n query WorkspacePlanLimits($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspacePlanLimits_Workspace\n }\n }\n": types.WorkspacePlanLimitsDocument,
"\n query WorkspaceLimits($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspacePlanLimits_Workspace\n }\n }\n": types.WorkspaceLimitsDocument,
"\n query WorkspaceUsage($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspaceUsage_Workspace\n }\n }\n": types.WorkspaceUsageDocument,
"\n subscription onWorkspaceUpdated(\n $workspaceId: String\n $workspaceSlug: String\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceUpdated(workspaceId: $workspaceId, workspaceSlug: $workspaceSlug) {\n id\n workspace {\n id\n ...WorkspaceProjectList_Workspace\n }\n }\n }\n": types.OnWorkspaceUpdatedDocument,
"\n query LegacyBranchRedirectMetadata($streamId: String!, $branchName: String!) {\n project(id: $streamId) {\n modelByName(name: $branchName) {\n id\n }\n }\n }\n": types.LegacyBranchRedirectMetadataDocument,
"\n query LegacyViewerCommitRedirectMetadata($streamId: String!, $commitId: String!) {\n project(id: $streamId) {\n version(id: $commitId) {\n id\n model {\n id\n }\n }\n }\n }\n": types.LegacyViewerCommitRedirectMetadataDocument,
@@ -1183,7 +1187,7 @@ export function graphql(source: "\n fragment ProjectsHiddenProjectWarning_User
/**
* 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 ProjectsMoveToWorkspaceDialog_Workspace on Workspace {\n id\n role\n projectCount: projects(limit: 0) {\n totalCount\n }\n name\n logo\n ...WorkspaceHasCustomDataResidency_Workspace\n ...ProjectsWorkspaceSelect_Workspace\n }\n"): (typeof documents)["\n fragment ProjectsMoveToWorkspaceDialog_Workspace on Workspace {\n id\n role\n projectCount: projects(limit: 0) {\n totalCount\n }\n name\n logo\n ...WorkspaceHasCustomDataResidency_Workspace\n ...ProjectsWorkspaceSelect_Workspace\n }\n"];
export function graphql(source: "\n fragment ProjectsMoveToWorkspaceDialog_Workspace on Workspace {\n id\n role\n name\n logo\n slug\n ...WorkspaceHasCustomDataResidency_Workspace\n ...ProjectsWorkspaceSelect_Workspace\n }\n"): (typeof documents)["\n fragment ProjectsMoveToWorkspaceDialog_Workspace on Workspace {\n id\n role\n name\n logo\n slug\n ...WorkspaceHasCustomDataResidency_Workspace\n ...ProjectsWorkspaceSelect_Workspace\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1323,7 +1327,7 @@ export function graphql(source: "\n fragment ViewerModelVersionCardItem on Vers
/**
* 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 MoveProjectsDialog_Workspace on Workspace {\n id\n ...ProjectsMoveToWorkspaceDialog_Workspace\n projects {\n items {\n id\n modelCount: models(limit: 0) {\n totalCount\n }\n versions(limit: 0) {\n totalCount\n }\n }\n }\n }\n"): (typeof documents)["\n fragment MoveProjectsDialog_Workspace on Workspace {\n id\n ...ProjectsMoveToWorkspaceDialog_Workspace\n projects {\n items {\n id\n modelCount: models(limit: 0) {\n totalCount\n }\n versions(limit: 0) {\n totalCount\n }\n }\n }\n }\n"];
export function graphql(source: "\n fragment MoveProjectsDialog_Workspace on Workspace {\n id\n ...ProjectsMoveToWorkspaceDialog_Workspace\n projects {\n items {\n id\n }\n }\n }\n"): (typeof documents)["\n fragment MoveProjectsDialog_Workspace on Workspace {\n id\n ...ProjectsMoveToWorkspaceDialog_Workspace\n projects {\n items {\n id\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1331,7 +1335,7 @@ export function graphql(source: "\n fragment MoveProjectsDialog_User on User {\
/**
* 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 WorkspaceProjectList_Workspace on Workspace {\n id\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n ...BillingAlert_Workspace\n ...MoveProjectsDialog_Workspace\n ...InviteDialogWorkspace_Workspace\n projects {\n ...WorkspaceProjectList_ProjectCollection\n }\n creationState {\n completed\n state\n }\n readOnly\n }\n"): (typeof documents)["\n fragment WorkspaceProjectList_Workspace on Workspace {\n id\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n ...BillingAlert_Workspace\n ...MoveProjectsDialog_Workspace\n ...InviteDialogWorkspace_Workspace\n projects {\n ...WorkspaceProjectList_ProjectCollection\n }\n creationState {\n completed\n state\n }\n readOnly\n }\n"];
export function graphql(source: "\n fragment WorkspaceProjectList_Workspace on Workspace {\n id\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n ...BillingAlert_Workspace\n ...InviteDialogWorkspace_Workspace\n projects {\n ...WorkspaceProjectList_ProjectCollection\n }\n creationState {\n completed\n state\n }\n readOnly\n }\n"): (typeof documents)["\n fragment WorkspaceProjectList_Workspace on Workspace {\n id\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n ...BillingAlert_Workspace\n ...InviteDialogWorkspace_Workspace\n projects {\n ...WorkspaceProjectList_ProjectCollection\n }\n creationState {\n completed\n state\n }\n readOnly\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -2172,6 +2176,10 @@ export function graphql(source: "\n fragment DiscoverableList_Discoverable on U
* 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 DiscoverableList_Requests on User {\n workspaceJoinRequests {\n items {\n id\n status\n workspace {\n id\n name\n logo\n slug\n team {\n totalCount\n items {\n avatar\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n fragment DiscoverableList_Requests on User {\n workspaceJoinRequests {\n items {\n id\n status\n workspace {\n id\n name\n logo\n slug\n team {\n totalCount\n items {\n avatar\n }\n }\n }\n }\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 WorkspacePlanLimits_Workspace on Workspace {\n id\n plan {\n name\n }\n }\n"): (typeof documents)["\n fragment WorkspacePlanLimits_Workspace on Workspace {\n id\n plan {\n name\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -2179,11 +2187,7 @@ export function graphql(source: "\n fragment UseWorkspaceInviteManager_PendingW
/**
* 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 WorkspacesPlan_Workspace on Workspace {\n id\n plan {\n status\n createdAt\n name\n paymentMethod\n usage {\n projectCount\n modelCount\n }\n }\n subscription {\n billingInterval\n currentBillingCycleEnd\n seats {\n editors {\n assigned\n available\n }\n viewers {\n assigned\n available\n }\n }\n }\n }\n"): (typeof documents)["\n fragment WorkspacesPlan_Workspace on Workspace {\n id\n plan {\n status\n createdAt\n name\n paymentMethod\n usage {\n projectCount\n modelCount\n }\n }\n subscription {\n billingInterval\n currentBillingCycleEnd\n seats {\n editors {\n assigned\n available\n }\n viewers {\n assigned\n available\n }\n }\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 WorkspacePlanLimits_Workspace on Workspace {\n id\n projects(limit: 0) {\n totalCount\n items {\n id\n models(limit: 0) {\n totalCount\n }\n }\n }\n plan {\n name\n }\n }\n"): (typeof documents)["\n fragment WorkspacePlanLimits_Workspace on Workspace {\n id\n projects(limit: 0) {\n totalCount\n items {\n id\n models(limit: 0) {\n totalCount\n }\n }\n }\n plan {\n name\n }\n }\n"];
export function graphql(source: "\n fragment WorkspacesPlan_Workspace on Workspace {\n id\n plan {\n status\n createdAt\n name\n paymentMethod\n usage {\n projectCount\n modelCount\n }\n }\n subscription {\n billingInterval\n currentBillingCycleEnd\n seats {\n editors {\n available\n assigned\n }\n viewers {\n available\n assigned\n }\n }\n }\n }\n"): (typeof documents)["\n fragment WorkspacesPlan_Workspace on Workspace {\n id\n plan {\n status\n createdAt\n name\n paymentMethod\n usage {\n projectCount\n modelCount\n }\n }\n subscription {\n billingInterval\n currentBillingCycleEnd\n seats {\n editors {\n available\n assigned\n }\n viewers {\n available\n assigned\n }\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -2204,6 +2208,10 @@ export function graphql(source: "\n fragment WorkspaceSsoStatus_Workspace on
* 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 WorkspaceSsoStatus_User on User {\n expiredSsoSessions {\n id\n slug\n }\n }\n "): (typeof documents)["\n fragment WorkspaceSsoStatus_User on User {\n expiredSsoSessions {\n id\n slug\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 WorkspaceUsage_Workspace on Workspace {\n id\n plan {\n usage {\n projectCount\n modelCount\n }\n }\n }\n"): (typeof documents)["\n fragment WorkspaceUsage_Workspace on Workspace {\n id\n plan {\n usage {\n projectCount\n modelCount\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -2347,7 +2355,11 @@ export function graphql(source: "\n query WorkspaceLastAdminCheck($slug: String
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query WorkspacePlanLimits($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspacePlanLimits_Workspace\n }\n }\n"): (typeof documents)["\n query WorkspacePlanLimits($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspacePlanLimits_Workspace\n }\n }\n"];
export function graphql(source: "\n query WorkspaceLimits($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspacePlanLimits_Workspace\n }\n }\n"): (typeof documents)["\n query WorkspaceLimits($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspacePlanLimits_Workspace\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 query WorkspaceUsage($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspaceUsage_Workspace\n }\n }\n"): (typeof documents)["\n query WorkspaceUsage($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspaceUsage_Workspace\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
@@ -0,0 +1,71 @@
import { graphql } from '~/lib/common/generated/gql/gql'
import { useQuery } from '@vue/apollo-composable'
import { workspaceLimitsQuery } from '~/lib/workspaces/graphql/queries'
import { WorkspacePlanConfigs } from '@speckle/shared'
import { useWorkspaceUsage } from '~/lib/workspaces/composables/usage'
graphql(`
fragment WorkspacePlanLimits_Workspace on Workspace {
id
plan {
name
}
}
`)
export const useWorkspaceLimits = (slug: string) => {
const { modelCount, projectCount } = useWorkspaceUsage(slug)
const { result } = useQuery(
workspaceLimitsQuery,
() => ({
slug
}),
() => ({
enabled: !!slug
})
)
// Plan limits
const limits = computed(() => {
const planName = result.value?.workspaceBySlug?.plan?.name
if (!planName) return { projectCount: 0, modelCount: 0 }
const planConfig = WorkspacePlanConfigs[planName]
return planConfig?.limits
})
const remainingProjectCount = computed(() =>
limits.value.projectCount ? limits.value.projectCount - projectCount.value : 0
)
const remainingModelCount = computed(() =>
limits.value.modelCount ? limits.value.modelCount - modelCount.value : 0
)
const canAddProject = computed(() => {
// Unlimited
if (limits.value.projectCount === null) return true
return projectCount.value + 1 <= limits.value.projectCount
})
const canAddModels = (additionalModels?: number) => {
// Unlimited
if (limits.value.modelCount === null) return true
if (!additionalModels) {
return remainingModelCount.value > 0
}
return modelCount.value + additionalModels <= limits.value.modelCount
}
return {
projectCount,
modelCount,
limits,
remainingProjectCount,
remainingModelCount,
canAddProject,
canAddModels
}
}
@@ -1,8 +1,5 @@
import { graphql } from '~~/lib/common/generated/gql'
import {
workspacePlanLimitsQuery,
workspacePlanQuery
} from '~~/lib/workspaces/graphql/queries'
import { workspacePlanQuery } from '~~/lib/workspaces/graphql/queries'
import { useQuery } from '@vue/apollo-composable'
import {
isNewWorkspacePlan,
@@ -149,103 +146,3 @@ export const useWorkspacePlan = (slug: string) => {
editorSeats
}
}
graphql(`
fragment WorkspacePlanLimits_Workspace on Workspace {
id
projects(limit: 0) {
totalCount
items {
id
models(limit: 0) {
totalCount
}
}
}
plan {
name
}
}
`)
export const useGetWorkspacePlanUsage = (slug: string) => {
const { result } = useQuery(
workspacePlanLimitsQuery,
() => ({
slug
}),
() => ({
enabled: !!slug
})
)
const projectCount = computed(
() => result.value?.workspaceBySlug?.projects?.totalCount ?? 0
)
const modelCount = computed(
() =>
result.value?.workspaceBySlug?.projects?.items?.reduce(
(total, project) => total + (project?.models?.totalCount ?? 0),
0
) ?? 0
)
return {
projectCount,
modelCount
}
}
export const useWorkspacePlanLimits = (
projectCount: ComputedRef<number>,
modelCount: ComputedRef<number>
) => {
const projectLimit = computed(() => 3)
const modelLimit = computed(() => 8)
const remainingProjects = computed(() => {
return projectLimit.value - projectCount.value
})
const remainingModels = computed(() => {
return modelLimit.value - modelCount.value
})
const limitType = computed(() => {
if (projectCount.value > projectLimit.value) {
return 'project'
}
if (modelCount.value > modelLimit.value) {
return 'model'
}
return null
})
const activeLimit = computed(() => {
const limit =
limitType.value === 'project'
? projectLimit.value
: limitType.value === 'model'
? modelLimit.value
: null
return limit
})
const canAddProject = computed(
() => remainingProjects.value !== null && remainingProjects.value > 0
)
const canAddModels = computed(
() => remainingModels.value !== null && remainingModels.value > 0
)
return {
projectLimit,
modelLimit,
remainingProjects,
remainingModels,
canAddProject,
canAddModels,
limitType,
activeLimit
}
}
@@ -0,0 +1,39 @@
import { graphql } from '~/lib/common/generated/gql/gql'
import { useQuery } from '@vue/apollo-composable'
import { workspaceUsageQuery } from '~/lib/workspaces/graphql/queries'
graphql(`
fragment WorkspaceUsage_Workspace on Workspace {
id
plan {
usage {
projectCount
modelCount
}
}
}
`)
export const useWorkspaceUsage = (slug: string) => {
const { result } = useQuery(
workspaceUsageQuery,
() => ({
slug
}),
() => ({
enabled: !!slug
})
)
const projectCount = computed(
() => result.value?.workspaceBySlug?.plan?.usage.projectCount ?? 0
)
const modelCount = computed(
() => result.value?.workspaceBySlug?.plan?.usage.modelCount ?? 0
)
return {
projectCount,
modelCount
}
}
@@ -158,10 +158,18 @@ export const workspaceLastAdminCheckQuery = graphql(`
}
`)
export const workspacePlanLimitsQuery = graphql(`
query WorkspacePlanLimits($slug: String!) {
export const workspaceLimitsQuery = graphql(`
query WorkspaceLimits($slug: String!) {
workspaceBySlug(slug: $slug) {
...WorkspacePlanLimits_Workspace
}
}
`)
export const workspaceUsageQuery = graphql(`
query WorkspaceUsage($slug: String!) {
workspaceBySlug(slug: $slug) {
...WorkspaceUsage_Workspace
}
}
`)