Álvaro Valenzuela Valdes
feat: implement local database management system with admin stats and clear functions
9c9cca9 | "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> | |
| ); | |
| } | |