import { useState, type DragEvent } from 'react'; import { Search, Star, Clock, ChevronRight, ChevronDown, GripVertical } from 'lucide-react'; import { componentCategories, componentItems } from '../data/components'; import type { ComponentItem, ActivityTab } from '../types'; interface LeftPanelProps { width: number; activityTab: ActivityTab; recentComponents: string[]; } const activityTabLabels: Record = { builder: 'Components', assets: 'Assets', templates: 'Templates', compliance: 'Compliance', routes: 'Routes', protocols: 'Protocols', agents: 'Agents', terminal: 'Terminal', audit: 'Audit', settings: 'Settings', }; const activityTabCategories: Partial> = { assets: ['assets'], templates: ['templates'], compliance: ['compliance'], routes: ['routing'], protocols: ['messaging'], }; export default function LeftPanel({ width, activityTab, recentComponents }: LeftPanelProps) { const [search, setSearch] = useState(''); const [expandedCategories, setExpandedCategories] = useState>( new Set(componentCategories.map(c => c.id)) ); const [favorites, setFavorites] = useState>(new Set(['transfer', 'swap', 'kyc'])); const [activeFilter, setActiveFilter] = useState<'all' | 'favorites' | 'recent'>('all'); const [tooltipItem, setTooltipItem] = useState(null); const [tooltipPos, setTooltipPos] = useState({ x: 0, y: 0 }); const toggleCategory = (id: string) => { const next = new Set(expandedCategories); if (next.has(id)) next.delete(id); else next.add(id); setExpandedCategories(next); }; const toggleFavorite = (id: string, e: React.MouseEvent) => { e.stopPropagation(); const next = new Set(favorites); if (next.has(id)) next.delete(id); else next.add(id); setFavorites(next); }; const onDragStart = (e: DragEvent, item: ComponentItem) => { e.dataTransfer.setData('application/transactflow-component', JSON.stringify(item)); e.dataTransfer.effectAllowed = 'move'; // Create drag preview const preview = document.createElement('div'); preview.className = 'drag-preview'; preview.innerHTML = `${item.icon} ${item.label}`; preview.style.cssText = 'position:fixed;top:-100px;left:-100px;background:#1a1a20;border:1px solid #3b82f6;border-radius:6px;padding:6px 12px;color:#e4e4e8;font-size:12px;display:flex;align-items:center;gap:6px;z-index:10000;pointer-events:none;'; document.body.appendChild(preview); e.dataTransfer.setDragImage(preview, 0, 0); setTimeout(() => document.body.removeChild(preview), 0); }; const showTooltip = (item: ComponentItem, e: React.MouseEvent) => { setTooltipItem(item); setTooltipPos({ x: e.clientX + 12, y: e.clientY - 10 }); }; const hideTooltip = () => setTooltipItem(null); // For non-builder tabs, show filtered content if (activityTab !== 'builder') { const categoryFilter = activityTabCategories[activityTab]; if (activityTab === 'settings') { return (
{activityTabLabels[activityTab]}
Workspace
ThemeDark
Font Size13px
Snap to GridEnabled
Grid Size16px
Canvas
Auto-saveOn
MinimapVisible
AnimationsEnabled
Compliance
Auto-validateOff
JurisdictionMulti
); } if (activityTab === 'terminal' || activityTab === 'audit') { return (
{activityTabLabels[activityTab]}

{activityTab === 'terminal' ? 'Terminal output is shown in the bottom panel.' : 'Audit trail is shown in the bottom panel.'}

Use Ctrl+` to toggle the bottom panel.

); } if (activityTab === 'agents') { const agentList = [ { name: 'Builder Agent', desc: 'Helps construct transaction flows', color: '#3b82f6' }, { name: 'Compliance Agent', desc: 'Monitors policy violations', color: '#22c55e' }, { name: 'Routing Agent', desc: 'Optimizes execution paths', color: '#f97316' }, { name: 'ISO-20022 Agent', desc: 'Generates messaging payloads', color: '#a855f7' }, { name: 'Settlement Agent', desc: 'Manages settlement instructions', color: '#eab308' }, { name: 'Risk Agent', desc: 'Evaluates transaction risk', color: '#ef4444' }, { name: 'Documentation Agent', desc: 'Generates deal memos', color: '#6b7280' }, ]; return (
Agents
{agentList.map(a => (
{a.name}
{a.desc}
))}
); } // For assets, templates, compliance, routes, protocols: show filtered components const filteredItems = categoryFilter ? componentItems.filter(i => categoryFilter.includes(i.category)) : componentItems; const searchFiltered = filteredItems.filter(item => item.label.toLowerCase().includes(search.toLowerCase()) || item.description.toLowerCase().includes(search.toLowerCase()) ); return (
{activityTabLabels[activityTab]}
setSearch(e.target.value)} />
{searchFiltered.map(item => (
onDragStart(e, item)} onMouseEnter={e => showTooltip(item, e)} onMouseLeave={hideTooltip} > {item.icon} {item.label}
))} {searchFiltered.length === 0 &&
No items found
}
{tooltipItem && (
{tooltipItem.icon} {tooltipItem.label}
{tooltipItem.description}
{componentCategories.find(c => c.id === tooltipItem.category)?.label}
{tooltipItem.inputs &&
Inputs: {tooltipItem.inputs.join(', ')}
} {tooltipItem.outputs &&
Outputs: {tooltipItem.outputs.join(', ')}
}
)}
); } // Builder tab: full component library const filtered = componentItems.filter(item => item.label.toLowerCase().includes(search.toLowerCase()) || item.description.toLowerCase().includes(search.toLowerCase()) ); const displayItems = activeFilter === 'favorites' ? filtered.filter(i => favorites.has(i.id)) : activeFilter === 'recent' ? filtered.filter(i => recentComponents.includes(i.id)).sort((a, b) => recentComponents.indexOf(a.id) - recentComponents.indexOf(b.id)) : filtered; return (
Components
setSearch(e.target.value)} />
{activeFilter === 'all' && !search ? ( componentCategories.map(cat => { const catItems = displayItems.filter(i => i.category === cat.id); if (catItems.length === 0) return null; const isExpanded = expandedCategories.has(cat.id); return (
toggleCategory(cat.id)}> {isExpanded ? : } {cat.icon} {cat.label} {catItems.length}
{isExpanded && (
{catItems.map(item => (
onDragStart(e, item)} onMouseEnter={e => showTooltip(item, e)} onMouseLeave={hideTooltip} > {item.icon} {item.label}
))}
)}
); }) ) : (
{displayItems.map(item => (
onDragStart(e, item)} onMouseEnter={e => showTooltip(item, e)} onMouseLeave={hideTooltip} > {item.icon} {item.label} {componentCategories.find(c => c.id === item.category)?.label}
))} {displayItems.length === 0 && (
{activeFilter === 'recent' ? 'No recently used components. Drag a component to the canvas to see it here.' : 'No matching components found.'}
)}
)}
{tooltipItem && (
{tooltipItem.icon} {tooltipItem.label}
{tooltipItem.description}
{componentCategories.find(c => c.id === tooltipItem.category)?.label}
)}
); }