111 lines
4.8 KiB
TypeScript
111 lines
4.8 KiB
TypeScript
import { useNavigate, useLocation } from 'react-router-dom';
|
|
import { useAuthStore } from '../store/auth';
|
|
import logoImg from '../assets/logo_32.png';
|
|
import styles from './Sidebar.module.css';
|
|
|
|
export function Sidebar() {
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const user = useAuthStore((s) => s.user);
|
|
const quota = useAuthStore((s) => s.quota);
|
|
|
|
const isActive = (path: string) => location.pathname === path;
|
|
const role = user?.role;
|
|
|
|
const dailyRemaining = quota
|
|
? (quota.daily_seconds_limit === -1 ? Infinity : Math.max(0, quota.daily_seconds_limit - quota.daily_seconds_used))
|
|
: 0;
|
|
|
|
return (
|
|
<aside className={styles.sidebar}>
|
|
{/* Logo */}
|
|
<div className={styles.logo} onClick={() => navigate(role === 'super_admin' ? '/admin/dashboard' : '/app')}>
|
|
<img src={logoImg} alt="AirDrama" width="32" height="32" />
|
|
</div>
|
|
|
|
{/* Nav items */}
|
|
<nav className={styles.navItems}>
|
|
{/* Video generation - team members and team admins only */}
|
|
{role !== 'super_admin' && (
|
|
<>
|
|
<div
|
|
className={`${styles.navItem} ${isActive('/app') ? styles.active : ''}`}
|
|
onClick={() => navigate('/app')}
|
|
>
|
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
|
|
<path d="M13 10V3L4 14h7v7l9-11h-7z" />
|
|
</svg>
|
|
<span>生成</span>
|
|
</div>
|
|
<div
|
|
className={`${styles.navItem} ${isActive('/user-assets') ? styles.active : ''}`}
|
|
onClick={() => navigate('/user-assets')}
|
|
>
|
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
|
|
<rect x="3" y="3" width="18" height="18" rx="2" />
|
|
<path d="M3 9h18M9 3v18" />
|
|
</svg>
|
|
<span>资产</span>
|
|
</div>
|
|
</>
|
|
)}
|
|
|
|
{/* Team management - team admin only */}
|
|
{role === 'team_admin' && (
|
|
<div
|
|
className={`${styles.navItem} ${location.pathname.startsWith('/team') ? styles.active : ''}`}
|
|
onClick={() => navigate('/team/dashboard')}
|
|
>
|
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
|
|
<path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" />
|
|
<circle cx="9" cy="7" r="4" />
|
|
<path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75" />
|
|
</svg>
|
|
<span>团队</span>
|
|
</div>
|
|
)}
|
|
</nav>
|
|
|
|
{/* Bottom section: quota + avatar + admin */}
|
|
<div className={styles.bottom}>
|
|
{/* Quota display - not for super admin */}
|
|
{role !== 'super_admin' && (
|
|
<div className={styles.quota} onClick={() => navigate('/profile')}>
|
|
<svg className={styles.diamondIcon} width="16" height="16" viewBox="0 0 24 24" fill="none">
|
|
<path d="M6 3h12l4 8-10 12L2 11l4-8z" fill="#6c63ff" opacity="0.85" />
|
|
<path d="M2 11h20M6 3l4 8M18 3l-4 8M12 23l-4-12M12 23l4-12" stroke="#fff" strokeWidth="0.8" opacity="0.4" />
|
|
</svg>
|
|
<span className={styles.quotaNumber}>
|
|
{dailyRemaining === Infinity ? '∞' : dailyRemaining.toLocaleString()}
|
|
</span>
|
|
<span className={styles.quotaLabel}>剩余额度</span>
|
|
</div>
|
|
)}
|
|
|
|
{/* Admin entry - super admin only */}
|
|
{role === 'super_admin' && (
|
|
<div
|
|
className={styles.adminBtn}
|
|
onClick={() => navigate('/admin/dashboard')}
|
|
title="管理后台"
|
|
>
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
|
|
<circle cx="12" cy="12" r="3" />
|
|
<path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 01-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z" />
|
|
</svg>
|
|
</div>
|
|
)}
|
|
|
|
{/* User avatar */}
|
|
<div
|
|
className={styles.avatar}
|
|
onClick={() => navigate(role === 'super_admin' ? '/admin/dashboard' : '/profile')}
|
|
title={user?.username || '个人中心'}
|
|
>
|
|
{user?.username?.charAt(0).toUpperCase() || 'U'}
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|