270 lines
6.5 KiB
Vue
270 lines
6.5 KiB
Vue
<template>
|
|
<v-app>
|
|
<!-- Navigation Drawer - Hidden on Login Page -->
|
|
<v-navigation-drawer
|
|
v-if="!isLoginPage"
|
|
v-model="drawer"
|
|
:rail="isCollapsed"
|
|
:width="260"
|
|
permanent
|
|
elevation="4"
|
|
class="sidebar-navigation"
|
|
theme="dark"
|
|
>
|
|
<!-- Logo Section -->
|
|
<div class="logo-section pa-4">
|
|
<div class="d-flex align-center" :class="{ 'justify-center': isCollapsed }">
|
|
<v-avatar
|
|
:size="isCollapsed ? 40 : 36"
|
|
class="gradient-avatar"
|
|
>
|
|
<v-icon size="22" color="white">mdi-security</v-icon>
|
|
</v-avatar>
|
|
<span v-if="!isCollapsed" class="text-h6 ml-3 white--text font-weight-bold">TARS</span>
|
|
</div>
|
|
</div>
|
|
<v-divider class="border-opacity-25"></v-divider>
|
|
|
|
<!-- Navigation Menu -->
|
|
<v-list nav class="px-2">
|
|
<v-list-item
|
|
v-for="item in flattenedMenuItems"
|
|
:key="item.name"
|
|
:to="item.route"
|
|
:value="item.name"
|
|
rounded="lg"
|
|
class="mb-1"
|
|
@click="item.name === 'logout' ? handleLogout() : null"
|
|
>
|
|
<template v-slot:prepend>
|
|
<v-icon :size="22">{{ item.icon }}</v-icon>
|
|
</template>
|
|
|
|
<v-list-item-title class="text-subtitle-2">
|
|
{{ item.label }}
|
|
</v-list-item-title>
|
|
|
|
<template v-slot:append v-if="item.badge && !isCollapsed">
|
|
<v-chip
|
|
size="x-small"
|
|
:color="item.badge.color"
|
|
class="font-weight-bold"
|
|
>
|
|
{{ item.badge.text }}
|
|
</v-chip>
|
|
</template>
|
|
</v-list-item>
|
|
</v-list>
|
|
|
|
<!-- Bottom Actions -->
|
|
<template v-slot:append>
|
|
<div class="pa-4">
|
|
<v-btn
|
|
block
|
|
@click="toggleDrawer"
|
|
color="primary"
|
|
variant="tonal"
|
|
:prepend-icon="isCollapsed ? 'mdi-chevron-right' : 'mdi-chevron-left'"
|
|
>
|
|
<span v-if="!isCollapsed">Recolher Menu</span>
|
|
</v-btn>
|
|
</div>
|
|
</template>
|
|
</v-navigation-drawer>
|
|
|
|
<!-- Top Bar - Hidden on Login Page -->
|
|
<v-app-bar
|
|
v-if="!isLoginPage"
|
|
elevation="1"
|
|
height="64"
|
|
color="background"
|
|
>
|
|
<v-spacer></v-spacer>
|
|
|
|
<!-- Admin User Profile -->
|
|
<div
|
|
class="d-flex align-center mr-2 profile-section pa-2 rounded"
|
|
style="cursor: pointer;"
|
|
@click="$router.push({ name: 'user-profile' })"
|
|
>
|
|
<v-avatar color="primary" size="32" class="mr-2">
|
|
<v-icon>mdi-account</v-icon>
|
|
</v-avatar>
|
|
<span class="text-subtitle-2 font-weight-medium">{{ currentUser?.name || 'Admin User' }}</span>
|
|
</div>
|
|
</v-app-bar>
|
|
|
|
<!-- Main Content -->
|
|
<v-main :class="{ 'pa-0 fill-height': isLoginPage }">
|
|
<v-container
|
|
:fluid="true"
|
|
:class="{
|
|
'pa-6': !isLoginPage,
|
|
'pa-0 ma-0 fill-height': isLoginPage
|
|
}"
|
|
>
|
|
<router-view v-slot="{ Component }">
|
|
<transition name="fade" mode="out-in">
|
|
<component :is="Component"></component>
|
|
</transition>
|
|
</router-view>
|
|
</v-container>
|
|
</v-main>
|
|
|
|
<!-- Status Snackbar -->
|
|
<v-snackbar
|
|
v-model="snackbar.show"
|
|
:color="snackbar.color"
|
|
:timeout="3000"
|
|
>
|
|
{{ snackbar.text }}
|
|
<template v-slot:actions>
|
|
<v-btn
|
|
color="white"
|
|
variant="text"
|
|
@click="snackbar.show = false"
|
|
>
|
|
Fechar
|
|
</v-btn>
|
|
</template>
|
|
</v-snackbar>
|
|
</v-app>
|
|
</template>
|
|
|
|
<script>
|
|
import { mapGetters } from 'vuex';
|
|
|
|
export default {
|
|
name: 'AppLayout',
|
|
|
|
data() {
|
|
return {
|
|
drawer: true,
|
|
isCollapsed: false,
|
|
snackbar: {
|
|
show: false,
|
|
text: '',
|
|
color: 'success'
|
|
},
|
|
flattenedMenuItems: [
|
|
{
|
|
name: 'home',
|
|
route: { name: 'home' },
|
|
icon: 'mdi-home',
|
|
label: 'Home'
|
|
},
|
|
{
|
|
name: 'dashboard',
|
|
route: { name: 'dashboard' },
|
|
icon: 'mdi-view-dashboard',
|
|
label: 'Dashboard',
|
|
badge: { text: 'Novo', color: 'success' }
|
|
},
|
|
{
|
|
name: 'analytics',
|
|
route: { name: 'analytics' },
|
|
icon: 'mdi-chart-box',
|
|
label: 'Analytics'
|
|
},
|
|
{
|
|
name: 'reports',
|
|
route: { name: 'reports' },
|
|
icon: 'mdi-file-chart',
|
|
label: 'Relatórios'
|
|
},
|
|
{
|
|
name: 'testing',
|
|
route: { name: 'testing' },
|
|
icon: 'mdi-flask',
|
|
label: 'Testes',
|
|
badge: { text: 'Dev', color: 'warning' }
|
|
},
|
|
{
|
|
name: 'register-user-cam',
|
|
route: { name: 'register-user-cam' },
|
|
icon: 'mdi-file-document-plus',
|
|
label: 'Registro'
|
|
},
|
|
{
|
|
name: 'users',
|
|
route: { name: 'users' },
|
|
icon: 'mdi-account-group',
|
|
label: 'Gerenciamento'
|
|
},
|
|
{
|
|
name: 'logout',
|
|
route: { name: 'login' },
|
|
icon: 'mdi-logout',
|
|
label: 'Sair',
|
|
}
|
|
]
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
...mapGetters('auth', [
|
|
'isAuthenticated',
|
|
'currentUser'
|
|
]),
|
|
|
|
// Add new computed property to check if current route is login page
|
|
isLoginPage() {
|
|
return this.$route.name === 'login';
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
toggleDrawer() {
|
|
this.isCollapsed = !this.isCollapsed;
|
|
},
|
|
|
|
showSnackbar(text, color = 'success') {
|
|
this.snackbar.text = text;
|
|
this.snackbar.color = color;
|
|
this.snackbar.show = true;
|
|
},
|
|
|
|
async handleLogout() {
|
|
try {
|
|
await this.$store.dispatch('auth/logout');
|
|
this.$router.push({ name: 'login' });
|
|
} catch (error) {
|
|
this.showSnackbar('Sessão Encerrada', 'sucess');
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.sidebar-navigation {
|
|
background: linear-gradient(145deg, #1a237e 0%, #0d47a1 100%);
|
|
}
|
|
|
|
.gradient-avatar {
|
|
background: linear-gradient(145deg, #2196f3 0%, #1565c0 100%);
|
|
}
|
|
|
|
.profile-section {
|
|
background-color: rgba(255, 255, 255, 0.05);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.profile-section:hover {
|
|
background-color: rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.v-list-item--active {
|
|
background-color: rgba(255, 255, 255, 0.1) !important;
|
|
}
|
|
|
|
.fade-enter-active,
|
|
.fade-leave-active {
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
|
|
.fade-enter-from,
|
|
.fade-leave-to {
|
|
opacity: 0;
|
|
}
|
|
</style> |