Files
speckle-server/packages/frontend-2/components/settings/server/ActiveUsers.vue
T
Mike b07935964b Fix: Minor styling/copy updates (#2755)
* Fix: Minor styling/copy updates

* Fix linting
2024-08-26 11:43:51 +02:00

176 lines
5.6 KiB
Vue

<template>
<section>
<div class="md:max-w-5xl md:mx-auto pb-6 md:pb-0">
<SettingsSectionHeader title="Active users" text="Manage server members" />
<div class="flex flex-col-reverse md:justify-between md:flex-row md:gap-x-4">
<div class="relative w-full md:max-w-md mt-6 md:mt-0">
<FormTextInput
name="search"
:custom-icon="MagnifyingGlassIcon"
color="foundation"
full-width
search
:show-clear="!!search"
placeholder="Search users"
class="rounded-md border border-outline-3"
v-bind="bind"
v-on="on"
/>
</div>
<FormButton @click="toggleInviteDialog">Invite</FormButton>
</div>
<LayoutTable
class="mt-6 md:mt-8"
:columns="[
{ id: 'name', header: 'Name', classes: 'col-span-3 truncate' },
{ id: 'email', header: 'Email', classes: 'col-span-3 truncate' },
{ id: 'emailState', header: 'Email state', classes: 'col-span-2' },
{ id: 'company', header: 'Company', classes: 'col-span-2 truncate' },
{ id: 'role', header: 'Role', classes: 'col-span-2' }
]"
:items="users"
:buttons="[{ icon: TrashIcon, label: 'Delete', action: openUserDeleteDialog }]"
>
<template #name="{ item }">
<div class="flex items-center gap-2">
<UserAvatar v-if="isUser(item)" :user="item" />
<span class="truncate">
{{ isUser(item) ? item.name : '' }}
</span>
</div>
</template>
<template #email="{ item }">
{{ isUser(item) ? item.email : '' }}
</template>
<template #emailState="{ item }">
<div class="flex items-center gap-2 select-none">
<template v-if="isUser(item) && item.verified">
<CheckCircleIcon class="h-4 w-4 text-primary" />
<span>Verified</span>
</template>
<template v-else>
<ExclamationCircleIcon class="h-4 w-4 text-danger" />
<span>Not verified</span>
</template>
</div>
</template>
<template #company="{ item }">
{{ isUser(item) ? item.company : '' }}
</template>
<template #role="{ item }">
<FormSelectServerRoles
:allow-guest="isGuestMode"
allow-admin
allow-archived
:model-value="isUser(item) ? item.role : undefined"
:disabled="isUser(item) && isCurrentUser(item)"
fully-control-value
@update:model-value="(newRoleValue) => isUser(item) && !isArray(newRoleValue) && newRoleValue && openChangeUserRoleDialog(item, newRoleValue as ServerRoles)"
/>
</template>
</LayoutTable>
<InfiniteLoading
v-if="users?.length"
:settings="{ identifier }"
class="py-4"
@infinite="onInfiniteLoad"
/>
<SettingsServerUserDeleteDialog
v-model:open="showUserDeleteDialog"
:user="userToModify"
/>
<SettingsServerUserChangeRoleDialog
v-model:open="showChangeUserRoleDialog"
:user="userToModify"
:old-role="oldRole"
:new-role="newRole"
hide-closer
/>
<SettingsServerUserInviteDialog v-model:open="showInviteDialog" />
</div>
</section>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { isArray } from 'lodash-es'
import type { Nullable, ServerRoles, Optional } from '@speckle/shared'
import { getUsersQuery } from '~~/lib/server-management/graphql/queries'
import type { ItemType, UserItem } from '~~/lib/server-management/helpers/types'
import { isUser } from '~~/lib/server-management/helpers/utils'
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
import {
MagnifyingGlassIcon,
ExclamationCircleIcon,
CheckCircleIcon,
TrashIcon
} from '@heroicons/vue/24/outline'
import { useServerInfo } from '~~/lib/core/composables/server'
import { useDebouncedTextInput } from '@speckle/ui-components'
import { usePaginatedQuery } from '~/lib/common/composables/graphql'
const { activeUser } = useActiveUser()
const { isGuestMode } = useServerInfo()
const { on, bind, value: search } = useDebouncedTextInput()
const userToModify: Ref<Nullable<UserItem>> = ref(null)
const showUserDeleteDialog = ref(false)
const showChangeUserRoleDialog = ref(false)
const newRole = ref<ServerRoles>()
const showInviteDialog = ref(false)
const {
identifier,
onInfiniteLoad,
query: { result }
} = usePaginatedQuery({
query: getUsersQuery,
baseVariables: computed(() => ({
query: search.value?.length ? search.value : null,
limit: 50
})),
resolveKey: (vars) => [vars.query || ''],
resolveCurrentResult: (res) => res?.admin.userList,
resolveNextPageVariables: (baseVars, cursor) => ({
...baseVars,
cursor
}),
resolveCursorFromVariables: (vars) => vars.cursor
})
const oldRole = computed(() => userToModify.value?.role as Optional<ServerRoles>)
const users = computed(() => result.value?.admin.userList.items || [])
const isCurrentUser = (userItem: UserItem) => {
return userItem.id === activeUser.value?.id
}
const openUserDeleteDialog = (item: ItemType) => {
if (isUser(item)) {
userToModify.value = item
showUserDeleteDialog.value = true
}
}
const openChangeUserRoleDialog = (user: UserItem, newRoleValue: ServerRoles) => {
if (user.role === newRoleValue) {
return
}
userToModify.value = user
newRole.value = newRoleValue
showChangeUserRoleDialog.value = true
}
const toggleInviteDialog = () => {
showInviteDialog.value = true
}
</script>