Files
FlowStack/frontend/src/components/Sidebar.tsx
T

180 lines
5.9 KiB
TypeScript

import {
GitBranch,
Server,
Key,
Cpu,
Wrench,
Download,
ChevronLeft,
ChevronRight,
Rocket,
Zap,
History,
CloudSync,
Puzzle,
Settings,
BookOpen,
Users,
Sun,
Moon,
LogOut,
} from 'lucide-react';
import { useTheme } from '@/providers/theme';
type ModuleType = 'pipelines' | 'gitops' | 'batch-release' | 'release-records' | 'environments' | 'credentials' | 'build-nodes' | 'tools' | 'jenkins-import' | 'plugins' | 'users' | 'docs' | 'settings';
interface SidebarProps {
activeModule: ModuleType;
onModuleChange: (module: ModuleType) => void;
collapsed: boolean;
onToggleCollapse: () => void;
onLogout?: () => void;
}
const menuGroups: { label: string; items: { id: ModuleType; label: string; icon: typeof GitBranch }[] }[] = [
{
label: '发布',
items: [
{ id: 'pipelines', label: '流水线', icon: GitBranch },
{ id: 'gitops', label: 'GitOps 部署', icon: CloudSync },
{ id: 'batch-release', label: '批量发布', icon: Zap },
{ id: 'release-records', label: '发布记录', icon: History },
],
},
{
label: '资源',
items: [
{ id: 'environments', label: '环境管理', icon: Server },
{ id: 'credentials', label: '凭证管理', icon: Key },
{ id: 'build-nodes', label: '构建节点', icon: Cpu },
],
},
{
label: '集成',
items: [
{ id: 'jenkins-import', label: 'Jenkins导入', icon: Download },
{ id: 'tools', label: '工具配置', icon: Wrench },
],
},
{
label: '系统',
items: [
{ id: 'docs', label: '文档', icon: BookOpen },
{ id: 'plugins', label: '插件市场', icon: Puzzle },
{ id: 'users', label: '用户管理', icon: Users },
{ id: 'settings', label: '系统设置', icon: Settings },
],
},
];
export default function Sidebar({ activeModule, onModuleChange, collapsed, onToggleCollapse, onLogout }: SidebarProps) {
const { toggle: themeToggle, isDark } = useTheme();
return (
<div
className="flex flex-col border-r transition-all duration-300 ease-in-out h-full"
style={{
width: collapsed ? '56px' : '200px',
background: 'var(--panel-bg)',
borderColor: 'var(--border-color)',
}}
>
{/* Logo area */}
<div
className="flex items-center gap-2 px-3 h-12 border-b flex-shrink-0"
style={{ borderColor: 'var(--border-color)' }}
>
<div
className="w-7 h-7 rounded-md flex items-center justify-center flex-shrink-0"
style={{ background: 'var(--accent-blue)' }}
>
<Rocket className="w-4 h-4 text-white" />
</div>
{!collapsed && (
<span className="text-[13px] font-semibold truncate" style={{ color: 'var(--text-primary)' }}>
应用发布
</span>
)}
</div>
{/* Menu groups */}
<div className="flex-1 py-2 overflow-y-auto custom-scrollbar">
{menuGroups.map((group) => (
<div key={group.label} className="mb-2">
{!collapsed && (
<div
className="px-3 py-1 text-[10px] font-semibold uppercase tracking-wider"
style={{ color: 'var(--text-tertiary)' }}
>
{group.label}
</div>
)}
<div className="px-2 space-y-[2px]">
{group.items.map((item) => {
const Icon = item.icon;
const isActive = activeModule === item.id;
return (
<button
key={item.id}
onClick={() => onModuleChange(item.id)}
className="w-full flex items-center gap-2.5 px-2.5 py-[7px] rounded-md transition-all duration-150 group relative"
style={{
background: isActive ? 'var(--accent-blue-light)' : 'transparent',
color: isActive ? 'var(--accent-blue)' : 'var(--text-secondary)',
}}
title={collapsed ? item.label : undefined}
>
<Icon className="w-[18px] h-[18px] flex-shrink-0" />
{!collapsed && (
<span className="text-[13px] font-medium truncate flex-1 text-left">
{item.label}
</span>
)}
{isActive && !collapsed && (
<div
className="absolute left-0 top-1/2 -translate-y-1/2 w-[3px] h-5 rounded-r-full"
style={{ background: 'var(--accent-blue)' }}
/>
)}
</button>
);
})}
</div>
</div>
))}
</div>
{/* Bottom bar: theme toggle + collapse */}
<div
className="px-2 py-2 border-t flex items-center justify-center gap-1"
style={{ borderColor: 'var(--border-color)' }}
>
<button
onClick={themeToggle}
className="p-1.5 rounded-md transition-colors hover:bg-black/5"
style={{ color: 'var(--text-tertiary)' }}
title="切换深色/浅色主题"
>
{isDark ? <Sun className="w-4 h-4" /> : <Moon className="w-4 h-4" />}
</button>
<button
onClick={onLogout}
className="p-1.5 rounded-md transition-colors hover:bg-black/5"
style={{ color: 'var(--text-tertiary)' }}
title="退出登录"
>
<LogOut className="w-4 h-4" />
</button>
<button
onClick={onToggleCollapse}
className="p-1.5 rounded-md transition-colors hover:bg-black/5"
style={{ color: 'var(--text-tertiary)' }}
title={collapsed ? '展开' : '收起'}
>
{collapsed ? <ChevronRight className="w-4 h-4" /> : <ChevronLeft className="w-4 h-4" />}
</button>
</div>
</div>
);
}