monacousa-portal/components/GlassSidebar.vue

312 lines
7.2 KiB
Vue

<template>
<aside
:class="[
'fixed left-0 top-0 h-full z-40 transition-all duration-300 glass-sidebar',
isCollapsed ? 'w-20' : 'w-72',
isMobile && !isOpen ? '-translate-x-full' : 'translate-x-0'
]"
>
<!-- Logo Section -->
<div class="flex items-center justify-between p-6 border-b border-white/20">
<div class="flex items-center gap-3">
<img
src="/MONACOUSA-Flags_376x376.png"
alt="MonacoUSA"
class="w-10 h-10 rounded-xl shadow-soft"
>
<transition name="fade">
<div v-if="!isCollapsed" class="flex flex-col">
<span class="text-lg font-bold text-gradient-monaco">MonacoUSA</span>
<span class="text-xs text-gray-500">Board Portal</span>
</div>
</transition>
</div>
<!-- Collapse Button (Desktop) -->
<button
v-if="!isMobile"
@click="toggleCollapse"
class="p-2 rounded-lg hover:bg-glass-monaco-soft transition-colors"
>
<ChevronLeft v-if="!isCollapsed" class="w-5 h-5 text-gray-600" />
<ChevronRight v-else class="w-5 h-5 text-gray-600" />
</button>
</div>
<!-- Navigation Section -->
<nav class="px-4 py-6 space-y-2 overflow-y-auto scrollbar-thin h-[calc(100%-200px)]">
<!-- Main Navigation -->
<div class="space-y-1">
<SidebarLink
v-for="item in mainNavItems"
:key="item.path"
:to="item.path"
:icon="item.icon"
:label="item.label"
:badge="item.badge"
:collapsed="isCollapsed"
/>
</div>
<!-- Divider -->
<div class="my-4 border-t border-white/20"></div>
<!-- Secondary Navigation -->
<div class="space-y-1">
<div v-if="!isCollapsed" class="px-3 py-2">
<span class="text-xs font-semibold text-gray-500 uppercase tracking-wider">
Management
</span>
</div>
<SidebarLink
v-for="item in managementItems"
:key="item.path"
:to="item.path"
:icon="item.icon"
:label="item.label"
:badge="item.badge"
:collapsed="isCollapsed"
/>
</div>
<!-- Settings Section -->
<div class="mt-6 pt-6 border-t border-white/20 space-y-1">
<SidebarLink
v-for="item in settingsItems"
:key="item.path"
:to="item.path"
:icon="item.icon"
:label="item.label"
:collapsed="isCollapsed"
/>
</div>
</nav>
<!-- User Profile Section -->
<div class="absolute bottom-0 left-0 right-0 p-4 border-t border-white/20 bg-glass-light">
<div class="flex items-center gap-3">
<div class="relative">
<img
:src="userAvatar"
alt="Profile"
class="w-10 h-10 rounded-full ring-2 ring-white/60"
>
<div class="absolute bottom-0 right-0 w-3 h-3 bg-green-500 rounded-full ring-2 ring-white"></div>
</div>
<transition name="fade">
<div v-if="!isCollapsed" class="flex-1">
<p class="text-sm font-semibold text-gray-800">{{ userName }}</p>
<p class="text-xs text-gray-500">{{ userRole }}</p>
</div>
</transition>
<transition name="fade">
<button
v-if="!isCollapsed"
@click="$emit('logout')"
class="p-2 rounded-lg hover:bg-glass-monaco-soft transition-colors"
>
<LogOut class="w-4 h-4 text-gray-600" />
</button>
</transition>
</div>
</div>
<!-- Mobile Close Overlay -->
<div
v-if="isMobile && isOpen"
@click="$emit('close')"
class="fixed inset-0 bg-black/20 backdrop-blur-sm -z-10"
></div>
</aside>
</template>
<script setup>
import { ref, computed } from 'vue'
import { useRoute } from 'vue-router'
import {
LayoutDashboard,
Users,
Calendar,
DollarSign,
FileText,
Mail,
BarChart3,
Settings,
HelpCircle,
LogOut,
ChevronLeft,
ChevronRight,
Bell,
Shield,
Globe,
Briefcase
} from 'lucide-vue-next'
// Component for individual sidebar links
import SidebarLink from './SidebarLink.vue'
const props = defineProps({
isOpen: {
type: Boolean,
default: true
},
isMobile: {
type: Boolean,
default: false
},
userName: {
type: String,
default: 'Board Member'
},
userRole: {
type: String,
default: 'Administrator'
},
userAvatar: {
type: String,
default: '/default-avatar.png'
}
})
const emit = defineEmits(['close', 'logout'])
const isCollapsed = ref(false)
const route = useRoute()
const toggleCollapse = () => {
isCollapsed.value = !isCollapsed.value
}
// Navigation items with Lucide icons
const mainNavItems = [
{
path: '/board/dashboard',
icon: LayoutDashboard,
label: 'Dashboard',
badge: null
},
{
path: '/board/members',
icon: Users,
label: 'Members',
badge: '1,234'
},
{
path: '/board/events',
icon: Calendar,
label: 'Events',
badge: '3'
},
{
path: '/board/dues',
icon: DollarSign,
label: 'Dues & Payments',
badge: '12'
}
]
const managementItems = [
{
path: '/board/documents',
icon: FileText,
label: 'Documents',
badge: null
},
{
path: '/board/communications',
icon: Mail,
label: 'Communications',
badge: '5'
},
{
path: '/board/reports',
icon: BarChart3,
label: 'Reports',
badge: null
},
{
path: '/board/governance',
icon: Shield,
label: 'Governance',
badge: null
}
]
const settingsItems = [
{
path: '/board/settings',
icon: Settings,
label: 'Settings',
badge: null
},
{
path: '/board/help',
icon: HelpCircle,
label: 'Help & Support',
badge: null
}
]
</script>
<style scoped>
/* Glass Sidebar Styles - Bolt.ai Style */
.glass-sidebar {
background: rgba(255, 255, 255, 0.6) !important;
backdrop-filter: blur(4px) !important;
-webkit-backdrop-filter: blur(4px) !important;
border-right: 1px solid rgba(0, 0, 0, 0.05);
box-shadow: 4px 0 12px rgba(0, 0, 0, 0.08);
}
/* Glass effects for internal elements */
.bg-glass-light {
background: rgba(255, 255, 255, 0.4) !important;
backdrop-filter: blur(2px) !important;
}
.bg-glass-monaco-soft {
background: rgba(254, 242, 242, 0.6) !important;
}
.hover\:bg-glass-monaco-soft:hover {
background: rgba(254, 242, 242, 0.8) !important;
}
/* Fade transition for collapsing elements */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
/* Custom scrollbar for navigation */
.scrollbar-thin::-webkit-scrollbar {
width: 6px;
}
.scrollbar-thin::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.03);
border-radius: 9999px;
}
.scrollbar-thin::-webkit-scrollbar-thumb {
background: rgba(220, 38, 38, 0.2);
border-radius: 9999px;
}
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
background: rgba(220, 38, 38, 0.3);
}
/* Text gradient for branding */
.text-gradient-monaco {
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
</style>