zioinfo-mail/workspace/guardia-manager/frontend/src/components/layout/Sidebar.tsx
DESKTOP-TKLFCPR\ython caa70a608b feat(frontend): 나머지 개발 — ITSM/Manager/Messenger 신규 UI
ITSM static (app.js + index.html):
- 사이드바: AI플랫폼·분석KPI·클라우드·외부연동·SaaS 5개 그룹 추가
- 23개 신규 뷰 핸들러 (rag_search, kpi_dashboard, bi_dashboard, jira_sync 등)
- 액션 헬퍼 함수 20개+ (재계산, 파인튜닝, 보고서 생성, 데이터 기여 등)

Manager 5개 신규 페이지:
- KpiDashboard.tsx: KPI 신호등 대시보드 + 재계산
- BiAnalytics.tsx: SR트렌드·카테고리파이·MTTR·엔지니어워크로드
- BillingManage.tsx: 구독플랜·사용량·청구서 이력
- IntegrationHub.tsx: Jira/Slack/ServiceNow/ERP/Kakao/SSO 탭
- AiPlatform.tsx: AI인사이트·LearningLoop·예측·벤치마킹

Messenger 신규 탭:
- insights.tsx: AI 주간 인사이트 + SLA 예측 + 이상 감지

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 06:24:54 +09:00

142 lines
5.1 KiB
TypeScript

import { NavLink } from 'react-router-dom'
import { useState } from 'react'
interface NavItem { label: string; path?: string; icon: string; children?: NavItem[] }
const NAV: NavItem[] = [
{ label: '대시보드', icon: '📊', path: '/' },
{ label: '인프라 관리', icon: '🖥️', children: [
{ label: '서버 목록', icon: '', path: '/servers' },
{ label: 'CMDB 현황', icon: '', path: '/cmdb' },
]},
{ label: '배포/CI-CD', icon: '🚀', children: [
{ label: '배포 이력', icon: '', path: '/deployments' },
{ label: '저장소 목록',icon: '', path: '/repos' },
]},
{ label: '사용자/테넌트',icon: '👥', children: [
{ label: '사용자 관리',icon: '', path: '/users' },
{ label: '기관 관리', icon: '', path: '/institutions' },
]},
{ label: '보안', icon: '🔒', children: [
{ label: 'API Key', icon: '', path: '/api-keys' },
{ label: '감사 로그', icon: '', path: '/audit' },
]},
{ label: 'AI / LLM', icon: '🤖', path: '/llm' },
{ label: '시스템 설정', icon: '⚙️', children: [
{ label: '환경변수', icon: '', path: '/config/env' },
{ label: 'Nginx 관리', icon: '', path: '/config/nginx' },
]},
{ label: '알림/리포트', icon: '🔔', path: '/notifications' },
{ label: '스크랩핑 봇', icon: '🕷️', path: '/scraping' },
{ label: '라이선스 관리',icon: '🪪', path: '/licenses' },
{ label: '데이터 연동', icon: '🔄', path: '/export-import' },
{ label: '운영 관제', icon: '🛰️', children: [
{ label: 'DR 재해복구', icon: '', path: '/dr' },
{ label: '네트워크 장비', icon: '', path: '/network' },
{ label: 'CSAP 점검', icon: '', path: '/csap' },
]},
// ── GUARDiA 확장 v3 ──
{ label: '분석 · KPI', icon: '📈', children: [
{ label: 'KPI 대시보드', icon: '', path: '/kpi' },
{ label: 'BI 대시보드', icon: '', path: '/bi' },
]},
{ label: 'AI 플랫폼', icon: '🧠', path: '/ai-platform' },
{ label: '외부 연동', icon: '🔗', path: '/integrations' },
{ label: '구독 · 과금', icon: '💰', path: '/billing' },
]
/* Variant 스타일 색상 상수 */
const V = {
navy: '#003366',
blue: '#005A8C',
cyan: '#00A0C8',
cyanLt: 'rgba(0,160,200,.10)',
cyanAct: 'rgba(0,160,200,.15)',
muted: '#64748B',
text: '#334155',
border: '#E2E8F0',
bg: '#F8FAFC',
}
function NavGroup({ item }: { item: NavItem }) {
const [open, setOpen] = useState(true)
if (!item.children) {
return (
<NavLink to={item.path!} style={({ isActive }) => ({
display: 'flex', alignItems: 'center', gap: 8,
padding: '8px 16px', fontSize: 13,
color: isActive ? V.navy : V.muted,
background: isActive ? V.cyanAct : 'transparent',
borderLeft: isActive ? `3px solid ${V.cyan}` : '3px solid transparent',
fontWeight: isActive ? 700 : 400,
transition: 'all .15s',
textDecoration: 'none',
})}>
<span style={{ width: 18, textAlign: 'center' }}>{item.icon}</span>
{item.label}
</NavLink>
)
}
return (
<div>
<button onClick={() => setOpen(o => !o)} style={{
width: '100%', display: 'flex', alignItems: 'center', gap: 8,
padding: '8px 16px', fontSize: 13, color: V.navy, background: 'none',
border: 'none', cursor: 'pointer', fontWeight: 700,
letterSpacing: '-.01em',
}}>
<span style={{ width: 18, textAlign: 'center' }}>{item.icon}</span>
{item.label}
<span style={{
marginLeft: 'auto', fontSize: 9, color: V.cyan,
transition: 'transform .2s',
transform: open ? 'rotate(180deg)' : 'rotate(0)',
}}></span>
</button>
{open && item.children.map(c => (
<NavLink key={c.path} to={c.path!} style={({ isActive }) => ({
display: 'flex', alignItems: 'center',
padding: '7px 16px 7px 42px', fontSize: 12.5,
color: isActive ? V.navy : V.muted,
background: isActive ? V.cyanAct : 'transparent',
borderLeft: isActive ? `3px solid ${V.cyan}` : '3px solid transparent',
fontWeight: isActive ? 600 : 400,
transition: 'all .15s',
textDecoration: 'none',
})}>
{c.label}
</NavLink>
))}
</div>
)
}
export function Sidebar() {
return (
<aside style={{
width: 'var(--sidebar-w)',
background: '#ffffff',
borderRight: `1px solid ${V.border}`,
height: '100%',
display: 'flex', flexDirection: 'column', overflowY: 'auto',
boxShadow: '2px 0 12px rgba(0,51,102,.06)',
}}>
{/* 사이드바 헤더 */}
<div style={{
padding: '16px 16px 12px',
borderBottom: `1px solid ${V.border}`,
}}>
<div style={{ fontSize: 10, fontWeight: 700, color: V.cyan,
letterSpacing: '.1em', textTransform: 'uppercase', marginBottom: 2 }}>
Navigation
</div>
</div>
<div style={{ padding: '8px 0', flex: 1 }}>
{NAV.map((item, i) => <NavGroup key={i} item={item} />)}
</div>
</aside>
)
}