import { useState } from "react"; import { useNavigate, useLocation, matchPath } from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; import { api } from "../api/client"; import { logout } from "../api/auth"; import { getInitials } from "../utils"; import type { MemberRead } from "../api/auth"; import { usePlayerStore } from "../stores/playerStore"; import { useBandStore } from "../stores/bandStore"; import { TopBandBar } from "./TopBandBar"; // ── Icons ──────────────────────────────────────────────────────────────────── function IconMenu() { return ( ); } function IconLibrary() { return ( ); } function IconPlay() { return ( ); } function IconSettings() { return ( ); } function IconSignOut() { return ( ); } // ── NavItem ────────────────────────────────────────────────────────────────── interface NavItemProps { icon: React.ReactNode; label: string; active: boolean; onClick: () => void; disabled?: boolean; badge?: number; collapsed: boolean; } function NavItem({ icon, label, active, onClick, disabled, badge, collapsed }: NavItemProps) { const [hovered, setHovered] = useState(false); const fg = active ? "#a78bfa" : disabled ? "rgba(255,255,255,0.16)" : hovered ? "rgba(232,233,240,0.7)" : "rgba(232,233,240,0.35)"; const bg = active ? "rgba(139,92,246,0.12)" : hovered && !disabled ? "rgba(255,255,255,0.04)" : "transparent"; return ( setHovered(true)} onMouseLeave={() => setHovered(false)} title={collapsed ? label : undefined} style={{ display: "flex", alignItems: "center", gap: 10, width: "100%", padding: "9px 10px", borderRadius: 8, border: "none", cursor: disabled ? "default" : "pointer", color: fg, background: bg, textAlign: "left", transition: "background 0.15s, color 0.15s", fontFamily: "inherit", position: "relative", overflow: "hidden", flexShrink: 0, }} > {/* Active indicator */} {active && ( )} {icon} {!collapsed && ( {label} )} {!collapsed && badge != null && badge > 0 && ( {badge} )} ); } // ── Sidebar ─────────────────────────────────────────────────────────────────── export function Sidebar({ children }: { children: React.ReactNode }) { const navigate = useNavigate(); const location = useLocation(); const [collapsed, setCollapsed] = useState(true); const { data: me } = useQuery({ queryKey: ["me"], queryFn: () => api.get("/auth/me"), }); const { activeBandId } = useBandStore(); const isLibrary = !!( matchPath({ path: "/bands/:bandId", end: true }, location.pathname) || matchPath("/bands/:bandId/sessions/:sessionId", location.pathname) || matchPath("/bands/:bandId/sessions/:sessionId/*", location.pathname) ); const isPlayer = !!matchPath("/bands/:bandId/songs/:songId", location.pathname); const isSettings = location.pathname.startsWith("/settings"); const { currentSongId, currentBandId: playerBandId, isPlaying: isPlayerPlaying } = usePlayerStore(); const hasActiveSong = !!currentSongId && !!playerBandId; const sidebarWidth = collapsed ? 68 : 230; const border = "rgba(255,255,255,0.06)"; return ( {/* ── Sidebar ── */} {/* ── Main content ── */} {children} ); }