mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-04-17 02:04:03 +00:00
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>
100 lines
2.6 KiB
Vue
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>
|