summaryrefslogtreecommitdiff
path: root/frontend/src/components/LeftSidebar.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components/LeftSidebar.tsx')
-rw-r--r--frontend/src/components/LeftSidebar.tsx159
1 files changed, 159 insertions, 0 deletions
diff --git a/frontend/src/components/LeftSidebar.tsx b/frontend/src/components/LeftSidebar.tsx
new file mode 100644
index 0000000..1eaa62c
--- /dev/null
+++ b/frontend/src/components/LeftSidebar.tsx
@@ -0,0 +1,159 @@
+import React, { useState } from 'react';
+import { Folder, FileText, Archive, ChevronLeft, ChevronRight, Trash2, MessageSquare } from 'lucide-react';
+import useFlowStore from '../store/flowStore';
+
+interface LeftSidebarProps {
+ isOpen: boolean;
+ onToggle: () => void;
+}
+
+const LeftSidebar: React.FC<LeftSidebarProps> = ({ isOpen, onToggle }) => {
+ const [activeTab, setActiveTab] = useState<'project' | 'files' | 'archive'>('project');
+ const { archivedNodes, removeFromArchive, createNodeFromArchive, theme } = useFlowStore();
+ const isDark = theme === 'dark';
+
+ const handleDragStart = (e: React.DragEvent, archiveId: string) => {
+ e.dataTransfer.setData('archiveId', archiveId);
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+
+ if (!isOpen) {
+ return (
+ <div className={`border-r h-screen flex flex-col items-center py-4 w-12 z-10 transition-all duration-300 ${
+ isDark ? 'border-gray-700 bg-gray-800' : 'border-gray-200 bg-white'
+ }`}>
+ <button
+ onClick={onToggle}
+ className={`p-2 rounded mb-4 ${isDark ? 'hover:bg-gray-700' : 'hover:bg-gray-100'}`}
+ title="Expand"
+ >
+ <ChevronRight size={20} className={isDark ? 'text-gray-400' : 'text-gray-500'} />
+ </button>
+ {/* Icons when collapsed */}
+ <div className="flex flex-col gap-4">
+ <Folder size={20} className={activeTab === 'project' ? "text-blue-500" : isDark ? "text-gray-500" : "text-gray-400"} />
+ <FileText size={20} className={activeTab === 'files' ? "text-blue-500" : isDark ? "text-gray-500" : "text-gray-400"} />
+ <Archive size={20} className={activeTab === 'archive' ? "text-blue-500" : isDark ? "text-gray-500" : "text-gray-400"} />
+ </div>
+ </div>
+ );
+ }
+
+ return (
+ <div className={`w-64 border-r h-screen flex flex-col shadow-xl z-10 transition-all duration-300 ${
+ isDark ? 'border-gray-700 bg-gray-800' : 'border-gray-200 bg-white'
+ }`}>
+ {/* Header */}
+ <div className={`p-3 border-b flex justify-between items-center ${
+ isDark ? 'border-gray-700 bg-gray-900' : 'border-gray-200 bg-gray-50'
+ }`}>
+ <h2 className={`font-bold text-sm uppercase ${isDark ? 'text-gray-300' : 'text-gray-700'}`}>Workspace</h2>
+ <button
+ onClick={onToggle}
+ className={`p-1 rounded ${isDark ? 'hover:bg-gray-700' : 'hover:bg-gray-200'}`}
+ >
+ <ChevronLeft size={16} className={isDark ? 'text-gray-400' : 'text-gray-500'} />
+ </button>
+ </div>
+
+ {/* Tabs */}
+ <div className={`flex border-b ${isDark ? 'border-gray-700' : 'border-gray-200'}`}>
+ <button
+ onClick={() => setActiveTab('project')}
+ className={`flex-1 p-3 text-xs flex justify-center items-center gap-2 ${
+ activeTab === 'project'
+ ? 'border-b-2 border-blue-500 text-blue-500 font-medium'
+ : isDark ? 'text-gray-400 hover:bg-gray-700' : 'text-gray-600 hover:bg-gray-50'
+ }`}
+ >
+ <Folder size={14} /> Project
+ </button>
+ <button
+ onClick={() => setActiveTab('files')}
+ className={`flex-1 p-3 text-xs flex justify-center items-center gap-2 ${
+ activeTab === 'files'
+ ? 'border-b-2 border-blue-500 text-blue-500 font-medium'
+ : isDark ? 'text-gray-400 hover:bg-gray-700' : 'text-gray-600 hover:bg-gray-50'
+ }`}
+ >
+ <FileText size={14} /> Files
+ </button>
+ <button
+ onClick={() => setActiveTab('archive')}
+ className={`flex-1 p-3 text-xs flex justify-center items-center gap-2 ${
+ activeTab === 'archive'
+ ? 'border-b-2 border-blue-500 text-blue-500 font-medium'
+ : isDark ? 'text-gray-400 hover:bg-gray-700' : 'text-gray-600 hover:bg-gray-50'
+ }`}
+ >
+ <Archive size={14} /> Archive
+ </button>
+ </div>
+
+ {/* Content Area */}
+ <div className={`flex-1 overflow-y-auto p-4 text-sm ${isDark ? 'text-gray-400' : 'text-gray-500'}`}>
+ {activeTab === 'project' && (
+ <div className="flex flex-col items-center justify-center h-full opacity-50">
+ <Folder size={48} className="mb-2" />
+ <p>Project settings coming soon</p>
+ </div>
+ )}
+ {activeTab === 'files' && (
+ <div className="flex flex-col items-center justify-center h-full opacity-50">
+ <FileText size={48} className="mb-2" />
+ <p>File manager coming soon</p>
+ </div>
+ )}
+ {activeTab === 'archive' && (
+ <div className="space-y-2">
+ {archivedNodes.length === 0 ? (
+ <div className="flex flex-col items-center justify-center h-40 opacity-50">
+ <Archive size={32} className="mb-2" />
+ <p className="text-xs text-center">
+ No archived nodes.<br/>
+ Right-click a node → "Add to Archive"
+ </p>
+ </div>
+ ) : (
+ <>
+ <p className={`text-xs mb-2 ${isDark ? 'text-gray-500' : 'text-gray-400'}`}>Drag to canvas to create a copy</p>
+ {archivedNodes.map((archived) => (
+ <div
+ key={archived.id}
+ draggable
+ onDragStart={(e) => handleDragStart(e, archived.id)}
+ className={`p-2 border rounded-md cursor-grab transition-colors group ${
+ isDark
+ ? 'bg-gray-700 border-gray-600 hover:bg-gray-600 hover:border-gray-500'
+ : 'bg-gray-50 border-gray-200 hover:bg-gray-100 hover:border-gray-300'
+ }`}
+ >
+ <div className="flex items-center justify-between">
+ <div className="flex items-center gap-2">
+ <MessageSquare size={14} className={isDark ? 'text-gray-400' : 'text-gray-500'} />
+ <span className={`text-sm font-medium truncate max-w-[140px] ${isDark ? 'text-gray-200' : ''}`}>{archived.label}</span>
+ </div>
+ <button
+ onClick={() => removeFromArchive(archived.id)}
+ className={`opacity-0 group-hover:opacity-100 p-1 rounded transition-all ${
+ isDark ? 'hover:bg-red-900 text-gray-400 hover:text-red-400' : 'hover:bg-red-100 text-gray-400 hover:text-red-500'
+ }`}
+ title="Remove from archive"
+ >
+ <Trash2 size={12} />
+ </button>
+ </div>
+ <div className={`text-[10px] mt-1 ${isDark ? 'text-gray-500' : 'text-gray-400'}`}>{archived.model}</div>
+ </div>
+ ))}
+ </>
+ )}
+ </div>
+ )}
+ </div>
+ </div>
+ );
+};
+
+export default LeftSidebar;
+