Files
system-monitor/frontend/components/SidebarNav.vue
2025-12-28 17:55:55 +09:00

203 lines
5.6 KiB
Vue
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<aside :class="['sidebar', { collapsed: isCollapsed }]">
<div class="sidebar-header">
<button class="toggle-btn" @click="toggle" :title="isCollapsed ? '메뉴 열기' : '메뉴 닫기'">
<span class="hamburger"></span>
</button>
<div class="sidebar-logo" v-show="!isCollapsed">
<span>OSOLIT Monitor</span>
</div>
</div>
<nav class="sidebar-nav">
<NuxtLink to="/" class="nav-item" :class="{ active: route.path === '/' }" :title="isCollapsed ? '대시보드' : ''">
<span class="icon">📊</span>
<span class="label">대시보드</span>
</NuxtLink>
<div class="nav-group-title" v-show="!isCollapsed">네트워크</div>
<div class="nav-divider" v-show="isCollapsed"></div>
<NuxtLink to="/network/pubnet" class="nav-item nav-sub-item" :class="{ active: route.path === '/network/pubnet' }" :title="isCollapsed ? 'Public Network' : ''">
<span class="icon">🌐</span>
<span class="label">Public Network</span>
</NuxtLink>
<NuxtLink to="/network/privnet" class="nav-item nav-sub-item" :class="{ active: route.path === '/network/privnet' }" :title="isCollapsed ? 'Private Network' : ''">
<span class="icon">🔒</span>
<span class="label">Private Network</span>
</NuxtLink>
<div class="nav-group-title" v-show="!isCollapsed">서버</div>
<div class="nav-divider" v-show="isCollapsed"></div>
<NuxtLink to="/server/list" class="nav-item nav-sub-item" :class="{ active: route.path === '/server/list' }" :title="isCollapsed ? 'Server Targets' : ''">
<span class="icon">🖥</span>
<span class="label">Server Targets</span>
</NuxtLink>
<NuxtLink to="/server/history" class="nav-item nav-sub-item" :class="{ active: route.path === '/server/history' }" :title="isCollapsed ? 'Server Status' : ''">
<span class="icon">📈</span>
<span class="label">Server Status</span>
</NuxtLink>
<NuxtLink to="/settings/thresholds" class="nav-item nav-sub-item" :class="{ active: route.path === '/settings/thresholds' }" :title="isCollapsed ? 'Thresholds' : ''">
<span class="icon"></span>
<span class="label">Thresholds</span>
</NuxtLink>
<div class="nav-group-title" v-show="!isCollapsed">이상감지</div>
<div class="nav-divider" v-show="isCollapsed"></div>
<NuxtLink to="/anomaly/short-term" class="nav-item nav-sub-item" :class="{ active: route.path === '/anomaly/short-term' }" :title="isCollapsed ? '단기 변화율' : ''">
<span class="icon"></span>
<span class="label">단기 변화율</span>
</NuxtLink>
<NuxtLink to="/anomaly/zscore" class="nav-item nav-sub-item" :class="{ active: route.path === '/anomaly/zscore' }" :title="isCollapsed ? 'Z-Score 분석' : ''">
<span class="icon">📊</span>
<span class="label">Z-Score 분석</span>
</NuxtLink>
<NuxtLink to="/anomaly/baseline" class="nav-item nav-sub-item" :class="{ active: route.path === '/anomaly/baseline' }" :title="isCollapsed ? '시간대별 베이스라인' : ''">
<span class="icon">🕐</span>
<span class="label">시간대별 베이스라인</span>
</NuxtLink>
<NuxtLink to="/anomaly/trend" class="nav-item nav-sub-item" :class="{ active: route.path === '/anomaly/trend' }" :title="isCollapsed ? '추세 분석' : ''">
<span class="icon">📉</span>
<span class="label">추세 분석</span>
</NuxtLink>
</nav>
</aside>
</template>
<script setup lang="ts">
const route = useRoute()
const { isCollapsed, toggle } = useSidebar()
</script>
<style scoped>
.sidebar {
width: 200px;
min-width: 200px;
background: var(--bg-secondary);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
transition: all 0.2s ease;
}
.sidebar.collapsed {
width: 50px;
min-width: 50px;
}
.sidebar-header {
display: flex;
align-items: center;
gap: 8px;
padding: 12px;
border-bottom: 1px solid var(--border-color);
}
.toggle-btn {
background: none;
border: none;
cursor: pointer;
padding: 6px 8px;
border-radius: 6px;
transition: background 0.2s;
flex-shrink: 0;
}
.toggle-btn:hover {
background: var(--bg-tertiary, #f1f5f9);
}
.hamburger {
font-size: 18px;
color: var(--text-primary);
}
.sidebar-logo {
font-size: 14px;
font-weight: 600;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
}
.sidebar-nav {
flex: 1;
padding: 8px;
overflow-y: auto;
}
.nav-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: 8px;
color: var(--text-secondary);
text-decoration: none;
transition: all 0.15s;
margin-bottom: 2px;
}
.collapsed .nav-item {
justify-content: center;
padding: 10px 8px;
}
.nav-item:hover {
background: var(--bg-tertiary, #f1f5f9);
color: var(--text-primary);
}
.nav-item.active {
background: var(--accent-bg, #eff6ff);
color: var(--accent-color, #3b82f6);
font-weight: 500;
}
.nav-item .icon {
font-size: 16px;
flex-shrink: 0;
}
.nav-item .label {
font-size: 13px;
white-space: nowrap;
overflow: hidden;
}
.collapsed .nav-item .label {
display: none;
}
.nav-group-title {
font-size: 11px;
font-weight: 600;
color: var(--text-muted);
padding: 12px 12px 6px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.nav-divider {
height: 1px;
background: var(--border-color);
margin: 8px 6px;
}
.nav-sub-item {
padding-left: 16px;
}
.collapsed .nav-sub-item {
padding-left: 8px;
}
</style>