124 lines
3.4 KiB
TypeScript
Executable File
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;
|