80a57ff0fe
Phase 6 completion: - Add Analytics page with charts (user growth, revenue, retention) - Add SystemMonitor page (CPU, memory, services health) - Add LogViewer page (log search, filtering, details) - Update sidebar navigation with Admin submenu - Add recharts to package.json for data visualization Phase 7 Day 1-2: - Create Asset and AssetLibrary domain entities - Define AssetRepository and AssetLibraryRepository interfaces - Implement InMemory repositories for testing - Implement PostgreSQL repository adapter - Update SQLAlchemy models for assets and asset_libraries - Add database migration script 004_asset_management.sql Architecture: Strict Clean Architecture compliance Testing: InMemory adapters ready for unit tests Database: Migration script with indexes and foreign keys
142 lines
3.1 KiB
TypeScript
142 lines
3.1 KiB
TypeScript
/**
|
|
* 侧边栏导航
|
|
*/
|
|
import React from 'react';
|
|
import { Layout, Menu } from 'antd';
|
|
import {
|
|
HomeOutlined,
|
|
AppstoreOutlined,
|
|
TeamOutlined,
|
|
CrownOutlined,
|
|
UserOutlined,
|
|
DashboardOutlined,
|
|
BarChartOutlined,
|
|
MonitorOutlined,
|
|
FileTextOutlined,
|
|
} from '@ant-design/icons';
|
|
import { useNavigate, useLocation } from 'react-router-dom';
|
|
import type { MenuProps } from 'antd';
|
|
|
|
const { Sider } = Layout;
|
|
|
|
interface SidebarProps {
|
|
collapsed: boolean;
|
|
}
|
|
|
|
const Sidebar: React.FC<SidebarProps> = ({ collapsed }) => {
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
|
|
const menuItems: MenuProps['items'] = [
|
|
{
|
|
key: '/workspaces',
|
|
icon: <HomeOutlined />,
|
|
label: '工作空间',
|
|
onClick: () => navigate('/workspaces'),
|
|
},
|
|
{
|
|
key: '/projects',
|
|
icon: <AppstoreOutlined />,
|
|
label: '项目',
|
|
onClick: () => navigate('/projects'),
|
|
},
|
|
{
|
|
key: '/members',
|
|
icon: <TeamOutlined />,
|
|
label: '成员管理',
|
|
onClick: () => navigate('/members'),
|
|
},
|
|
{
|
|
key: '/subscription',
|
|
icon: <CrownOutlined />,
|
|
label: '订阅管理',
|
|
onClick: () => navigate('/subscription'),
|
|
},
|
|
{
|
|
key: '/admin',
|
|
icon: <DashboardOutlined />,
|
|
label: 'Admin',
|
|
children: [
|
|
{
|
|
key: '/admin',
|
|
icon: <DashboardOutlined />,
|
|
label: 'Dashboard',
|
|
onClick: () => navigate('/admin'),
|
|
},
|
|
{
|
|
key: '/admin/users',
|
|
icon: <TeamOutlined />,
|
|
label: '用户管理',
|
|
onClick: () => navigate('/admin/users'),
|
|
},
|
|
{
|
|
key: '/admin/analytics',
|
|
icon: <BarChartOutlined />,
|
|
label: '数据分析',
|
|
onClick: () => navigate('/admin/analytics'),
|
|
},
|
|
{
|
|
key: '/admin/monitor',
|
|
icon: <MonitorOutlined />,
|
|
label: '系统监控',
|
|
onClick: () => navigate('/admin/monitor'),
|
|
},
|
|
{
|
|
key: '/admin/logs',
|
|
icon: <FileTextOutlined />,
|
|
label: '日志查看',
|
|
onClick: () => navigate('/admin/logs'),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
key: '/profile',
|
|
icon: <UserOutlined />,
|
|
label: '个人中心',
|
|
onClick: () => navigate('/profile'),
|
|
},
|
|
];
|
|
|
|
// 根据当前路径选中菜单项
|
|
const selectedKey = '/' + location.pathname.split('/')[1];
|
|
|
|
return (
|
|
<Sider
|
|
collapsible
|
|
collapsed={collapsed}
|
|
trigger={null}
|
|
width={200}
|
|
style={{
|
|
overflow: 'auto',
|
|
height: '100vh',
|
|
position: 'sticky',
|
|
left: 0,
|
|
top: 0,
|
|
bottom: 0,
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
height: 64,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
color: '#fff',
|
|
fontSize: '18px',
|
|
fontWeight: 'bold',
|
|
}}
|
|
>
|
|
{collapsed ? '🦐' : '小虾 SaaS'}
|
|
</div>
|
|
<Menu
|
|
theme="dark"
|
|
mode="inline"
|
|
selectedKeys={[selectedKey]}
|
|
items={menuItems}
|
|
/>
|
|
</Sider>
|
|
);
|
|
};
|
|
|
|
export default Sidebar;
|