Álvaro Valenzuela Valdes
Swap profile images for superhero avatar and remove redundant superhero section (cleaned)
6314c8a | "use client"; | |
| import { translations, Language } from "../lib/translations"; | |
| import { avatarBase64 } from "../lib/avatar"; | |
| import { userPhotoBase64 } from "../lib/user_photo"; | |
| import { useState, type Dispatch, type SetStateAction } from "react"; | |
| type SidebarTab = | |
| | "Dashboard" | |
| | "Tender Search" | |
| | "My Portfolio" | |
| | "Market Monitor" | |
| | "Company Profile" | |
| | "Agent Analysis" | |
| | "Proposal Draft" | |
| | "History" | |
| | "Documentation" | |
| | "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 "Documentation": return { label: t.documentation, 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.7 • AMD Hackathon Final</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 space-y-4 pt-6 border-t border-white/5"> | |
| {/* User Profile Section */} | |
| <div className={`flex items-center gap-3 transition-all duration-300 ${isExpanded ? 'px-2' : 'justify-center'}`}> | |
| <div className="relative group"> | |
| <div className="absolute -inset-0.5 bg-gradient-to-r from-purple-600 to-cyan-600 rounded-xl blur opacity-20 group-hover:opacity-40 transition"></div> | |
| <div className="relative w-10 h-10 rounded-xl overflow-hidden border border-white/10 bg-slate-900"> | |
| <img | |
| src={avatarBase64} | |
| alt="Profile" | |
| className="w-full h-full object-cover" | |
| /> | |
| </div> | |
| </div> | |
| {isExpanded && ( | |
| <div className="flex-1 min-w-0 animate-in fade-in slide-in-from-left-2 duration-500 text-left"> | |
| <p className="text-white font-bold text-sm tracking-tight truncate">Álvaro Valenzuela</p> | |
| <p className="text-[9px] font-black uppercase tracking-widest text-cyan opacity-60">Architect & CEO @ <a href="https://www.rew.cl" target="_blank" rel="noopener noreferrer" className="hover:text-white transition-colors underline decoration-cyan/30">REW.cl</a></p> | |
| </div> | |
| )} | |
| </div> | |
| <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" : | |
| status === "listening" ? "bg-blue-500 animate-bounce" : | |
| status === "warning" ? "bg-yellow-500 animate-pulse" : | |
| "bg-red-500 shadow-[0_0_8px_rgba(239,68,68,0.5)]" | |
| }`} /> | |
| <span className={`text-xs font-medium ${ | |
| status === "connected" ? "text-green-300" : | |
| status === "listening" ? "text-blue-300" : | |
| status === "warning" ? "text-yellow-300" : | |
| "text-red-300" | |
| }`}> | |
| {status === "connected" ? "Systems Nominal" : | |
| status === "listening" ? "Synchronizing..." : | |
| status === "warning" ? "Limited Connectivity" : | |
| "Connection Offline"} | |
| </span> | |
| </div> | |
| </> | |
| ) : ( | |
| <div className={`w-3 h-3 rounded-full ${ | |
| status === "connected" ? "bg-green-500 animate-pulse" : | |
| status === "listening" ? "bg-blue-500 animate-bounce" : | |
| status === "warning" ? "bg-yellow-500 animate-pulse" : | |
| "bg-red-500 shadow-[0_0_8px_rgba(239,68,68,0.5)]" | |
| }`} /> | |
| )} | |
| </div> | |
| </div> | |
| </aside> | |
| ); | |
| } | |