Files
speckle-server/packages/frontend-2/components/settings/workspaces/members/actions/Menu.vue
T
2025-06-19 15:44:12 +02:00

183 lines
5.9 KiB
Vue

<template>
<div :key="computedKey">
<LayoutMenu
v-if="actionItems.length"
v-model:open="showMenu"
:items="actionItems"
mount-menu-on-body
size="lg"
:menu-position="HorizontalDirection.Left"
:menu-id="`member-actions-${targetUser.id}`"
@chosen="({ item: actionItem }) => onActionChosen(actionItem)"
>
<FormButton
:color="showMenu ? 'outline' : 'subtle'"
hide-text
:icon-right="showMenu ? XMarkIcon : EllipsisHorizontalIcon"
@click="toggleMenu"
/>
</LayoutMenu>
<SettingsWorkspacesMembersActionsUpdateRoleDialog
v-if="dialogToShow.updateRole"
v-model:open="showDialog"
:user="targetUser"
:workspace="workspace"
:new-role="newRole"
:is-active-user-target-user="isActiveUserTargetUser"
:is-only-admin="isLastAdmin"
:is-domain-compliant="targetUser.user.workspaceDomainPolicyCompliant"
@success="onDialogSuccess"
/>
<SettingsWorkspacesMembersActionsUpdateAdminDialog
v-if="dialogToShow.updateAdmin"
v-model:open="showDialog"
:user="targetUser"
:workspace="workspace"
:is-active-user-target-user="isActiveUserTargetUser"
:action="adminAction"
@success="onDialogSuccess"
/>
<SettingsWorkspacesMembersActionsUpdateSeatTypeDialog
v-if="dialogToShow.updateSeatType"
v-model:open="showDialog"
:user="targetUser"
:workspace="workspace"
@success="onDialogSuccess"
/>
<SettingsWorkspacesMembersActionsRemoveFromWorkspaceDialog
v-if="dialogToShow.removeFromWorkspace"
v-model:open="showDialog"
:user="targetUser"
:workspace="workspace"
@success="onDialogSuccess"
/>
<SettingsWorkspacesGeneralLeaveDialog
v-if="dialogToShow.leaveWorkspace"
v-model:open="showDialog"
:workspace="workspace"
/>
<SettingsWorkspacesMembersActionsProjectPermissionsDialog
v-if="dialogToShow.projectPermissions"
v-model:open="showDialog"
:user="targetUser"
:workspace-id="workspace?.id || ''"
@success="onDialogSuccess"
/>
</div>
</template>
<script setup lang="ts">
import { Roles, type MaybeNullOrUndefined } from '@speckle/shared'
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 { useSettingsMembersActions } from '~/lib/settings/composables/menu'
import type {
SettingsWorkspacesMembersActionsMenu_UserFragment,
SettingsWorkspacesMembersTableHeader_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: SettingsWorkspacesMembersActionsMenu_UserFragment
workspace?: MaybeNullOrUndefined<SettingsWorkspacesMembersTableHeader_WorkspaceFragment>
}>()
const showMenu = ref(false)
const showDialog = ref(false)
const dialogType = ref<WorkspaceUserActionTypes>()
const { isLastAdmin } = useWorkspaceLastAdminCheck({
workspaceSlug: computed(() => props.workspace?.slug)
})
const computedKey = computed(() => `${props.targetUser.id}-${props.targetUser.role}`)
const { actionItems, isActiveUserTargetUser } = useSettingsMembersActions({
workspaceRole: computed(() => props.workspace?.role),
workspaceSlug: computed(() => props.workspace?.slug),
targetUser: computed(() => props.targetUser)
})
const dialogToShow = computed(() => ({
updateRole:
dialogType.value === WorkspaceUserActionTypes.MakeGuest ||
dialogType.value === WorkspaceUserActionTypes.MakeMember,
updateAdmin:
dialogType.value === WorkspaceUserActionTypes.MakeAdmin ||
dialogType.value === WorkspaceUserActionTypes.RemoveAdmin,
updateSeatType:
dialogType.value === WorkspaceUserActionTypes.UpgradeEditor ||
dialogType.value === WorkspaceUserActionTypes.DowngradeEditor,
removeFromWorkspace:
dialogType.value === WorkspaceUserActionTypes.RemoveFromWorkspace,
leaveWorkspace: dialogType.value === WorkspaceUserActionTypes.LeaveWorkspace,
projectPermissions:
dialogType.value === WorkspaceUserActionTypes.UpdateProjectPermissions
}))
const newRole = computed(() => {
const roleMap: Record<WorkspaceUserActionTypes, string | undefined> = {
[WorkspaceUserActionTypes.MakeAdmin]: Roles.Workspace.Admin,
[WorkspaceUserActionTypes.MakeMember]: Roles.Workspace.Member,
[WorkspaceUserActionTypes.MakeGuest]: Roles.Workspace.Guest,
[WorkspaceUserActionTypes.RemoveAdmin]: Roles.Workspace.Member,
[WorkspaceUserActionTypes.UpgradeEditor]: undefined,
[WorkspaceUserActionTypes.DowngradeEditor]: undefined,
[WorkspaceUserActionTypes.RemoveFromWorkspace]: undefined,
[WorkspaceUserActionTypes.LeaveWorkspace]: undefined,
[WorkspaceUserActionTypes.UpdateProjectPermissions]: Roles.Workspace.Admin
}
return dialogType.value ? roleMap[dialogType.value] : undefined
})
const adminAction = computed(() => {
switch (dialogType.value) {
case WorkspaceUserActionTypes.MakeAdmin:
return 'make' as const
case WorkspaceUserActionTypes.RemoveAdmin:
return 'remove' as const
default:
return undefined
}
})
const onActionChosen = (actionItem: LayoutMenuItem) => {
dialogType.value = actionItem.id as WorkspaceUserActionTypes
showDialog.value = true
}
const toggleMenu = () => {
showMenu.value = !showMenu.value
}
const onDialogSuccess = async () => {
showDialog.value = false
dialogType.value = undefined
}
</script>