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.tsx134
1 files changed, 134 insertions, 0 deletions
diff --git a/frontend/src/components/LeftSidebar.tsx b/frontend/src/components/LeftSidebar.tsx
new file mode 100644
index 0000000..fa8b471
--- /dev/null
+++ b/frontend/src/components/LeftSidebar.tsx
@@ -0,0 +1,134 @@
+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 } = useFlowStore();
+
+ const handleDragStart = (e: React.DragEvent, archiveId: string) => {
+ e.dataTransfer.setData('archiveId', archiveId);
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+
+ if (!isOpen) {
+ return (
+ <div className="border-r border-gray-200 h-screen bg-white flex flex-col items-center py-4 w-12 z-10 transition-all duration-300">
+ <button
+ onClick={onToggle}
+ className="p-2 hover:bg-gray-100 rounded mb-4"
+ title="Expand"
+ >
+ <ChevronRight size={20} className="text-gray-500" />
+ </button>
+ {/* Icons when collapsed */}
+ <div className="flex flex-col gap-4">
+ <Folder size={20} className={activeTab === 'project' ? "text-blue-500" : "text-gray-400"} />
+ <FileText size={20} className={activeTab === 'files' ? "text-blue-500" : "text-gray-400"} />
+ <Archive size={20} className={activeTab === 'archive' ? "text-blue-500" : "text-gray-400"} />
+ </div>
+ </div>
+ );
+ }
+
+ return (
+ <div className="w-64 border-r border-gray-200 h-screen flex flex-col bg-white shadow-xl z-10 transition-all duration-300">
+ {/* Header */}
+ <div className="p-3 border-b border-gray-200 flex justify-between items-center bg-gray-50">
+ <h2 className="font-bold text-sm text-gray-700 uppercase">Workspace</h2>
+ <button
+ onClick={onToggle}
+ className="p-1 hover:bg-gray-200 rounded"
+ >
+ <ChevronLeft size={16} className="text-gray-500" />
+ </button>
+ </div>
+
+ {/* Tabs */}
+ <div className="flex border-b 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-600 font-medium' : '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-600 font-medium' : '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-600 font-medium' : '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 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 text-gray-400 mb-2">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 bg-gray-50 border border-gray-200 rounded-md cursor-grab hover:bg-gray-100 hover:border-gray-300 transition-colors group"
+ >
+ <div className="flex items-center justify-between">
+ <div className="flex items-center gap-2">
+ <MessageSquare size={14} className="text-gray-500" />
+ <span className="text-sm font-medium truncate max-w-[140px]">{archived.label}</span>
+ </div>
+ <button
+ onClick={() => removeFromArchive(archived.id)}
+ className="opacity-0 group-hover:opacity-100 p-1 hover:bg-red-100 rounded text-gray-400 hover:text-red-500 transition-all"
+ title="Remove from archive"
+ >
+ <Trash2 size={12} />
+ </button>
+ </div>
+ <div className="text-[10px] text-gray-400 mt-1">{archived.model}</div>
+ </div>
+ ))}
+ </>
+ )}
+ </div>
+ )}
+ </div>
+ </div>
+ );
+};
+
+export default LeftSidebar;
+