Files
speckle-server/packages/frontend-2/components/settings/workspaces/members/actions/ProjectPermissionsDialog.vue
T
andrewwallacespeckle cccda1747e Close on no projects
2025-04-05 16:54:52 +01:00

184 lines
5.8 KiB
Vue

<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">
<NuxtLink
:to="projectRoute(projectRole.project.id)"
target="_blank"
class="group flex gap-1 items-center max-w-max border-b border-transparent hover:border-gray-300/90"
>
{{ projectRole.project.name }}
<ArrowTopRightOnSquareIcon
class="hidden group-hover:block w-3 h-3 opacity-60"
/>
</NuxtLink>
</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'
import { projectRoute } from '~~/lib/common/helpers/route'
import { ArrowTopRightOnSquareIcon } from '@heroicons/vue/24/outline'
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)
if (projectCount.value === 0) {
open.value = false
}
showRemoveUserFromProjectConfirmationDialog.value = false
projectToRemove.value = null
}
}
}
])
</script>