Tidy fragments. Add project update dialog

This commit is contained in:
andrewwallacespeckle
2025-04-03 14:18:10 +02:00
parent 6cea72e4f8
commit bfe74b3133
16 changed files with 354 additions and 293 deletions
@@ -1,95 +0,0 @@
<template>
<LayoutDialog v-model:open="open" max-width="sm">
<template #header>Change project permissions</template>
<div class="text-foreground mb-8">
<div v-if="projectCount > 0" class="flex flex-col gap-4">
<p class="font-medium text-body-xs">
Projects {{ user?.user.name }} has access to:
</p>
<FormTextInput
v-bind="searchBind"
name="searchGuests"
color="foundation"
type="text"
size="lg"
:placeholder="`Search ${projectCount} project${
projectCount !== 1 ? 's' : ''
}...`"
class="px-3 py-2 border border-outline-3 rounded-md focus:outline-none focus:ring-2 focus:ring-primary"
v-on="searchOn"
/>
<div
class="flex flex-col divide-y divide-outline-3 rounded-md border border-outline-3"
>
<div
v-for="projectRole in filteredProjectRoles"
:key="projectRole.project.id"
class="flex items-center justify-between p-4"
>
<span class="text-body-sm">{{ projectRole.project.name }}</span>
<ProjectPageTeamPermissionSelect
:model-value="projectRole.role"
:disabled="false"
mount-menu-on-body
hide-owner
@update:model-value="
(newRole) => updateProjectRole(projectRole.project.id, newRole)
"
@delete="() => updateProjectRole(projectRole.project.id, null)"
/>
</div>
</div>
</div>
<div v-else>
This guest doesn't have access to any projects in this workspace.
</div>
</div>
</LayoutDialog>
</template>
<script setup lang="ts">
import type { StreamRoles, MaybeNullOrUndefined } from '@speckle/shared'
import { useUpdateUserRole } from '~~/lib/projects/composables/projectManagement'
import type { WorkspaceCollaborator } from '~/lib/common/generated/gql/graphql'
import { useDebouncedTextInput } from '@speckle/ui-components'
const props = defineProps<{
user: WorkspaceCollaborator
workspaceId: MaybeNullOrUndefined<string>
}>()
const open = defineModel<boolean>('open', { required: true })
const loading = ref(false)
const searchTerm = ref('')
const { on: searchOn, bind: searchBind } = useDebouncedTextInput({
model: searchTerm,
debouncedBy: 300
})
const project = computed(() => ({ workspaceId: props.workspaceId }))
const filteredProjectRoles = computed(() => {
const roles = props.user?.projectRoles
if (!searchTerm.value) return roles || []
return (roles || []).filter((projectRole) =>
projectRole.project.name.toLowerCase().includes(searchTerm.value.toLowerCase())
)
})
const updateRole = useUpdateUserRole(project)
const updateProjectRole = async (projectId: string, newRole: StreamRoles | null) => {
if (!props.user) return
loading.value = true
await updateRole({
projectId,
userId: props.user.id,
role: newRole
})
loading.value = false
}
const projectCount = computed(() => props.user?.projectRoles?.length)
</script>
@@ -22,11 +22,12 @@
:columns="[
{ id: 'name', header: 'Name', classes: 'col-span-4' },
{ id: 'seat', header: 'Seat', classes: 'col-span-2' },
{ id: 'joined', header: 'Joined', classes: 'col-span-4' },
{ id: 'joined', header: 'Joined', classes: 'col-span-3' },
{ id: 'projects', header: 'Projects', classes: 'col-span-2' },
{
id: 'actions',
header: '',
classes: 'col-span-2 flex items-center justify-end'
classes: 'col-span-1 flex items-center justify-end'
}
]"
:items="guests"
@@ -59,16 +60,25 @@
{{ formattedFullDate(item.joinDate) }}
</span>
</template>
<template #projects="{ item }">
<button
v-if="
item.projectRoles.length > 0 &&
isWorkspaceAdmin &&
item.role !== Roles.Workspace.Admin
"
class="text-foreground-2 max-w-max text-body-2xs select-none"
>
{{ item.projectRoles.length }} projects
</button>
<div v-else class="text-foreground-2 max-w-max text-body-2xs select-none">
{{ item.projectRoles.length }} projects
</div>
</template>
<template #actions="{ item }">
<SettingsWorkspacesMembersActionsMenu
v-if="isWorkspaceAdmin"
:target-user="{
...item.user,
role: item.role,
seatType: item.seatType,
joinDate: item.joinDate,
workspaceDomainPolicyCompliant: item.user.workspaceDomainPolicyCompliant
}"
:target-user="item"
:workspace="workspace"
/>
<span v-else />
@@ -80,54 +90,15 @@
<script setup lang="ts">
import {
WorkspaceSeatType,
type SettingsWorkspacesMembersGuestsTable_WorkspaceFragment
type SettingsWorkspacesMembersTable_WorkspaceFragment
} from '~/lib/common/generated/gql/graphql'
import { graphql } from '~/lib/common/generated/gql'
import { Roles, type MaybeNullOrUndefined } from '@speckle/shared'
import { settingsWorkspacesMembersSearchQuery } from '~~/lib/settings/graphql/queries'
import { useQuery } from '@vue/apollo-composable'
import { LearnMoreRolesSeatsUrl } from '~~/lib/common/helpers/route'
graphql(`
fragment SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {
id
role
seatType
joinDate
user {
id
avatar
name
workspaceDomainPolicyCompliant(workspaceSlug: $slug)
}
projectRoles {
role
project {
id
name
}
}
}
`)
graphql(`
fragment SettingsWorkspacesMembersGuestsTable_Workspace on Workspace {
id
slug
name
...SettingsWorkspacesMembersTableHeader_Workspace
...SettingsSharedDeleteUserDialog_Workspace
team(limit: 250) {
items {
id
...SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator
}
}
}
`)
const props = defineProps<{
workspace: MaybeNullOrUndefined<SettingsWorkspacesMembersGuestsTable_WorkspaceFragment>
workspace: MaybeNullOrUndefined<SettingsWorkspacesMembersTable_WorkspaceFragment>
workspaceSlug: string
}>()
@@ -25,10 +25,11 @@
{ id: 'name', header: 'Name', classes: 'col-span-4' },
{ id: 'seat', header: 'Seat', classes: 'col-span-2' },
{ id: 'joined', header: 'Joined', classes: 'col-span-3' },
{ id: 'projects', header: 'Projects', classes: 'col-span-2' },
{
id: 'actions',
header: '',
classes: 'col-span-3 flex items-center justify-end'
classes: 'col-span-1 flex items-center justify-end'
}
]"
:items="members"
@@ -41,12 +42,14 @@
<div class="flex items-center gap-2">
<UserAvatar
hide-tooltip
:user="item"
:user="item.user"
light-style
class="bg-foundation"
no-bg
/>
<span class="truncate text-body-xs text-foreground">{{ item.name }}</span>
<span class="truncate text-body-xs text-foreground">
{{ item.user.name }}
</span>
<CommonBadge
v-if="item.role === Roles.Workspace.Admin"
rounded
@@ -56,7 +59,7 @@
</CommonBadge>
<div
v-if="
item.workspaceDomainPolicyCompliant === false &&
item.user.workspaceDomainPolicyCompliant === false &&
item.role !== Roles.Workspace.Guest
"
v-tippy="
@@ -73,13 +76,41 @@
<template #joined="{ item }">
<span class="text-foreground-2">{{ formattedFullDate(item.joinDate) }}</span>
</template>
<template #projects="{ item }">
<button
v-if="
item.projectRoles.length > 0 &&
isWorkspaceAdmin &&
item.role !== Roles.Workspace.Admin
"
class="text-foreground-2 max-w-max text-body-2xs select-none"
@click="
() => {
targetUser = item
showProjectPermissionsDialog = true
}
"
>
{{ item.projectRoles.length }} projects
</button>
<div v-else class="text-foreground-2 max-w-max text-body-2xs select-none">
{{ item.projectRoles.length }} projects
</div>
</template>
<template #actions="{ item }">
<SettingsWorkspacesMembersActionsMenu
:target-user="item"
:workspace="workspace"
:initial-action="selectedAction[item.id]"
/>
</template>
</LayoutTable>
<SettingsWorkspacesMembersActionsProjectPermissionsDialog
v-model:open="showProjectPermissionsDialog"
:user="targetUser"
:workspace-id="workspace?.id || ''"
@success="showProjectPermissionsDialog = false"
/>
</div>
</template>
@@ -89,25 +120,23 @@ import { settingsWorkspacesMembersSearchQuery } from '~~/lib/settings/graphql/qu
import { useQuery } from '@vue/apollo-composable'
import {
WorkspaceSeatType,
type SettingsWorkspacesMembersTable_WorkspaceFragment
type SettingsWorkspacesMembersTable_WorkspaceFragment,
type SettingsWorkspacesMembersActionsMenu_UserFragment
} from '~~/lib/common/generated/gql/graphql'
import { graphql } from '~/lib/common/generated/gql'
import { ExclamationCircleIcon } from '@heroicons/vue/24/outline'
import { LearnMoreRolesSeatsUrl } from '~~/lib/common/helpers/route'
export type UserItem = (typeof members)['value'][0]
import type { WorkspaceUserActionTypes } from '~/lib/settings/helpers/types'
graphql(`
fragment SettingsWorkspacesMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {
id
role
seatType
joinDate
user {
id
avatar
name
workspaceDomainPolicyCompliant(workspaceSlug: $slug)
projectRoles {
project {
id
}
}
...SettingsWorkspacesMembersActionsMenu_User
}
`)
@@ -135,6 +164,10 @@ const props = defineProps<{
const search = ref('')
const roleFilter = ref<WorkspaceRoles>()
const seatTypeFilter = ref<WorkspaceSeatType>()
const showProjectPermissionsDialog = ref(false)
const targetUser = ref<SettingsWorkspacesMembersActionsMenu_UserFragment | undefined>(
undefined
)
const { result: searchResult, loading: searchResultLoading } = useQuery(
settingsWorkspacesMembersSearchQuery,
@@ -160,10 +193,9 @@ const members = computed(() => {
? searchResult.value?.workspaceBySlug?.team.items
: props.workspace?.team.items
return (memberArray || [])
.map(({ user, seatType, ...rest }) => ({
...user,
seatType: seatType || WorkspaceSeatType.Viewer,
...rest
.map((member) => ({
...member,
seatType: member.seatType || WorkspaceSeatType.Viewer
}))
.filter((user) => user.role !== Roles.Workspace.Guest)
})
@@ -173,4 +205,8 @@ const hasNoResults = computed(
(search.value.length || roleFilter.value || seatTypeFilter.value) &&
searchResult.value?.workspaceBySlug?.team.items.length === 0
)
const isWorkspaceAdmin = computed(() => props.workspace?.role === Roles.Workspace.Admin)
const selectedAction = ref<Record<string, WorkspaceUserActionTypes>>({})
</script>
@@ -22,18 +22,12 @@
<script setup lang="ts">
import type { LayoutDialogButton } from '@speckle/ui-components'
import { useWorkspaceUpdateRole } from '~/lib/workspaces/composables/management'
import type {
SettingsWorkspacesMembersGuestsTable_WorkspaceFragment,
SettingsWorkspacesMembersTable_WorkspaceFragment
} from '~/lib/common/generated/gql/graphql'
import type { SettingsWorkspacesMembersTable_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
import { useActiveUser } from '~/lib/auth/composables/activeUser'
import type { MaybeNullOrUndefined } from '@speckle/shared'
const props = defineProps<{
workspace: MaybeNullOrUndefined<
| SettingsWorkspacesMembersTable_WorkspaceFragment
| SettingsWorkspacesMembersGuestsTable_WorkspaceFragment
>
workspace: MaybeNullOrUndefined<SettingsWorkspacesMembersTable_WorkspaceFragment>
isOnlyAdmin: boolean
}>()
@@ -25,7 +25,7 @@
:new-role="newRole"
:is-active-user-target-user="isActiveUserTargetUser"
:is-only-admin="hasSingleAdmin"
:is-domain-compliant="targetUser.workspaceDomainPolicyCompliant"
:is-domain-compliant="targetUser.user.workspaceDomainPolicyCompliant"
@success="onDialogSuccess"
/>
@@ -63,6 +63,14 @@
:is-only-admin="hasSingleAdmin"
@success="onDialogSuccess"
/>
<SettingsWorkspacesMembersActionsProjectPermissionsDialog
v-if="dialogToShow.projectPermissions"
v-model:open="showDialog"
:user="targetUser"
:workspace-id="workspace?.id || ''"
@success="onDialogSuccess"
/>
</div>
</template>
@@ -72,20 +80,33 @@ import { EllipsisHorizontalIcon, XMarkIcon } from '@heroicons/vue/24/outline'
import type { LayoutMenuItem } from '~~/lib/layout/helpers/components'
import { HorizontalDirection } from '~~/lib/common/composables/window'
import { WorkspaceUserActionTypes } from '~/lib/settings/helpers/types'
import type { UserItem } from '~/components/settings/workspaces/members/MembersTable.vue'
import { useSettingsMembersActions } from '~/lib/settings/composables/menu'
import type {
SettingsWorkspacesMembersGuestsTable_WorkspaceFragment,
SettingsWorkspacesMembersActionsMenu_UserFragment,
SettingsWorkspacesMembersTable_WorkspaceFragment
} from '~/lib/common/generated/gql/graphql'
import { useWorkspaceLastAdminCheck } from '~/lib/workspaces/composables/management'
import { graphql } from '~/lib/common/generated/gql'
graphql(`
fragment SettingsWorkspacesMembersActionsMenu_User on WorkspaceCollaborator {
id
role
seatType
joinDate
user {
id
name
avatar
workspaceDomainPolicyCompliant(workspaceSlug: $slug)
}
...SettingsWorkspacesMembersActionsProjectPermissionsDialog_User
}
`)
const props = defineProps<{
targetUser: UserItem
workspace?: MaybeNullOrUndefined<
| SettingsWorkspacesMembersTable_WorkspaceFragment
| SettingsWorkspacesMembersGuestsTable_WorkspaceFragment
>
targetUser: SettingsWorkspacesMembersActionsMenu_UserFragment
workspace?: MaybeNullOrUndefined<SettingsWorkspacesMembersTable_WorkspaceFragment>
}>()
const showMenu = ref(false)
@@ -114,7 +135,9 @@ const dialogToShow = computed(() => ({
dialogType.value === WorkspaceUserActionTypes.DowngradeEditor,
removeFromWorkspace:
dialogType.value === WorkspaceUserActionTypes.RemoveFromWorkspace,
leaveWorkspace: dialogType.value === WorkspaceUserActionTypes.LeaveWorkspace
leaveWorkspace: dialogType.value === WorkspaceUserActionTypes.LeaveWorkspace,
projectPermissions:
dialogType.value === WorkspaceUserActionTypes.UpdateProjectPermissions
}))
const newRole = computed(() => {
@@ -127,7 +150,8 @@ const newRole = computed(() => {
[WorkspaceUserActionTypes.UpgradeEditor]: undefined,
[WorkspaceUserActionTypes.DowngradeEditor]: undefined,
[WorkspaceUserActionTypes.RemoveFromWorkspace]: undefined,
[WorkspaceUserActionTypes.LeaveWorkspace]: undefined
[WorkspaceUserActionTypes.LeaveWorkspace]: undefined,
[WorkspaceUserActionTypes.UpdateProjectPermissions]: Roles.Workspace.Admin
}
return dialogType.value ? roleMap[dialogType.value] : undefined
})
@@ -0,0 +1,169 @@
<template>
<LayoutDialog v-model:open="open" max-width="sm">
<template #header>Manage project access</template>
<div class="text-foreground mb-8">
<div v-if="projectCount && projectCount > 0" class="flex flex-col gap-4">
<p class="font-medium text-body-xs">
Projects {{ user?.user.name }} has access to:
</p>
<FormTextInput
v-bind="searchBind"
name="searchGuests"
color="foundation"
type="text"
size="lg"
:placeholder="`Search ${projectCount} project${
projectCount !== 1 ? 's' : ''
}...`"
class="px-3 py-2 border border-outline-3 rounded-md focus:outline-none focus:ring-2 focus:ring-primary"
v-on="searchOn"
/>
<CommonCard
class="border border-outline-3 bg-foundation-2 text-body-2xs !p-2 flex flex-col gap-2"
>
<div
v-for="projectRole in filteredProjectRoles"
:key="projectRole.project.id"
class="flex items-center relative"
>
<div class="text-body-xs flex-1 relative z-10 mr-40">
{{ projectRole.project.name }}
</div>
<div class="flex items-center gap-2 absolute right-0">
<ProjectPageTeamPermissionSelect
:model-value="projectRole.role"
:disabled="false"
mount-menu-on-body
hide-owner
show-remove
@update:model-value="
(newRole) => updateProjectRole(projectRole.project.id, newRole)
"
/>
<FormButton
color="outline"
size="sm"
@click="
() => {
projectToRemove = {
id: projectRole.project.id,
name: projectRole.project.name
}
showRemoveUserFromProjectConfirmationDialog = true
}
"
>
Remove
</FormButton>
</div>
</div>
</CommonCard>
</div>
<div v-else>This user doesn't have access to any projects in this workspace.</div>
</div>
<LayoutDialog
v-model:open="showRemoveUserFromProjectConfirmationDialog"
:buttons="dialogButtons"
max-width="xs"
>
<template #header>Remove user from project?</template>
<CommonCard class="!p-2 border border-outline-3 bg-foundation-2">
<div class="flex items-center gap-2">
<UserAvatar :user="user?.user" />
<div class="text-body-xs">
{{ user?.user.name }}
</div>
</div>
</CommonCard>
<div class="text-body-xs my-2">
Are you sure you want to remove this user from
<span class="font-medium">{{ projectToRemove?.name }}</span>
?
</div>
</LayoutDialog>
</LayoutDialog>
</template>
<script setup lang="ts">
import type { StreamRoles } from '@speckle/shared'
import { useUpdateUserRole } from '~~/lib/projects/composables/projectManagement'
import { useDebouncedTextInput, type LayoutDialogButton } from '@speckle/ui-components'
import { graphql } from '~/lib/common/generated/gql'
import type { SettingsWorkspacesMembersActionsMenu_UserFragment } from '~/lib/common/generated/gql/graphql'
graphql(`
fragment SettingsWorkspacesMembersActionsProjectPermissionsDialog_User on WorkspaceCollaborator {
projectRoles {
project {
id
name
}
role
}
}
`)
const props = defineProps<{
user?: SettingsWorkspacesMembersActionsMenu_UserFragment
workspaceId: string
}>()
const open = defineModel<boolean>('open', { required: true })
const loading = ref(false)
const showRemoveUserFromProjectConfirmationDialog = ref(false)
const projectToRemove = ref<{ id: string; name: string } | null>(null)
const searchTerm = ref('')
const { on: searchOn, bind: searchBind } = useDebouncedTextInput({
model: searchTerm,
debouncedBy: 300
})
const project = computed(() => ({ workspaceId: props.workspaceId }))
const filteredProjectRoles = computed(() => {
const roles = props.user?.projectRoles
if (!searchTerm.value) return roles || []
return (roles || []).filter((projectRole) =>
projectRole.project.name.toLowerCase().includes(searchTerm.value.toLowerCase())
)
})
const updateRole = useUpdateUserRole(project)
const updateProjectRole = async (projectId: string, newRole: StreamRoles | null) => {
if (!props.user) return
loading.value = true
await updateRole({
projectId,
userId: props.user.id,
role: newRole
})
loading.value = false
}
const projectCount = computed(() => props.user?.projectRoles?.length)
const dialogButtons = computed((): LayoutDialogButton[] => [
{
text: 'Cancel',
props: { color: 'outline' },
onClick: () => {
showRemoveUserFromProjectConfirmationDialog.value = false
projectToRemove.value = null
}
},
{
text: 'Confirm',
onClick: () => {
if (projectToRemove.value?.id) {
updateProjectRole(projectToRemove.value.id, null)
showRemoveUserFromProjectConfirmationDialog.value = false
projectToRemove.value = null
}
}
}
])
</script>
@@ -6,12 +6,12 @@
<div class="flex flex-row gap-x-2 items-center">
<UserAvatar
hide-tooltip
:user="user"
:user="user.user"
light-style
class="bg-foundation"
no-bg
/>
{{ user.name }}
{{ user.user.name }}
</div>
</CommonCard>
@@ -26,20 +26,16 @@
<script setup lang="ts">
import type { LayoutDialogButton } from '@speckle/ui-components'
import type { UserItem } from '~/components/settings/workspaces/members/MembersTable.vue'
import { useWorkspaceUpdateRole } from '~/lib/workspaces/composables/management'
import type {
SettingsWorkspacesMembersGuestsTable_WorkspaceFragment,
SettingsWorkspacesMembersActionsMenu_UserFragment,
SettingsWorkspacesMembersTable_WorkspaceFragment
} from '~/lib/common/generated/gql/graphql'
import type { MaybeNullOrUndefined } from '@speckle/shared'
const props = defineProps<{
user: UserItem
workspace?: MaybeNullOrUndefined<
| SettingsWorkspacesMembersTable_WorkspaceFragment
| SettingsWorkspacesMembersGuestsTable_WorkspaceFragment
>
user: SettingsWorkspacesMembersActionsMenu_UserFragment
workspace?: MaybeNullOrUndefined<SettingsWorkspacesMembersTable_WorkspaceFragment>
}>()
const emit = defineEmits<{
@@ -19,12 +19,12 @@
<div class="flex flex-row gap-x-2 items-center">
<UserAvatar
hide-tooltip
:user="user"
:user="user.user"
light-style
class="bg-foundation"
no-bg
/>
{{ user.name }}
{{ user.user.name }}
</div>
</CommonCard>
@@ -63,7 +63,6 @@
<script setup lang="ts">
import type { LayoutDialogButton } from '@speckle/ui-components'
import type { UserItem } from '~/components/settings/workspaces/members/MembersTable.vue'
import { LearnMoreRolesSeatsUrl } from '~/lib/common/helpers/route'
import { Roles, SeatTypes } from '@speckle/shared'
import { WorkspaceRoleDescriptions } from '~/lib/settings/helpers/constants'
@@ -71,17 +70,14 @@ import { useWorkspaceUpdateRole } from '~/lib/workspaces/composables/management'
import { useWorkspacePlan } from '~/lib/workspaces/composables/plan'
import SeatTransitionCards from './SeatTransitionCards.vue'
import type {
SettingsWorkspacesMembersGuestsTable_WorkspaceFragment,
SettingsWorkspacesMembersActionsMenu_UserFragment,
SettingsWorkspacesMembersTable_WorkspaceFragment
} from '~/lib/common/generated/gql/graphql'
import type { MaybeNullOrUndefined } from '@speckle/shared'
const props = defineProps<{
user: UserItem
workspace?: MaybeNullOrUndefined<
| SettingsWorkspacesMembersTable_WorkspaceFragment
| SettingsWorkspacesMembersGuestsTable_WorkspaceFragment
>
user: SettingsWorkspacesMembersActionsMenu_UserFragment
workspace?: MaybeNullOrUndefined<SettingsWorkspacesMembersTable_WorkspaceFragment>
isActiveUserTargetUser: boolean
isOnlyAdmin: boolean
action?: 'make' | 'remove' | 'resign'
@@ -2,7 +2,7 @@
<LayoutDialog v-model:open="open" max-width="sm" :buttons="dialogButtons">
<template #header>{{ title }}</template>
<CommonAlert
v-if="props.isDomainCompliant === false"
v-if="props.user.user.workspaceDomainPolicyCompliant === false"
color="danger"
hide-icon
size="xs"
@@ -21,12 +21,12 @@
<div class="flex flex-row gap-x-2 items-center">
<UserAvatar
hide-tooltip
:user="user"
:user="user.user"
light-style
class="bg-foundation"
no-bg
/>
{{ user.name }}
{{ user.user.name }}
</div>
</CommonCard>
@@ -50,25 +50,20 @@
<script setup lang="ts">
import type { LayoutDialogButton } from '@speckle/ui-components'
import type { UserItem } from '~/components/settings/workspaces/members/MembersTable.vue'
import { LearnMoreRolesSeatsUrl } from '~/lib/common/helpers/route'
import { Roles } from '@speckle/shared'
import { WorkspaceRoleDescriptions } from '~/lib/settings/helpers/constants'
import { useWorkspaceUpdateRole } from '~/lib/workspaces/composables/management'
import type {
SettingsWorkspacesMembersGuestsTable_WorkspaceFragment,
SettingsWorkspacesMembersActionsMenu_UserFragment,
SettingsWorkspacesMembersTable_WorkspaceFragment
} from '~/lib/common/generated/gql/graphql'
import type { MaybeNullOrUndefined } from '@speckle/shared'
const props = defineProps<{
user: UserItem
user: SettingsWorkspacesMembersActionsMenu_UserFragment
newRole: MaybeNullOrUndefined<string>
isDomainCompliant?: MaybeNullOrUndefined<boolean>
workspace?: MaybeNullOrUndefined<
| SettingsWorkspacesMembersTable_WorkspaceFragment
| SettingsWorkspacesMembersGuestsTable_WorkspaceFragment
>
workspace?: MaybeNullOrUndefined<SettingsWorkspacesMembersTable_WorkspaceFragment>
}>()
const emit = defineEmits<{
@@ -93,7 +88,7 @@ const buttonText = computed(() => {
const mainMessage = computed(() => {
if (!props.newRole) return undefined
if (props.isDomainCompliant === false) return undefined
if (props.user.user.workspaceDomainPolicyCompliant === false) return undefined
if (props.newRole === Roles.Workspace.Member) {
return 'They will be able to access all projects.'
}
@@ -102,7 +97,7 @@ const mainMessage = computed(() => {
const roleInfo = computed(() => {
if (!props.newRole) return undefined
if (props.isDomainCompliant === false) return undefined
if (props.user.user.workspaceDomainPolicyCompliant === false) return undefined
return WorkspaceRoleDescriptions[
props.newRole as keyof typeof WorkspaceRoleDescriptions
]
@@ -133,7 +128,7 @@ const dialogButtons = computed((): LayoutDialogButton[] => [
color: 'primary'
},
onClick: handleConfirm,
disabled: props.isDomainCompliant === false
disabled: props.user.user.workspaceDomainPolicyCompliant === false
}
])
</script>
@@ -2,7 +2,7 @@
<LayoutDialog v-model:open="open" max-width="sm" :buttons="dialogButtons">
<template #header>{{ title }}</template>
<div class="flex flex-col mb-4">
<p class="text-body-sm mb-4">Confirm {{ user.name }}'s new seat.</p>
<p class="text-body-sm mb-4">Confirm {{ user.user.name }}'s new seat.</p>
<SeatTransitionCards
:is-upgrading="isUpgrading"
@@ -27,7 +27,6 @@
<script setup lang="ts">
import type { LayoutDialogButton } from '@speckle/ui-components'
import type { UserItem } from '~/components/settings/workspaces/members/MembersTable.vue'
import {
SeatTypes,
type WorkspaceSeatType,
@@ -38,16 +37,13 @@ import { useWorkspacePlan } from '~/lib/workspaces/composables/plan'
import { LearnMoreRolesSeatsUrl } from '~/lib/common/helpers/route'
import SeatTransitionCards from './SeatTransitionCards.vue'
import type {
SettingsWorkspacesMembersGuestsTable_WorkspaceFragment,
SettingsWorkspacesMembersActionsMenu_UserFragment,
SettingsWorkspacesMembersTable_WorkspaceFragment
} from '~/lib/common/generated/gql/graphql'
const props = defineProps<{
user: UserItem
workspace?: MaybeNullOrUndefined<
| SettingsWorkspacesMembersTable_WorkspaceFragment
| SettingsWorkspacesMembersGuestsTable_WorkspaceFragment
>
user: SettingsWorkspacesMembersActionsMenu_UserFragment
workspace?: MaybeNullOrUndefined<SettingsWorkspacesMembersTable_WorkspaceFragment>
}>()
const emit = defineEmits<{
@@ -122,19 +122,18 @@ type Documents = {
"\n fragment SettingsWorkspacesGeneralEditSlugDialog_Workspace on Workspace {\n id\n name\n slug\n }\n": typeof types.SettingsWorkspacesGeneralEditSlugDialog_WorkspaceFragmentDoc,
"\n fragment WorkspaceBillingPage_Workspace on Workspace {\n id\n role\n }\n": typeof types.WorkspaceBillingPage_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersChangeRoleDialog_Workspace on Workspace {\n id\n plan {\n status\n name\n }\n subscription {\n currentBillingCycleEnd\n seats {\n guest\n plan\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersChangeRoleDialog_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n seatType\n joinDate\n user {\n id\n avatar\n name\n workspaceDomainPolicyCompliant(workspaceSlug: $slug)\n }\n projectRoles {\n role\n project {\n id\n name\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersGuestsTable_WorkspaceCollaboratorFragmentDoc,
"\n fragment SettingsWorkspacesMembersGuestsTable_Workspace on Workspace {\n id\n slug\n name\n ...SettingsWorkspacesMembersTableHeader_Workspace\n ...SettingsSharedDeleteUserDialog_Workspace\n team(limit: 250) {\n items {\n id\n ...SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersGuestsTable_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n inviteId\n role\n title\n updatedAt\n user {\n id\n ...LimitedUserAvatar\n }\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n }\n": typeof types.SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment SettingsWorkspacesMembersInvitesTable_Workspace on Workspace {\n id\n ...SettingsWorkspacesMembersTableHeader_Workspace\n invitedTeam {\n ...SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator\n }\n }\n": typeof types.SettingsWorkspacesMembersInvitesTable_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersRequestsTable_Workspace on Workspace {\n ...SettingsWorkspacesMembersTableHeader_Workspace\n id\n adminWorkspacesJoinRequests {\n totalCount\n items {\n ...WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest\n id\n createdAt\n status\n user {\n id\n avatar\n name\n }\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersRequestsTable_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n seatType\n joinDate\n user {\n id\n avatar\n name\n workspaceDomainPolicyCompliant(workspaceSlug: $slug)\n }\n }\n": typeof types.SettingsWorkspacesMembersTable_WorkspaceCollaboratorFragmentDoc,
"\n fragment SettingsWorkspacesMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n projectRoles {\n project {\n id\n }\n }\n ...SettingsWorkspacesMembersActionsMenu_User\n }\n": typeof types.SettingsWorkspacesMembersTable_WorkspaceCollaboratorFragmentDoc,
"\n fragment SettingsWorkspacesMembersTable_Workspace on Workspace {\n id\n slug\n name\n ...SettingsSharedDeleteUserDialog_Workspace\n ...SettingsWorkspacesMembersTableHeader_Workspace\n team(limit: 250) {\n items {\n id\n ...SettingsWorkspacesMembersTable_WorkspaceCollaborator\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersTable_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersTableHeader_Workspace on Workspace {\n id\n role\n ...InviteDialogWorkspace_Workspace\n }\n": typeof types.SettingsWorkspacesMembersTableHeader_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersActionsMenu_User on WorkspaceCollaborator {\n id\n role\n seatType\n joinDate\n user {\n id\n name\n avatar\n workspaceDomainPolicyCompliant(workspaceSlug: $slug)\n }\n ...SettingsWorkspacesMembersActionsProjectPermissionsDialog_User\n }\n": typeof types.SettingsWorkspacesMembersActionsMenu_UserFragmentDoc,
"\n fragment SettingsWorkspacesMembersActionsProjectPermissionsDialog_User on WorkspaceCollaborator {\n projectRoles {\n project {\n id\n name\n }\n role\n }\n }\n": typeof types.SettingsWorkspacesMembersActionsProjectPermissionsDialog_UserFragmentDoc,
"\n fragment SettingsWorkspacesRegionsSelect_ServerRegionItem on ServerRegionItem {\n id\n key\n name\n description\n }\n": typeof types.SettingsWorkspacesRegionsSelect_ServerRegionItemFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain on WorkspaceDomain {\n id\n domain\n }\n": typeof types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomainFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_Workspace on Workspace {\n id\n domains {\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n }\n": typeof types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecuritySsoWrapper_Workspace on Workspace {\n id\n role\n slug\n sso {\n provider {\n id\n name\n clientId\n issuerUrl\n }\n }\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\n }\n": typeof types.SettingsWorkspacesSecuritySsoWrapper_WorkspaceFragmentDoc,
"\n fragment Sidebar_User on User {\n id\n automateFunctions {\n items {\n id\n name\n description\n logo\n }\n }\n }\n": typeof types.Sidebar_UserFragmentDoc,
"\n fragment ModelPageProject on Project {\n id\n createdAt\n name\n visibility\n workspace {\n id\n slug\n name\n }\n }\n": typeof types.ModelPageProjectFragmentDoc,
"\n fragment ThreadCommentAttachment on Comment {\n text {\n attachments {\n id\n fileName\n fileType\n fileSize\n }\n }\n }\n": typeof types.ThreadCommentAttachmentFragmentDoc,
"\n fragment ViewerCommentsListItem on Comment {\n id\n rawText\n archived\n author {\n ...LimitedUserAvatar\n }\n createdAt\n viewedAt\n replies {\n totalCount\n cursor\n items {\n ...ViewerCommentsReplyItem\n }\n }\n replyAuthors(limit: 4) {\n totalCount\n items {\n ...FormUsersSelectItem\n }\n }\n resources {\n resourceId\n resourceType\n }\n }\n": typeof types.ViewerCommentsListItemFragmentDoc,
@@ -310,14 +309,13 @@ type Documents = {
"\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 SettingsSidebarAutomateFunctions {\n activeUser {\n ...Sidebar_User\n }\n }\n": typeof types.SettingsSidebarAutomateFunctionsDocument,
"\n query SettingsSidebarAutomateFunctions {\n activeUser {\n ...SettingsSidebar_User\n }\n }\n": typeof types.SettingsSidebarAutomateFunctionsDocument,
"\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,
"\n query SettingsWorkspaceRegions($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n ...SettingsWorkspacesRegions_Workspace\n }\n serverInfo {\n ...SettingsWorkspacesRegions_ServerInfo\n }\n }\n": typeof types.SettingsWorkspaceRegionsDocument,
"\n query SettingsWorkspacesMembers($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembers_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersDocument,
"\n query SettingsWorkspacesMembersTable($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersTableDocument,
"\n query SettingsWorkspacesMembersGuests($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersGuestsTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersGuestsDocument,
"\n query SettingsWorkspacesMembersInvites($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersInvitesTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersInvitesDocument,
"\n query SettingsWorkspacesMembersRequests($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersRequestsTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersRequestsDocument,
"\n query SettingsWorkspacesMembersSearch($slug: String!, $filter: WorkspaceTeamFilter) {\n workspaceBySlug(slug: $slug) {\n id\n team(filter: $filter, limit: 250) {\n items {\n id\n ...SettingsWorkspacesMembersTable_WorkspaceCollaborator\n }\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersSearchDocument,
@@ -528,19 +526,18 @@ const documents: Documents = {
"\n fragment SettingsWorkspacesGeneralEditSlugDialog_Workspace on Workspace {\n id\n name\n slug\n }\n": types.SettingsWorkspacesGeneralEditSlugDialog_WorkspaceFragmentDoc,
"\n fragment WorkspaceBillingPage_Workspace on Workspace {\n id\n role\n }\n": types.WorkspaceBillingPage_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersChangeRoleDialog_Workspace on Workspace {\n id\n plan {\n status\n name\n }\n subscription {\n currentBillingCycleEnd\n seats {\n guest\n plan\n }\n }\n }\n": types.SettingsWorkspacesMembersChangeRoleDialog_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n seatType\n joinDate\n user {\n id\n avatar\n name\n workspaceDomainPolicyCompliant(workspaceSlug: $slug)\n }\n projectRoles {\n role\n project {\n id\n name\n }\n }\n }\n": types.SettingsWorkspacesMembersGuestsTable_WorkspaceCollaboratorFragmentDoc,
"\n fragment SettingsWorkspacesMembersGuestsTable_Workspace on Workspace {\n id\n slug\n name\n ...SettingsWorkspacesMembersTableHeader_Workspace\n ...SettingsSharedDeleteUserDialog_Workspace\n team(limit: 250) {\n items {\n id\n ...SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator\n }\n }\n }\n": types.SettingsWorkspacesMembersGuestsTable_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n inviteId\n role\n title\n updatedAt\n user {\n id\n ...LimitedUserAvatar\n }\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n }\n": types.SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment SettingsWorkspacesMembersInvitesTable_Workspace on Workspace {\n id\n ...SettingsWorkspacesMembersTableHeader_Workspace\n invitedTeam {\n ...SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator\n }\n }\n": types.SettingsWorkspacesMembersInvitesTable_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersRequestsTable_Workspace on Workspace {\n ...SettingsWorkspacesMembersTableHeader_Workspace\n id\n adminWorkspacesJoinRequests {\n totalCount\n items {\n ...WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest\n id\n createdAt\n status\n user {\n id\n avatar\n name\n }\n }\n }\n }\n": types.SettingsWorkspacesMembersRequestsTable_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n seatType\n joinDate\n user {\n id\n avatar\n name\n workspaceDomainPolicyCompliant(workspaceSlug: $slug)\n }\n }\n": types.SettingsWorkspacesMembersTable_WorkspaceCollaboratorFragmentDoc,
"\n fragment SettingsWorkspacesMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n projectRoles {\n project {\n id\n }\n }\n ...SettingsWorkspacesMembersActionsMenu_User\n }\n": types.SettingsWorkspacesMembersTable_WorkspaceCollaboratorFragmentDoc,
"\n fragment SettingsWorkspacesMembersTable_Workspace on Workspace {\n id\n slug\n name\n ...SettingsSharedDeleteUserDialog_Workspace\n ...SettingsWorkspacesMembersTableHeader_Workspace\n team(limit: 250) {\n items {\n id\n ...SettingsWorkspacesMembersTable_WorkspaceCollaborator\n }\n }\n }\n": types.SettingsWorkspacesMembersTable_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersTableHeader_Workspace on Workspace {\n id\n role\n ...InviteDialogWorkspace_Workspace\n }\n": types.SettingsWorkspacesMembersTableHeader_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesMembersActionsMenu_User on WorkspaceCollaborator {\n id\n role\n seatType\n joinDate\n user {\n id\n name\n avatar\n workspaceDomainPolicyCompliant(workspaceSlug: $slug)\n }\n ...SettingsWorkspacesMembersActionsProjectPermissionsDialog_User\n }\n": types.SettingsWorkspacesMembersActionsMenu_UserFragmentDoc,
"\n fragment SettingsWorkspacesMembersActionsProjectPermissionsDialog_User on WorkspaceCollaborator {\n projectRoles {\n project {\n id\n name\n }\n role\n }\n }\n": types.SettingsWorkspacesMembersActionsProjectPermissionsDialog_UserFragmentDoc,
"\n fragment SettingsWorkspacesRegionsSelect_ServerRegionItem on ServerRegionItem {\n id\n key\n name\n description\n }\n": types.SettingsWorkspacesRegionsSelect_ServerRegionItemFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain on WorkspaceDomain {\n id\n domain\n }\n": types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomainFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_Workspace on Workspace {\n id\n domains {\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n }\n": types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecuritySsoWrapper_Workspace on Workspace {\n id\n role\n slug\n sso {\n provider {\n id\n name\n clientId\n issuerUrl\n }\n }\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\n }\n": types.SettingsWorkspacesSecuritySsoWrapper_WorkspaceFragmentDoc,
"\n fragment Sidebar_User on User {\n id\n automateFunctions {\n items {\n id\n name\n description\n logo\n }\n }\n }\n": types.Sidebar_UserFragmentDoc,
"\n fragment ModelPageProject on Project {\n id\n createdAt\n name\n visibility\n workspace {\n id\n slug\n name\n }\n }\n": types.ModelPageProjectFragmentDoc,
"\n fragment ThreadCommentAttachment on Comment {\n text {\n attachments {\n id\n fileName\n fileType\n fileSize\n }\n }\n }\n": types.ThreadCommentAttachmentFragmentDoc,
"\n fragment ViewerCommentsListItem on Comment {\n id\n rawText\n archived\n author {\n ...LimitedUserAvatar\n }\n createdAt\n viewedAt\n replies {\n totalCount\n cursor\n items {\n ...ViewerCommentsReplyItem\n }\n }\n replyAuthors(limit: 4) {\n totalCount\n items {\n ...FormUsersSelectItem\n }\n }\n resources {\n resourceId\n resourceType\n }\n }\n": types.ViewerCommentsListItemFragmentDoc,
@@ -716,14 +713,13 @@ const documents: Documents = {
"\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 SettingsSidebarAutomateFunctions {\n activeUser {\n ...Sidebar_User\n }\n }\n": types.SettingsSidebarAutomateFunctionsDocument,
"\n query SettingsSidebarAutomateFunctions {\n activeUser {\n ...SettingsSidebar_User\n }\n }\n": types.SettingsSidebarAutomateFunctionsDocument,
"\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,
"\n query SettingsWorkspaceRegions($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n ...SettingsWorkspacesRegions_Workspace\n }\n serverInfo {\n ...SettingsWorkspacesRegions_ServerInfo\n }\n }\n": types.SettingsWorkspaceRegionsDocument,
"\n query SettingsWorkspacesMembers($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembers_Workspace\n }\n }\n": types.SettingsWorkspacesMembersDocument,
"\n query SettingsWorkspacesMembersTable($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersTable_Workspace\n }\n }\n": types.SettingsWorkspacesMembersTableDocument,
"\n query SettingsWorkspacesMembersGuests($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersGuestsTable_Workspace\n }\n }\n": types.SettingsWorkspacesMembersGuestsDocument,
"\n query SettingsWorkspacesMembersInvites($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersInvitesTable_Workspace\n }\n }\n": types.SettingsWorkspacesMembersInvitesDocument,
"\n query SettingsWorkspacesMembersRequests($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersRequestsTable_Workspace\n }\n }\n": types.SettingsWorkspacesMembersRequestsDocument,
"\n query SettingsWorkspacesMembersSearch($slug: String!, $filter: WorkspaceTeamFilter) {\n workspaceBySlug(slug: $slug) {\n id\n team(filter: $filter, limit: 250) {\n items {\n id\n ...SettingsWorkspacesMembersTable_WorkspaceCollaborator\n }\n }\n }\n }\n": types.SettingsWorkspacesMembersSearchDocument,
@@ -1272,14 +1268,6 @@ export function graphql(source: "\n fragment WorkspaceBillingPage_Workspace on
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment SettingsWorkspacesMembersChangeRoleDialog_Workspace on Workspace {\n id\n plan {\n status\n name\n }\n subscription {\n currentBillingCycleEnd\n seats {\n guest\n plan\n }\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesMembersChangeRoleDialog_Workspace on Workspace {\n id\n plan {\n status\n name\n }\n subscription {\n currentBillingCycleEnd\n seats {\n guest\n plan\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 SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n seatType\n joinDate\n user {\n id\n avatar\n name\n workspaceDomainPolicyCompliant(workspaceSlug: $slug)\n }\n projectRoles {\n role\n project {\n id\n name\n }\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n seatType\n joinDate\n user {\n id\n avatar\n name\n workspaceDomainPolicyCompliant(workspaceSlug: $slug)\n }\n projectRoles {\n role\n project {\n id\n name\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 SettingsWorkspacesMembersGuestsTable_Workspace on Workspace {\n id\n slug\n name\n ...SettingsWorkspacesMembersTableHeader_Workspace\n ...SettingsSharedDeleteUserDialog_Workspace\n team(limit: 250) {\n items {\n id\n ...SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator\n }\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesMembersGuestsTable_Workspace on Workspace {\n id\n slug\n name\n ...SettingsWorkspacesMembersTableHeader_Workspace\n ...SettingsSharedDeleteUserDialog_Workspace\n team(limit: 250) {\n items {\n id\n ...SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1295,7 +1283,7 @@ export function graphql(source: "\n fragment SettingsWorkspacesMembersRequestsT
/**
* 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 SettingsWorkspacesMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n seatType\n joinDate\n user {\n id\n avatar\n name\n workspaceDomainPolicyCompliant(workspaceSlug: $slug)\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n seatType\n joinDate\n user {\n id\n avatar\n name\n workspaceDomainPolicyCompliant(workspaceSlug: $slug)\n }\n }\n"];
export function graphql(source: "\n fragment SettingsWorkspacesMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n projectRoles {\n project {\n id\n }\n }\n ...SettingsWorkspacesMembersActionsMenu_User\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n projectRoles {\n project {\n id\n }\n }\n ...SettingsWorkspacesMembersActionsMenu_User\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1304,6 +1292,14 @@ export function graphql(source: "\n fragment SettingsWorkspacesMembersTable_Wor
* 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 SettingsWorkspacesMembersTableHeader_Workspace on Workspace {\n id\n role\n ...InviteDialogWorkspace_Workspace\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesMembersTableHeader_Workspace on Workspace {\n id\n role\n ...InviteDialogWorkspace_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 SettingsWorkspacesMembersActionsMenu_User on WorkspaceCollaborator {\n id\n role\n seatType\n joinDate\n user {\n id\n name\n avatar\n workspaceDomainPolicyCompliant(workspaceSlug: $slug)\n }\n ...SettingsWorkspacesMembersActionsProjectPermissionsDialog_User\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesMembersActionsMenu_User on WorkspaceCollaborator {\n id\n role\n seatType\n joinDate\n user {\n id\n name\n avatar\n workspaceDomainPolicyCompliant(workspaceSlug: $slug)\n }\n ...SettingsWorkspacesMembersActionsProjectPermissionsDialog_User\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 SettingsWorkspacesMembersActionsProjectPermissionsDialog_User on WorkspaceCollaborator {\n projectRoles {\n project {\n id\n name\n }\n role\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesMembersActionsProjectPermissionsDialog_User on WorkspaceCollaborator {\n projectRoles {\n project {\n id\n name\n }\n role\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1320,10 +1316,6 @@ export function graphql(source: "\n fragment SettingsWorkspacesSecurityDomainRe
* 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 SettingsWorkspacesSecuritySsoWrapper_Workspace on Workspace {\n id\n role\n slug\n sso {\n provider {\n id\n name\n clientId\n issuerUrl\n }\n }\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesSecuritySsoWrapper_Workspace on Workspace {\n id\n role\n slug\n sso {\n provider {\n id\n name\n clientId\n issuerUrl\n }\n }\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\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 Sidebar_User on User {\n id\n automateFunctions {\n items {\n id\n name\n description\n logo\n }\n }\n }\n"): (typeof documents)["\n fragment Sidebar_User on User {\n id\n automateFunctions {\n items {\n id\n name\n description\n logo\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -2027,7 +2019,7 @@ export function graphql(source: "\n query SettingsSidebar {\n activeUser {\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 SettingsSidebarAutomateFunctions {\n activeUser {\n ...Sidebar_User\n }\n }\n"): (typeof documents)["\n query SettingsSidebarAutomateFunctions {\n activeUser {\n ...Sidebar_User\n }\n }\n"];
export function graphql(source: "\n query SettingsSidebarAutomateFunctions {\n activeUser {\n ...SettingsSidebar_User\n }\n }\n"): (typeof documents)["\n query SettingsSidebarAutomateFunctions {\n activeUser {\n ...SettingsSidebar_User\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -2052,10 +2044,6 @@ export function graphql(source: "\n query SettingsWorkspacesMembers($slug: Stri
* 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 SettingsWorkspacesMembersTable($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersTable_Workspace\n }\n }\n"): (typeof documents)["\n query SettingsWorkspacesMembersTable($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersTable_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 SettingsWorkspacesMembersGuests($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersGuestsTable_Workspace\n }\n }\n"): (typeof documents)["\n query SettingsWorkspacesMembersGuests($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersGuestsTable_Workspace\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
File diff suppressed because one or more lines are too long
@@ -5,7 +5,6 @@ import {
} from '~/lib/settings/helpers/types'
import { useIsMultipleEmailsEnabled, useActiveUser } from '~/composables/globals'
import { Roles, SeatTypes, type MaybeNullOrUndefined } from '@speckle/shared'
import type { UserItem } from '~/components/settings/workspaces/members/MembersTable.vue'
import { useIsMultiregionEnabled } from '~/lib/multiregion/composables/main'
import { graphql } from '~/lib/common/generated/gql'
import {
@@ -14,6 +13,7 @@ import {
settingsServerRoutes
} from '~/lib/common/helpers/route'
import type { LayoutMenuItem } from '@speckle/ui-components'
import type { SettingsWorkspacesMembersActionsMenu_UserFragment } from '~/lib/common/generated/gql/graphql'
graphql(`
fragment SettingsMenu_Workspace on Workspace {
@@ -142,7 +142,7 @@ export const useSettingsMenuState = () =>
export const useSettingsMembersActions = (params: {
workspaceRole?: MaybeNullOrUndefined<string>
targetUser: UserItem
targetUser: SettingsWorkspacesMembersActionsMenu_UserFragment
}) => {
const { activeUser } = useActiveUser()
@@ -198,6 +198,8 @@ export const useSettingsMembersActions = (params: {
() => canModifyUser.value && targetUserRole.value !== Roles.Workspace.Admin
)
const canUpdateProjectPermissions = computed(() => canModifyUser.value)
const canLeaveWorkspace = computed(() => isActiveUserTargetUser.value)
const canResignAdmin = computed(
@@ -238,6 +240,12 @@ export const useSettingsMembersActions = (params: {
id: WorkspaceUserActionTypes.DowngradeEditor
})
}
if (canUpdateProjectPermissions.value) {
mainItems.push({
title: 'Manage project access...',
id: WorkspaceUserActionTypes.UpdateProjectPermissions
})
}
if (canRemoveAdmin.value) {
footerItems.push({
@@ -274,6 +282,7 @@ export const useSettingsMembersActions = (params: {
actionItems,
isActiveUserWorkspaceAdmin,
isActiveUserTargetUser,
canUpdateProjectPermissions,
canMakeAdmin,
canRemoveAdmin,
canMakeGuest,
@@ -11,7 +11,7 @@ export const settingsSidebarQuery = graphql(`
export const settingsSidebarAutomateFunctionsQuery = graphql(`
query SettingsSidebarAutomateFunctions {
activeUser {
...Sidebar_User
...SettingsSidebar_User
}
}
`)
@@ -69,14 +69,6 @@ export const settingsWorkspacesMembersTableQuery = graphql(`
}
`)
export const settingsWorkspacesMembersGuestsQuery = graphql(`
query SettingsWorkspacesMembersGuests($slug: String!) {
workspaceBySlug(slug: $slug) {
...SettingsWorkspacesMembersGuestsTable_Workspace
}
}
`)
export const settingsWorkspacesMembersInvitesQuery = graphql(`
query SettingsWorkspacesMembersInvites($slug: String!) {
workspaceBySlug(slug: $slug) {
@@ -25,7 +25,8 @@ export enum WorkspaceUserActionTypes {
MakeMember = 'make-member',
UpgradeEditor = 'upgrade-editor',
DowngradeEditor = 'downgrade-editor',
ResignAdmin = 'resign-admin'
ResignAdmin = 'resign-admin',
UpdateProjectPermissions = 'update-project-permissions'
}
export type WorkspaceUserUpdateShowOptions = {
@@ -4,12 +4,12 @@
<script setup lang="ts">
import { useQuery } from '@vue/apollo-composable'
import { settingsWorkspacesMembersGuestsQuery } from '~/lib/settings/graphql/queries'
import { settingsWorkspacesMembersTableQuery } from '~/lib/settings/graphql/queries'
const route = useRoute()
const slug = computed(() => (route.params.slug as string) || '')
const { result } = useQuery(settingsWorkspacesMembersGuestsQuery, () => ({
const { result } = useQuery(settingsWorkspacesMembersTableQuery, () => ({
slug: slug.value
}))