Files
InvoiceShelf/resources/scripts-v2/layouts/CompanyLayout.vue
Darko Gjorgjijoski 774b2614f0 Phase 4a: Feature modules — layouts, auth, admin, dashboard,
customers, items, invoices, estimates, shared document form

77 files, 14451 lines. Typed layouts (CompanyLayout, AuthLayout,
header, sidebar, company switcher), auth views (login, register,
forgot/reset password), admin feature (dashboard, companies, users,
settings with typed store), company features (dashboard with chart/
stats, customers CRUD, items CRUD, invoices CRUD with full store,
estimates CRUD with full store), and shared document form components
(items table, item row, totals, notes, tax popup, template select,
exchange rate converter, calculation composable).

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

100 lines
2.6 KiB
Vue

<template>
<div v-if="isAppLoaded" class="h-full">
<NotificationRoot />
<ImpersonationBanner />
<SiteHeader />
<SiteSidebar v-if="hasCompany" />
<main
:class="[
'h-screen h-screen-ios overflow-y-auto min-h-0 transition-all duration-300',
hasCompany
? globalStore.isSidebarCollapsed
? 'md:pl-16'
: 'md:pl-56 xl:pl-64'
: '',
]"
>
<div class="pt-16 pb-16">
<router-view />
</div>
</main>
</div>
<BaseGlobalLoader v-else />
</template>
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { onMounted, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useGlobalStore } from '../../stores/global.store'
import { useUserStore } from '../../stores/user.store'
import { useModalStore } from '../../stores/modal.store'
import { useCompanyStore } from '../../stores/company.store'
import SiteHeader from './partials/SiteHeader.vue'
import SiteSidebar from './partials/SiteSidebar.vue'
import NotificationRoot from '../../components/notifications/NotificationRoot.vue'
import ImpersonationBanner from './partials/ImpersonationBanner.vue'
interface RouteMeta {
ability?: string | string[]
isSuperAdmin?: boolean
isOwner?: boolean
}
const globalStore = useGlobalStore()
const route = useRoute()
const userStore = useUserStore()
const router = useRouter()
const modalStore = useModalStore()
const { t } = useI18n()
const companyStore = useCompanyStore()
const isAppLoaded = computed<boolean>(() => {
return globalStore.isAppLoaded
})
const hasCompany = computed<boolean>(() => {
return !!companyStore.selectedCompany || companyStore.isAdminMode
})
onMounted(() => {
globalStore.bootstrap().then((res) => {
if (companyStore.isAdminMode) {
return
}
if (!res.current_company) {
if (route.name !== 'no.company') {
router.push({ name: 'no.company' })
}
return
}
const meta = route.meta as RouteMeta
if (meta.ability && !userStore.hasAbilities(meta.ability as string | string[])) {
router.push({ name: 'account.settings' })
} else if (meta.isSuperAdmin && !userStore.currentUser?.is_super_admin) {
router.push({ name: 'dashboard' })
} else if (meta.isOwner && !userStore.currentUser?.is_owner) {
router.push({ name: 'account.settings' })
}
if (
companyStore.selectedCompanySettings.bulk_exchange_rate_configured === 'NO'
) {
modalStore.openModal({
componentName: 'ExchangeRateBulkUpdateModal',
title: t('exchange_rates.bulk_update'),
size: 'sm',
})
}
})
})
</script>