218 lines
6.7 KiB
Vue
218 lines
6.7 KiB
Vue
<template>
|
|
<div>
|
|
<div v-if="showLoading" class="py-4 flex items-center justify-center w-full h-32">
|
|
<CommonLoadingIcon size="sm" />
|
|
</div>
|
|
<template v-else>
|
|
<div v-if="hasWorkspaces">
|
|
<p class="mb-4">Select an existing workspaces or create a new one.</p>
|
|
<div class="flex flex-col gap-2">
|
|
<div
|
|
v-for="ws in sortedWorkspaces"
|
|
:key="`${ws.id}-${ws.permissions?.canMoveProjectToWorkspace?.code}`"
|
|
v-tippy="getWorkspaceTooltip(ws)"
|
|
>
|
|
<button
|
|
class="w-full"
|
|
:class="!isWorkspaceDisabled(ws) ? 'cursor-not-allowed' : ''"
|
|
:disabled="isWorkspaceDisabled(ws)"
|
|
@click="handleWorkspaceClick(ws)"
|
|
>
|
|
<WorkspaceCard
|
|
:logo="ws.logo ?? ''"
|
|
:name="ws.name"
|
|
:clickable="!isWorkspaceDisabled(ws)"
|
|
>
|
|
<template #text>
|
|
<div class="flex flex-col gap-2 items-start">
|
|
<CommonBadge
|
|
v-if="isSsoRequired(ws)"
|
|
color="secondary"
|
|
class="capitalize"
|
|
rounded
|
|
>
|
|
SSO login required
|
|
</CommonBadge>
|
|
<p>
|
|
{{ ws.projects.totalCount }} projects,
|
|
{{ ws.projects.totalCount }} models
|
|
</p>
|
|
<UserAvatarGroup
|
|
:users="ws.team.items.map((t) => t.user)"
|
|
:max-count="6"
|
|
size="sm"
|
|
/>
|
|
</div>
|
|
</template>
|
|
<template #actions>
|
|
<CommonBadge color="secondary" rounded>
|
|
{{ formatName(ws.plan?.name) }}
|
|
</CommonBadge>
|
|
</template>
|
|
</WorkspaceCard>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<p v-else class="text-body-xs text-foreground">
|
|
Looks like you haven't created any workspaces yet. Workspaces help you easily
|
|
organise and control your digital projects. Create one to move your project
|
|
into.
|
|
</p>
|
|
</template>
|
|
|
|
<WorkspacePlanLimitReachedDialog
|
|
v-model:open="showLimitDialog"
|
|
subtitle="Upgrade your plan to move project"
|
|
>
|
|
<template v-if="limitReachedWorkspace">
|
|
<p class="text-body-xs text-foreground-2">
|
|
The workspace
|
|
<span class="font-bold">{{ limitReachedWorkspace.name }}</span>
|
|
is on a {{ formatName(limitReachedWorkspace.plan?.name) }} plan with a limit
|
|
of 1 project and 5 models. Upgrade the workspace to add more projects.
|
|
</p>
|
|
</template>
|
|
</WorkspacePlanLimitReachedDialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { graphql } from '~~/lib/common/generated/gql'
|
|
import type {
|
|
WorkspaceMoveProjectManager_ProjectFragment,
|
|
WorkspaceMoveProjectManager_WorkspaceFragment,
|
|
WorkspacePermissionChecks
|
|
} from '~~/lib/common/generated/gql/graphql'
|
|
import { useQuery } from '@vue/apollo-composable'
|
|
import { UserAvatarGroup } from '@speckle/ui-components'
|
|
import { workspaceMoveProjectManagerUserQuery } from '~/lib/workspaces/graphql/queries'
|
|
import { formatName } from '~/lib/billing/helpers/plan'
|
|
import { Roles } from '@speckle/shared'
|
|
|
|
graphql(`
|
|
fragment WorkspaceMoveProjectSelectWorkspace_User on User {
|
|
workspaces {
|
|
items {
|
|
...WorkspaceMoveProjectManager_Workspace
|
|
}
|
|
}
|
|
projects(cursor: $cursor, filter: $filter) {
|
|
items {
|
|
...WorkspaceMoveProjectManager_Project
|
|
}
|
|
cursor
|
|
totalCount
|
|
}
|
|
}
|
|
`)
|
|
|
|
const props = defineProps<{
|
|
project: WorkspaceMoveProjectManager_ProjectFragment
|
|
workspacePermissions?: WorkspacePermissionChecks
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(
|
|
e: 'workspace-selected',
|
|
workspace: WorkspaceMoveProjectManager_WorkspaceFragment
|
|
): void
|
|
}>()
|
|
|
|
const { result, loading: initialLoading } = useQuery(
|
|
workspaceMoveProjectManagerUserQuery,
|
|
() => ({
|
|
cursor: null,
|
|
filter: {},
|
|
projectId: props.project.id
|
|
})
|
|
)
|
|
|
|
const workspaces = computed(() => result.value?.activeUser?.workspaces.items ?? [])
|
|
const hasWorkspaces = computed(() => workspaces.value.length > 0)
|
|
const showLoading = computed(
|
|
() => initialLoading.value && workspaces.value.length === 0
|
|
)
|
|
|
|
const showLimitDialog = ref(false)
|
|
const limitReachedWorkspace = ref<WorkspaceMoveProjectManager_WorkspaceFragment | null>(
|
|
null
|
|
)
|
|
|
|
const isWorkspaceAdmin = computed(
|
|
() => (workspace: WorkspaceMoveProjectManager_WorkspaceFragment | null) => {
|
|
if (!workspace) return false
|
|
return workspace.role === Roles.Workspace.Admin
|
|
}
|
|
)
|
|
|
|
const isWorkspaceDisabled = computed(
|
|
() => (workspace: WorkspaceMoveProjectManager_WorkspaceFragment) => {
|
|
if (!isWorkspaceAdmin.value(workspace)) {
|
|
return true
|
|
}
|
|
|
|
const permission = workspace.permissions?.canMoveProjectToWorkspace
|
|
return !permission?.authorized && permission?.code !== 'WorkspaceLimitsReached'
|
|
}
|
|
)
|
|
|
|
const getWorkspaceTooltip = computed(
|
|
() => (workspace: WorkspaceMoveProjectManager_WorkspaceFragment) => {
|
|
if (workspace.permissions.canMoveProjectToWorkspace.authorized) {
|
|
return undefined
|
|
}
|
|
if (
|
|
workspace.permissions.canMoveProjectToWorkspace.code === 'WorkspaceLimitsReached'
|
|
) {
|
|
return undefined
|
|
}
|
|
if (!isWorkspaceAdmin.value(workspace)) {
|
|
return 'Only workspace administrators can move projects to this workspace'
|
|
}
|
|
|
|
const permission = workspace.permissions?.canMoveProjectToWorkspace
|
|
return permission?.message
|
|
}
|
|
)
|
|
|
|
const sortedWorkspaces = computed(() => {
|
|
return [...workspaces.value].sort((a, b) => {
|
|
const aEnabled =
|
|
a.permissions?.canMoveProjectToWorkspace?.authorized ||
|
|
a.permissions?.canMoveProjectToWorkspace?.code === 'WorkspaceLimitsReached'
|
|
const bEnabled =
|
|
b.permissions?.canMoveProjectToWorkspace?.authorized ||
|
|
b.permissions?.canMoveProjectToWorkspace?.code === 'WorkspaceLimitsReached'
|
|
|
|
if (aEnabled && !bEnabled) return -1
|
|
if (!aEnabled && bEnabled) return 1
|
|
return 0
|
|
})
|
|
})
|
|
|
|
const handleWorkspaceClick = (
|
|
workspace: WorkspaceMoveProjectManager_WorkspaceFragment
|
|
) => {
|
|
const permission = workspace.permissions?.canMoveProjectToWorkspace
|
|
if (permission?.code === 'WorkspaceLimitsReached') {
|
|
limitReachedWorkspace.value = workspace
|
|
showLimitDialog.value = true
|
|
return
|
|
}
|
|
|
|
if (permission?.authorized) {
|
|
emit('workspace-selected', workspace)
|
|
}
|
|
}
|
|
|
|
const isSsoRequired = computed(
|
|
() => (workspace: WorkspaceMoveProjectManager_WorkspaceFragment) => {
|
|
return (
|
|
workspace.permissions?.canMoveProjectToWorkspace?.code ===
|
|
'WorkspaceSsoSessionNoAccess'
|
|
)
|
|
}
|
|
)
|
|
</script>
|