o2o-castad-frontend/src/components/Sidebar.tsx

124 lines
3.4 KiB
TypeScript
Executable File

import React, { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { UserMeResponse } from '../types/api';
import { logout } from '../utils/api';
import { NAV_ITEMS, SETTINGS_ID } from './navItems';
const TOP_NAV_ITEMS = NAV_ITEMS.filter(item => item.id !== SETTINGS_ID);
const SETTINGS_NAV_ITEM = NAV_ITEMS.find(item => item.id === SETTINGS_ID)!;
import SettingsPanel from './SettingsPanel';
interface SidebarItemProps {
icon: React.ReactNode;
label: string;
isActive?: boolean;
onClick?: () => void;
}
const SidebarItem: React.FC<SidebarItemProps> = ({ icon, label, isActive, onClick }) => {
return (
<div
onClick={onClick}
className={`sidebar-item ${isActive ? 'active' : ''}`}
>
<div className="sidebar-item-icon">
{icon}
</div>
<span className="sidebar-item-label">{label}</span>
</div>
);
};
interface SidebarProps {
activeItem: string;
onNavigate: (id: string) => void;
onLogoClick?: () => void;
userInfo?: UserMeResponse | null;
onLogout?: () => void;
credits?: number | null;
isGuest: boolean;
onLoginClick: () => void;
}
const Sidebar: React.FC<SidebarProps> = ({ activeItem, onNavigate, onLogoClick, userInfo, onLogout, credits, isGuest, onLoginClick }) => {
const { t } = useTranslation();
const [isLoggingOut, setIsLoggingOut] = useState(false);
const [settingsOpen, setSettingsOpen] = useState(false);
const settingsAnchorRef = useRef<HTMLDivElement>(null);
const handleLogout = async () => {
if (isLoggingOut) return;
setIsLoggingOut(true);
try {
await logout();
onLogout?.();
} catch (error) {
console.error('Logout failed:', error);
onLogout?.();
} finally {
setIsLoggingOut(false);
}
};
const handleItemClick = (id: string) => {
if (id === SETTINGS_ID) {
setSettingsOpen(v => !v);
return;
}
onNavigate(id);
};
return (
<div className="sidebar expanded">
<div className="sidebar-header">
<img
onClick={onLogoClick}
src="/assets/images/ado2-sidebar-logo.svg"
alt="ADO2"
className="sidebar-logo"
/>
</div>
<div className="sidebar-menu no-scrollbar">
{TOP_NAV_ITEMS.map(item => (
<SidebarItem
key={item.id}
icon={item.icon}
label={t(item.labelKey)}
isActive={activeItem === item.id}
onClick={() => handleItemClick(item.id)}
/>
))}
</div>
<div className="sidebar-footer">
<div className="sidebar-settings-anchor" ref={settingsAnchorRef}>
<SidebarItem
icon={SETTINGS_NAV_ITEM.icon}
label={t(SETTINGS_NAV_ITEM.labelKey)}
isActive={settingsOpen}
onClick={() => handleItemClick(SETTINGS_NAV_ITEM.id)}
/>
{settingsOpen && (
<SettingsPanel
variant="sidebar"
onClose={() => setSettingsOpen(false)}
onNavigate={onNavigate}
activeItem={activeItem}
userInfo={userInfo}
credits={credits}
isGuest={isGuest}
isLoggingOut={isLoggingOut}
onLogout={handleLogout}
onLoginClick={onLoginClick}
anchorRef={settingsAnchorRef}
/>
)}
</div>
</div>
</div>
);
};
export default Sidebar;