Feat: Refactor workspace dashboard to improve performance (WIP) (#4500)

This commit is contained in:
Mike
2025-04-21 12:13:31 +02:00
committed by GitHub
parent acc7ef6ce0
commit 3c12f2bc45
34 changed files with 1103 additions and 1221 deletions
@@ -27,6 +27,7 @@ export type ActiveUserMutations = {
emailMutations: UserEmailMutations;
/** Mark onboarding as complete */
finishOnboarding: Scalars['Boolean']['output'];
meta: UserMetaMutations;
setActiveWorkspace: Scalars['Boolean']['output'];
/** Edit a user's profile */
update: User;
@@ -432,6 +433,7 @@ export type Automation = {
id: Scalars['ID']['output'];
isTestAutomation: Scalars['Boolean']['output'];
name: Scalars['String']['output'];
permissions: AutomationPermissionChecks;
runs: AutomateRunCollection;
updatedAt: Scalars['DateTime']['output'];
};
@@ -449,6 +451,12 @@ export type AutomationCollection = {
totalCount: Scalars['Int']['output'];
};
export type AutomationPermissionChecks = {
__typename?: 'AutomationPermissionChecks';
canRead: PermissionCheckResult;
canUpdate: PermissionCheckResult;
};
export type AutomationRevision = {
__typename?: 'AutomationRevision';
functions: Array<AutomationRevisionFunction>;
@@ -2552,8 +2560,10 @@ export enum ProjectPendingVersionsUpdatedMessageType {
export type ProjectPermissionChecks = {
__typename?: 'ProjectPermissionChecks';
canBroadcastActivity: PermissionCheckResult;
canCreateAutomation: PermissionCheckResult;
canCreateComment: PermissionCheckResult;
canCreateModel: PermissionCheckResult;
canDelete: PermissionCheckResult;
canLeave: PermissionCheckResult;
canMoveToWorkspace: PermissionCheckResult;
canRead: PermissionCheckResult;
@@ -3199,7 +3209,7 @@ export type SetPrimaryUserEmailInput = {
id: Scalars['ID']['input'];
};
/** Visbility without the "discoverable" option */
/** Visibility without the "discoverable" option */
export enum SimpleProjectVisibility {
Private = 'PRIVATE',
Unlisted = 'UNLISTED'
@@ -3848,6 +3858,7 @@ export type User = {
isOnboardingFinished?: Maybe<Scalars['Boolean']['output']>;
/** Returns `true` if last visited project was "legacy" "personal project" outside of a workspace */
isProjectsActive?: Maybe<Scalars['Boolean']['output']>;
meta: UserMeta;
name: Scalars['String']['output'];
notificationPreferences: Scalars['JSONObject']['output'];
permissions: RootPermissionChecks;
@@ -4068,6 +4079,28 @@ export type UserGendoAiCredits = {
used: Scalars['Int']['output'];
};
export type UserMeta = {
__typename?: 'UserMeta';
legacyProjectsExplainerCollapsed: Scalars['Boolean']['output'];
newWorkspaceExplainerDismissed: Scalars['Boolean']['output'];
};
export type UserMetaMutations = {
__typename?: 'UserMetaMutations';
setLegacyProjectsExplainerCollapsed: Scalars['Boolean']['output'];
setNewWorkspaceExplainerDismissed: Scalars['Boolean']['output'];
};
export type UserMetaMutationsSetLegacyProjectsExplainerCollapsedArgs = {
value: Scalars['Boolean']['input'];
};
export type UserMetaMutationsSetNewWorkspaceExplainerDismissedArgs = {
value: Scalars['Boolean']['input'];
};
export type UserProjectCollection = {
__typename?: 'UserProjectCollection';
cursor?: Maybe<Scalars['String']['output']>;
@@ -30,90 +30,94 @@
class="border-r border-outline-3 px-2 pt-3 pb-2 bg-foundation-page"
>
<LayoutSidebarMenu>
<LayoutSidebarMenuGroup v-if="isWorkspacesEnabled && isMobile">
<LayoutSidebarMenuGroup v-if="isWorkspacesEnabled" class="lg:hidden mb-4">
<HeaderWorkspaceSwitcher />
</LayoutSidebarMenuGroup>
<LayoutSidebarMenuGroup>
<NuxtLink :to="projectsLink" @click="isOpenMobile = false">
<LayoutSidebarMenuGroupItem
label="Projects"
:active="route.name === 'workspaces-slug' || isActive(projectsRoute)"
<div class="flex flex-col gap-y-2 lg:gap-y-4">
<LayoutSidebarMenuGroup>
<NuxtLink :to="projectsLink" @click="isOpenMobile = false">
<LayoutSidebarMenuGroupItem
label="Projects"
:active="
route.name === 'workspaces-slug' || isActive(projectsRoute)
"
>
<template #icon>
<IconProjects class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
<NuxtLink :to="connectorsRoute" @click="isOpenMobile = false">
<LayoutSidebarMenuGroupItem
label="Connectors"
:active="isActive(connectorsRoute)"
>
<template #icon>
<IconConnectors class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
<NuxtLink :to="tutorialsRoute" @click="isOpenMobile = false">
<LayoutSidebarMenuGroupItem
label="Tutorials"
:active="isActive(tutorialsRoute)"
>
<template #icon>
<IconTutorials class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
</LayoutSidebarMenuGroup>
<LayoutSidebarMenuGroup title="Resources" collapsible>
<NuxtLink
to="https://speckle.community/"
target="_blank"
@click="isOpenMobile = false"
>
<template #icon>
<IconProjects class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
<LayoutSidebarMenuGroupItem label="Community forum" external>
<template #icon>
<IconCommunity class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
<NuxtLink :to="connectorsRoute" @click="isOpenMobile = false">
<LayoutSidebarMenuGroupItem
label="Connectors"
:active="isActive(connectorsRoute)"
<div @click="openFeedbackDialog">
<LayoutSidebarMenuGroupItem label="Give us feedback">
<template #icon>
<IconFeedback class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</div>
<NuxtLink
to="https://speckle.guide/"
target="_blank"
@click="isOpenMobile = false"
>
<template #icon>
<IconConnectors class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
<LayoutSidebarMenuGroupItem label="Documentation" external>
<template #icon>
<IconDocumentation class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
<NuxtLink :to="tutorialsRoute" @click="isOpenMobile = false">
<LayoutSidebarMenuGroupItem
label="Tutorials"
:active="isActive(tutorialsRoute)"
<NuxtLink
to="https://speckle.community/c/making-speckle/changelog"
target="_blank"
@click="isOpenMobile = false"
>
<template #icon>
<IconTutorials class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
</LayoutSidebarMenuGroup>
<LayoutSidebarMenuGroup title="Resources" collapsible>
<NuxtLink
to="https://speckle.community/"
target="_blank"
@click="isOpenMobile = false"
>
<LayoutSidebarMenuGroupItem label="Community forum" external>
<template #icon>
<IconCommunity class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
<div @click="openFeedbackDialog">
<LayoutSidebarMenuGroupItem label="Give us feedback">
<template #icon>
<IconFeedback class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</div>
<NuxtLink
to="https://speckle.guide/"
target="_blank"
@click="isOpenMobile = false"
>
<LayoutSidebarMenuGroupItem label="Documentation" external>
<template #icon>
<IconDocumentation class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
<NuxtLink
to="https://speckle.community/c/making-speckle/changelog"
target="_blank"
@click="isOpenMobile = false"
>
<LayoutSidebarMenuGroupItem label="Changelog" external>
<template #icon>
<IconChangelog class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
</LayoutSidebarMenuGroup>
<LayoutSidebarMenuGroupItem label="Changelog" external>
<template #icon>
<IconChangelog class="size-4 text-foreground-2" />
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
</LayoutSidebarMenuGroup>
</div>
</LayoutSidebarMenu>
</LayoutSidebar>
</div>
@@ -139,15 +143,11 @@ import {
import { useRoute } from 'vue-router'
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
import { useNavigation } from '~~/lib/navigation/composables/navigation'
import { TailwindBreakpoints } from '~~/lib/common/helpers/tailwind'
import { useBreakpoints } from '@vueuse/core'
const { isLoggedIn } = useActiveUser()
const isWorkspacesEnabled = useIsWorkspacesEnabled()
const route = useRoute()
const { activeWorkspaceSlug } = useNavigation()
const breakpoints = useBreakpoints(TailwindBreakpoints)
const isMobile = breakpoints.smaller('lg')
const isOpenMobile = ref(false)
const showFeedbackDialog = ref(false)
@@ -7,7 +7,7 @@
<template v-if="activeWorkspaceSlug || isProjectsActive">
<div class="relative">
<WorkspaceAvatar
:size="isMobile ? 'sm' : 'base'"
size="base"
:name="displayName || ''"
:logo="displayLogo"
/>
@@ -36,7 +36,7 @@
leave-to-class="transform opacity-0 scale-95"
>
<MenuItems
class="absolute left-2 lg:left-3 top-12 lg:top-14 w-full lg:w-[17rem] origin-top-right bg-foundation outline outline-1 outline-primary-muted rounded-md shadow-lg overflow-hidden divide-y divide-outline-2"
class="absolute left-2 lg:left-3 top-[3.2rem] lg:top-14 w-[17rem] origin-top-right bg-foundation outline outline-1 outline-primary-muted rounded-md shadow-lg overflow-hidden divide-y divide-outline-2"
>
<HeaderWorkspaceSwitcherHeaderSsoExpired
v-if="activeWorkspaceHasExpiredSsoSession"
@@ -46,6 +46,7 @@
<HeaderWorkspaceSwitcherHeaderWorkspace
v-else-if="!!activeWorkspace"
:workspace="activeWorkspace"
@show-invite-dialog="showInviteDialog = true"
/>
<div
class="p-2 pt-1 max-h-[60vh] lg:max-h-96 overflow-y-auto simple-scrollbar"
@@ -100,6 +101,11 @@
<WorkspaceDiscoverableWorkspacesModal
v-model:open="showDiscoverableWorkspacesModal"
/>
<InviteDialogWorkspace
v-model:open="showInviteDialog"
:workspace="activeWorkspace"
/>
</div>
</template>
<script setup lang="ts">
@@ -120,15 +126,14 @@ import { graphql } from '~/lib/common/generated/gql'
import { useNavigation } from '~~/lib/navigation/composables/navigation'
import { Roles, WorkspacePlans } from '@speckle/shared'
import type { HeaderWorkspaceSwitcherWorkspaceList_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
import { TailwindBreakpoints } from '~~/lib/common/helpers/tailwind'
import { useBreakpoints } from '@vueuse/core'
graphql(`
fragment HeaderWorkspaceSwitcherActiveWorkspace_Workspace on Workspace {
...HeaderWorkspaceSwitcherHeaderWorkspace_Workspace
...InviteDialogWorkspace_Workspace
id
name
logo
...HeaderWorkspaceSwitcherHeaderWorkspace_Workspace
}
`)
@@ -184,10 +189,9 @@ const {
hasDiscoverableWorkspacesOrJoinRequests
} = useDiscoverableWorkspaces()
const { hasProjectsToMove } = useActiveUserProjectsToMove()
const breakpoints = useBreakpoints(TailwindBreakpoints)
const isMobile = breakpoints.smaller('lg')
const showDiscoverableWorkspacesModal = ref(false)
const showInviteDialog = ref(false)
const activeWorkspace = computed(() => {
return activeWorkspaceData.value
@@ -35,7 +35,7 @@
color="outline"
size="sm"
:disabled="!isAdmin"
@click="showInviteDialog = true"
@click="$emit('show-invite-dialog')"
>
Invite members
</FormButton>
@@ -44,8 +44,6 @@
</div>
</template>
</HeaderWorkspaceSwitcherHeader>
<InviteDialogWorkspace v-model:open="showInviteDialog" :workspace="workspace" />
</div>
</template>
@@ -60,7 +58,6 @@ import { formatName } from '~/lib/billing/helpers/plan'
graphql(`
fragment HeaderWorkspaceSwitcherHeaderWorkspace_Workspace on Workspace {
...InviteDialogWorkspace_Workspace
id
name
logo
@@ -74,13 +71,13 @@ graphql(`
}
`)
defineEmits(['show-invite-dialog'])
const props = defineProps<{
workspace: MaybeNullOrUndefined<HeaderWorkspaceSwitcherHeaderWorkspace_WorkspaceFragment>
}>()
const { activeWorkspaceSlug } = useNavigation()
const showInviteDialog = ref(false)
const isAdmin = computed(() => props.workspace?.role === Roles.Workspace.Admin)
</script>
@@ -24,7 +24,7 @@
class="border-r border-outline-3 px-2 pt-3 pb-2 bg-foundation-page"
>
<LayoutSidebarMenu>
<LayoutSidebarMenuGroup v-if="!isMobile">
<LayoutSidebarMenuGroup class="hidden lg:block lg:mb-4">
<NuxtLink
:to="exitSettingsRoute"
class="items-center gap-x-1.5 px-2.5 flex"
@@ -33,80 +33,76 @@
<p class="text-body-xs font-medium text-foreground">Exit settings</p>
</NuxtLink>
</LayoutSidebarMenuGroup>
<LayoutSidebarMenuGroup title="User settings">
<template #title-icon>
<IconAccount class="size-4" />
</template>
<NuxtLink
v-for="sidebarMenuItem in userMenuItems"
:key="`user-item-${sidebarMenuItem.route}`"
:to="sidebarMenuItem.route"
@click="isOpenMobile = false"
>
<LayoutSidebarMenuGroupItem
:label="sidebarMenuItem.title"
:active="route.path === sidebarMenuItem.route"
/>
</NuxtLink>
</LayoutSidebarMenuGroup>
<LayoutSidebarMenuGroup v-if="isAdmin" title="Server settings">
<template #title-icon>
<IconServer class="size-4" />
</template>
<NuxtLink
v-for="sidebarMenuItem in serverMenuItems"
:key="`server-item-${sidebarMenuItem.route}`"
:to="sidebarMenuItem.route"
@click="isOpenMobile = false"
>
<LayoutSidebarMenuGroupItem
:label="sidebarMenuItem.title"
:active="route.path === sidebarMenuItem.route"
/>
</NuxtLink>
</LayoutSidebarMenuGroup>
<LayoutSidebarMenuGroup
v-if="showWorkspaceSettings"
title="Workspace settings"
>
<template #title-icon>
<IconWorkspaces class="size-4" />
</template>
<template v-if="activeWorkspaceItem">
<div class="flex flex-col gap-y-2 lg:gap-y-4">
<LayoutSidebarMenuGroup title="User settings">
<template #title-icon>
<IconAccount class="size-4" />
</template>
<NuxtLink
v-for="workspaceMenuItem in workspaceMenuItems"
:key="`workspace-menu-item-${workspaceMenuItem.name}-${activeWorkspaceItem}`"
v-for="sidebarMenuItem in userMenuItems"
:key="`user-item-${sidebarMenuItem.route}`"
:to="sidebarMenuItem.route"
@click="isOpenMobile = false"
>
<LayoutSidebarMenuGroupItem
:label="sidebarMenuItem.title"
:active="route.path === sidebarMenuItem.route"
/>
</NuxtLink>
</LayoutSidebarMenuGroup>
<LayoutSidebarMenuGroup v-if="isServerAdmin" title="Server settings">
<template #title-icon>
<IconServer class="size-4" />
</template>
<NuxtLink
v-for="sidebarMenuItem in serverMenuItems"
:key="`server-item-${sidebarMenuItem.route}`"
:to="sidebarMenuItem.route"
@click="isOpenMobile = false"
>
<LayoutSidebarMenuGroupItem
:label="sidebarMenuItem.title"
:active="route.path === sidebarMenuItem.route"
/>
</NuxtLink>
</LayoutSidebarMenuGroup>
<LayoutSidebarMenuGroup
v-if="showWorkspaceSettings"
title="Workspace settings"
>
<template #title-icon>
<IconWorkspaces class="size-4" />
</template>
<NuxtLink
v-for="workspaceMenuItem in filteredWorkspaceMenuItems"
:key="`workspace-menu-item-${workspaceMenuItem.name}`"
:to="
!isAdmin &&
(workspaceMenuItem.disabled ||
needsSsoSession(activeWorkspaceItem, workspaceMenuItem.name))
workspaceMenuItem.disabled || needsSsoSession(workspaceMenuItem.name)
? undefined
: workspaceMenuItem.route(activeWorkspaceItem.slug)
: workspaceMenuItem.route(workspace?.slug)
"
@click="isOpenMobile = false"
>
<LayoutSidebarMenuGroupItem
v-if="workspaceMenuItem.permission?.includes(activeWorkspaceItem.role as WorkspaceRoles)"
:label="workspaceMenuItem.title"
:active="
route.name?.toString().startsWith(workspaceMenuItem.name) &&
route.params.slug === activeWorkspaceItem.slug
"
:active="route.name?.toString().startsWith(workspaceMenuItem.name)"
:tooltip-text="
needsSsoSession(activeWorkspaceItem, workspaceMenuItem.name)
needsSsoSession(workspaceMenuItem.name)
? 'Log in with your SSO provider to access this page'
: workspaceMenuItem.tooltipText
"
:disabled="
!isAdmin &&
!isServerAdmin &&
(workspaceMenuItem.disabled ||
needsSsoSession(activeWorkspaceItem, workspaceMenuItem.name))
needsSsoSession(workspaceMenuItem.name))
"
class="!pl-8"
/>
</NuxtLink>
</template>
</LayoutSidebarMenuGroup>
</LayoutSidebarMenuGroup>
</div>
</LayoutSidebarMenu>
</LayoutSidebar>
</div>
@@ -114,6 +110,7 @@
</template>
<script setup lang="ts">
import type { WorkspaceRoles } from '@speckle/shared'
import { useIsWorkspacesEnabled } from '~/composables/globals'
import { useQuery } from '@vue/apollo-composable'
import { settingsSidebarQuery } from '~/lib/settings/graphql/queries'
@@ -126,15 +123,11 @@ import {
LayoutSidebarMenuGroup
} from '@speckle/ui-components'
import { graphql } from '~~/lib/common/generated/gql'
import type { WorkspaceRoles } from '@speckle/shared'
import {
projectsRoute,
settingsWorkspaceRoutes,
workspaceRoute
} from '~/lib/common/helpers/route'
import type { SettingsMenu_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
import { TailwindBreakpoints } from '~~/lib/common/helpers/tailwind'
import { useBreakpoints } from '@vueuse/core'
import { useNavigation } from '~~/lib/navigation/composables/navigation'
graphql(`
@@ -143,67 +136,42 @@ graphql(`
id
slug
role
name
logo
plan {
status
name
}
creationState {
completed
}
}
`)
graphql(`
fragment SettingsSidebar_User on User {
id
workspaces {
items {
...SettingsSidebar_Workspace
}
}
}
`)
const isWorkspacesEnabled = useIsWorkspacesEnabled()
const { activeWorkspaceSlug } = useNavigation()
const settingsMenuState = useSettingsMenuState()
const { isAdmin } = useActiveUser()
const { isAdmin: isServerAdmin } = useActiveUser()
const route = useRoute()
const { result: workspaceResult } = useQuery(settingsSidebarQuery, null, {
enabled: computed(() => isWorkspacesEnabled.value)
})
const { result: workspaceResult } = useQuery(settingsSidebarQuery, null, () => ({
enabled: isWorkspacesEnabled.value
}))
const { userMenuItems, serverMenuItems, workspaceMenuItems } = useSettingsMenu()
const breakpoints = useBreakpoints(TailwindBreakpoints)
const isMobile = breakpoints.smaller('lg')
const isOpenMobile = ref(false)
const workspaceItems = computed(
() =>
workspaceResult.value?.activeUser?.workspaces.items.filter(
(item) => item.creationState?.completed !== false // Removed workspaces that are not completely created
) || []
)
const activeWorkspaceItem = computed(() =>
workspaceItems.value.find((item) => item.slug === activeWorkspaceSlug.value)
const workspace = computed(() => workspaceResult.value?.activeUser?.activeWorkspace)
const filteredWorkspaceMenuItems = computed(() =>
workspaceMenuItems.value.filter(
(item) =>
!item.permission ||
item.permission.includes(workspace.value?.role as WorkspaceRoles)
)
)
const wrapperClasses = computed(() => {
return [
'absolute z-40 lg:static h-full flex shrink-0 transition-all',
`w-[13rem]`,
'absolute z-40 lg:static h-full flex shrink-0 transition-all w-[13rem]',
isOpenMobile.value ? '' : `-translate-x-[13rem] lg:translate-x-0`
]
})
const needsSsoSession = (
workspace: SettingsMenu_WorkspaceFragment,
routeName?: string
) => {
return workspace.sso?.provider?.id &&
const needsSsoSession = (routeName?: string) => {
return workspace.value?.sso?.provider?.id &&
routeName !== settingsWorkspaceRoutes.general.name
? !workspace.sso?.session?.validUntil
? !workspace.value?.sso?.session?.validUntil
: false
}
@@ -24,7 +24,7 @@
:workspace="workspace"
:new-role="newRole"
:is-active-user-target-user="isActiveUserTargetUser"
:is-only-admin="hasSingleAdmin"
:is-only-admin="isLastAdmin"
:is-domain-compliant="targetUser.user.workspaceDomainPolicyCompliant"
@success="onDialogSuccess"
/>
@@ -110,7 +110,7 @@ const showMenu = ref(false)
const showDialog = ref(false)
const dialogType = ref<WorkspaceUserActionTypes>()
const { hasSingleAdmin } = useWorkspaceLastAdminCheck({
const { isLastAdmin } = useWorkspaceLastAdminCheck({
workspaceSlug: props.workspace?.slug || ''
})
@@ -0,0 +1,145 @@
<template>
<div>
<LayoutMenu
v-model:open="showMenu"
:items="menuItems"
:menu-position="HorizontalDirection.Left"
:menu-id="menuId"
@click.stop.prevent
@chosen="onActionChosen"
>
<FormButton
color="outline"
:class="hideTextOnMobile ? 'hidden sm:block' : ''"
@click="showMenu = !showMenu"
>
<div class="flex items-center gap-1">
{{ ctaLabel || 'Add project' }}
<ChevronDownIcon class="h-3 w-3" />
</div>
</FormButton>
<FormButton
color="outline"
:class="hideTextOnMobile ? 'sm:hidden' : 'hidden'"
hide-text
:icon-left="PlusIcon"
@click="showMenu = !showMenu"
>
Add project
</FormButton>
</LayoutMenu>
<WorkspaceMoveProjectManager v-model:open="showMoveProjectDialog" />
<ProjectsAddDialog v-model:open="showNewProjectDialog" />
<ClientOnly>
<WorkspacePlanProjectModelLimitReachedDialog
v-model:open="showLimitDialog"
:workspace-name="workspace?.name"
:plan="workspace?.plan?.name"
:workspace-role="workspace?.role"
:workspace-slug="workspaceSlug"
/>
</ClientOnly>
</div>
</template>
<script setup lang="ts">
// TODO: The ClientOnly is to avoid the dialog from being rendered on the server and have hydration sideeffects. These need to be addressed and fixed instead of the ClientOnly
import { ChevronDownIcon, PlusIcon } from '@heroicons/vue/24/outline'
import type { WorkspaceAddProjectMenu_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
import { HorizontalDirection } from '~~/lib/common/composables/window'
import type { LayoutMenuItem } from '~~/lib/layout/helpers/components'
import type { MaybeNullOrUndefined } from '@speckle/shared'
import { graphql } from '~~/lib/common/generated/gql'
graphql(`
fragment WorkspaceAddProjectMenu_Workspace on Workspace {
id
name
slug
role
plan {
name
}
permissions {
canCreateProject {
...FullPermissionCheckResult
}
canMoveProjectToWorkspace {
...FullPermissionCheckResult
}
}
}
`)
enum AddNewProjectActionTypes {
NewProject = 'new-project',
MoveProject = 'move-project'
}
const props = defineProps<{
workspaceSlug: string
workspace: MaybeNullOrUndefined<WorkspaceAddProjectMenu_WorkspaceFragment>
hideTextOnMobile?: boolean
ctaLabel?: string
}>()
const menuId = useId()
const showMenu = ref(false)
const showLimitDialog = ref(false)
const showMoveProjectDialog = ref(false)
const showNewProjectDialog = ref(false)
const isLimitReached = computed(() => {
return (
props.workspace?.permissions.canCreateProject?.code === 'WorkspaceLimitsReached'
)
})
const isDisabled = computed(() => {
return (
!props.workspace?.permissions.canCreateProject?.authorized && !isLimitReached.value
)
})
const menuItems = computed<LayoutMenuItem[][]>(() => [
[
{
title: 'Create new project...',
id: AddNewProjectActionTypes.NewProject,
disabled: isDisabled.value,
disabledTooltip: isDisabled.value
? props.workspace?.permissions.canCreateProject?.message
: undefined
},
{
title: 'Move existing project...',
id: AddNewProjectActionTypes.MoveProject,
disabled: isDisabled.value,
disabledTooltip: isDisabled.value
? props.workspace?.permissions.canMoveProjectToWorkspace?.message
: undefined
}
]
])
const onActionChosen = (params: { item: LayoutMenuItem; event: MouseEvent }) => {
const { item } = params
if (isLimitReached.value) {
showMoveProjectDialog.value = false
showNewProjectDialog.value = false
showLimitDialog.value = true
return
}
switch (item.id) {
case AddNewProjectActionTypes.NewProject:
showNewProjectDialog.value = true
break
case AddNewProjectActionTypes.MoveProject:
showMoveProjectDialog.value = true
break
}
}
</script>
@@ -1,264 +0,0 @@
<template>
<div>
<Portal to="right-sidebar">
<WorkspaceSidebar
v-if="workspace"
:workspace-info="workspace"
@show-invite-dialog="showInviteDialog = true"
/>
</Portal>
<div v-if="workspaceInvite" class="flex justify-center">
<WorkspaceInviteBlock :invite="workspaceInvite" />
</div>
<template v-else>
<Portal v-if="workspace?.name" to="navigation">
<HeaderNavLink
:to="workspaceRoute(workspaceSlug)"
name="Projects"
:separator="false"
/>
</Portal>
<WorkspaceHeader
v-if="workspace"
:icon="Squares2X2Icon"
:workspace-info="workspace"
@show-move-projects-dialog="onMoveProject"
@show-new-project-dialog="openNewProject = true"
@show-invite-dialog="showInviteDialog = true"
/>
<div v-if="showSearchBar" class="mt-2 lg:mt-4">
<FormTextInput
name="modelsearch"
:show-label="false"
:placeholder="`Search ${projects?.totalCount} ${
projects?.totalCount === 1 ? 'project' : 'projects'
}...`"
:custom-icon="MagnifyingGlassIcon"
color="foundation"
wrapper-classes="w-full lg:w-60"
show-clear
v-bind="bind"
v-on="on"
/>
</div>
<CommonLoadingBar :loading="showLoadingBar" class="my-2" />
<section
v-if="showEmptyState"
class="bg-foundation-page h-96 flex flex-col items-center justify-center gap-4"
>
<WorkspaceEmptyStateIllustration />
<span class="text-body-2xs text-foreground-2 text-center">
Workspace is empty
</span>
<WorkspaceHeaderAddProjectMenu
button-copy="Add your first project"
:workspace-name="workspace?.name || ''"
:workspace-slug="workspaceSlug"
:workspace-role="workspace?.role"
:workspace-plan="workspace?.plan?.name"
:can-create-project="canCreateProject"
:can-move-project-to-workspace="canMoveProjectToWorkspace"
@new-project="openNewProject = true"
@move-project="onMoveProject"
/>
</section>
<template v-else-if="projects?.items?.length">
<ProjectsDashboardFilled :projects="projects" workspace-page />
<InfiniteLoading :settings="{ identifier }" @infinite="onInfiniteLoad" />
</template>
<CommonEmptySearchState v-else-if="!showLoadingBar" @clear-search="clearSearch" />
<ProjectsAddDialog v-model:open="openNewProject" :workspace-id="workspace?.id" />
<template v-if="workspace">
<InviteDialogWorkspace v-model:open="showInviteDialog" :workspace="workspace" />
<WorkspaceMoveProjectManager
v-model:open="showMoveProjectsDialog"
:workspace-slug="workspaceSlug"
/>
</template>
</template>
</div>
</template>
<script setup lang="ts">
import { MagnifyingGlassIcon, Squares2X2Icon } from '@heroicons/vue/24/outline'
import { useQuery, useQueryLoading } from '@vue/apollo-composable'
import type { Nullable, Optional, StreamRoles } from '@speckle/shared'
import {
workspacePageQuery,
workspaceProjectsQuery
} from '~~/lib/workspaces/graphql/queries'
import { useDebouncedTextInput } from '@speckle/ui-components'
import { usePaginatedQuery } from '~/lib/common/composables/graphql'
import { graphql } from '~~/lib/common/generated/gql'
import type { WorkspaceProjectsQueryQueryVariables } from '~~/lib/common/generated/gql/graphql'
import { workspaceRoute } from '~/lib/common/helpers/route'
import { useBillingActions } from '~/lib/billing/composables/actions'
import { useWorkspacesWizard } from '~/lib/workspaces/composables/wizard'
import type { WorkspaceWizardState } from '~/lib/workspaces/helpers/types'
import { useMixpanel } from '~/lib/core/composables/mp'
graphql(`
fragment WorkspaceProjectList_Workspace on Workspace {
id
...WorkspaceBase_Workspace
...WorkspaceTeam_Workspace
...WorkspaceSecurity_Workspace
...WorkspaceHeader_Workspace
...BillingAlert_Workspace
...InviteDialogWorkspace_Workspace
projects {
...WorkspaceProjectList_ProjectCollection
}
creationState {
completed
state
}
permissions {
canCreateProject {
...FullPermissionCheckResult
}
}
}
`)
graphql(`
fragment WorkspaceProjectList_ProjectCollection on ProjectCollection {
totalCount
items {
...ProjectDashboardItem
}
cursor
}
`)
const props = defineProps<{
workspaceSlug: string
}>()
const { validateCheckoutSession } = useBillingActions()
const areQueriesLoading = useQueryLoading()
const route = useRoute()
const {
on,
bind,
value: search
} = useDebouncedTextInput({
debouncedBy: 800
})
const showMoveProjectsDialog = ref(false)
const selectedRoles = ref(undefined as Optional<StreamRoles[]>)
const openNewProject = ref(false)
const showInviteDialog = ref(false)
const token = computed(() => route.query.token as Optional<string>)
const pageFetchPolicy = usePageQueryStandardFetchPolicy()
const { result: initialQueryResult, onResult } = useQuery(
workspacePageQuery,
() => ({
workspaceSlug: props.workspaceSlug,
token: token.value || null
}),
() => ({
fetchPolicy: pageFetchPolicy.value
})
)
const { query, identifier, onInfiniteLoad } = usePaginatedQuery({
query: workspaceProjectsQuery,
baseVariables: computed(() => ({
workspaceSlug: props.workspaceSlug,
filter: {
search: (search.value || '').trim() || null
},
cursor: null as Nullable<string>
})),
resolveKey: (vars: WorkspaceProjectsQueryQueryVariables) => ({
workspaceSlug: vars.workspaceSlug,
search: vars.filter?.search || ''
}),
resolveInitialResult: () =>
!search.value ? initialQueryResult.value?.workspaceBySlug.projects : undefined,
resolveCurrentResult: (result) => result?.workspaceBySlug?.projects,
resolveNextPageVariables: (baseVariables, newCursor) => ({
...baseVariables,
cursor: newCursor
}),
resolveCursorFromVariables: (vars) => vars.cursor
})
const { finalizeWizard } = useWorkspacesWizard()
const canCreateProject = computed(
() => initialQueryResult.value?.workspaceBySlug?.permissions.canCreateProject
)
const canMoveProjectToWorkspace = computed(
() => initialQueryResult.value?.workspaceBySlug?.permissions.canMoveProjectToWorkspace
)
const projects = computed(() => query.result.value?.workspaceBySlug?.projects)
const workspaceInvite = computed(() => initialQueryResult.value?.workspaceInvite)
const workspace = computed(() => initialQueryResult.value?.workspaceBySlug)
const showEmptyState = computed(() => {
if (search.value) return false
return projects.value && !projects.value?.items?.length
})
const showLoadingBar = computed(() => {
const isLoading = areQueriesLoading.value || (!!search.value && query.loading.value)
return isLoading
})
const showSearchBar = computed(() => {
return projects?.value?.totalCount || search.value
})
const clearSearch = () => {
search.value = ''
selectedRoles.value = []
}
const hasFinalized = ref(false)
const mixpanel = useMixpanel()
const onMoveProject = () => {
mixpanel.track('Move Project CTA Clicked', {
location: 'workspace',
// eslint-disable-next-line camelcase
workspace_id: props.workspaceSlug
})
showMoveProjectsDialog.value = true
}
onResult((queryResult) => {
if (
queryResult.data?.workspaceBySlug.creationState?.completed === false &&
queryResult.data.workspaceBySlug.creationState.state
) {
if (import.meta.server) return
if (hasFinalized.value) return
hasFinalized.value = true
finalizeWizard(
queryResult.data.workspaceBySlug.creationState.state as WorkspaceWizardState,
queryResult.data.workspaceBySlug.id
)
}
if (queryResult.data?.workspaceBySlug) {
useHeadSafe({
title: queryResult.data.workspaceBySlug.name
})
validateCheckoutSession(queryResult.data.workspaceBySlug)
}
})
</script>
@@ -0,0 +1,97 @@
<template>
<div>
<Portal to="navigation">
<HeaderNavLink
:to="workspaceRoute(workspaceSlug)"
name="Projects"
:separator="false"
/>
</Portal>
<WorkspaceDashboardHeader
:workspace="workspace"
:workspace-slug="workspaceSlug"
:show-billing-alert="showBillingAlert"
/>
<WorkspaceDashboardProjectList
:workspace-slug="workspaceSlug"
:workspace="workspace"
class="mt-2 lg:mt-4"
/>
</div>
</template>
<script setup lang="ts">
import { useQuery } from '@vue/apollo-composable'
import { Roles, WorkspacePlanStatuses } from '@speckle/shared'
import { workspaceDashboardQuery } from '~~/lib/workspaces/graphql/queries'
import { graphql } from '~~/lib/common/generated/gql'
import { workspaceRoute } from '~/lib/common/helpers/route'
import { useWorkspacesWizard } from '~/lib/workspaces/composables/wizard'
import type { WorkspaceWizardState } from '~/lib/workspaces/helpers/types'
import { useBillingActions } from '~/lib/billing/composables/actions'
graphql(`
fragment WorkspaceDashboard_Workspace on Workspace {
...WorkspaceSidebarMembers_Workspace
...WorkspaceDashboardHeader_Workspace
...WorkspaceDashboardProjectList_Workspace
id
name
role
creationState {
completed
state
}
}
`)
const props = defineProps<{
workspaceSlug: string
}>()
const { validateCheckoutSession } = useBillingActions()
const { finalizeWizard } = useWorkspacesWizard()
const pageFetchPolicy = usePageQueryStandardFetchPolicy()
const { result: workspaceResult, onResult } = useQuery(
workspaceDashboardQuery,
() => ({
workspaceSlug: props.workspaceSlug
}),
() => ({
fetchPolicy: pageFetchPolicy.value
})
)
const hasFinalized = ref(false)
const workspace = computed(() => workspaceResult.value?.workspaceBySlug)
const showBillingAlert = computed(
() =>
workspace?.value?.role === Roles.Workspace.Guest &&
(workspace.value?.plan?.status === WorkspacePlanStatuses.PaymentFailed ||
workspace.value?.plan?.status === WorkspacePlanStatuses.Canceled ||
workspace.value?.plan?.status === WorkspacePlanStatuses.CancelationScheduled)
)
onResult((queryResult) => {
if (
queryResult.data?.workspaceBySlug.creationState?.completed === false &&
queryResult.data.workspaceBySlug.creationState.state &&
!hasFinalized.value &&
import.meta.client
) {
hasFinalized.value = true
finalizeWizard(
queryResult.data.workspaceBySlug.creationState.state as WorkspaceWizardState,
queryResult.data.workspaceBySlug.id
)
}
if (queryResult.data?.workspaceBySlug) {
useHeadSafe({
title: queryResult.data.workspaceBySlug.name
})
validateCheckoutSession(queryResult.data.workspaceBySlug)
}
})
</script>
@@ -0,0 +1,82 @@
<template>
<div class="flex flex-col gap-3 lg:gap-4">
<div v-if="showBillingAlert">
<BillingAlert :workspace="workspace" />
</div>
<div class="flex items-center justify-between gap-4">
<div class="flex items-center gap-x-2">
<h1 class="text-heading-sm md:text-heading line-clamp-2">
Hello, {{ activeUser?.name }}
</h1>
<CommonBadge
v-if="!isWorkspaceMember"
rounded
color-classes="bg-highlight-3 text-foreground-2"
>
<span class="capitalize">
{{ workspace?.role?.split(':').reverse()[0] }}
</span>
</CommonBadge>
</div>
<div class="flex gap-1.5 md:gap-2">
<WorkspaceAddProjectMenu
:workspace-slug="workspaceSlug"
:workspace="workspace"
hide-text-on-mobile
/>
<FormButton
color="outline"
:icon-left="Cog8ToothIcon"
hide-text
@click="navigateTo(settingsWorkspaceRoutes.general.route(workspace?.slug))"
>
Settings
</FormButton>
</div>
</div>
<div class="lg:hidden mb-2">
<WorkspaceSidebarMembers
:workspace="workspace"
:is-workspace-admin="isWorkspaceAdmin"
:is-workspace-guest="isWorkspaceGuest"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { graphql } from '~~/lib/common/generated/gql'
import type { WorkspaceDashboardHeader_WorkspaceFragment } from '~~/lib/common/generated/gql/graphql'
import { Cog8ToothIcon } from '@heroicons/vue/24/outline'
import { Roles, type MaybeNullOrUndefined } from '@speckle/shared'
import { settingsWorkspaceRoutes } from '~/lib/common/helpers/route'
graphql(`
fragment WorkspaceDashboardHeader_Workspace on Workspace {
...WorkspaceSidebarMembers_Workspace
...WorkspaceAddProjectMenu_Workspace
...BillingAlert_Workspace
id
role
plan {
status
}
}
`)
const props = defineProps<{
workspaceSlug: string
workspace: MaybeNullOrUndefined<WorkspaceDashboardHeader_WorkspaceFragment>
showBillingAlert?: boolean
}>()
const { activeUser } = useActiveUser()
const isWorkspaceAdmin = computed(() => props.workspace?.role === Roles.Workspace.Admin)
const isWorkspaceGuest = computed(() => props.workspace?.role === Roles.Workspace.Guest)
const isWorkspaceMember = computed(
() => props.workspace?.role === Roles.Workspace.Member
)
</script>
@@ -0,0 +1,124 @@
<template>
<div>
<div v-if="showSearchBar">
<FormTextInput
name="modelsearch"
:show-label="false"
:placeholder="`Search ${projects?.totalCount} ${
projects?.totalCount === 1 ? 'project' : 'projects'
}...`"
:custom-icon="MagnifyingGlassIcon"
color="foundation"
wrapper-classes="w-full lg:w-60"
show-clear
v-bind="bind"
v-on="on"
/>
</div>
<CommonLoadingBar :loading="showLoadingBar" class="my-2" />
<section
v-if="showEmptyState"
class="bg-foundation-page h-96 flex flex-col items-center justify-center gap-4"
>
<WorkspaceEmptyStateIllustration />
<span class="text-body-2xs text-foreground-2 text-center">
Workspace is empty
</span>
<WorkspaceAddProjectMenu
:workspace="workspace"
:workspace-slug="workspaceSlug"
cta-label="Add your first project"
/>
</section>
<section v-else-if="projects?.items?.length">
<ProjectsDashboardFilled :projects="projects" workspace-page />
<InfiniteLoading :settings="{ identifier }" @infinite="onInfiniteLoad" />
</section>
<CommonEmptySearchState v-else-if="!showLoadingBar" @clear-search="clearSearch" />
</div>
</template>
<script setup lang="ts">
import { MagnifyingGlassIcon } from '@heroicons/vue/24/outline'
import type { MaybeNullOrUndefined, Nullable } from '@speckle/shared'
import { workspaceProjectsQuery } from '~~/lib/workspaces/graphql/queries'
import { useDebouncedTextInput } from '@speckle/ui-components'
import { usePaginatedQuery } from '~/lib/common/composables/graphql'
import { graphql } from '~~/lib/common/generated/gql'
import type {
WorkspaceProjectsQueryQueryVariables,
WorkspaceDashboardProjectList_WorkspaceFragment
} from '~~/lib/common/generated/gql/graphql'
graphql(`
fragment WorkspaceDashboardProjectList_ProjectCollection on ProjectCollection {
totalCount
items {
...ProjectDashboardItem
}
cursor
}
`)
graphql(`
fragment WorkspaceDashboardProjectList_Workspace on Workspace {
...WorkspaceAddProjectMenu_Workspace
id
}
`)
const props = defineProps<{
workspaceSlug: string
workspace: MaybeNullOrUndefined<WorkspaceDashboardProjectList_WorkspaceFragment>
}>()
const {
on,
bind,
value: search
} = useDebouncedTextInput({
debouncedBy: 800
})
const {
query: projectsQuery,
identifier,
onInfiniteLoad
} = usePaginatedQuery({
query: workspaceProjectsQuery,
baseVariables: computed(() => ({
workspaceSlug: props.workspaceSlug,
filter: {
search: (search.value || '').trim() || null
},
cursor: null as Nullable<string>
})),
resolveKey: (vars: WorkspaceProjectsQueryQueryVariables) => ({
workspaceSlug: vars.workspaceSlug,
search: vars.filter?.search || ''
}),
resolveCurrentResult: (result) => result?.workspaceBySlug?.projects,
resolveNextPageVariables: (baseVariables, newCursor) => ({
...baseVariables,
cursor: newCursor
}),
resolveCursorFromVariables: (vars) => vars.cursor
})
const projects = computed(() => projectsQuery.result.value?.workspaceBySlug?.projects)
const showSearchBar = computed(() => {
return projects?.value?.totalCount || search.value
})
const showLoadingBar = computed(() => projectsQuery.loading.value)
const showEmptyState = computed(() =>
search.value ? false : projects.value && !projects.value?.items?.length
)
const clearSearch = () => {
search.value = ''
}
</script>
@@ -1,119 +0,0 @@
<template>
<div>
<LayoutMenu
v-model:open="showMenu"
:items="menuItems"
:menu-position="HorizontalDirection.Left"
:menu-id="menuId"
@click.stop.prevent
@chosen="onActionChosen"
>
<FormButton
color="outline"
:class="hideTextOnMobile ? 'hidden md:block' : ''"
@click="showMenu = !showMenu"
>
<div class="flex items-center gap-1">
{{ buttonCopy || 'Add project' }}
<ChevronDownIcon class="h-3 w-3" />
</div>
</FormButton>
<FormButton
color="outline"
:class="hideTextOnMobile ? 'md:hidden' : 'hidden'"
hide-text
:icon-left="PlusIcon"
@click="showMenu = !showMenu"
>
Add project
</FormButton>
</LayoutMenu>
<WorkspacePlanProjectModelLimitReachedDialog
v-model:open="showLimitDialog"
:workspace-name="props.workspaceName"
:plan="props.workspacePlan"
:workspace-role="props.workspaceRole"
:workspace-slug="props.workspaceSlug"
location="add project menu"
limit-type="project"
/>
</div>
</template>
<script setup lang="ts">
import { ChevronDownIcon, PlusIcon } from '@heroicons/vue/24/outline'
import type { FullPermissionCheckResultFragment } from '~/lib/common/generated/gql/graphql'
import { HorizontalDirection } from '~~/lib/common/composables/window'
import type { LayoutMenuItem } from '~~/lib/layout/helpers/components'
import type { MaybeNullOrUndefined, WorkspacePlans } from '@speckle/shared'
enum AddNewProjectActionTypes {
NewProject = 'new-project',
MoveProject = 'move-project'
}
const emit = defineEmits<{
(e: 'new-project'): void
(e: 'move-project'): void
}>()
const props = defineProps<{
workspaceName: string
workspaceSlug: string
workspacePlan?: WorkspacePlans
hideTextOnMobile?: boolean
buttonCopy?: string
canCreateProject: FullPermissionCheckResultFragment | undefined
canMoveProjectToWorkspace: FullPermissionCheckResultFragment | undefined
workspaceRole: MaybeNullOrUndefined<string>
}>()
const menuId = useId()
const showMenu = ref(false)
const showLimitDialog = ref(false)
const menuItems = computed<LayoutMenuItem[][]>(() => [
[
{
title: 'Create new project...',
id: AddNewProjectActionTypes.NewProject,
disabled: isDisabled.value,
disabledTooltip: isDisabled.value ? props.canCreateProject?.message : undefined
},
{
title: 'Move existing project...',
id: AddNewProjectActionTypes.MoveProject,
disabled: isDisabled.value,
disabledTooltip: isDisabled.value
? props.canMoveProjectToWorkspace?.message
: undefined
}
]
])
const onActionChosen = (params: { item: LayoutMenuItem; event: MouseEvent }) => {
const { item } = params
if (isLimitReached.value) {
showLimitDialog.value = true
return
}
switch (item.id) {
case AddNewProjectActionTypes.NewProject:
emit('new-project')
break
case AddNewProjectActionTypes.MoveProject:
emit('move-project')
break
}
}
const isLimitReached = computed(() => {
return props.canCreateProject?.code === 'WorkspaceLimitsReached'
})
const isDisabled = computed(() => {
return !props.canCreateProject?.authorized && !isLimitReached.value
})
</script>
@@ -1,125 +0,0 @@
<template>
<div class="flex flex-col gap-3 lg:gap-4">
<div v-if="!isWorkspaceGuest && showBillingAlert">
<BillingAlert :workspace="workspaceInfo" />
</div>
<div class="flex items-center justify-between gap-4">
<div class="flex items-center gap-x-2">
<h1 class="text-heading-sm md:text-heading line-clamp-2">
Hello, {{ activeUser?.name }}
</h1>
<CommonBadge
v-if="!isWorkspaceMember"
rounded
color-classes="bg-highlight-3 text-foreground-2"
>
<span class="capitalize">
{{ workspaceInfo.role?.split(':').reverse()[0] }}
</span>
</CommonBadge>
</div>
<div class="flex gap-1.5 md:gap-2">
<WorkspaceHeaderAddProjectMenu
:workspace-name="workspaceInfo.name"
:workspace-slug="workspaceInfo.slug"
:workspace-role="workspaceInfo.role"
:workspace-plan="
workspaceInfo.plan?.name ? workspaceInfo.plan?.name : undefined
"
hide-text-on-mobile
:can-create-project="canCreateProject"
:can-move-project-to-workspace="canMoveProjectToWorkspace"
@new-project="$emit('show-new-project-dialog')"
@move-project="$emit('show-move-projects-dialog')"
/>
<FormButton
color="outline"
:icon-left="Cog8ToothIcon"
hide-text
@click="navigateTo(settingsWorkspaceRoutes.general.route(workspaceInfo.slug))"
>
Settings
</FormButton>
<ClientOnly>
<PortalTarget name="workspace-sidebar-toggle"></PortalTarget>
</ClientOnly>
</div>
</div>
<div class="lg:hidden mb-2">
<WorkspaceSidebarMembers
v-if="!isWorkspaceGuest"
:workspace-info="workspaceInfo"
:is-workspace-admin="isWorkspaceAdmin"
:is-workspace-guest="isWorkspaceGuest"
@show-invite-dialog="$emit('show-invite-dialog')"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { graphql } from '~~/lib/common/generated/gql'
import {
WorkspacePlanStatuses,
type WorkspaceHeader_WorkspaceFragment
} from '~~/lib/common/generated/gql/graphql'
import { Cog8ToothIcon } from '@heroicons/vue/24/outline'
import { Roles } from '@speckle/shared'
import { settingsWorkspaceRoutes } from '~/lib/common/helpers/route'
graphql(`
fragment WorkspaceHeader_Workspace on Workspace {
...WorkspaceBase_Workspace
...WorkspaceTeam_Workspace
...BillingAlert_Workspace
slug
readOnly
permissions {
canCreateProject {
...FullPermissionCheckResult
}
canMoveProjectToWorkspace {
...FullPermissionCheckResult
}
}
}
`)
defineEmits<{
(e: 'show-move-projects-dialog'): void
(e: 'show-new-project-dialog'): void
(e: 'show-invite-dialog'): void
}>()
const props = defineProps<{
workspaceInfo: WorkspaceHeader_WorkspaceFragment
}>()
const { activeUser } = useActiveUser()
const isWorkspaceAdmin = computed(
() => props.workspaceInfo.role === Roles.Workspace.Admin
)
const isWorkspaceGuest = computed(
() => props.workspaceInfo.role === Roles.Workspace.Guest
)
const isWorkspaceMember = computed(
() => props.workspaceInfo.role === Roles.Workspace.Member
)
const canCreateProject = computed(
() => props.workspaceInfo.permissions.canCreateProject
)
const canMoveProjectToWorkspace = computed(
() => props.workspaceInfo.permissions.canMoveProjectToWorkspace
)
const showBillingAlert = computed(
() =>
props.workspaceInfo.plan?.status === WorkspacePlanStatuses.PaymentFailed ||
props.workspaceInfo.plan?.status === WorkspacePlanStatuses.Canceled ||
props.workspaceInfo.plan?.status === WorkspacePlanStatuses.CancelationScheduled
)
</script>
@@ -16,7 +16,7 @@ import type { MaybeNullOrUndefined, WorkspacePlans } from '@speckle/shared'
import { Roles } from '@speckle/shared'
import type { LayoutDialogButton } from '@speckle/ui-components'
import { settingsWorkspaceRoutes } from '~/lib/common/helpers/route'
import { useWorkspaceLimits } from '~/lib/workspaces/composables/limits'
import { useWorkspaceUsage } from '~/lib/workspaces/composables/usage'
import { formatName } from '~/lib/billing/helpers/plan'
import { useMixpanel } from '~/lib/core/composables/mp'
@@ -24,13 +24,13 @@ const props = defineProps<{
workspaceSlug: string
workspaceName?: string
workspaceRole?: MaybeNullOrUndefined<string>
plan?: WorkspacePlans
plan?: MaybeNullOrUndefined<WorkspacePlans>
type?: 'version' | 'model'
location?: string
}>()
const mixpanel = useMixpanel()
const { modelCount, projectCount } = useWorkspaceLimits(props.workspaceSlug)
const { modelCount, projectCount } = useWorkspaceUsage(props.workspaceSlug)
const dialogOpen = defineModel<boolean>('open', {
required: true
@@ -1,47 +1,53 @@
<template>
<LayoutSidebarMenuGroup
:title="collapsible ? 'About' : undefined"
:collapsible="collapsible"
:icon="iconName"
:icon-click="iconClick"
:icon-text="iconText"
no-hover
>
<div class="flex flex-col gap-4 text-body-2xs text-foreground-2 pb-0 lg:pb-4 mt-1">
<div v-if="isEditing">
<FormTextArea
v-model="editedDescription"
color="foundation"
size="sm"
name="Workspace description"
placeholder="Workspace description"
:rules="[isStringOfLength({ maxLength: 512 })]"
validate-on-value-update
@keyup.enter="saveDescription"
@keyup.esc="cancelEdit"
/>
<div class="flex gap-1 mt-2">
<FormButton size="sm" color="primary" @click="saveDescription">
Save
<div class="px-4 py-2">
<LayoutSidebarMenuGroup
title="About"
collapsible
:icon="iconName"
:icon-click="iconClick"
:icon-text="iconText"
no-hover
>
<div
class="flex flex-col gap-4 text-body-2xs text-foreground-2 pb-0 lg:pb-4 mt-1"
>
<div v-if="isEditing">
<FormTextArea
v-model="editedDescription"
color="foundation"
size="sm"
name="Workspace description"
placeholder="Workspace description"
:rules="[isStringOfLength({ maxLength: 512 })]"
validate-on-value-update
@keyup.enter="saveDescription"
@keyup.esc="cancelEdit"
/>
<div class="flex gap-1 mt-2">
<FormButton size="sm" color="primary" @click="saveDescription">
Save
</FormButton>
<FormButton size="sm" color="outline" @click="cancelEdit">
Cancel
</FormButton>
</div>
</div>
<template v-else>
<div class="whitespace-pre-wrap">
{{ workspace?.description || 'No workspace description' }}
</div>
<FormButton
v-if="!workspace?.description && isWorkspaceAdmin"
color="outline"
size="sm"
@click="startEdit"
>
Add description
</FormButton>
<FormButton size="sm" color="outline" @click="cancelEdit">Cancel</FormButton>
</div>
</template>
</div>
<template v-else>
<div class="whitespace-pre-wrap">
{{ workspaceInfo.description || 'No workspace description' }}
</div>
<FormButton
v-if="!workspaceInfo.description && isWorkspaceAdmin"
color="outline"
size="sm"
@click="startEdit"
>
Add description
</FormButton>
</template>
</div>
</LayoutSidebarMenuGroup>
</LayoutSidebarMenuGroup>
</div>
</template>
<script setup lang="ts">
@@ -56,16 +62,18 @@ import {
import { isStringOfLength } from '~~/lib/common/helpers/validation'
import type { WorkspaceSidebarAbout_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
import { useMixpanel } from '~/lib/core/composables/mp'
import type { MaybeNullOrUndefined } from '@speckle/shared'
graphql(`
fragment WorkspaceSidebarAbout_Workspace on Workspace {
...WorkspaceDashboardAbout_Workspace
id
name
description
}
`)
const props = defineProps<{
workspaceInfo: WorkspaceSidebarAbout_WorkspaceFragment
collapsible?: boolean
workspace: MaybeNullOrUndefined<WorkspaceSidebarAbout_WorkspaceFragment>
isWorkspaceAdmin?: boolean
}>()
@@ -79,7 +87,7 @@ const editedDescription = ref('')
const iconName = computed(() => {
if (!props.isWorkspaceAdmin) return undefined
if (isEditing.value) return undefined
return props.workspaceInfo.description ? 'edit' : 'add'
return props.workspace?.description ? 'edit' : 'add'
})
const iconClick = computed(() => {
@@ -91,11 +99,11 @@ const iconClick = computed(() => {
const iconText = computed(() => {
if (!props.isWorkspaceAdmin) return undefined
if (isEditing.value) return undefined
return props.workspaceInfo.description ? 'Edit description' : 'Add description'
return props.workspace?.description ? 'Edit description' : 'Add description'
})
const startEdit = () => {
editedDescription.value = props.workspaceInfo.description || ''
editedDescription.value = props.workspace?.description || ''
isEditing.value = true
}
@@ -105,9 +113,11 @@ const cancelEdit = () => {
}
const saveDescription = async () => {
if (!props.workspace?.id) return
const result = await updateMutation({
input: {
id: props.workspaceInfo.id,
id: props.workspace?.id,
description: editedDescription.value
}
}).catch(convertThrowIntoFetchResult)
@@ -120,7 +130,7 @@ const saveDescription = async () => {
mixpanel.track('Workspace General Settings Updated', {
fields: ['description'],
// eslint-disable-next-line camelcase
workspace_id: props.workspaceInfo.id,
workspace_id: props.workspace?.id,
source: 'sidebar'
})
isEditing.value = false
@@ -0,0 +1,25 @@
<template>
<div class="p-4">
<div
class="p-2 pl-3 bg-info-lighter rounded-md flex items-center justify-between gap-x-2"
>
<p class="text-primary-focus text-body-3xs font-semibold dark:text-foreground">
You're on a free plan.
</p>
<FormButton
size="sm"
@click="navigateTo(settingsWorkspaceRoutes.billing.route(slug))"
>
Upgrade
</FormButton>
</div>
</div>
</template>
<script setup lang="ts">
import { settingsWorkspaceRoutes } from '~/lib/common/helpers/route'
defineProps<{
slug: string
}>()
</script>
@@ -1,96 +1,130 @@
<template>
<LayoutSidebarMenuGroup
:title="collapsible ? 'Members' : undefined"
:collapsible="collapsible"
:icon="iconName"
:icon-click="iconClick"
:icon-text="iconText"
no-hover
>
<div class="flex lg:flex-col items-center lg:items-start gap-y-3 pb-0 lg:pb-4 mt-1">
<div class="flex gap-y-3 flex-col w-full">
<UserAvatarGroup
:overlap="false"
:users="team.map((teamMember) => teamMember.user)"
:max-avatars="isDesktop ? 5 : 3"
class="shrink-0"
:on-hidden-count-click="
() => {
navigateTo(settingsWorkspaceRoutes.members.route(workspaceInfo.slug))
}
"
/>
<div class="w-full flex items-center gap-x-2">
<button
v-if="adminWorkspacesJoinRequestsCount && isWorkspaceAdmin"
class="hidden md:flex items-center shrink-0 justify-center text-body-3xs px-2 h-8 rounded-full border border-dashed border-outline-2 hover:bg-foundation select-none"
@click="
navigateTo(
settingsWorkspaceRoutes.membersRequests.route(workspaceInfo.slug)
)
"
>
{{ adminWorkspacesJoinRequestsCount }} join
{{ adminWorkspacesJoinRequestsCount > 1 ? 'requests' : 'request' }}
</button>
<button
v-if="invitedTeamCount && isWorkspaceAdmin"
class="hidden md:flex items-center shrink-0 justify-center text-body-3xs px-2 h-8 rounded-full border border-dashed border-outline-2 hover:bg-foundation select-none"
@click="
navigateTo(
settingsWorkspaceRoutes.membersInvites.route(workspaceInfo.slug)
)
"
>
{{ invitedTeamCount }} pending
</button>
</div>
</div>
<FormButton
v-if="isWorkspaceAdmin"
color="outline"
size="sm"
@click="$emit('show-invite-dialog')"
<div class="lg:px-4 lg:py-2">
<LayoutSidebarMenuGroup
:title="collapsible ? 'Members' : undefined"
:collapsible="collapsible"
:icon="iconName"
:icon-click="iconClick"
:icon-text="iconText"
no-hover
>
<div
class="flex lg:flex-col items-center lg:items-start gap-y-3 pb-0 lg:pb-4 mt-1"
>
Invite your team
</FormButton>
</div>
</LayoutSidebarMenuGroup>
<div class="flex gap-y-3 flex-col w-full">
<UserAvatarGroup
:overlap="false"
:users="team.map((teamMember) => teamMember.user)"
:max-avatars="5"
class="shrink-0"
:on-hidden-count-click="
() => {
navigateTo(settingsWorkspaceRoutes.members.route(workspace?.slug || ''))
}
"
/>
<div
v-if="
isWorkspaceAdmin && (adminWorkspacesJoinRequestsCount || invitedTeamCount)
"
class="w-full flex items-center gap-x-2"
>
<button
v-if="adminWorkspacesJoinRequestsCount"
class="hidden md:flex items-center shrink-0 justify-center text-body-3xs px-2 h-8 rounded-full border border-dashed border-outline-2 hover:bg-foundation select-none"
@click="
navigateTo(
settingsWorkspaceRoutes.membersRequests.route(workspace?.slug || '')
)
"
>
{{ adminWorkspacesJoinRequestsCount }} join
{{ adminWorkspacesJoinRequestsCount > 1 ? 'requests' : 'request' }}
</button>
<button
v-if="invitedTeamCount"
class="hidden md:flex items-center shrink-0 justify-center text-body-3xs px-2 h-8 rounded-full border border-dashed border-outline-2 hover:bg-foundation select-none"
@click="
navigateTo(
settingsWorkspaceRoutes.membersInvites.route(workspace?.slug || '')
)
"
>
{{ invitedTeamCount }} pending
</button>
</div>
</div>
<FormButton
v-if="isWorkspaceAdmin"
color="outline"
size="sm"
@click="showInviteDialog = true"
>
Invite your team
</FormButton>
</div>
</LayoutSidebarMenuGroup>
<InviteDialogWorkspace v-model:open="showInviteDialog" :workspace="workspace" />
</div>
</template>
<script setup lang="ts">
import {
type WorkspaceTeam_WorkspaceFragment,
type WorkspaceSidebarMembers_WorkspaceFragment,
WorkspaceJoinRequestStatus
} from '~/lib/common/generated/gql/graphql'
import { settingsWorkspaceRoutes } from '~/lib/common/helpers/route'
import { TailwindBreakpoints } from '~~/lib/common/helpers/tailwind'
import { useBreakpoints } from '@vueuse/core'
import { graphql } from '~~/lib/common/generated/gql'
import type { MaybeNullOrUndefined } from '@speckle/shared'
defineEmits<{
(e: 'show-invite-dialog'): void
}>()
graphql(`
fragment WorkspaceSidebarMembers_Workspace on Workspace {
...InviteDialogWorkspace_Workspace
id
slug
team {
totalCount
items {
id
user {
id
name
...LimitedUserAvatar
}
}
}
invitedTeam(filter: $invitesFilter) {
id
role
email
}
adminWorkspacesJoinRequests {
totalCount
items {
status
id
}
}
}
`)
const props = defineProps<{
workspaceInfo: WorkspaceTeam_WorkspaceFragment
workspace: MaybeNullOrUndefined<WorkspaceSidebarMembers_WorkspaceFragment>
collapsible?: boolean
isWorkspaceAdmin?: boolean
isWorkspaceGuest?: boolean
}>()
const breakpoints = useBreakpoints(TailwindBreakpoints)
const isDesktop = breakpoints.greaterOrEqual('lg')
const showInviteDialog = ref(false)
const team = computed(() => props.workspaceInfo.team.items || [])
const team = computed(() => props.workspace?.team.items || [])
const iconName = computed(() => {
if (props.isWorkspaceAdmin) return 'edit'
return 'view'
})
const iconName = computed(() => (props.isWorkspaceAdmin ? 'edit' : 'view'))
const iconClick = computed(() => {
if (props.isWorkspaceGuest) return undefined
return () =>
navigateTo(settingsWorkspaceRoutes.members.route(props.workspaceInfo.slug))
navigateTo(settingsWorkspaceRoutes.members.route(props.workspace?.slug || ''))
})
const iconText = computed(() => {
@@ -98,10 +132,11 @@ const iconText = computed(() => {
return 'View members'
})
const invitedTeamCount = computed(() => props.workspaceInfo?.invitedTeam?.length ?? 0)
const invitedTeamCount = computed(() => props.workspace?.invitedTeam?.length ?? 0)
const adminWorkspacesJoinRequestsCount = computed(
() =>
props.workspaceInfo?.adminWorkspacesJoinRequests?.items.filter(
props.workspace?.adminWorkspacesJoinRequests?.items.filter(
(request) => request.status === WorkspaceJoinRequestStatus.Pending
).length
)
@@ -1,42 +1,50 @@
<template>
<LayoutSidebarMenuGroup
title="Security"
collapsible
icon="add"
:icon-click="
() => navigateTo(settingsWorkspaceRoutes.security.route(workspaceInfo.slug))
"
icon-text="Add domain"
no-hover
>
<div class="text-body-2xs text-foreground-2 pb-4 mt-1">
<div class="flex flex-col gap-4">
Verified domains not set.
<FormButton
color="outline"
size="sm"
@click="
navigateTo(settingsWorkspaceRoutes.security.route(workspaceInfo.slug))
"
>
Improve security
</FormButton>
<div class="px-4 py-2">
<LayoutSidebarMenuGroup
title="Security"
collapsible
icon="add"
:icon-click="
() => navigateTo(settingsWorkspaceRoutes.security.route(workspace?.slug || ''))
"
icon-text="Add domain"
no-hover
>
<div class="text-body-2xs text-foreground-2 pb-4 mt-1">
<div class="flex flex-col gap-4">
Verified domains not set.
<FormButton
color="outline"
size="sm"
@click="
navigateTo(settingsWorkspaceRoutes.security.route(workspace?.slug || ''))
"
>
Improve security
</FormButton>
</div>
</div>
</div>
</LayoutSidebarMenuGroup>
</LayoutSidebarMenuGroup>
</div>
</template>
<script setup lang="ts">
import { graphql } from '~~/lib/common/generated/gql'
import { settingsWorkspaceRoutes } from '~/lib/common/helpers/route'
import type { WorkspaceSidebarSecurity_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
import type { MaybeNullOrUndefined } from '@speckle/shared'
graphql(`
fragment WorkspaceSidebarSecurity_Workspace on Workspace {
...WorkspaceSecurity_Workspace
id
slug
domains {
id
domain
}
}
`)
defineProps<{
workspaceInfo: WorkspaceSidebarSecurity_WorkspaceFragment
workspace: MaybeNullOrUndefined<WorkspaceSidebarSecurity_WorkspaceFragment>
}>()
</script>
@@ -1,86 +1,63 @@
<template>
<div class="w-full">
<div class="w-full">
<LayoutSidebar>
<div class="flex flex-col divide-y divide-outline-3">
<div v-if="!isWorkspaceGuest && isFreePlan" class="p-4">
<div
class="p-2 pl-3 bg-info-lighter rounded-md flex items-center justify-between gap-x-2"
>
<p
class="text-primary-focus text-body-3xs font-semibold dark:text-foreground"
>
You're on a free plan.
</p>
<FormButton
size="sm"
@click="
navigateTo(settingsWorkspaceRoutes.billing.route(workspaceInfo.slug))
"
>
Upgrade
</FormButton>
</div>
</div>
<div class="px-4 py-2">
<WorkspaceSidebarAbout
:workspace-info="workspaceInfo"
collapsible
:is-workspace-admin="isWorkspaceAdmin"
/>
</div>
<div v-if="!isWorkspaceGuest" class="px-4 py-2">
<WorkspaceSidebarMembers
:workspace-info="workspaceInfo"
:is-workspace-admin="isWorkspaceAdmin"
collapsible
@show-invite-dialog="$emit('show-invite-dialog')"
/>
</div>
<div v-if="isWorkspaceAdmin && !hasDomains" class="px-4 py-2">
<WorkspaceSidebarSecurity :workspace-info="workspaceInfo" />
</div>
</div>
</LayoutSidebar>
<LayoutSidebar class="w-full">
<div class="flex flex-col divide-y divide-outline-3">
<WorkspaceSidebarFreePlanAlert
v-if="!isWorkspaceGuest && isFreePlan"
:slug="workspaceSlug"
/>
<WorkspaceSidebarAbout
:workspace="workspace"
:is-workspace-admin="isWorkspaceAdmin"
/>
<WorkspaceSidebarMembers
v-if="!isWorkspaceGuest"
:workspace="workspace"
:is-workspace-admin="isWorkspaceAdmin"
collapsible
/>
<WorkspaceSidebarSecurity
v-if="isWorkspaceAdmin && !hasDomains"
:workspace="workspace"
/>
</div>
</div>
</LayoutSidebar>
</template>
<script setup lang="ts">
import { Roles } from '@speckle/shared'
import { LayoutSidebar } from '@speckle/ui-components'
import type { WorkspaceProjectList_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
import { graphql } from '~~/lib/common/generated/gql'
import { settingsWorkspaceRoutes } from '~/lib/common/helpers/route'
import { useWorkspacePlan } from '~/lib/workspaces/composables/plan'
import { useQuery } from '@vue/apollo-composable'
import { workspaceSidebarQuery } from '~/lib/workspaces/graphql/queries'
graphql(`
fragment WorkspaceSidebar_Workspace on Workspace {
...WorkspaceDashboardAbout_Workspace
...WorkspaceTeam_Workspace
...WorkspaceSecurity_Workspace
...WorkspaceSidebarMembers_Workspace
...WorkspaceSidebarAbout_Workspace
...WorkspaceSidebarSecurity_Workspace
id
role
slug
plan {
status
domains {
id
}
}
`)
defineEmits<{
(e: 'show-invite-dialog'): void
(e: 'show-move-projects-dialog'): void
}>()
const props = defineProps<{
workspaceInfo: WorkspaceProjectList_WorkspaceFragment
workspaceSlug: string
}>()
const { isFreePlan } = useWorkspacePlan(props.workspaceInfo.slug)
const { result: workspaceResult } = useQuery(workspaceSidebarQuery, () => ({
workspaceSlug: props.workspaceSlug
}))
const isWorkspaceGuest = computed(
() => props.workspaceInfo.role === Roles.Workspace.Guest
)
const isWorkspaceAdmin = computed(
() => props.workspaceInfo.role === Roles.Workspace.Admin
)
const hasDomains = computed(() => props.workspaceInfo.domains?.length)
const workspace = computed(() => workspaceResult.value?.workspaceBySlug)
const { isFreePlan } = useWorkspacePlan(props.workspaceSlug)
const isWorkspaceGuest = computed(() => workspace.value?.slug === Roles.Workspace.Guest)
const isWorkspaceAdmin = computed(() => workspace.value?.role === Roles.Workspace.Admin)
const hasDomains = computed(() => workspace.value?.domains?.length)
</script>
@@ -8,24 +8,7 @@
<div class="relative flex h-[calc(100dvh-3rem)]">
<DashboardSidebar />
<main class="w-full h-full overflow-y-auto simple-scrollbar pt-4 lg:pt-6 pb-16">
<div class="container mx-auto px-6 md:px-8">
<slot />
</div>
</main>
<div
class="hidden lg:flex h-full w-[17rem] shrink-0 border-l border-outline-3 bg-foundation-page"
>
<ClientOnly>
<PortalTarget name="right-sidebar">
<div class="h-full w-full flex items-center justify-center">
<CommonLoadingIcon />
</div>
</PortalTarget>
</ClientOnly>
</div>
<slot />
</div>
</div>
</div>
@@ -1,4 +1,5 @@
import { WorkspacePlans } from '@speckle/shared'
import type { MaybeNullOrUndefined } from '@speckle/shared'
export const formatPrice = (price?: { amount: number; currency: string }) => {
if (!price) return ''
@@ -13,7 +14,7 @@ export const formatPrice = (price?: { amount: number; currency: string }) => {
}
// Internal plan names dont match the names we use in the product
export const formatName = (plan?: WorkspacePlans) => {
export const formatName = (plan?: MaybeNullOrUndefined<WorkspacePlans>) => {
if (!plan) return ''
const formattedPlanNames: Record<WorkspacePlans, string> = {
@@ -45,11 +45,11 @@ 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 HeaderWorkspaceSwitcherActiveWorkspace_Workspace on Workspace {\n id\n name\n logo\n ...HeaderWorkspaceSwitcherHeaderWorkspace_Workspace\n }\n": typeof types.HeaderWorkspaceSwitcherActiveWorkspace_WorkspaceFragmentDoc,
"\n fragment HeaderWorkspaceSwitcherActiveWorkspace_Workspace on Workspace {\n ...HeaderWorkspaceSwitcherHeaderWorkspace_Workspace\n ...InviteDialogWorkspace_Workspace\n id\n name\n logo\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 HeaderWorkspaceSwitcherHeaderWorkspace_Workspace on 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,
@@ -102,8 +102,7 @@ type Documents = {
"\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 ProjectsWorkspaceSelect_Workspace on Workspace {\n id\n role\n name\n logo\n readOnly\n slug\n }\n": typeof types.ProjectsWorkspaceSelect_WorkspaceFragmentDoc,
"\n fragment ProjectsInviteBanner on PendingStreamCollaborator {\n id\n invitedBy {\n ...LimitedUserAvatar\n }\n projectId\n projectName\n token\n user {\n id\n }\n }\n": typeof types.ProjectsInviteBannerFragmentDoc,
"\n fragment SettingsSidebar_Workspace on Workspace {\n ...SettingsMenu_Workspace\n id\n slug\n role\n name\n logo\n plan {\n status\n name\n }\n creationState {\n completed\n }\n }\n": typeof types.SettingsSidebar_WorkspaceFragmentDoc,
"\n fragment SettingsSidebar_User on User {\n id\n workspaces {\n items {\n ...SettingsSidebar_Workspace\n }\n }\n }\n": typeof types.SettingsSidebar_UserFragmentDoc,
"\n fragment SettingsSidebar_Workspace on Workspace {\n ...SettingsMenu_Workspace\n id\n slug\n role\n }\n": typeof types.SettingsSidebar_WorkspaceFragmentDoc,
"\n fragment SettingsServerRegionsAddEditDialog_ServerRegionItem on ServerRegionItem {\n id\n name\n description\n key\n }\n": typeof types.SettingsServerRegionsAddEditDialog_ServerRegionItemFragmentDoc,
"\n fragment SettingsServerRegionsTable_ServerRegionItem on ServerRegionItem {\n id\n name\n key\n description\n }\n": typeof types.SettingsServerRegionsTable_ServerRegionItemFragmentDoc,
"\n fragment SettingsSharedProjects_Project on Project {\n ...ProjectsDeleteDialog_Project\n id\n name\n visibility\n createdAt\n updatedAt\n models(limit: 0) {\n totalCount\n }\n versions(limit: 0) {\n totalCount\n }\n team {\n id\n user {\n name\n id\n avatar\n }\n }\n permissions {\n canDelete {\n ...FullPermissionCheckResult\n }\n canReadSettings {\n ...FullPermissionCheckResult\n }\n canRead {\n ...FullPermissionCheckResult\n }\n }\n }\n": typeof types.SettingsSharedProjects_ProjectFragmentDoc,
@@ -133,9 +132,11 @@ type Documents = {
"\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 ViewerGendoPanel_Project on Project {\n id\n permissions {\n canRequestRender {\n ...FullPermissionCheckResult\n }\n }\n }\n": typeof types.ViewerGendoPanel_ProjectFragmentDoc,
"\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 WorkspaceProjectList_Workspace on Workspace {\n id\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n ...WorkspaceHeader_Workspace\n ...BillingAlert_Workspace\n ...InviteDialogWorkspace_Workspace\n projects {\n ...WorkspaceProjectList_ProjectCollection\n }\n creationState {\n completed\n state\n }\n permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n }\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 permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n canMoveProjectToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n }\n": typeof types.WorkspaceHeader_WorkspaceFragmentDoc,
"\n fragment WorkspaceAddProjectMenu_Workspace on Workspace {\n id\n name\n slug\n role\n plan {\n name\n }\n permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n canMoveProjectToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n }\n": typeof types.WorkspaceAddProjectMenu_WorkspaceFragmentDoc,
"\n fragment WorkspaceDashboard_Workspace on Workspace {\n ...WorkspaceSidebarMembers_Workspace\n ...WorkspaceDashboardHeader_Workspace\n ...WorkspaceDashboardProjectList_Workspace\n id\n name\n role\n creationState {\n completed\n state\n }\n }\n": typeof types.WorkspaceDashboard_WorkspaceFragmentDoc,
"\n fragment WorkspaceDashboardHeader_Workspace on Workspace {\n ...WorkspaceSidebarMembers_Workspace\n ...WorkspaceAddProjectMenu_Workspace\n ...BillingAlert_Workspace\n id\n role\n plan {\n status\n }\n }\n": typeof types.WorkspaceDashboardHeader_WorkspaceFragmentDoc,
"\n fragment WorkspaceDashboardProjectList_ProjectCollection on ProjectCollection {\n totalCount\n items {\n ...ProjectDashboardItem\n }\n cursor\n }\n": typeof types.WorkspaceDashboardProjectList_ProjectCollectionFragmentDoc,
"\n fragment WorkspaceDashboardProjectList_Workspace on Workspace {\n ...WorkspaceAddProjectMenu_Workspace\n id\n }\n": typeof types.WorkspaceDashboardProjectList_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,
"\n fragment WorkspaceInviteBlock_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n workspaceId\n workspaceName\n token\n user {\n id\n name\n ...LimitedUserAvatar\n }\n title\n email\n ...UseWorkspaceInviteManager_PendingWorkspaceCollaborator\n }\n": typeof types.WorkspaceInviteBlock_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest on WorkspaceJoinRequest {\n id\n user {\n id\n name\n }\n workspace {\n id\n }\n }\n": typeof types.WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequestFragmentDoc,
@@ -143,9 +144,10 @@ type Documents = {
"\n fragment WorkspaceMoveProjectManager_Project on Project {\n ...WorkspaceMoveProjectManager_ProjectBase\n permissions {\n canMoveToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n workspace {\n id\n slug\n permissions {\n canMoveProjectToWorkspace(projectId: $projectId) {\n ...FullPermissionCheckResult\n }\n }\n }\n }\n": typeof types.WorkspaceMoveProjectManager_ProjectFragmentDoc,
"\n fragment WorkspaceMoveProjectManager_Workspace on Workspace {\n id\n role\n name\n logo\n slug\n plan {\n name\n usage {\n projectCount\n modelCount\n }\n }\n permissions {\n canMoveProjectToWorkspace(projectId: $projectId) {\n ...FullPermissionCheckResult\n }\n }\n projects {\n totalCount\n }\n team {\n items {\n user {\n id\n name\n avatar\n }\n }\n }\n }\n": typeof types.WorkspaceMoveProjectManager_WorkspaceFragmentDoc,
"\n fragment WorkspaceMoveProjectSelectWorkspace_User on User {\n workspaces {\n items {\n ...WorkspaceMoveProjectManager_Workspace\n }\n }\n projects(cursor: $cursor, filter: $filter) {\n items {\n ...WorkspaceMoveProjectManager_Project\n }\n cursor\n totalCount\n }\n }\n": typeof types.WorkspaceMoveProjectSelectWorkspace_UserFragmentDoc,
"\n fragment WorkspaceSidebarAbout_Workspace on Workspace {\n ...WorkspaceDashboardAbout_Workspace\n }\n": typeof types.WorkspaceSidebarAbout_WorkspaceFragmentDoc,
"\n fragment WorkspaceSidebarSecurity_Workspace on Workspace {\n ...WorkspaceSecurity_Workspace\n }\n": typeof types.WorkspaceSidebarSecurity_WorkspaceFragmentDoc,
"\n fragment WorkspaceSidebar_Workspace on Workspace {\n ...WorkspaceDashboardAbout_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n slug\n plan {\n status\n }\n }\n": typeof types.WorkspaceSidebar_WorkspaceFragmentDoc,
"\n fragment WorkspaceSidebarAbout_Workspace on Workspace {\n id\n name\n description\n }\n": typeof types.WorkspaceSidebarAbout_WorkspaceFragmentDoc,
"\n fragment WorkspaceSidebarMembers_Workspace on Workspace {\n ...InviteDialogWorkspace_Workspace\n id\n slug\n team {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n invitedTeam(filter: $invitesFilter) {\n id\n role\n email\n }\n adminWorkspacesJoinRequests {\n totalCount\n items {\n status\n id\n }\n }\n }\n": typeof types.WorkspaceSidebarMembers_WorkspaceFragmentDoc,
"\n fragment WorkspaceSidebarSecurity_Workspace on Workspace {\n id\n slug\n domains {\n id\n domain\n }\n }\n": typeof types.WorkspaceSidebarSecurity_WorkspaceFragmentDoc,
"\n fragment WorkspaceSidebar_Workspace on Workspace {\n ...WorkspaceSidebarMembers_Workspace\n ...WorkspaceSidebarAbout_Workspace\n ...WorkspaceSidebarSecurity_Workspace\n id\n role\n slug\n domains {\n id\n }\n }\n": typeof types.WorkspaceSidebar_WorkspaceFragmentDoc,
"\n fragment WorkspaceWizard_Workspace on Workspace {\n creationState {\n completed\n state\n }\n name\n slug\n }\n": typeof types.WorkspaceWizard_WorkspaceFragmentDoc,
"\n fragment WorkspaceWizardStepRegion_ServerInfo on ServerInfo {\n multiRegion {\n regions {\n id\n ...SettingsWorkspacesRegionsSelect_ServerRegionItem\n }\n }\n }\n": typeof types.WorkspaceWizardStepRegion_ServerInfoFragmentDoc,
"\n query ActiveUserMainMetadata {\n activeUser {\n id\n email\n emails {\n id\n email\n verified\n }\n company\n bio\n name\n role\n avatar\n isOnboardingFinished\n createdAt\n verified\n notificationPreferences\n versions(limit: 0) {\n totalCount\n }\n }\n }\n": typeof types.ActiveUserMainMetadataDocument,
@@ -311,7 +313,7 @@ type Documents = {
"\n mutation DeleteWorkspaceDomain($input: WorkspaceDomainDeleteInput!) {\n workspaceMutations {\n deleteDomain(input: $input) {\n ...SettingsWorkspacesSecurityDomainRemoveDialog_Workspace\n }\n }\n }\n": typeof types.DeleteWorkspaceDomainDocument,
"\n mutation SettingsLeaveWorkspace($leaveId: ID!) {\n workspaceMutations {\n leave(id: $leaveId)\n }\n }\n": typeof types.SettingsLeaveWorkspaceDocument,
"\n mutation SettingsBillingCancelCheckoutSession($input: CancelCheckoutSessionInput!) {\n workspaceMutations {\n billing {\n cancelCheckoutSession(input: $input)\n }\n }\n }\n": typeof types.SettingsBillingCancelCheckoutSessionDocument,
"\n query SettingsSidebar {\n activeUser {\n ...SettingsSidebar_User\n }\n }\n": typeof types.SettingsSidebarDocument,
"\n query SettingsSidebar {\n activeUser {\n activeWorkspace {\n ...SettingsSidebar_Workspace\n }\n }\n }\n": typeof types.SettingsSidebarDocument,
"\n query SettingsWorkspaceGeneral($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesGeneral_Workspace\n }\n }\n": typeof types.SettingsWorkspaceGeneralDocument,
"\n query SettingsWorkspaceBilling($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n ...WorkspaceBillingPage_Workspace\n }\n }\n": typeof types.SettingsWorkspaceBillingDocument,
"\n query SettingsWorkspaceBillingCustomerPortal($workspaceId: String!) {\n workspace(id: $workspaceId) {\n customerPortalUrl\n }\n }\n": typeof types.SettingsWorkspaceBillingCustomerPortalDocument,
@@ -368,12 +370,6 @@ type Documents = {
"\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 slug\n plan {\n usage {\n projectCount\n modelCount\n }\n }\n team {\n totalCount\n }\n teamByRole {\n admins {\n totalCount\n }\n members {\n totalCount\n }\n guests {\n totalCount\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,
"\n fragment WorkspaceTeam_Workspace on Workspace {\n id\n slug\n team(limit: 250) {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n adminWorkspacesJoinRequests {\n totalCount\n items {\n status\n id\n }\n }\n ...WorkspaceInvitedTeam_Workspace\n }\n": typeof types.WorkspaceTeam_WorkspaceFragmentDoc,
"\n fragment WorkspaceSecurity_Workspace on Workspace {\n id\n slug\n domains {\n id\n domain\n }\n }\n": typeof types.WorkspaceSecurity_WorkspaceFragmentDoc,
"\n fragment WorkspaceLastAdminCheck_Workspace on Workspace {\n id\n team(limit: 50, filter: { roles: [\"workspace:admin\"] }) {\n items {\n id\n }\n }\n }\n": typeof types.WorkspaceLastAdminCheck_WorkspaceFragmentDoc,
"\n mutation UpdateRole($input: WorkspaceRoleUpdateInput!) {\n workspaceMutations {\n updateRole(input: $input) {\n team {\n items {\n id\n role\n }\n }\n }\n }\n }\n": typeof types.UpdateRoleDocument,
"\n mutation WorkspacesUpdateSeatType($input: WorkspaceUpdateSeatTypeInput!) {\n workspaceMutations {\n updateSeatType(input: $input) {\n team {\n items {\n id\n seatType\n }\n }\n }\n }\n }\n": typeof types.WorkspacesUpdateSeatTypeDocument,
"\n mutation InviteToWorkspace(\n $workspaceId: String!\n $input: [WorkspaceInviteCreateInput!]!\n ) {\n workspaceMutations {\n invites {\n batchCreate(workspaceId: $workspaceId, input: $input) {\n id\n invitedTeam {\n ...SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator\n }\n }\n }\n }\n }\n": typeof types.InviteToWorkspaceDocument,
@@ -389,8 +385,9 @@ type Documents = {
"\n mutation RequestToJoinWorkspace($input: WorkspaceRequestToJoinInput!) {\n workspaceMutations {\n requestToJoin(input: $input)\n }\n }\n": typeof types.RequestToJoinWorkspaceDocument,
"\n mutation DismissDiscoverableWorkspace($input: WorkspaceDismissInput!) {\n workspaceMutations {\n dismiss(input: $input)\n }\n }\n": typeof types.DismissDiscoverableWorkspaceDocument,
"\n query WorkspaceAccessCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n }\n }\n": typeof types.WorkspaceAccessCheckDocument,
"\n query WorkspacePageQuery(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n $token: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceProjectList_Workspace\n }\n workspaceInvite(\n workspaceId: $workspaceSlug\n token: $token\n options: { useSlug: true }\n ) {\n id\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n ...WorkspaceInviteBlock_PendingWorkspaceCollaborator\n }\n }\n": typeof types.WorkspacePageQueryDocument,
"\n query WorkspaceProjectsQuery(\n $workspaceSlug: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceProjectList_ProjectCollection\n }\n }\n }\n": typeof types.WorkspaceProjectsQueryDocument,
"\n query WorkspaceSidebar(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceSidebar_Workspace\n }\n }\n": typeof types.WorkspaceSidebarDocument,
"\n query WorkspaceDashboard(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceDashboard_Workspace\n }\n }\n": typeof types.WorkspaceDashboardDocument,
"\n query WorkspaceProjectsQuery(\n $workspaceSlug: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceDashboardProjectList_ProjectCollection\n }\n }\n }\n": typeof types.WorkspaceProjectsQueryDocument,
"\n query WorkspaceFunctionsQuery($workspaceSlug: String!) {\n ...AutomateFunctionsPageHeader_Query\n workspaceBySlug(slug: $workspaceSlug) {\n id\n name\n automateFunctions {\n items {\n id\n ...AutomationsFunctionsCard_AutomateFunction\n ...AutomateAutomationCreateDialog_AutomateFunction\n }\n }\n }\n }\n": typeof types.WorkspaceFunctionsQueryDocument,
"\n query WorkspaceInvite(\n $workspaceId: String\n $token: String\n $options: WorkspaceInviteLookupOptions\n ) {\n workspaceInvite(workspaceId: $workspaceId, token: $token, options: $options) {\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n ...WorkspaceInviteBlock_PendingWorkspaceCollaborator\n }\n }\n": typeof types.WorkspaceInviteDocument,
"\n query ValidateWorkspaceSlug($slug: String!) {\n validateWorkspaceSlug(slug: $slug)\n }\n": typeof types.ValidateWorkspaceSlugDocument,
@@ -402,13 +399,13 @@ 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 activeWorkspace($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...ActiveWorkspace_Workspace\n }\n }\n": typeof types.ActiveWorkspaceDocument,
"\n query WorkspaceLastAdminCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspaceLastAdminCheck_Workspace\n }\n }\n": typeof types.WorkspaceLastAdminCheckDocument,
"\n query WorkspaceLastAdminCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n teamByRole {\n admins {\n totalCount\n }\n }\n }\n }\n": typeof types.WorkspaceLastAdminCheckDocument,
"\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 query WorkspaceMoveProjectManagerProject($projectId: String!) {\n project(id: $projectId) {\n ...WorkspaceMoveProjectManager_Project\n }\n }\n": typeof types.WorkspaceMoveProjectManagerProjectDocument,
"\n query WorkspaceMoveProjectManagerWorkspace(\n $workspaceSlug: String!\n $projectId: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceMoveProjectManager_Workspace\n }\n }\n": typeof types.WorkspaceMoveProjectManagerWorkspaceDocument,
"\n query WorkspaceMoveProjectManagerUser(\n $cursor: String\n $filter: UserProjectsFilter\n $projectId: String\n ) {\n activeUser {\n ...WorkspaceMoveProjectSelectWorkspace_User\n }\n }\n": typeof types.WorkspaceMoveProjectManagerUserDocument,
"\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 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 ...WorkspaceDashboard_Workspace\n ...WorkspaceDashboardProjectList_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,
"\n query LegacyViewerStreamRedirectMetadata($streamId: String!) {\n project(id: $streamId) {\n id\n versions(limit: 1) {\n totalCount\n items {\n id\n model {\n id\n }\n }\n }\n }\n }\n": typeof types.LegacyViewerStreamRedirectMetadataDocument,
@@ -466,11 +463,11 @@ 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 HeaderWorkspaceSwitcherActiveWorkspace_Workspace on Workspace {\n id\n name\n logo\n ...HeaderWorkspaceSwitcherHeaderWorkspace_Workspace\n }\n": types.HeaderWorkspaceSwitcherActiveWorkspace_WorkspaceFragmentDoc,
"\n fragment HeaderWorkspaceSwitcherActiveWorkspace_Workspace on Workspace {\n ...HeaderWorkspaceSwitcherHeaderWorkspace_Workspace\n ...InviteDialogWorkspace_Workspace\n id\n name\n logo\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 HeaderWorkspaceSwitcherHeaderWorkspace_Workspace on 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,
@@ -523,8 +520,7 @@ const documents: Documents = {
"\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 ProjectsWorkspaceSelect_Workspace on Workspace {\n id\n role\n name\n logo\n readOnly\n slug\n }\n": types.ProjectsWorkspaceSelect_WorkspaceFragmentDoc,
"\n fragment ProjectsInviteBanner on PendingStreamCollaborator {\n id\n invitedBy {\n ...LimitedUserAvatar\n }\n projectId\n projectName\n token\n user {\n id\n }\n }\n": types.ProjectsInviteBannerFragmentDoc,
"\n fragment SettingsSidebar_Workspace on Workspace {\n ...SettingsMenu_Workspace\n id\n slug\n role\n name\n logo\n plan {\n status\n name\n }\n creationState {\n completed\n }\n }\n": types.SettingsSidebar_WorkspaceFragmentDoc,
"\n fragment SettingsSidebar_User on User {\n id\n workspaces {\n items {\n ...SettingsSidebar_Workspace\n }\n }\n }\n": types.SettingsSidebar_UserFragmentDoc,
"\n fragment SettingsSidebar_Workspace on Workspace {\n ...SettingsMenu_Workspace\n id\n slug\n role\n }\n": types.SettingsSidebar_WorkspaceFragmentDoc,
"\n fragment SettingsServerRegionsAddEditDialog_ServerRegionItem on ServerRegionItem {\n id\n name\n description\n key\n }\n": types.SettingsServerRegionsAddEditDialog_ServerRegionItemFragmentDoc,
"\n fragment SettingsServerRegionsTable_ServerRegionItem on ServerRegionItem {\n id\n name\n key\n description\n }\n": types.SettingsServerRegionsTable_ServerRegionItemFragmentDoc,
"\n fragment SettingsSharedProjects_Project on Project {\n ...ProjectsDeleteDialog_Project\n id\n name\n visibility\n createdAt\n updatedAt\n models(limit: 0) {\n totalCount\n }\n versions(limit: 0) {\n totalCount\n }\n team {\n id\n user {\n name\n id\n avatar\n }\n }\n permissions {\n canDelete {\n ...FullPermissionCheckResult\n }\n canReadSettings {\n ...FullPermissionCheckResult\n }\n canRead {\n ...FullPermissionCheckResult\n }\n }\n }\n": types.SettingsSharedProjects_ProjectFragmentDoc,
@@ -554,9 +550,11 @@ const documents: Documents = {
"\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 ViewerGendoPanel_Project on Project {\n id\n permissions {\n canRequestRender {\n ...FullPermissionCheckResult\n }\n }\n }\n": types.ViewerGendoPanel_ProjectFragmentDoc,
"\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 WorkspaceProjectList_Workspace on Workspace {\n id\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n ...WorkspaceHeader_Workspace\n ...BillingAlert_Workspace\n ...InviteDialogWorkspace_Workspace\n projects {\n ...WorkspaceProjectList_ProjectCollection\n }\n creationState {\n completed\n state\n }\n permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n }\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 permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n canMoveProjectToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n }\n": types.WorkspaceHeader_WorkspaceFragmentDoc,
"\n fragment WorkspaceAddProjectMenu_Workspace on Workspace {\n id\n name\n slug\n role\n plan {\n name\n }\n permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n canMoveProjectToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n }\n": types.WorkspaceAddProjectMenu_WorkspaceFragmentDoc,
"\n fragment WorkspaceDashboard_Workspace on Workspace {\n ...WorkspaceSidebarMembers_Workspace\n ...WorkspaceDashboardHeader_Workspace\n ...WorkspaceDashboardProjectList_Workspace\n id\n name\n role\n creationState {\n completed\n state\n }\n }\n": types.WorkspaceDashboard_WorkspaceFragmentDoc,
"\n fragment WorkspaceDashboardHeader_Workspace on Workspace {\n ...WorkspaceSidebarMembers_Workspace\n ...WorkspaceAddProjectMenu_Workspace\n ...BillingAlert_Workspace\n id\n role\n plan {\n status\n }\n }\n": types.WorkspaceDashboardHeader_WorkspaceFragmentDoc,
"\n fragment WorkspaceDashboardProjectList_ProjectCollection on ProjectCollection {\n totalCount\n items {\n ...ProjectDashboardItem\n }\n cursor\n }\n": types.WorkspaceDashboardProjectList_ProjectCollectionFragmentDoc,
"\n fragment WorkspaceDashboardProjectList_Workspace on Workspace {\n ...WorkspaceAddProjectMenu_Workspace\n id\n }\n": types.WorkspaceDashboardProjectList_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,
"\n fragment WorkspaceInviteBlock_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n workspaceId\n workspaceName\n token\n user {\n id\n name\n ...LimitedUserAvatar\n }\n title\n email\n ...UseWorkspaceInviteManager_PendingWorkspaceCollaborator\n }\n": types.WorkspaceInviteBlock_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest on WorkspaceJoinRequest {\n id\n user {\n id\n name\n }\n workspace {\n id\n }\n }\n": types.WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequestFragmentDoc,
@@ -564,9 +562,10 @@ const documents: Documents = {
"\n fragment WorkspaceMoveProjectManager_Project on Project {\n ...WorkspaceMoveProjectManager_ProjectBase\n permissions {\n canMoveToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n workspace {\n id\n slug\n permissions {\n canMoveProjectToWorkspace(projectId: $projectId) {\n ...FullPermissionCheckResult\n }\n }\n }\n }\n": types.WorkspaceMoveProjectManager_ProjectFragmentDoc,
"\n fragment WorkspaceMoveProjectManager_Workspace on Workspace {\n id\n role\n name\n logo\n slug\n plan {\n name\n usage {\n projectCount\n modelCount\n }\n }\n permissions {\n canMoveProjectToWorkspace(projectId: $projectId) {\n ...FullPermissionCheckResult\n }\n }\n projects {\n totalCount\n }\n team {\n items {\n user {\n id\n name\n avatar\n }\n }\n }\n }\n": types.WorkspaceMoveProjectManager_WorkspaceFragmentDoc,
"\n fragment WorkspaceMoveProjectSelectWorkspace_User on User {\n workspaces {\n items {\n ...WorkspaceMoveProjectManager_Workspace\n }\n }\n projects(cursor: $cursor, filter: $filter) {\n items {\n ...WorkspaceMoveProjectManager_Project\n }\n cursor\n totalCount\n }\n }\n": types.WorkspaceMoveProjectSelectWorkspace_UserFragmentDoc,
"\n fragment WorkspaceSidebarAbout_Workspace on Workspace {\n ...WorkspaceDashboardAbout_Workspace\n }\n": types.WorkspaceSidebarAbout_WorkspaceFragmentDoc,
"\n fragment WorkspaceSidebarSecurity_Workspace on Workspace {\n ...WorkspaceSecurity_Workspace\n }\n": types.WorkspaceSidebarSecurity_WorkspaceFragmentDoc,
"\n fragment WorkspaceSidebar_Workspace on Workspace {\n ...WorkspaceDashboardAbout_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n slug\n plan {\n status\n }\n }\n": types.WorkspaceSidebar_WorkspaceFragmentDoc,
"\n fragment WorkspaceSidebarAbout_Workspace on Workspace {\n id\n name\n description\n }\n": types.WorkspaceSidebarAbout_WorkspaceFragmentDoc,
"\n fragment WorkspaceSidebarMembers_Workspace on Workspace {\n ...InviteDialogWorkspace_Workspace\n id\n slug\n team {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n invitedTeam(filter: $invitesFilter) {\n id\n role\n email\n }\n adminWorkspacesJoinRequests {\n totalCount\n items {\n status\n id\n }\n }\n }\n": types.WorkspaceSidebarMembers_WorkspaceFragmentDoc,
"\n fragment WorkspaceSidebarSecurity_Workspace on Workspace {\n id\n slug\n domains {\n id\n domain\n }\n }\n": types.WorkspaceSidebarSecurity_WorkspaceFragmentDoc,
"\n fragment WorkspaceSidebar_Workspace on Workspace {\n ...WorkspaceSidebarMembers_Workspace\n ...WorkspaceSidebarAbout_Workspace\n ...WorkspaceSidebarSecurity_Workspace\n id\n role\n slug\n domains {\n id\n }\n }\n": types.WorkspaceSidebar_WorkspaceFragmentDoc,
"\n fragment WorkspaceWizard_Workspace on Workspace {\n creationState {\n completed\n state\n }\n name\n slug\n }\n": types.WorkspaceWizard_WorkspaceFragmentDoc,
"\n fragment WorkspaceWizardStepRegion_ServerInfo on ServerInfo {\n multiRegion {\n regions {\n id\n ...SettingsWorkspacesRegionsSelect_ServerRegionItem\n }\n }\n }\n": types.WorkspaceWizardStepRegion_ServerInfoFragmentDoc,
"\n query ActiveUserMainMetadata {\n activeUser {\n id\n email\n emails {\n id\n email\n verified\n }\n company\n bio\n name\n role\n avatar\n isOnboardingFinished\n createdAt\n verified\n notificationPreferences\n versions(limit: 0) {\n totalCount\n }\n }\n }\n": types.ActiveUserMainMetadataDocument,
@@ -732,7 +731,7 @@ const documents: Documents = {
"\n mutation DeleteWorkspaceDomain($input: WorkspaceDomainDeleteInput!) {\n workspaceMutations {\n deleteDomain(input: $input) {\n ...SettingsWorkspacesSecurityDomainRemoveDialog_Workspace\n }\n }\n }\n": types.DeleteWorkspaceDomainDocument,
"\n mutation SettingsLeaveWorkspace($leaveId: ID!) {\n workspaceMutations {\n leave(id: $leaveId)\n }\n }\n": types.SettingsLeaveWorkspaceDocument,
"\n mutation SettingsBillingCancelCheckoutSession($input: CancelCheckoutSessionInput!) {\n workspaceMutations {\n billing {\n cancelCheckoutSession(input: $input)\n }\n }\n }\n": types.SettingsBillingCancelCheckoutSessionDocument,
"\n query SettingsSidebar {\n activeUser {\n ...SettingsSidebar_User\n }\n }\n": types.SettingsSidebarDocument,
"\n query SettingsSidebar {\n activeUser {\n activeWorkspace {\n ...SettingsSidebar_Workspace\n }\n }\n }\n": types.SettingsSidebarDocument,
"\n query SettingsWorkspaceGeneral($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesGeneral_Workspace\n }\n }\n": types.SettingsWorkspaceGeneralDocument,
"\n query SettingsWorkspaceBilling($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n ...WorkspaceBillingPage_Workspace\n }\n }\n": types.SettingsWorkspaceBillingDocument,
"\n query SettingsWorkspaceBillingCustomerPortal($workspaceId: String!) {\n workspace(id: $workspaceId) {\n customerPortalUrl\n }\n }\n": types.SettingsWorkspaceBillingCustomerPortalDocument,
@@ -789,12 +788,6 @@ const documents: Documents = {
"\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 slug\n plan {\n usage {\n projectCount\n modelCount\n }\n }\n team {\n totalCount\n }\n teamByRole {\n admins {\n totalCount\n }\n members {\n totalCount\n }\n guests {\n totalCount\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,
"\n fragment WorkspaceTeam_Workspace on Workspace {\n id\n slug\n team(limit: 250) {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n adminWorkspacesJoinRequests {\n totalCount\n items {\n status\n id\n }\n }\n ...WorkspaceInvitedTeam_Workspace\n }\n": types.WorkspaceTeam_WorkspaceFragmentDoc,
"\n fragment WorkspaceSecurity_Workspace on Workspace {\n id\n slug\n domains {\n id\n domain\n }\n }\n": types.WorkspaceSecurity_WorkspaceFragmentDoc,
"\n fragment WorkspaceLastAdminCheck_Workspace on Workspace {\n id\n team(limit: 50, filter: { roles: [\"workspace:admin\"] }) {\n items {\n id\n }\n }\n }\n": types.WorkspaceLastAdminCheck_WorkspaceFragmentDoc,
"\n mutation UpdateRole($input: WorkspaceRoleUpdateInput!) {\n workspaceMutations {\n updateRole(input: $input) {\n team {\n items {\n id\n role\n }\n }\n }\n }\n }\n": types.UpdateRoleDocument,
"\n mutation WorkspacesUpdateSeatType($input: WorkspaceUpdateSeatTypeInput!) {\n workspaceMutations {\n updateSeatType(input: $input) {\n team {\n items {\n id\n seatType\n }\n }\n }\n }\n }\n": types.WorkspacesUpdateSeatTypeDocument,
"\n mutation InviteToWorkspace(\n $workspaceId: String!\n $input: [WorkspaceInviteCreateInput!]!\n ) {\n workspaceMutations {\n invites {\n batchCreate(workspaceId: $workspaceId, input: $input) {\n id\n invitedTeam {\n ...SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator\n }\n }\n }\n }\n }\n": types.InviteToWorkspaceDocument,
@@ -810,8 +803,9 @@ const documents: Documents = {
"\n mutation RequestToJoinWorkspace($input: WorkspaceRequestToJoinInput!) {\n workspaceMutations {\n requestToJoin(input: $input)\n }\n }\n": types.RequestToJoinWorkspaceDocument,
"\n mutation DismissDiscoverableWorkspace($input: WorkspaceDismissInput!) {\n workspaceMutations {\n dismiss(input: $input)\n }\n }\n": types.DismissDiscoverableWorkspaceDocument,
"\n query WorkspaceAccessCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n }\n }\n": types.WorkspaceAccessCheckDocument,
"\n query WorkspacePageQuery(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n $token: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceProjectList_Workspace\n }\n workspaceInvite(\n workspaceId: $workspaceSlug\n token: $token\n options: { useSlug: true }\n ) {\n id\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n ...WorkspaceInviteBlock_PendingWorkspaceCollaborator\n }\n }\n": types.WorkspacePageQueryDocument,
"\n query WorkspaceProjectsQuery(\n $workspaceSlug: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceProjectList_ProjectCollection\n }\n }\n }\n": types.WorkspaceProjectsQueryDocument,
"\n query WorkspaceSidebar(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceSidebar_Workspace\n }\n }\n": types.WorkspaceSidebarDocument,
"\n query WorkspaceDashboard(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceDashboard_Workspace\n }\n }\n": types.WorkspaceDashboardDocument,
"\n query WorkspaceProjectsQuery(\n $workspaceSlug: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceDashboardProjectList_ProjectCollection\n }\n }\n }\n": types.WorkspaceProjectsQueryDocument,
"\n query WorkspaceFunctionsQuery($workspaceSlug: String!) {\n ...AutomateFunctionsPageHeader_Query\n workspaceBySlug(slug: $workspaceSlug) {\n id\n name\n automateFunctions {\n items {\n id\n ...AutomationsFunctionsCard_AutomateFunction\n ...AutomateAutomationCreateDialog_AutomateFunction\n }\n }\n }\n }\n": types.WorkspaceFunctionsQueryDocument,
"\n query WorkspaceInvite(\n $workspaceId: String\n $token: String\n $options: WorkspaceInviteLookupOptions\n ) {\n workspaceInvite(workspaceId: $workspaceId, token: $token, options: $options) {\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n ...WorkspaceInviteBlock_PendingWorkspaceCollaborator\n }\n }\n": types.WorkspaceInviteDocument,
"\n query ValidateWorkspaceSlug($slug: String!) {\n validateWorkspaceSlug(slug: $slug)\n }\n": types.ValidateWorkspaceSlugDocument,
@@ -823,13 +817,13 @@ 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 activeWorkspace($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...ActiveWorkspace_Workspace\n }\n }\n": types.ActiveWorkspaceDocument,
"\n query WorkspaceLastAdminCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspaceLastAdminCheck_Workspace\n }\n }\n": types.WorkspaceLastAdminCheckDocument,
"\n query WorkspaceLastAdminCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n teamByRole {\n admins {\n totalCount\n }\n }\n }\n }\n": types.WorkspaceLastAdminCheckDocument,
"\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 query WorkspaceMoveProjectManagerProject($projectId: String!) {\n project(id: $projectId) {\n ...WorkspaceMoveProjectManager_Project\n }\n }\n": types.WorkspaceMoveProjectManagerProjectDocument,
"\n query WorkspaceMoveProjectManagerWorkspace(\n $workspaceSlug: String!\n $projectId: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceMoveProjectManager_Workspace\n }\n }\n": types.WorkspaceMoveProjectManagerWorkspaceDocument,
"\n query WorkspaceMoveProjectManagerUser(\n $cursor: String\n $filter: UserProjectsFilter\n $projectId: String\n ) {\n activeUser {\n ...WorkspaceMoveProjectSelectWorkspace_User\n }\n }\n": types.WorkspaceMoveProjectManagerUserDocument,
"\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 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 ...WorkspaceDashboard_Workspace\n ...WorkspaceDashboardProjectList_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,
"\n query LegacyViewerStreamRedirectMetadata($streamId: String!) {\n project(id: $streamId) {\n id\n versions(limit: 1) {\n totalCount\n items {\n id\n model {\n id\n }\n }\n }\n }\n }\n": types.LegacyViewerStreamRedirectMetadataDocument,
@@ -997,7 +991,7 @@ export function graphql(source: "\n fragment FormUsersSelectItem on LimitedUser
/**
* 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 HeaderWorkspaceSwitcherActiveWorkspace_Workspace on Workspace {\n id\n name\n logo\n ...HeaderWorkspaceSwitcherHeaderWorkspace_Workspace\n }\n"): (typeof documents)["\n fragment HeaderWorkspaceSwitcherActiveWorkspace_Workspace on Workspace {\n id\n name\n logo\n ...HeaderWorkspaceSwitcherHeaderWorkspace_Workspace\n }\n"];
export function graphql(source: "\n fragment HeaderWorkspaceSwitcherActiveWorkspace_Workspace on Workspace {\n ...HeaderWorkspaceSwitcherHeaderWorkspace_Workspace\n ...InviteDialogWorkspace_Workspace\n id\n name\n logo\n }\n"): (typeof documents)["\n fragment HeaderWorkspaceSwitcherActiveWorkspace_Workspace on Workspace {\n ...HeaderWorkspaceSwitcherHeaderWorkspace_Workspace\n ...InviteDialogWorkspace_Workspace\n id\n name\n logo\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1013,7 +1007,7 @@ 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"];
export function graphql(source: "\n fragment HeaderWorkspaceSwitcherHeaderWorkspace_Workspace on 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 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.
*/
@@ -1225,11 +1219,7 @@ export function graphql(source: "\n fragment ProjectsInviteBanner on PendingStr
/**
* 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 SettingsSidebar_Workspace on Workspace {\n ...SettingsMenu_Workspace\n id\n slug\n role\n name\n logo\n plan {\n status\n name\n }\n creationState {\n completed\n }\n }\n"): (typeof documents)["\n fragment SettingsSidebar_Workspace on Workspace {\n ...SettingsMenu_Workspace\n id\n slug\n role\n name\n logo\n plan {\n status\n name\n }\n creationState {\n completed\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 SettingsSidebar_User on User {\n id\n workspaces {\n items {\n ...SettingsSidebar_Workspace\n }\n }\n }\n"): (typeof documents)["\n fragment SettingsSidebar_User on User {\n id\n workspaces {\n items {\n ...SettingsSidebar_Workspace\n }\n }\n }\n"];
export function graphql(source: "\n fragment SettingsSidebar_Workspace on Workspace {\n ...SettingsMenu_Workspace\n id\n slug\n role\n }\n"): (typeof documents)["\n fragment SettingsSidebar_Workspace on Workspace {\n ...SettingsMenu_Workspace\n id\n slug\n role\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1349,15 +1339,23 @@ 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 WorkspaceProjectList_Workspace on Workspace {\n id\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n ...WorkspaceHeader_Workspace\n ...BillingAlert_Workspace\n ...InviteDialogWorkspace_Workspace\n projects {\n ...WorkspaceProjectList_ProjectCollection\n }\n creationState {\n completed\n state\n }\n permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n }\n }\n"): (typeof documents)["\n fragment WorkspaceProjectList_Workspace on Workspace {\n id\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n ...WorkspaceHeader_Workspace\n ...BillingAlert_Workspace\n ...InviteDialogWorkspace_Workspace\n projects {\n ...WorkspaceProjectList_ProjectCollection\n }\n creationState {\n completed\n state\n }\n permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n }\n }\n"];
export function graphql(source: "\n fragment WorkspaceAddProjectMenu_Workspace on Workspace {\n id\n name\n slug\n role\n plan {\n name\n }\n permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n canMoveProjectToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n }\n"): (typeof documents)["\n fragment WorkspaceAddProjectMenu_Workspace on Workspace {\n id\n name\n slug\n role\n plan {\n name\n }\n permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n canMoveProjectToWorkspace {\n ...FullPermissionCheckResult\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 WorkspaceProjectList_ProjectCollection on ProjectCollection {\n totalCount\n items {\n ...ProjectDashboardItem\n }\n cursor\n }\n"): (typeof documents)["\n fragment WorkspaceProjectList_ProjectCollection on ProjectCollection {\n totalCount\n items {\n ...ProjectDashboardItem\n }\n cursor\n }\n"];
export function graphql(source: "\n fragment WorkspaceDashboard_Workspace on Workspace {\n ...WorkspaceSidebarMembers_Workspace\n ...WorkspaceDashboardHeader_Workspace\n ...WorkspaceDashboardProjectList_Workspace\n id\n name\n role\n creationState {\n completed\n state\n }\n }\n"): (typeof documents)["\n fragment WorkspaceDashboard_Workspace on Workspace {\n ...WorkspaceSidebarMembers_Workspace\n ...WorkspaceDashboardHeader_Workspace\n ...WorkspaceDashboardProjectList_Workspace\n id\n name\n role\n creationState {\n completed\n state\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 WorkspaceHeader_Workspace on Workspace {\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...BillingAlert_Workspace\n slug\n readOnly\n permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n canMoveProjectToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n }\n"): (typeof documents)["\n fragment WorkspaceHeader_Workspace on Workspace {\n ...WorkspaceBase_Workspace\n ...WorkspaceTeam_Workspace\n ...BillingAlert_Workspace\n slug\n readOnly\n permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n canMoveProjectToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n }\n"];
export function graphql(source: "\n fragment WorkspaceDashboardHeader_Workspace on Workspace {\n ...WorkspaceSidebarMembers_Workspace\n ...WorkspaceAddProjectMenu_Workspace\n ...BillingAlert_Workspace\n id\n role\n plan {\n status\n }\n }\n"): (typeof documents)["\n fragment WorkspaceDashboardHeader_Workspace on Workspace {\n ...WorkspaceSidebarMembers_Workspace\n ...WorkspaceAddProjectMenu_Workspace\n ...BillingAlert_Workspace\n id\n role\n plan {\n status\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 WorkspaceDashboardProjectList_ProjectCollection on ProjectCollection {\n totalCount\n items {\n ...ProjectDashboardItem\n }\n cursor\n }\n"): (typeof documents)["\n fragment WorkspaceDashboardProjectList_ProjectCollection on ProjectCollection {\n totalCount\n items {\n ...ProjectDashboardItem\n }\n cursor\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 WorkspaceDashboardProjectList_Workspace on Workspace {\n ...WorkspaceAddProjectMenu_Workspace\n id\n }\n"): (typeof documents)["\n fragment WorkspaceDashboardProjectList_Workspace on Workspace {\n ...WorkspaceAddProjectMenu_Workspace\n id\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1389,15 +1387,19 @@ export function graphql(source: "\n fragment WorkspaceMoveProjectSelectWorkspac
/**
* 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 WorkspaceSidebarAbout_Workspace on Workspace {\n ...WorkspaceDashboardAbout_Workspace\n }\n"): (typeof documents)["\n fragment WorkspaceSidebarAbout_Workspace on Workspace {\n ...WorkspaceDashboardAbout_Workspace\n }\n"];
export function graphql(source: "\n fragment WorkspaceSidebarAbout_Workspace on Workspace {\n id\n name\n description\n }\n"): (typeof documents)["\n fragment WorkspaceSidebarAbout_Workspace on Workspace {\n id\n name\n description\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 WorkspaceSidebarSecurity_Workspace on Workspace {\n ...WorkspaceSecurity_Workspace\n }\n"): (typeof documents)["\n fragment WorkspaceSidebarSecurity_Workspace on Workspace {\n ...WorkspaceSecurity_Workspace\n }\n"];
export function graphql(source: "\n fragment WorkspaceSidebarMembers_Workspace on Workspace {\n ...InviteDialogWorkspace_Workspace\n id\n slug\n team {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n invitedTeam(filter: $invitesFilter) {\n id\n role\n email\n }\n adminWorkspacesJoinRequests {\n totalCount\n items {\n status\n id\n }\n }\n }\n"): (typeof documents)["\n fragment WorkspaceSidebarMembers_Workspace on Workspace {\n ...InviteDialogWorkspace_Workspace\n id\n slug\n team {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n invitedTeam(filter: $invitesFilter) {\n id\n role\n email\n }\n adminWorkspacesJoinRequests {\n totalCount\n items {\n status\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.
*/
export function graphql(source: "\n fragment WorkspaceSidebar_Workspace on Workspace {\n ...WorkspaceDashboardAbout_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n slug\n plan {\n status\n }\n }\n"): (typeof documents)["\n fragment WorkspaceSidebar_Workspace on Workspace {\n ...WorkspaceDashboardAbout_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n slug\n plan {\n status\n }\n }\n"];
export function graphql(source: "\n fragment WorkspaceSidebarSecurity_Workspace on Workspace {\n id\n slug\n domains {\n id\n domain\n }\n }\n"): (typeof documents)["\n fragment WorkspaceSidebarSecurity_Workspace on Workspace {\n id\n slug\n domains {\n id\n domain\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 WorkspaceSidebar_Workspace on Workspace {\n ...WorkspaceSidebarMembers_Workspace\n ...WorkspaceSidebarAbout_Workspace\n ...WorkspaceSidebarSecurity_Workspace\n id\n role\n slug\n domains {\n id\n }\n }\n"): (typeof documents)["\n fragment WorkspaceSidebar_Workspace on Workspace {\n ...WorkspaceSidebarMembers_Workspace\n ...WorkspaceSidebarAbout_Workspace\n ...WorkspaceSidebarSecurity_Workspace\n id\n role\n slug\n domains {\n id\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -2061,7 +2063,7 @@ export function graphql(source: "\n mutation SettingsBillingCancelCheckoutSessi
/**
* 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 SettingsSidebar {\n activeUser {\n ...SettingsSidebar_User\n }\n }\n"): (typeof documents)["\n query SettingsSidebar {\n activeUser {\n ...SettingsSidebar_User\n }\n }\n"];
export function graphql(source: "\n query SettingsSidebar {\n activeUser {\n activeWorkspace {\n ...SettingsSidebar_Workspace\n }\n }\n }\n"): (typeof documents)["\n query SettingsSidebar {\n activeUser {\n activeWorkspace {\n ...SettingsSidebar_Workspace\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -2286,30 +2288,6 @@ export function graphql(source: "\n fragment WorkspaceSsoStatus_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 WorkspaceUsage_Workspace on Workspace {\n id\n slug\n plan {\n usage {\n projectCount\n modelCount\n }\n }\n team {\n totalCount\n }\n teamByRole {\n admins {\n totalCount\n }\n members {\n totalCount\n }\n guests {\n totalCount\n }\n }\n }\n"): (typeof documents)["\n fragment WorkspaceUsage_Workspace on Workspace {\n id\n slug\n plan {\n usage {\n projectCount\n modelCount\n }\n }\n team {\n totalCount\n }\n teamByRole {\n admins {\n totalCount\n }\n members {\n totalCount\n }\n guests {\n totalCount\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 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 documents)["\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"];
/**
* 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 WorkspaceDashboardAbout_Workspace on Workspace {\n id\n name\n description\n }\n"): (typeof documents)["\n fragment WorkspaceDashboardAbout_Workspace on Workspace {\n id\n name\n description\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 WorkspaceInvitedTeam_Workspace on Workspace {\n id\n invitedTeam(filter: $invitesFilter) {\n id\n role\n email\n }\n }\n"): (typeof documents)["\n fragment WorkspaceInvitedTeam_Workspace on Workspace {\n id\n invitedTeam(filter: $invitesFilter) {\n id\n role\n email\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 WorkspaceTeam_Workspace on Workspace {\n id\n slug\n team(limit: 250) {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n adminWorkspacesJoinRequests {\n totalCount\n items {\n status\n id\n }\n }\n ...WorkspaceInvitedTeam_Workspace\n }\n"): (typeof documents)["\n fragment WorkspaceTeam_Workspace on Workspace {\n id\n slug\n team(limit: 250) {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n adminWorkspacesJoinRequests {\n totalCount\n items {\n status\n id\n }\n }\n ...WorkspaceInvitedTeam_Workspace\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 WorkspaceSecurity_Workspace on Workspace {\n id\n slug\n domains {\n id\n domain\n }\n }\n"): (typeof documents)["\n fragment WorkspaceSecurity_Workspace on Workspace {\n id\n slug\n domains {\n id\n domain\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 WorkspaceLastAdminCheck_Workspace on Workspace {\n id\n team(limit: 50, filter: { roles: [\"workspace:admin\"] }) {\n items {\n id\n }\n }\n }\n"): (typeof documents)["\n fragment WorkspaceLastAdminCheck_Workspace on Workspace {\n id\n team(limit: 50, filter: { roles: [\"workspace:admin\"] }) {\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.
*/
@@ -2373,11 +2351,15 @@ export function graphql(source: "\n query WorkspaceAccessCheck($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 WorkspacePageQuery(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n $token: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceProjectList_Workspace\n }\n workspaceInvite(\n workspaceId: $workspaceSlug\n token: $token\n options: { useSlug: true }\n ) {\n id\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n ...WorkspaceInviteBlock_PendingWorkspaceCollaborator\n }\n }\n"): (typeof documents)["\n query WorkspacePageQuery(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n $token: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceProjectList_Workspace\n }\n workspaceInvite(\n workspaceId: $workspaceSlug\n token: $token\n options: { useSlug: true }\n ) {\n id\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n ...WorkspaceInviteBlock_PendingWorkspaceCollaborator\n }\n }\n"];
export function graphql(source: "\n query WorkspaceSidebar(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceSidebar_Workspace\n }\n }\n"): (typeof documents)["\n query WorkspaceSidebar(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceSidebar_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 WorkspaceProjectsQuery(\n $workspaceSlug: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceProjectList_ProjectCollection\n }\n }\n }\n"): (typeof documents)["\n query WorkspaceProjectsQuery(\n $workspaceSlug: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceProjectList_ProjectCollection\n }\n }\n }\n"];
export function graphql(source: "\n query WorkspaceDashboard(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceDashboard_Workspace\n }\n }\n"): (typeof documents)["\n query WorkspaceDashboard(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceDashboard_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 WorkspaceProjectsQuery(\n $workspaceSlug: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceDashboardProjectList_ProjectCollection\n }\n }\n }\n"): (typeof documents)["\n query WorkspaceProjectsQuery(\n $workspaceSlug: String!\n $filter: WorkspaceProjectsFilter\n $cursor: String\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n projects(filter: $filter, cursor: $cursor, limit: 10) {\n ...WorkspaceDashboardProjectList_ProjectCollection\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -2425,7 +2407,7 @@ export function graphql(source: "\n query activeWorkspace($slug: String!) {\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 WorkspaceLastAdminCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspaceLastAdminCheck_Workspace\n }\n }\n"): (typeof documents)["\n query WorkspaceLastAdminCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...WorkspaceLastAdminCheck_Workspace\n }\n }\n"];
export function graphql(source: "\n query WorkspaceLastAdminCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n teamByRole {\n admins {\n totalCount\n }\n }\n }\n }\n"): (typeof documents)["\n query WorkspaceLastAdminCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n teamByRole {\n admins {\n totalCount\n }\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -2449,7 +2431,7 @@ export function graphql(source: "\n query WorkspaceMoveProjectManagerUser(\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 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 documents)["\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"];
export function graphql(source: "\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 ...WorkspaceDashboard_Workspace\n ...WorkspaceDashboardProjectList_Workspace\n }\n }\n }\n"): (typeof documents)["\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 ...WorkspaceDashboard_Workspace\n ...WorkspaceDashboardProjectList_Workspace\n }\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
@@ -41,39 +41,39 @@ export const settingsServerRoutes = {
export const settingsWorkspaceRoutes = {
general: {
name: 'settings-workspaces-slug-general',
route: (slug: string) => `/settings/workspaces/${slug}/general`
route: (slug?: string) => `/settings/workspaces/${slug}/general`
},
members: {
name: 'settings-workspaces-slug-members',
route: (slug: string) => `/settings/workspaces/${slug}/members`
route: (slug?: string) => `/settings/workspaces/${slug}/members`
},
membersGuests: {
name: 'settings-workspaces-slug-members-guests',
route: (slug: string) => `/settings/workspaces/${slug}/members/guests`
route: (slug?: string) => `/settings/workspaces/${slug}/members/guests`
},
membersInvites: {
name: 'settings-workspaces-slug-members-invites',
route: (slug: string) => `/settings/workspaces/${slug}/members/invites`
route: (slug?: string) => `/settings/workspaces/${slug}/members/invites`
},
membersRequests: {
name: 'settings-workspaces-slug-members-requests',
route: (slug: string) => `/settings/workspaces/${slug}/members/requests`
route: (slug?: string) => `/settings/workspaces/${slug}/members/requests`
},
projects: {
name: 'settings-workspaces-slug-projects',
route: (slug: string) => `/settings/workspaces/${slug}/projects`
route: (slug?: string) => `/settings/workspaces/${slug}/projects`
},
security: {
name: 'settings-workspaces-slug-security',
route: (slug: string) => `/settings/workspaces/${slug}/security`
route: (slug?: string) => `/settings/workspaces/${slug}/security`
},
billing: {
name: 'settings-workspaces-slug-billing',
route: (slug: string) => `/settings/workspaces/${slug}/billing`
route: (slug?: string) => `/settings/workspaces/${slug}/billing`
},
regions: {
name: 'settings-workspaces-slug-regions',
route: (slug: string) => `/settings/workspaces/${slug}/regions`
route: (slug?: string) => `/settings/workspaces/${slug}/regions`
}
}
@@ -39,37 +39,37 @@ export const useSettingsMenu = () => {
{
title: 'General',
name: settingsWorkspaceRoutes.general.name,
route: (slug: string) => settingsWorkspaceRoutes.general.route(slug),
route: (slug?: string) => settingsWorkspaceRoutes.general.route(slug),
permission: [Roles.Workspace.Admin, Roles.Workspace.Member, Roles.Workspace.Guest]
},
{
title: 'People',
name: settingsWorkspaceRoutes.members.name,
route: (slug: string) => settingsWorkspaceRoutes.members.route(slug),
route: (slug?: string) => settingsWorkspaceRoutes.members.route(slug),
permission: [Roles.Workspace.Admin, Roles.Workspace.Member]
},
{
title: 'Projects',
name: settingsWorkspaceRoutes.projects.name,
route: (slug: string) => settingsWorkspaceRoutes.projects.route(slug),
route: (slug?: string) => settingsWorkspaceRoutes.projects.route(slug),
permission: [Roles.Workspace.Admin, Roles.Workspace.Member]
},
{
title: 'Security',
name: settingsWorkspaceRoutes.security.name,
route: (slug: string) => settingsWorkspaceRoutes.security.route(slug),
route: (slug?: string) => settingsWorkspaceRoutes.security.route(slug),
permission: [Roles.Workspace.Admin]
},
{
title: 'Billing',
name: settingsWorkspaceRoutes.billing.name,
route: (slug: string) => settingsWorkspaceRoutes.billing.route(slug),
route: (slug?: string) => settingsWorkspaceRoutes.billing.route(slug),
permission: [Roles.Workspace.Admin, Roles.Workspace.Member]
},
{
title: 'Data residency',
name: settingsWorkspaceRoutes.regions.name,
route: (slug: string) => settingsWorkspaceRoutes.regions.route(slug),
route: (slug?: string) => settingsWorkspaceRoutes.regions.route(slug),
permission: [Roles.Workspace.Admin, Roles.Workspace.Member],
...(!isMultiRegionEnabled
? {
@@ -149,7 +149,7 @@ export const useSettingsMembersActions = (params: {
}) => {
const { activeUser } = useActiveUser()
const { hasSingleAdmin } = useWorkspaceLastAdminCheck({
const { isLastAdmin } = useWorkspaceLastAdminCheck({
workspaceSlug: params.workspaceSlug.value || ''
})
@@ -164,7 +164,7 @@ export const useSettingsMembersActions = (params: {
)
const isOnlyAdmin = computed(
() => hasSingleAdmin.value && isActiveUserWorkspaceAdmin.value
() => isLastAdmin.value && isActiveUserWorkspaceAdmin.value
)
const isActiveUserTargetUser = computed(
@@ -3,7 +3,9 @@ import { graphql } from '~~/lib/common/generated/gql'
export const settingsSidebarQuery = graphql(`
query SettingsSidebar {
activeUser {
...SettingsSidebar_User
activeWorkspace {
...SettingsSidebar_Workspace
}
}
}
`)
@@ -13,7 +13,7 @@ export type GenericSettingsMenuItem = BaseSettingsMenuItem & {
export type WorkspaceSettingsMenuItem = BaseSettingsMenuItem & {
name: string
route: (slug: string) => string
route: (slug?: string) => string
}
export enum WorkspaceUserActionTypes {
@@ -2,7 +2,6 @@ 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'
import type { WorkspacePlanLimits_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
graphql(`
@@ -19,7 +18,6 @@ export const useLimitsState = () =>
useState<WorkspacePlanLimits_WorkspaceFragment | null>('limits', () => null)
export const useWorkspaceLimits = (slug: string) => {
const { modelCount, projectCount } = useWorkspaceUsage(slug)
const limitsState = useLimitsState()
const { onResult } = useQuery(
@@ -67,19 +65,8 @@ export const useWorkspaceLimits = (slug: string) => {
return `${value} ${unit}`
})
const remainingProjectCount = computed(() =>
limits.value.projectCount ? limits.value.projectCount - projectCount.value : 0
)
const remainingModelCount = computed(() =>
limits.value.modelCount ? limits.value.modelCount - modelCount.value : 0
)
return {
projectCount,
modelCount,
limits,
remainingProjectCount,
remainingModelCount,
versionLimitFormatted,
commentLimitFormatted
}
@@ -658,12 +658,11 @@ export const useWorkspaceLastAdminCheck = (params: { workspaceSlug: string }) =>
slug: workspaceSlug
})
const hasSingleAdmin = computed(() => {
const admins = result.value?.workspaceBySlug?.team.items || []
return admins.length === 1
})
const isLastAdmin = computed(
() => result.value?.workspaceBySlug?.teamByRole?.admins?.totalCount === 1
)
return {
hasSingleAdmin
isLastAdmin
}
}
@@ -1,84 +0,0 @@
import { graphql } from '~~/lib/common/generated/gql'
// Base workspace fragment
export const workspaceBaseFragment = graphql(`
fragment WorkspaceBase_Workspace on Workspace {
id
name
slug
role
description
logo
plan {
status
createdAt
}
}
`)
export const workspaceDashboardAboutFragment = graphql(`
fragment WorkspaceDashboardAbout_Workspace on Workspace {
id
name
description
}
`)
export const workspaceInvitedTeamFragment = graphql(`
fragment WorkspaceInvitedTeam_Workspace on Workspace {
id
invitedTeam(filter: $invitesFilter) {
id
role
email
}
}
`)
export const workspaceTeamFragment = graphql(`
fragment WorkspaceTeam_Workspace on Workspace {
id
slug
team(limit: 250) {
totalCount
items {
id
user {
id
name
...LimitedUserAvatar
}
}
}
adminWorkspacesJoinRequests {
totalCount
items {
status
id
}
}
...WorkspaceInvitedTeam_Workspace
}
`)
export const workspaceSecurityFragment = graphql(`
fragment WorkspaceSecurity_Workspace on Workspace {
id
slug
domains {
id
domain
}
}
`)
export const workspaceLastAdminCheckFragment = graphql(`
fragment WorkspaceLastAdminCheck_Workspace on Workspace {
id
team(limit: 50, filter: { roles: ["workspace:admin"] }) {
items {
id
}
}
}
`)
@@ -8,23 +8,24 @@ export const workspaceAccessCheckQuery = graphql(`
}
`)
export const workspacePageQuery = graphql(`
query WorkspacePageQuery(
export const workspaceSidebarQuery = graphql(`
query WorkspaceSidebar(
$workspaceSlug: String!
$invitesFilter: PendingWorkspaceCollaboratorsFilter
$token: String
) {
workspaceBySlug(slug: $workspaceSlug) {
...WorkspaceProjectList_Workspace
...WorkspaceSidebar_Workspace
}
workspaceInvite(
workspaceId: $workspaceSlug
token: $token
options: { useSlug: true }
) {
id
...WorkspaceInviteBanner_PendingWorkspaceCollaborator
...WorkspaceInviteBlock_PendingWorkspaceCollaborator
}
`)
export const workspaceDashboardQuery = graphql(`
query WorkspaceDashboard(
$workspaceSlug: String!
$invitesFilter: PendingWorkspaceCollaboratorsFilter
) {
workspaceBySlug(slug: $workspaceSlug) {
...WorkspaceDashboard_Workspace
}
}
`)
@@ -38,7 +39,7 @@ export const workspaceProjectsQuery = graphql(`
workspaceBySlug(slug: $workspaceSlug) {
id
projects(filter: $filter, cursor: $cursor, limit: 10) {
...WorkspaceProjectList_ProjectCollection
...WorkspaceDashboardProjectList_ProjectCollection
}
}
}
@@ -153,7 +154,11 @@ export const activeWorkspaceQuery = graphql(`
export const workspaceLastAdminCheckQuery = graphql(`
query WorkspaceLastAdminCheck($slug: String!) {
workspaceBySlug(slug: $slug) {
...WorkspaceLastAdminCheck_Workspace
teamByRole {
admins {
totalCount
}
}
}
}
`)
@@ -10,7 +10,8 @@ export const onWorkspaceUpdatedSubscription = graphql(`
id
workspace {
id
...WorkspaceProjectList_Workspace
...WorkspaceDashboard_Workspace
...WorkspaceDashboardProjectList_Workspace
}
}
}
@@ -1,11 +1,24 @@
<template>
<div>
<WorkspaceInviteWrapper
v-if="token"
:workspace-slug="workspaceSlug"
:token="token"
/>
<WorkspaceProjectList v-else :workspace-slug="workspaceSlug" />
<div class="flex w-full">
<main class="flex-1 h-full overflow-y-auto simple-scrollbar pt-4 lg:pt-6 pb-16">
<div class="container mx-auto px-6 md:px-8">
<WorkspaceInviteWrapper
v-if="token"
:workspace-slug="workspaceSlug"
:token="token"
/>
<WorkspaceDashboard v-else :workspace-slug="workspaceSlug" />
</div>
</main>
<div
v-if="!token"
class="hidden lg:flex h-full w-[17rem] shrink-0 border-l border-outline-3 bg-foundation-page"
>
<div class="h-full w-full">
<WorkspaceSidebar :workspace-slug="workspaceSlug" />
</div>
</div>
</div>
</template>
@@ -1,5 +1,5 @@
<template>
<nav class="flex flex-col space-y-4">
<nav class="flex flex-col">
<slot></slot>
</nav>
</template>