Files
crewli/apps/app/src/layouts/components/DefaultLayoutWithVerticalNav.vue
bert.hausmans 89645eab60 fix: impersonation banner overlapping sidebar and navbar
The previous paddingTop on a wrapper div didn't affect the Vuexy
layout's fixed-position sidebar or sticky navbar. Replace with
scoped :deep() CSS overrides that shift both elements down 48px
when impersonation is active.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 02:58:27 +02:00

114 lines
3.6 KiB
Vue

<script lang="ts" setup>
import { orgNavItems, platformNavItems } from '@/navigation/vertical'
import { useAuthStore } from '@/stores/useAuthStore'
import { useImpersonationStore } from '@/stores/useImpersonationStore'
const authStore = useAuthStore()
const impersonationStore = useImpersonationStore()
const hasOrganisation = computed(() => !!authStore.organisations.length)
const navItems = computed(() => {
const orgName = authStore.currentOrganisation?.name ?? 'Beheer'
let orgItems = orgNavItems.map(item => {
if ('heading' in item && item.heading === 'Beheer') {
return { ...item, heading: orgName }
}
return item
})
// During impersonation: hide org-dependent items if user has no organisation
if (impersonationStore.isImpersonating && !hasOrganisation.value) {
orgItems = orgItems.filter(item => {
if ('heading' in item) return false
return 'to' in item && item.to?.name === 'dashboard'
})
}
// Platform items: only for super_admin AND only when NOT impersonating
if (authStore.isSuperAdmin && !impersonationStore.isImpersonating) {
return [...orgItems, ...platformNavItems]
}
return orgItems
})
// Components
import ImpersonationBanner from '@/components/platform/ImpersonationBanner.vue'
import Footer from '@/layouts/components/Footer.vue'
import NavBarNotifications from '@/layouts/components/NavBarNotifications.vue'
import NavSearchBar from '@/layouts/components/NavSearchBar.vue'
import NavbarShortcuts from '@/layouts/components/NavbarShortcuts.vue'
import NavbarThemeSwitcher from '@/layouts/components/NavbarThemeSwitcher.vue'
import UserProfile from '@/layouts/components/UserProfile.vue'
import OrganisationSwitcher from '@/components/layout/OrganisationSwitcher.vue'
// @layouts plugin
import { VerticalNavLayout } from '@layouts'
</script>
<template>
<div :class="{ 'impersonation-active': impersonationStore.isImpersonating }">
<VerticalNavLayout :nav-items="navItems">
<!-- 👉 Organisation switcher -->
<template #before-vertical-nav-items>
<OrganisationSwitcher />
<div class="vertical-nav-items-shadow" />
</template>
<!-- 👉 navbar (match Vuexy full-version: search + actions; search flex-grows) -->
<template #navbar="{ toggleVerticalOverlayNavActive }">
<div
class="d-flex h-100 align-center w-100"
style="min-inline-size: 0;"
>
<IconBtn
id="vertical-nav-toggle-btn"
class="ms-n3 d-lg-none flex-shrink-0"
@click="toggleVerticalOverlayNavActive(true)"
>
<VIcon
size="26"
icon="tabler-menu-2"
/>
</IconBtn>
<NavSearchBar class="flex-grow-1 ms-lg-n3 min-w-0" />
<NavbarThemeSwitcher class="flex-shrink-0 me-2" />
<NavbarShortcuts class="flex-shrink-0" />
<NavBarNotifications class="flex-shrink-0 me-1" />
<UserProfile class="flex-shrink-0" />
</div>
</template>
<!-- 👉 Impersonation Banner -->
<ImpersonationBanner />
<!-- 👉 Pages -->
<slot />
<!-- 👉 Footer -->
<template #footer>
<Footer />
</template>
<!-- 👉 Customizer -->
<!-- <TheCustomizer /> -->
</VerticalNavLayout>
</div>
</template>
<style scoped>
/* Push sidebar, navbar, and content below the fixed 48px impersonation banner */
.impersonation-active :deep(.layout-vertical-nav) {
inset-block-start: 48px;
block-size: calc(100% - 48px);
}
.impersonation-active :deep(.layout-navbar-sticky .layout-navbar) {
inset-block-start: 48px;
}
</style>