177 lines
6.8 KiB
TypeScript
177 lines
6.8 KiB
TypeScript
"use client";
|
|
|
|
import { useAuth } from "@/hooks/use-auth";
|
|
import Link from "next/link";
|
|
import { usePathname } from "next/navigation";
|
|
import { ThemeToggle } from "@/components/theme-toggle";
|
|
|
|
function DashboardIcon() {
|
|
return (
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
|
<rect x="3" y="3" width="7" height="7" rx="1.5" />
|
|
<rect x="14" y="3" width="7" height="7" rx="1.5" />
|
|
<rect x="3" y="14" width="7" height="7" rx="1.5" />
|
|
<rect x="14" y="14" width="7" height="7" rx="1.5" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
function TargetIcon() {
|
|
return (
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
|
<circle cx="12" cy="12" r="10" />
|
|
<circle cx="12" cy="12" r="6" />
|
|
<circle cx="12" cy="12" r="2" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
function FireIcon() {
|
|
return (
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M12 2c.5 2.5-.5 5-2 7 1 0 2.5.5 3 2.5.5-2 2-3 3-4-1 3-1 6-4 8.5-1.5 1-3.5 1.5-5 1-1.5-.5-2.5-2-2.5-3.5 0-3 3-5 5-7.5C10 5 11 3.5 12 2z" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
function StrategyIcon() {
|
|
return (
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M4 19V5" />
|
|
<path d="M4 19h16" />
|
|
<path d="M7 15l3-4 3 2 4-7" />
|
|
<path d="M17 6h3v3" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
function DiagnoseIcon() {
|
|
return (
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M9 11l3 3L22 4" />
|
|
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
function ChatIcon() {
|
|
return (
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
function WatchlistIcon() {
|
|
return (
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
function UsersIcon() {
|
|
return (
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
|
<circle cx="9" cy="7" r="4" />
|
|
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
|
|
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
function SettingsIcon() {
|
|
return (
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
|
<circle cx="12" cy="12" r="3" />
|
|
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
function SideNavItem({ href, icon, label }: { href: string; icon: React.ReactNode; label: string }) {
|
|
const pathname = usePathname();
|
|
const isActive = pathname === href || (href !== "/dashboard" && pathname.startsWith(href));
|
|
|
|
return (
|
|
<Link
|
|
href={href}
|
|
className={`flex items-center gap-3 px-4 py-2.5 rounded-xl text-sm transition-all duration-200 ${
|
|
isActive
|
|
? "text-text-primary bg-surface-4"
|
|
: "text-text-secondary hover:text-text-primary hover:bg-surface-3"
|
|
}`}
|
|
>
|
|
<span className="text-base opacity-70">{icon}</span>
|
|
<span className="font-medium">{label}</span>
|
|
</Link>
|
|
);
|
|
}
|
|
|
|
export function SidebarNav() {
|
|
const { user } = useAuth();
|
|
|
|
return (
|
|
<nav className="flex-1 py-5 px-3 space-y-1">
|
|
<SideNavItem href="/dashboard" icon={<DashboardIcon />} label="今日作战" />
|
|
<SideNavItem href="/recommendations" icon={<TargetIcon />} label="推荐池" />
|
|
<SideNavItem href="/sectors" icon={<FireIcon />} label="板块主线" />
|
|
<SideNavItem href="/watchlists" icon={<WatchlistIcon />} label="自选股" />
|
|
<SideNavItem href="/chat" icon={<ChatIcon />} label="系统智能体" />
|
|
<SideNavItem href="/diagnose" icon={<DiagnoseIcon />} label="个股诊断" />
|
|
{user?.role === "admin" && (
|
|
<>
|
|
<SideNavItem href="/strategy" icon={<StrategyIcon />} label="系统校准" />
|
|
<SideNavItem href="/settings" icon={<SettingsIcon />} label="系统设置" />
|
|
</>
|
|
)}
|
|
</nav>
|
|
);
|
|
}
|
|
|
|
function MobileNavItem({ href, label, children }: { href: string; label: string; children: React.ReactNode }) {
|
|
const pathname = usePathname();
|
|
const isActive = pathname === href;
|
|
|
|
return (
|
|
<Link
|
|
href={href}
|
|
className={`flex flex-col items-center gap-1 transition-colors active:scale-95 ${
|
|
isActive ? "text-amber-400" : "text-text-muted hover:text-text-primary"
|
|
}`}
|
|
>
|
|
<span className="text-lg">{children}</span>
|
|
<span className="text-xs font-medium">{label}</span>
|
|
</Link>
|
|
);
|
|
}
|
|
|
|
export function MobileBottomNav() {
|
|
const { user } = useAuth();
|
|
|
|
return (
|
|
<nav className="fixed bottom-0 left-0 right-0 md:hidden z-50 bg-bg-secondary/95 backdrop-blur-xl border-t border-border-subtle">
|
|
<div className="flex justify-around py-2 pb-[max(0.5rem,env(safe-area-inset-bottom))]">
|
|
<MobileNavItem href="/dashboard" label="作战">
|
|
<DashboardIcon />
|
|
</MobileNavItem>
|
|
<MobileNavItem href="/recommendations" label="推荐池">
|
|
<TargetIcon />
|
|
</MobileNavItem>
|
|
<MobileNavItem href="/chat" label="智能体">
|
|
<ChatIcon />
|
|
</MobileNavItem>
|
|
<MobileNavItem href="/watchlists" label="自选">
|
|
<WatchlistIcon />
|
|
</MobileNavItem>
|
|
{user?.role === "admin" ? (
|
|
<MobileNavItem href="/strategy" label="校准">
|
|
<StrategyIcon />
|
|
</MobileNavItem>
|
|
) : null}
|
|
</div>
|
|
</nav>
|
|
);
|
|
}
|