101 lines
2.2 KiB
Vue
101 lines
2.2 KiB
Vue
<script setup lang="ts">
|
|
import { ref, computed } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
import AppTopbar from '@/components/layout/AppTopbar.vue'
|
|
import AppSidebar from '@/components/layout/AppSidebar.vue'
|
|
import AppBottomNav from '@/components/layout/AppBottomNav.vue'
|
|
import ToastNotification from '@/components/ui/ToastNotification.vue'
|
|
|
|
const auth = useAuthStore()
|
|
const route = useRoute()
|
|
|
|
const isAuthPage = computed(() => Boolean(route.meta.public))
|
|
|
|
interface Toast {
|
|
id: number
|
|
message: string
|
|
type: 'success' | 'error' | 'info'
|
|
}
|
|
const toasts = ref<Toast[]>([])
|
|
let toastId = 0
|
|
|
|
function addToast(msg: string, type: Toast['type'] = 'success') {
|
|
toasts.value.push({ id: ++toastId, message: msg, type })
|
|
}
|
|
|
|
function removeToast(id: number) {
|
|
toasts.value = toasts.value.filter((t) => t.id !== id)
|
|
}
|
|
|
|
// expose globally via provide
|
|
import { provide } from 'vue'
|
|
provide('addToast', addToast)
|
|
</script>
|
|
|
|
<template>
|
|
<div class="app-root">
|
|
<!-- Auth page: no layout -->
|
|
<template v-if="isAuthPage">
|
|
<RouterView />
|
|
</template>
|
|
|
|
<!-- Main layout -->
|
|
<template v-else-if="auth.isAuthenticated">
|
|
<AppTopbar />
|
|
<AppSidebar />
|
|
<main class="main-content">
|
|
<RouterView />
|
|
</main>
|
|
<AppBottomNav />
|
|
</template>
|
|
|
|
<!-- Redirect to login if not authenticated -->
|
|
<template v-else>
|
|
<RouterView />
|
|
</template>
|
|
|
|
<!-- Toast container -->
|
|
<div class="toast-container">
|
|
<ToastNotification
|
|
v-for="t in toasts"
|
|
:key="t.id"
|
|
:message="t.message"
|
|
:type="t.type"
|
|
@close="removeToast(t.id)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.app-root {
|
|
min-height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.main-content {
|
|
margin-top: var(--topbar-height);
|
|
margin-left: var(--sidebar-width);
|
|
min-height: calc(100vh - var(--topbar-height));
|
|
padding: 28px 28px 80px;
|
|
}
|
|
.toast-container {
|
|
position: fixed;
|
|
bottom: 80px;
|
|
right: 20px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
z-index: 300;
|
|
pointer-events: none;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.main-content {
|
|
margin-left: 0;
|
|
padding: 16px 16px 80px;
|
|
}
|
|
}
|
|
</style>
|