191 lines
4.9 KiB
Vue
191 lines
4.9 KiB
Vue
<template>
|
|
<v-app>
|
|
<!-- Navigation Drawer -->
|
|
<v-navigation-drawer
|
|
v-model="drawer"
|
|
:rail="rail"
|
|
permanent
|
|
color="grey-darken-4"
|
|
>
|
|
<v-list>
|
|
<v-list-item
|
|
prepend-avatar="/Port Nimara New Logo-Circular Frame.png"
|
|
:title="rail ? '' : 'Port Nimara'"
|
|
:subtitle="rail ? '' : 'Client Portal'"
|
|
nav
|
|
>
|
|
<template v-slot:append>
|
|
<v-btn
|
|
v-if="!rail"
|
|
icon="mdi-chevron-left"
|
|
variant="text"
|
|
@click.stop="rail = !rail"
|
|
size="small"
|
|
/>
|
|
</template>
|
|
</v-list-item>
|
|
</v-list>
|
|
|
|
<v-divider />
|
|
|
|
<v-list density="compact" nav>
|
|
<v-list-item
|
|
v-for="item in navigationItems"
|
|
:key="item.title"
|
|
:to="item.to"
|
|
:prepend-icon="item.icon"
|
|
:title="item.title"
|
|
:value="item.title"
|
|
color="primary"
|
|
/>
|
|
</v-list>
|
|
|
|
<template v-slot:append>
|
|
<v-list density="compact" nav>
|
|
<v-list-item
|
|
@click="handleLogout"
|
|
prepend-icon="mdi-logout"
|
|
title="Logout"
|
|
value="logout"
|
|
color="primary"
|
|
/>
|
|
</v-list>
|
|
</template>
|
|
</v-navigation-drawer>
|
|
|
|
<!-- App Bar -->
|
|
<v-app-bar
|
|
color="primary"
|
|
density="compact"
|
|
elevation="0"
|
|
>
|
|
<v-app-bar-nav-icon
|
|
@click="rail = !rail"
|
|
v-if="rail"
|
|
/>
|
|
|
|
<v-toolbar-title>{{ pageTitle }}</v-toolbar-title>
|
|
|
|
<v-spacer />
|
|
|
|
<v-chip
|
|
v-if="authState?.user"
|
|
class="mr-2"
|
|
color="white"
|
|
variant="tonal"
|
|
>
|
|
<v-icon start>mdi-account</v-icon>
|
|
{{ authState.user.name || authState.user.email }}
|
|
</v-chip>
|
|
|
|
<v-chip
|
|
v-if="authState?.groups?.includes('admin')"
|
|
color="orange"
|
|
variant="tonal"
|
|
class="mr-2"
|
|
>
|
|
Admin
|
|
</v-chip>
|
|
|
|
<v-chip
|
|
v-else-if="authState?.groups?.includes('sales')"
|
|
color="green"
|
|
variant="tonal"
|
|
class="mr-2"
|
|
>
|
|
Sales
|
|
</v-chip>
|
|
</v-app-bar>
|
|
|
|
<!-- Main Content -->
|
|
<v-main>
|
|
<slot />
|
|
</v-main>
|
|
</v-app>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed } from 'vue';
|
|
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const nuxtApp = useNuxtApp();
|
|
|
|
// Navigation state
|
|
const drawer = ref(true);
|
|
const rail = ref(false);
|
|
|
|
// Get auth state - with fallback to prevent errors
|
|
const authState = computed(() => {
|
|
const data = nuxtApp.payload?.data?.authState;
|
|
// Only return data if it's properly initialized
|
|
if (data && data.authenticated !== undefined) {
|
|
return data;
|
|
}
|
|
return null;
|
|
});
|
|
|
|
// Page title based on current route
|
|
const pageTitle = computed(() => {
|
|
const routeName = route.name as string;
|
|
if (routeName === 'dashboard') return 'Dashboard';
|
|
if (routeName === 'dashboard-expenses') return 'Expense Tracking';
|
|
if (routeName === 'dashboard-interest-list') return 'Interest List';
|
|
if (routeName === 'dashboard-berth-list') return 'Berth List';
|
|
if (routeName === 'dashboard-admin-audit-logs') return 'Audit Logs';
|
|
if (routeName === 'dashboard-admin-system-logs') return 'System Logs';
|
|
if (routeName === 'dashboard-admin-duplicates') return 'Duplicate Management';
|
|
|
|
// Default: capitalize route name
|
|
return routeName?.split('-').map(word =>
|
|
word.charAt(0).toUpperCase() + word.slice(1)
|
|
).join(' ') || 'Dashboard';
|
|
});
|
|
|
|
// Navigation items based on user role
|
|
const navigationItems = computed(() => {
|
|
const items = [
|
|
{ title: 'Dashboard', icon: 'mdi-view-dashboard', to: '/dashboard' },
|
|
{ title: 'Interest List', icon: 'mdi-account-group', to: '/dashboard/interest-list' },
|
|
{ title: 'Berth List', icon: 'mdi-sailboat', to: '/dashboard/berth-list' },
|
|
];
|
|
|
|
// Add sales/admin specific items
|
|
if (authState.value?.groups?.includes('sales') || authState.value?.groups?.includes('admin')) {
|
|
items.push(
|
|
{ title: 'Expenses', icon: 'mdi-receipt', to: '/dashboard/expenses' },
|
|
{ title: 'Interest Status', icon: 'mdi-chart-timeline', to: '/dashboard/interest-status' },
|
|
{ title: 'Interest Emails', icon: 'mdi-email-multiple', to: '/dashboard/interest-emails' },
|
|
);
|
|
}
|
|
|
|
// Add admin-only items
|
|
if (authState.value?.groups?.includes('admin')) {
|
|
items.push(
|
|
{ title: 'Admin', icon: 'mdi-shield-crown', to: '/dashboard/admin' },
|
|
{ title: 'File Browser', icon: 'mdi-folder-open', to: '/dashboard/file-browser' },
|
|
);
|
|
}
|
|
|
|
return items;
|
|
});
|
|
|
|
// Logout handler
|
|
const handleLogout = async () => {
|
|
try {
|
|
await $fetch('/api/auth/logout', { method: 'POST' });
|
|
await router.push('/login');
|
|
} catch (error) {
|
|
console.error('Logout error:', error);
|
|
// Even if logout fails, redirect to login
|
|
await router.push('/login');
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.v-navigation-drawer {
|
|
top: 0 !important;
|
|
}
|
|
</style>
|