Merge pull request #2447 from specklesystems/mike/move-server-management-table-to-layout-table

Fix: Change ServerManagementTable to LayoutTable
This commit is contained in:
Mike
2024-06-28 09:59:01 +02:00
committed by GitHub
4 changed files with 39 additions and 178 deletions
@@ -1,109 +0,0 @@
<!-- eslint-disable vuejs-accessibility/no-static-element-interactions -->
<template>
<div class="text-foreground">
<div class="w-full text-sm overflow-x-auto overflow-y-visible simple-scrollbar">
<div
class="grid z-10 grid-cols-12 items-center gap-6 font-semibold bg-foundation rounded-t-lg w-full border-b border-outline-3 pb-2 pt-4 px-4 min-w-[900px]"
:style="{ paddingRight: paddingRightStyle }"
>
<div
v-for="header in headers"
:key="header.id"
:class="columnClasses[header.id]"
class="capitalize"
>
{{ header.title }}
</div>
</div>
<div
class="divide-y divide-outline-3 h-full overflow-visible"
:class="{ 'pb-32': overflowCells }"
>
<div
v-for="item in items"
:key="item.id"
class="relative grid grid-cols-12 items-center gap-6 px-4 py-1 min-w-[900px] bg-foundation"
:style="{ paddingRight: paddingRightStyle }"
:class="{ 'cursor-pointer hover:bg-primary-muted': !!props.onRowClick }"
tabindex="0"
@click="handleRowClick(item)"
@keypress="keyboardClick(() => handleRowClick(item))"
>
<template v-for="(column, colIndex) in headers" :key="column.id">
<div :class="getClasses(column.id, colIndex)" tabindex="0">
<slot :name="column.id" :item="item">
<div class="text-gray-900 font-medium order-1">
{{ (item as any)[column.id] }}
</div>
</slot>
</div>
</template>
<div class="absolute right-0 flex items-center p-0 pr-0.5">
<div v-for="button in buttons" :key="button.label" class="p-1">
<FormButton
:icon-left="button.icon"
size="sm"
color="secondary"
hide-text
class="text-red-500"
@click.stop="button.action(item)"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import type { ConcreteComponent } from 'vue'
import type {
ItemType,
UserItem,
ProjectItem,
InviteItem
} from '~~/lib/server-management/helpers/types'
import { keyboardClick } from '@speckle/ui-components'
type OnRowClickType = (item: ItemType) => void
interface RowButton {
icon: ConcreteComponent
label: string
action: (item: ItemType) => void
}
interface Header {
id: string
title: string
}
const props = defineProps<{
headers: Header[]
items: Array<UserItem | ProjectItem | InviteItem>
buttons?: RowButton[]
columnClasses: Record<string, string>
overflowCells?: boolean
onRowClick?: OnRowClickType
}>()
const paddingRightStyle = computed(() => {
const padding = 52 + ((props.buttons?.length || 0) - 1) * 25
return `${padding}px`
})
const getClasses = (column: string, colIndex: number): string => {
const columnClass = props.columnClasses[column]
if (colIndex === 0) {
return `bg-transparent py-3 pr-5 px-1 ${columnClass}`
}
return `lg:p-0 px-1 ${columnClass}`
}
const handleRowClick = (item: ItemType) => {
props.onRowClick?.(item)
}
</script>
@@ -29,25 +29,17 @@
@change="($event) => searchUpdateHandler($event.value)"
/>
<ServerManagementTable
<LayoutTable
class="mt-8"
:headers="[
{ id: 'name', title: 'Name' },
{ id: 'email', title: 'Email' },
{ id: 'emailState', title: 'Email State' },
{ id: 'company', title: 'Company' },
{ id: 'role', title: 'Role' }
: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 }]"
:column-classes="{
name: 'col-span-3 truncate',
email: 'col-span-3 truncate',
emailState: 'col-span-2',
company: 'col-span-2 truncate',
role: 'col-span-2'
}"
:overflow-cells="true"
>
<template #name="{ item }">
<div class="flex items-center gap-2">
@@ -65,12 +57,12 @@
<template #emailState="{ item }">
<div class="flex items-center gap-2 select-none">
<template v-if="isUser(item) && item.verified">
<ShieldCheckIcon class="h-4 w-4 text-primary" />
<span>verified</span>
<CheckCircleIcon class="h-4 w-4 text-primary" />
<span>Verified</span>
</template>
<template v-else>
<ShieldExclamationIcon class="h-4 w-4 text-danger" />
<span>not verified</span>
<ExclamationCircleIcon class="h-4 w-4 text-danger" />
<span>Not verified</span>
</template>
</div>
</template>
@@ -90,7 +82,7 @@
@update:model-value="(newRoleValue) => isUser(item) && !isArray(newRoleValue) && newRoleValue && openChangeUserRoleDialog(item, newRoleValue as ServerRoles)"
/>
</template>
</ServerManagementTable>
</LayoutTable>
<CommonLoadingBar v-if="loading && !users?.length" loading />
@@ -131,11 +123,11 @@ import { isUser } from '~~/lib/server-management/helpers/utils'
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
import {
MagnifyingGlassIcon,
ShieldExclamationIcon,
ShieldCheckIcon,
ExclamationCircleIcon,
CheckCircleIcon,
TrashIcon,
UserPlusIcon
} from '@heroicons/vue/20/solid'
} from '@heroicons/vue/24/outline'
import { useServerInfo } from '~~/lib/core/composables/server'
useHead({
@@ -29,22 +29,17 @@
@change="($event) => searchUpdateHandler($event.value)"
/>
<ServerManagementTable
<LayoutTable
class="mt-8"
:headers="[
{ id: 'email', title: 'Email' },
{ id: 'invitedBy', title: 'Invited By' },
{ id: 'resend', title: '' }
:columns="[
{ id: 'email', header: 'Email', classes: 'col-span-5 truncate' },
{ id: 'invitedBy', header: 'Invited By', classes: 'col-span-4' },
{ id: 'resend', header: '', classes: 'col-span-3' }
]"
:items="invites"
:buttons="[
{ icon: TrashIcon, label: 'Delete', action: openDeleteInvitationDialog }
]"
:column-classes="{
email: 'col-span-5 truncate',
invitedBy: 'col-span-4',
resend: 'col-span-3'
}"
>
<template #email="{ item }">
{{ isInvite(item) ? item.email : '' }}
@@ -77,7 +72,7 @@
}}
</FormButton>
</template>
</ServerManagementTable>
</LayoutTable>
<ServerManagementDeleteInvitationDialog
v-model:open="showDeleteInvitationDialog"
@@ -101,7 +96,7 @@
import { ref } from 'vue'
import { debounce } from 'lodash-es'
import { useQuery, useMutation } from '@vue/apollo-composable'
import { MagnifyingGlassIcon, TrashIcon, UserPlusIcon } from '@heroicons/vue/20/solid'
import { MagnifyingGlassIcon, TrashIcon, UserPlusIcon } from '@heroicons/vue/24/outline'
import type { ItemType, InviteItem } from '~~/lib/server-management/helpers/types'
import type { InfiniteLoaderState } from '~~/lib/global/helpers/components'
import { getInvitesQuery } from '~~/lib/server-management/graphql/queries'
@@ -24,28 +24,19 @@
@change="($event) => searchUpdateHandler($event.value)"
/>
<ServerManagementTable
<LayoutTable
class="mt-8"
:headers="[
{ id: 'name', title: 'Name' },
{ id: 'type', title: 'Type' },
{ id: 'created', title: 'Created' },
{ id: 'modified', title: 'Modified' },
{ id: 'models', title: 'Models' },
{ id: 'versions', title: 'Versions' },
{ id: 'contributors', title: 'Contributors' }
:columns="[
{ id: 'name', header: 'Name', classes: 'col-span-3 truncate' },
{ id: 'type', header: 'Type', classes: 'col-span-1' },
{ id: 'created', header: 'Created', classes: 'col-span-2' },
{ id: 'modified', header: 'Modified', classes: 'col-span-2' },
{ id: 'models', header: 'Models', classes: 'col-span-1 text-right' },
{ id: 'versions', header: 'Versions', classes: 'col-span-1 text-right' },
{ id: 'contributors', header: 'Contributors', classes: 'col-span-2' }
]"
:items="projects"
:buttons="[{ icon: TrashIcon, label: 'Delete', action: openProjectDeleteDialog }]"
:column-classes="{
name: 'col-span-3 truncate',
type: 'col-span-1',
created: 'col-span-2',
modified: 'col-span-2',
models: 'col-span-1 text-right',
versions: 'col-span-1 text-right',
contributors: 'col-span-2'
}"
:on-row-click="handleProjectClick"
>
<template #name="{ item }">
@@ -53,33 +44,25 @@
</template>
<template #type="{ item }">
<div class="capitalize">
<span class="capitalize">
{{ isProject(item) ? item.visibility.toLowerCase() : '' }}
</div>
</span>
</template>
<template #created="{ item }">
<div class="font-mono text-xs">
{{ isProject(item) ? new Date(item.createdAt).toLocaleString('en-GB') : '' }}
</div>
{{ isProject(item) ? new Date(item.createdAt).toLocaleString('en-GB') : '' }}
</template>
<template #modified="{ item }">
<div class="font-mono text-xs">
{{ isProject(item) ? new Date(item.updatedAt).toLocaleString('en-GB') : '' }}
</div>
{{ isProject(item) ? new Date(item.updatedAt).toLocaleString('en-GB') : '' }}
</template>
<template #models="{ item }">
<div class="font-mono text-xs">
{{ isProject(item) ? item.models.totalCount : '' }}
</div>
{{ isProject(item) ? item.models.totalCount : '' }}
</template>
<template #versions="{ item }">
<div class="font-mono text-xs">
{{ isProject(item) ? item.versions.totalCount : '' }}
</div>
{{ isProject(item) ? item.versions.totalCount : '' }}
</template>
<template #contributors="{ item }">
@@ -87,7 +70,7 @@
<UserAvatarGroup :users="item.team.map((t) => t.user)" :max-count="3" />
</div>
</template>
</ServerManagementTable>
</LayoutTable>
<CommonLoadingBar v-if="loading && !projects?.length" loading />
@@ -113,7 +96,7 @@
import { ref } from 'vue'
import { debounce } from 'lodash-es'
import { useQuery } from '@vue/apollo-composable'
import { MagnifyingGlassIcon, TrashIcon, PlusIcon } from '@heroicons/vue/20/solid'
import { MagnifyingGlassIcon, TrashIcon, PlusIcon } from '@heroicons/vue/24/outline'
import { getProjectsQuery } from '~~/lib/server-management/graphql/queries'
import type { ItemType, ProjectItem } from '~~/lib/server-management/helpers/types'
import type { InfiniteLoaderState } from '~~/lib/global/helpers/components'