Files
hanarang-dashboard/frontend/components/common/Sidebar.tsx

62 lines
4.0 KiB
TypeScript

'use client';
import React from 'react';
import styled from 'styled-components';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { useAuth } from '@/lib/AuthContext';
const NAV_ITEMS = [
{ href: '/', label: '🟠 Live' },
{ href: '/control', label: '🧭 Control' },
{ href: '/rails', label: '🚦 레일' },
{ href: '/settings', label: '⚙️ 설정' },
];
const SidebarWrapper = styled.aside`
width: 240px;flex-shrink: 0;border-right: 1px solid var(--border-color);display: flex;flex-direction: column;padding: var(--space-xl) var(--space-lg);position: sticky;top: 0;height: 100vh;overflow-y: auto;
@media (min-width: 768px) and (max-width: 1199px) { width: 64px;padding: var(--space-xl) var(--space-md);align-items: center; }
@media (max-width: 767px) { display: none; }
`;
const LogoContainer = styled.div`display:flex;align-items:center;gap:4px;margin-bottom:var(--space-xxl);@media (min-width:768px) and (max-width:1199px){margin-bottom:var(--space-xl);}`;
const CircleFull = styled.div`width:20px;height:20px;background-color:var(--text-primary);border-radius:50%;flex-shrink:0;`;
const CircleHalf = styled.div`width:10px;height:20px;background-color:var(--text-primary);border-radius:0 20px 20px 0;flex-shrink:0;`;
const NavMenu = styled.ul`list-style:none;display:flex;flex-direction:column;gap:var(--space-sm);width:100%;`;
const NavLink = styled(Link)<{ $active: boolean }>`color:${({ $active }) => $active ? 'var(--accent-hover)' : 'var(--text-secondary)'};text-decoration:none;font-size:14px;font-weight:500;padding:var(--space-sm) 0;transition:color .2s ease;display:block;width:100%;&:hover{color:var(--accent-hover)}${({ $active }) => $active && `&::before { content: "["; margin-right: 4px; color: var(--text-secondary); } &::after { content: "]"; margin-left: 4px; color: var(--text-secondary); }`}@media (min-width:768px) and (max-width:1199px){font-size:11px;text-align:center;padding:6px 0;}`;
const BottomTabBar = styled.nav`display:none;@media (max-width:767px){display:flex;position:fixed;bottom:0;left:0;right:0;height:56px;background:var(--bg-main);border-top:1px solid var(--border-color);z-index:100;overflow-x:auto;scrollbar-width:none;&::-webkit-scrollbar{display:none;}}`;
const TabItem = styled(Link)<{ $active: boolean }>`flex:1;min-width:48px;display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:10px;font-weight:600;text-transform:uppercase;color:${({ $active }) => $active ? 'var(--text-primary)' : 'var(--text-secondary)'};text-decoration:none;padding:6px 4px;transition:color .15s;border-top:${({ $active }) => $active ? '1px solid var(--text-primary)' : '1px solid transparent'};&:hover{color:var(--text-primary)}`;
export default function Sidebar() {
const pathname = usePathname();
const { user, logout } = useAuth();
const isActive = (href: string) => href === '/' ? pathname === '/' : pathname.startsWith(href);
return (
<>
<SidebarWrapper>
<LogoContainer><CircleFull /><CircleHalf /></LogoContainer>
<nav>
<NavMenu>
{NAV_ITEMS.map((item) => (
<li key={item.href}>
<NavLink href={item.href} $active={isActive(item.href)}>{item.label}</NavLink>
</li>
))}
</NavMenu>
</nav>
{user && (
<div style={{ marginTop: 'auto', paddingTop: 'var(--space-xl)' }}>
<div style={{ fontSize: '11px', color: 'var(--text-secondary)', fontFamily: 'var(--font-mono)', marginBottom: 'var(--space-sm)' }}>[{user.username}]</div>
<button onClick={logout} style={{ background: 'transparent', border: 'none', color: 'var(--text-secondary)', fontSize: '13px', cursor: 'pointer', padding: 0, textAlign: 'left', transition: 'color 0.15s', fontFamily: 'inherit' }}></button>
</div>
)}
</SidebarWrapper>
<BottomTabBar>
{NAV_ITEMS.map((item) => (
<TabItem key={item.href} href={item.href} $active={isActive(item.href)}>{item.label}</TabItem>
))}
</BottomTabBar>
</>
);
}