monolito_djanco_poonto/src/App.vue
2025-02-14 23:09:20 -03:00

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>