File size: 4,557 Bytes
b0c3c39 |
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 131 132 133 134 135 136 137 138 139 140 141 |
import React from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { Node, Edge } from 'reactflow';
import styles from './SidePane.module.css';
import MindMapList from './MindMapList';
import axios from '../utils/axios';
import { MindMapData } from '../utils/jsonUtils';
// Icons with improved styling
const HomeIcon = () => (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<polyline points="9 22 9 12 15 12 15 22" />
</svg>
);
const MindMapIcon = () => (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10" />
<path d="M12 2v20M2 12h20" />
<path d="M12 2a10 10 0 0 1 10 10M12 22a10 10 0 0 1-10-10" />
</svg>
);
const LogoutIcon = () => (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
<polyline points="16 17 21 12 16 7" />
<line x1="21" y1="12" x2="9" y2="12" />
</svg>
);
const ChevronIcon = ({ isCollapsed }: { isCollapsed: boolean }) => (
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
style={{
transform: isCollapsed ? 'rotate(180deg)' : 'rotate(0deg)',
transition: 'transform 0.3s ease',
}}
>
<polyline points="15 18 9 12 15 6" />
</svg>
);
interface SidePaneProps {
onSelectMindMap: (mindMap: MindMapData) => void;
onNewMindMap: () => void;
isCollapsed: boolean;
setIsCollapsed: (collapsed: boolean) => void;
onDeleteMindMap: (id: string) => void;
}
const SidePane: React.FC<SidePaneProps> = ({ onSelectMindMap, onNewMindMap, isCollapsed, setIsCollapsed, onDeleteMindMap }) => {
const navigate = useNavigate();
const location = useLocation();
const isEditor = location.pathname === '/editor';
const handleLogout = async () => {
try {
await axios.post('/logout');
} catch (error) {
// Ignore error, proceed to clear token and redirect
}
localStorage.removeItem('token');
navigate('/');
};
const handleNavigation = (path: string) => (e: React.MouseEvent) => {
e.preventDefault();
try {
navigate(path);
} catch (error) {
console.error('Navigation error:', error);
// Fallback to window.location if navigation fails
window.location.href = path;
}
};
return (
<div className={`${styles.sidePane} ${isCollapsed ? styles.collapsed : styles.expanded}`}>
<button
className={styles.toggleButton}
onClick={() => setIsCollapsed(!isCollapsed)}
aria-label={isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}
>
<ChevronIcon isCollapsed={isCollapsed} />
</button>
<div className={styles.menuItems}>
<a
href="/"
className={`${styles.menuItem} ${location.pathname === '/' ? styles.active : ''}`}
onClick={handleNavigation('/')}
>
<span className={styles.icon}>
<HomeIcon />
</span>
<span className={styles.label}>Home</span>
</a>
<a
href="/editor"
className={`${styles.menuItem} ${location.pathname === '/editor' ? styles.active : ''}`}
onClick={handleNavigation('/editor')}
>
<span className={styles.icon}>
<MindMapIcon />
</span>
<span className={styles.label}>Mind Map</span>
</a>
{isEditor && (
<MindMapList
isCollapsed={isCollapsed}
onSelectMindMap={onSelectMindMap}
onNewMindMap={onNewMindMap}
onDeleteMindMap={onDeleteMindMap}
/>
)}
</div>
<div className={`${styles.logoutButton} ${isCollapsed ? styles.collapsed : ''}`}>
<button onClick={handleLogout} aria-label="Logout">
<span className={styles.icon}>
<LogoutIcon />
</span>
<span className={styles.label}>Logout</span>
</button>
</div>
</div>
);
};
export default SidePane; |