Files
speckle-server/packages/frontend-2/components/settings/Sidebar.vue
T

199 lines
6.8 KiB
Vue

<!-- eslint-disable vuejs-accessibility/no-static-element-interactions -->
<!-- eslint-disable vuejs-accessibility/click-events-have-key-events -->
<template>
<div class="group">
<Portal to="mobile-navigation">
<div class="lg:hidden flex items-center justify-between">
<FormButton
:color="isOpenMobile ? 'outline' : 'subtle'"
size="sm"
@click="isOpenMobile = !isOpenMobile"
>
<IconSidebar v-if="!isOpenMobile" class="h-4 w-4 -ml-1 -mr-1" />
<IconSidebarClose v-else class="h-4 w-4 -ml-1 -mr-1" />
</FormButton>
<NuxtLink :to="exitSettingsRoute" class="flex items-center gap-x-1 pl-0.5">
<ChevronLeftIcon class="h-4 w-4 text-foreground-2" />
<p class="text-body-xs font-medium text-foreground">Settings</p>
</NuxtLink>
</div>
</Portal>
<div :class="wrapperClasses">
<LayoutSidebar
class="border-r border-outline-3 px-2 pt-3 pb-2 bg-foundation-page"
>
<LayoutSidebarMenu>
<LayoutSidebarMenuGroup class="hidden lg:block lg:mb-4">
<NuxtLink
:to="exitSettingsRoute"
class="items-center gap-x-1.5 px-2.5 flex"
>
<ChevronLeftIcon class="h-3 w-3 text-foreground-2" />
<p class="text-body-xs font-medium text-foreground">Exit settings</p>
</NuxtLink>
</LayoutSidebarMenuGroup>
<div class="flex flex-col gap-y-2 lg:gap-y-4">
<LayoutSidebarMenuGroup title="User settings">
<template #title-icon>
<IconAccount class="size-4" />
</template>
<NuxtLink
v-for="sidebarMenuItem in userMenuItems"
:key="`user-item-${sidebarMenuItem.route}`"
:to="sidebarMenuItem.route"
@click="isOpenMobile = false"
>
<LayoutSidebarMenuGroupItem
:label="sidebarMenuItem.title"
:active="route.path === sidebarMenuItem.route"
/>
</NuxtLink>
</LayoutSidebarMenuGroup>
<LayoutSidebarMenuGroup v-if="isServerAdmin" title="Server settings">
<template #title-icon>
<IconServer class="size-4" />
</template>
<NuxtLink
v-for="sidebarMenuItem in serverMenuItems"
:key="`server-item-${sidebarMenuItem.route}`"
:to="sidebarMenuItem.route"
@click="isOpenMobile = false"
>
<LayoutSidebarMenuGroupItem
:label="sidebarMenuItem.title"
:active="route.path === sidebarMenuItem.route"
/>
</NuxtLink>
</LayoutSidebarMenuGroup>
<LayoutSidebarMenuGroup
v-if="showWorkspaceSettings"
title="Workspace settings"
>
<template #title-icon>
<IconWorkspaces class="size-4" />
</template>
<NuxtLink
v-for="workspaceMenuItem in filteredWorkspaceMenuItems"
:key="`workspace-menu-item-${workspaceMenuItem.name}`"
:to="
workspaceMenuItem.disabled || needsSsoSession(workspaceMenuItem.name)
? undefined
: workspaceMenuItem.route(workspace?.slug)
"
@click="isOpenMobile = false"
>
<LayoutSidebarMenuGroupItem
:label="workspaceMenuItem.title"
:active="route.name?.toString().startsWith(workspaceMenuItem.name)"
:tooltip-text="
needsSsoSession(workspaceMenuItem.name)
? 'Log in with your SSO provider to access this page'
: workspaceMenuItem.tooltipText
"
:disabled="
!isServerAdmin &&
(workspaceMenuItem.disabled ||
needsSsoSession(workspaceMenuItem.name))
"
class="!pl-8"
/>
</NuxtLink>
</LayoutSidebarMenuGroup>
</div>
</LayoutSidebarMenu>
</LayoutSidebar>
</div>
</div>
</template>
<script setup lang="ts">
import type { WorkspaceRoles } from '@speckle/shared'
import { useIsWorkspacesEnabled } from '~/composables/globals'
import { useQuery } from '@vue/apollo-composable'
import { settingsSidebarQuery } from '~/lib/settings/graphql/queries'
import { ChevronLeftIcon } from '@heroicons/vue/24/outline'
import { useActiveUser } from '~/lib/auth/composables/activeUser'
import { useSettingsMenu, useSettingsMenuState } from '~/lib/settings/composables/menu'
import {
LayoutSidebar,
LayoutSidebarMenu,
LayoutSidebarMenuGroup
} from '@speckle/ui-components'
import { graphql } from '~~/lib/common/generated/gql'
import {
projectsRoute,
settingsWorkspaceRoutes,
workspaceRoute
} from '~/lib/common/helpers/route'
import { useNavigation } from '~~/lib/navigation/composables/navigation'
graphql(`
fragment SettingsSidebar_Workspace on Workspace {
...SettingsMenu_Workspace
id
slug
role
}
`)
const isWorkspacesEnabled = useIsWorkspacesEnabled()
const { activeWorkspaceSlug } = useNavigation()
const settingsMenuState = useSettingsMenuState()
const { isAdmin: isServerAdmin } = useActiveUser()
const route = useRoute()
const { result: workspaceResult } = useQuery(
settingsSidebarQuery,
() => ({
slug: activeWorkspaceSlug.value as string
}),
() => ({
enabled: isWorkspacesEnabled.value && !!activeWorkspaceSlug.value
})
)
const { userMenuItems, serverMenuItems, workspaceMenuItems } = useSettingsMenu()
const isOpenMobile = ref(false)
const workspace = computed(() => workspaceResult.value?.workspaceBySlug)
const filteredWorkspaceMenuItems = computed(() =>
workspaceMenuItems.value.filter(
(item) =>
!item.permission ||
item.permission.includes(workspace.value?.role as WorkspaceRoles)
)
)
const wrapperClasses = computed(() => {
return [
'absolute z-40 lg:static h-full flex shrink-0 transition-all w-[13rem]',
isOpenMobile.value ? '' : `-translate-x-[13rem] lg:translate-x-0`
]
})
const needsSsoSession = (routeName?: string) => {
return workspace.value?.sso?.provider?.id &&
routeName !== settingsWorkspaceRoutes.general.name
? !workspace.value?.sso?.session?.validUntil
: false
}
const exitSettingsRoute = computed(() => {
if (!settingsMenuState.value.previousRoute) {
return activeWorkspaceSlug.value
? workspaceRoute(activeWorkspaceSlug.value)
: projectsRoute
}
return settingsMenuState.value.previousRoute
})
const showWorkspaceSettings = computed(() => {
if (!isWorkspacesEnabled.value) return false
return !!activeWorkspaceSlug.value
})
</script>