monacousa-portal/layouts/member.vue

253 lines
6.5 KiB
Vue

<template>
<v-app>
<v-navigation-drawer v-model="drawer" app width="280">
<!-- Logo Section -->
<v-list-item class="pa-4 text-center">
<v-img
src="/MONACOUSA-Flags_376x376.png"
width="80"
height="80"
class="mx-auto mb-2"
/>
<div class="text-h6 font-weight-bold" style="color: #dc2626;">
MonacoUSA Portal
</div>
<div class="text-caption text-medium-emphasis">Member Portal</div>
</v-list-item>
<v-divider />
<!-- Navigation Menu -->
<v-list nav>
<v-list-item
to="/member/dashboard"
prepend-icon="mdi-view-dashboard"
title="Dashboard"
value="dashboard"
/>
<v-list-item
to="/member/profile"
prepend-icon="mdi-account"
title="My Profile"
value="profile"
/>
<v-list-item
to="/member/events"
prepend-icon="mdi-calendar"
title="Events"
value="events"
/>
<v-list-item
to="/member/payments"
prepend-icon="mdi-credit-card"
title="Payments & Dues"
value="payments"
/>
<v-list-item
to="/member/resources"
prepend-icon="mdi-book-open-variant"
title="Resources"
value="resources"
/>
<v-list-item
to="/member/directory"
prepend-icon="mdi-contacts"
title="Member Directory"
value="directory"
/>
</v-list>
<!-- Footer -->
<template v-slot:append>
<div class="pa-4">
<v-card
variant="tonal"
color="primary"
class="text-center pa-3"
>
<v-icon size="small" class="mb-1">mdi-account</v-icon>
<div class="text-caption font-weight-bold">MEMBER ACCESS</div>
</v-card>
</div>
</template>
</v-navigation-drawer>
<v-app-bar app color="primary" elevation="0" flat>
<!-- Navigation Toggle -->
<v-btn
icon
@click="drawer = !drawer"
class="mr-2"
>
<v-icon>mdi-menu</v-icon>
</v-btn>
<v-toolbar-title class="text-white font-weight-bold">
Member Portal
</v-toolbar-title>
<v-spacer />
<!-- Quick Actions -->
<v-btn icon color="white">
<v-badge
:content="notifications"
:value="notifications > 0"
color="error"
>
<v-icon>mdi-bell</v-icon>
</v-badge>
</v-btn>
<!-- User Menu -->
<v-menu offset-y>
<template v-slot:activator="{ props }">
<v-btn icon v-bind="props" color="white">
<ProfileAvatar
:member-id="memberData?.member_id"
:member-name="user?.name"
:first-name="user?.firstName || memberData?.first_name"
:last-name="user?.lastName || memberData?.last_name"
size="small"
:lazy="false"
show-border
/>
</v-btn>
</template>
<v-list min-width="250">
<v-list-item>
<v-list-item-title class="font-weight-bold">
{{ user?.name || 'Member' }}
</v-list-item-title>
<v-list-item-subtitle>
{{ user?.email }}
</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<v-chip
color="info"
size="x-small"
variant="flat"
>
MEMBER
</v-chip>
</v-list-item>
<v-divider class="my-2" />
<v-list-item to="/member/profile">
<template v-slot:prepend>
<v-icon>mdi-account</v-icon>
</template>
<v-list-item-title>My Profile</v-list-item-title>
</v-list-item>
<v-list-item to="/member/settings">
<template v-slot:prepend>
<v-icon>mdi-cog</v-icon>
</template>
<v-list-item-title>Settings</v-list-item-title>
</v-list-item>
<v-divider class="my-2" />
<v-list-item @click="handleLogout" class="text-error">
<template v-slot:prepend>
<v-icon color="error">mdi-logout</v-icon>
</template>
<v-list-item-title>Logout</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-app-bar>
<v-main>
<!-- Dues Payment Banner for Members -->
<DuesPaymentBanner />
<v-container fluid class="pa-6">
<slot />
</v-container>
</v-main>
</v-app>
</template>
<script setup lang="ts">
import type { Member } from '~/utils/types';
const { user, logout } = useAuth();
const drawer = ref(true);
const notifications = ref(0);
// Fetch member data
const { data: sessionData } = await useFetch<{ success: boolean; member: Member | null }>('/api/auth/session', {
server: false
});
const memberData = computed<Member | null>(() => sessionData.value?.member || null);
// Check for notifications
onMounted(async () => {
// Check for upcoming events, dues reminders, etc.
try {
const { data } = await $fetch('/api/member/notifications/count');
notifications.value = data?.count || 0;
} catch (error) {
console.error('Error fetching notifications:', error);
}
});
const handleLogout = async () => {
await logout();
};
// Responsive drawer behavior
const { width } = useDisplay();
watch(width, (newWidth) => {
drawer.value = newWidth >= 1024;
}, { immediate: true });
</script>
<style scoped>
.v-navigation-drawer {
background: linear-gradient(180deg, #ffffff 0%, #fafafa 100%);
border-right: 1px solid rgba(0, 0, 0, 0.08);
}
.v-list-item {
border-radius: 12px;
margin: 4px 12px;
transition: all 0.2s ease;
}
.v-list-item:hover {
background-color: rgba(220, 38, 38, 0.04);
}
.v-list-item--active {
background: linear-gradient(135deg, rgba(220, 38, 38, 0.1) 0%, rgba(220, 38, 38, 0.05) 100%) !important;
color: #dc2626 !important;
border-left: 3px solid #dc2626;
}
.v-list-item--active .v-icon {
color: #dc2626 !important;
}
.v-app-bar {
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%) !important;
box-shadow: 0 2px 8px rgba(220, 38, 38, 0.15) !important;
}
.v-main {
background: linear-gradient(180deg, #fafafa 0%, #f5f5f5 100%);
min-height: 100vh;
}
</style>