diff options
Diffstat (limited to 'frontend/src/components/LeftSidebar.tsx')
| -rw-r--r-- | frontend/src/components/LeftSidebar.tsx | 134 |
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; + |
