Refactor: Extract sidebar navigation from individual views into a new AppSidebar component.
This commit is contained in:
89
src/components/AppSidebar.vue
Normal file
89
src/components/AppSidebar.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import Button from 'primevue/button'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const handleLogout = () => {
|
||||
localStorage.removeItem('auth_code')
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
// Check active route for styling
|
||||
const isActive = (path) => {
|
||||
// Exact match for home, startsWith for others
|
||||
if (path === '/') return route.path === '/'
|
||||
return route.path.startsWith(path)
|
||||
}
|
||||
|
||||
const navItems = [
|
||||
{ path: '/', icon: '🏠', tooltip: 'Home' },
|
||||
{ path: '/assets', icon: '📂', tooltip: 'Assets' },
|
||||
{ path: '/generation', icon: '🎨', tooltip: 'Image Generation' },
|
||||
{ path: '/flexible-generation', icon: '🖌️', tooltip: 'Flexible Generation' },
|
||||
{ path: '/characters', icon: '👥', tooltip: 'Characters' },
|
||||
{ path: '/image-to-prompt', icon: '✨', tooltip: 'Image to Prompt' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="contents">
|
||||
<!-- Sidebar (Desktop) -->
|
||||
<nav
|
||||
class="hidden md:flex glass-panel w-20 m-4 flex-col items-center py-6 rounded-3xl z-40 border border-white/5 bg-slate-900/50 backdrop-blur-md h-[calc(100vh-2rem)]">
|
||||
<div class="mb-12">
|
||||
<div
|
||||
class="w-10 h-10 bg-gradient-to-br from-violet-600 to-cyan-500 rounded-xl flex items-center justify-center font-bold text-white text-xl shadow-lg shadow-violet-500/20">
|
||||
AI
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 flex flex-col gap-6 w-full items-center">
|
||||
<div v-for="item in navItems" :key="item.path" :class="[
|
||||
'w-12 h-12 flex items-center justify-center rounded-xl cursor-pointer transition-all duration-300',
|
||||
isActive(item.path)
|
||||
? 'bg-white/10 text-slate-50 shadow-inner'
|
||||
: 'text-slate-400 hover:bg-white/10 hover:text-slate-50'
|
||||
]" @click="router.push(item.path)" v-tooltip.right="item.tooltip">
|
||||
<span class="text-2xl">{{ item.icon }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-auto flex flex-col items-center gap-4">
|
||||
<div @click="handleLogout"
|
||||
class="w-10 h-10 rounded-xl bg-red-500/10 text-red-400 flex items-center justify-center cursor-pointer hover:bg-red-500/20 transition-all font-bold"
|
||||
v-tooltip.right="'Logout'">
|
||||
<i class="pi pi-power-off"></i>
|
||||
</div>
|
||||
<!-- Profile Avatar Placeholder -->
|
||||
<div class="w-10 h-10 rounded-full bg-slate-800 border-2 border-violet-600 flex items-center justify-center font-bold text-slate-50 cursor-pointer hover:scale-105 transition-all"
|
||||
title="Profile">
|
||||
U
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Mobile Bottom Nav -->
|
||||
<nav
|
||||
class="md:hidden fixed bottom-0 left-0 right-0 h-16 bg-slate-900/90 backdrop-blur-xl border-t border-white/10 z-50 flex justify-around items-center px-2">
|
||||
<div v-for="item in navItems" :key="item.path" :class="[
|
||||
'flex flex-col items-center gap-1 p-2 rounded-xl transition-all',
|
||||
isActive(item.path)
|
||||
? 'text-white bg-white/10 relative top-[-10px] shadow-lg shadow-violet-500/20 border border-violet-500/30'
|
||||
: 'text-slate-400 hover:text-slate-200'
|
||||
]" @click="router.push(item.path)">
|
||||
<span class="text-xl">{{ item.icon }}</span>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.glass-panel {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user