File size: 5,974 Bytes
b8e6434 850319d b8e6434 b61d95b b8e6434 19c8899 5f2460b b8e6434 ee5433a 9c9cca9 1ed9c25 b8e6434 850319d 8a93890 b8e6434 8a93890 850319d 8a93890 850319d a4c7d51 5f2460b a4c7d51 9c9cca9 a4c7d51 850319d a4c7d51 b8e6434 a4c7d51 8a93890 bd7895c a4c7d51 77efd75 3e74920 b8e6434 77efd75 43e744c b8e6434 77efd75 a4c7d51 b8e6434 77efd75 b8e6434 77efd75 b8e6434 77efd75 b8e6434 759a61a 77efd75 759a61a 77efd75 759a61a b8e6434 a4c7d51 77efd75 b8e6434 77efd75 a4c7d51 7372799 a4c7d51 77efd75 b8e6434 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | "use client";
import { translations, Language } from "../lib/translations";
import { useState, type Dispatch, type SetStateAction } from "react";
type SidebarTab =
| "Dashboard"
| "Tender Search"
| "My Portfolio"
| "Market Monitor"
| "Company Profile"
| "Agent Analysis"
| "Proposal Draft"
| "History"
| "Database"
| "About";
type Props = {
tabs: readonly SidebarTab[];
activeTab: SidebarTab;
onTabSelect: Dispatch<SetStateAction<SidebarTab>>;
status: string;
lang: Language;
forceExpanded?: boolean;
};
export default function Sidebar({ tabs, activeTab, onTabSelect, status, lang, forceExpanded = false }: Props) {
const t = translations[lang];
const [isHovered, setIsHovered] = useState(false);
const isExpanded = forceExpanded || isHovered;
const getTabLabel = (tab: SidebarTab) => {
switch(tab) {
case "Dashboard": return { label: t.dashboard, icon: "π" };
case "Tender Search": return { label: t.tenderSearch, icon: "π‘" };
case "My Portfolio": return { label: t.myPortfolio, icon: "β
" };
case "Market Monitor": return { label: "Market Monitor", icon: "π" };
case "Company Profile": return { label: t.companyProfile, icon: "π’" };
case "Agent Analysis": return { label: t.agentAnalysis, icon: "π€" };
case "Proposal Draft": return { label: t.proposalDraft, icon: "βοΈ" };
case "History": return { label: t.history, icon: "π" };
case "Database": return { label: "Local DB", icon: "ποΈ" };
case "About": return { label: t.about, icon: "βΉοΈ" };
default: return { label: tab, icon: "β’" };
}
};
return (
<aside
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className={`glass-card rounded-2xl md:rounded-3xl h-full md:h-[calc(100vh-3rem)] md:sticky md:top-6 p-3 md:p-4 flex flex-col gap-4 md:gap-8 transition-all duration-500 ease-in-out z-50 border-white/10 ${isExpanded ? 'w-full md:w-72 shadow-2xl shadow-purple-500/10 bg-black/60' : 'w-[84px] shadow-none border-white/5 bg-white/[0.02]'}`}
>
<div className={`flex items-center gap-3 px-2 transition-all duration-300 ${isExpanded ? 'justify-start' : 'justify-center'}`}>
<div className="w-10 h-10 premium-gradient rounded-xl flex-shrink-0 flex items-center justify-center shadow-lg shadow-purple-500/20">
<span className="text-white font-bold text-xl">A</span>
</div>
{isExpanded && (
<div className="animate-in fade-in duration-500">
<h1 className="text-xl font-bold tracking-tight text-white whitespace-nowrap">AndesOps</h1>
<p className="text-[8px] font-black uppercase tracking-[0.3em] text-cyan opacity-50">v1.2.0 β’ MONOLITH</p>
</div>
)}
</div>
<nav className="flex-1 flex flex-col gap-2 overflow-y-auto overflow-x-hidden custom-scrollbar pr-1">
{tabs.map((tab) => {
const isActive = activeTab === tab;
const { label, icon } = getTabLabel(tab);
const tabSlug = tab.toLowerCase().replace(/ /g, "_");
return (
<button
key={tab}
onClick={() => {
onTabSelect(tab);
window.history.pushState({}, '', `?tab=${tabSlug}`);
}}
className={`flex items-center rounded-2xl transition-all duration-300 active:scale-90 group relative ${
isActive
? "bg-white/10 text-white shadow-[inset_0_0_20px_rgba(255,255,255,0.05)] border border-white/10"
: "text-slate-400 hover:bg-white/5 hover:text-white"
} ${isExpanded ? 'px-5 py-4 gap-4' : 'px-0 py-4 w-full justify-center'}`}
>
<span className={`text-xl transition-all duration-300 ${isActive ? 'scale-110' : 'group-hover:scale-110 opacity-70 group-hover:opacity-100'}`}>
{icon}
</span>
{isExpanded && (
<span className="font-medium text-sm whitespace-nowrap animate-in slide-in-from-left-2 duration-300">
{label}
</span>
)}
{!isExpanded && isActive && (
<div className="absolute right-0 top-1/2 -translate-y-1/2 w-1 h-6 bg-purple-500 rounded-l-full shadow-[0_0_12px_rgba(168,85,247,0.8)]" />
)}
{/* Tooltip for collapsed mode */}
{!isExpanded && (
<div className="absolute left-full ml-4 px-3 py-2 bg-slate-900 text-white text-[10px] font-bold rounded-lg opacity-0 pointer-events-none group-hover:opacity-100 transition-opacity border border-white/10 whitespace-nowrap z-50">
{label}
</div>
)}
</button>
);
})}
</nav>
<div className="mt-auto pt-6 border-t border-white/5">
<div className={`rounded-xl transition-all duration-500 bg-gradient-to-br from-indigo-500/10 to-purple-500/10 border border-indigo-500/20 ${isExpanded ? 'px-4 py-3' : 'p-2 flex justify-center'}`}>
{isExpanded ? (
<>
<p className="text-[10px] uppercase tracking-widest text-indigo-300 font-bold mb-1">Status</p>
<div className="flex items-center gap-2">
<div className={`w-2 h-2 rounded-full ${status === "connected" ? "bg-green-500 animate-pulse" : "bg-yellow-500"}`} />
<span className={`text-xs font-medium ${status === "connected" ? "text-green-300" : "text-yellow-300"}`}>
{status === "connected" ? "Systems Nominal" : "Connection Warning"}
</span>
</div>
</>
) : (
<div className={`w-3 h-3 rounded-full ${status === "connected" ? "bg-green-500 animate-pulse" : "bg-yellow-500"}`} />
)}
</div>
</div>
</aside>
);
}
|