import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ChevronIcon, TrashIcon, EditIcon, FolderIcon, ChevronsLeftIcon, LogoutIcon, ChevronDownIcon, } from '../ui/icons'; import { getTagColorStyle } from '../utils/colors'; const FolderNode = ({ node, depth, isSelected, onToggle, onSelect, onDrop, onDragOver, onDragLeave, onDelete, onRename, renderChildren, onFolderDragStart, onFolderDragEnd, draggingFolderId, }) => { const isRoot = node.id === 'root'; const hasChildren = Boolean(node.hasChildren); const canToggle = hasChildren || !node.loaded; const showChevron = canToggle; const icon = showChevron ? : null; const canDrag = !isRoot; const isDragging = draggingFolderId === node.id; const isExpanded = Boolean(node.expanded); const rowClasses = ['folder-row']; if (isSelected) { rowClasses.push('active'); } const handleToggleClick = (event) => { event.stopPropagation(); if (canToggle) { onToggle(node.id); } }; return (
  • onSelect(node.id)} onDragOver={(event) => onDragOver(event, node.id)} onDragLeave={onDragLeave} onDrop={(event) => onDrop(event, node.id)} onDragStart={(event) => { if (!canDrag || !onFolderDragStart) return; onFolderDragStart(event, node.id); }} onDragEnd={(event) => { if (onFolderDragEnd) { onFolderDragEnd(event); } }} > {icon} {node.name} {node.id !== 'root' && (
    )}
    {isExpanded && node.children.length > 0 && ( )}
  • ); }; const Sidebar = ({ folderNodes, onToggle, onSelect, onDrop, onDragOver, onDragLeave, onDeleteFolder, onRenameFolder, selectedFolder, onFolderDragStart, onFolderDragEnd, draggedFolderId, tags = [], activeTagIds = [], onToggleTagFilter, correspondents = [], activeCorrespondentIds = [], onToggleCorrespondentFilter, onManageTags, onManageCorrespondents, searchQuery = '', onSearchChange, onSearchSubmit, onSearchClear, isFilterActive, appStatus, loading, previewActive, onLogout, status, onCollapse, tenantSlug, tenants = [], activeTenantId = null, onSelectTenant, }) => { const sortedCorrespondents = useMemo( () => [...correspondents].sort((a, b) => (a?.name || '').localeCompare(b?.name || '', undefined, { sensitivity: 'base' }), ), [correspondents], ); const activeCorrespondentSet = useMemo( () => new Set(activeCorrespondentIds || []), [activeCorrespondentIds], ); const handleToggleTag = onToggleTagFilter || (() => {}); const activeTagSet = new Set(activeTagIds); const handleManageTags = onManageTags || (() => {}); const handleManageCorrespondents = onManageCorrespondents || (() => {}); const handleSearchInputChange = useCallback( (event) => { onSearchChange?.(event.target.value); }, [onSearchChange], ); const handleSearchFormSubmit = useCallback( (event) => { event.preventDefault(); onSearchSubmit?.(); }, [onSearchSubmit], ); const handleSearchClear = useCallback(() => { onSearchClear?.(); }, [onSearchClear]); const tenantButtonRef = useRef(null); const tenantMenuRef = useRef(null); const [tenantMenuOpen, setTenantMenuOpen] = useState(false); const toggleTenantMenu = useCallback(() => { const next = !tenantMenuOpen; setTenantMenuOpen(next); if (next && tenants.length === 0 && onSelectTenant) { onSelectTenant(null, { refreshOnly: true }); } }, [tenantMenuOpen, tenants.length, onSelectTenant]); useEffect(() => { if (!tenantMenuOpen) { return undefined; } const handlePointer = (event) => { const menuNode = tenantMenuRef.current; const buttonNode = tenantButtonRef.current; if (!menuNode) return; if (menuNode.contains(event.target)) return; if (buttonNode && buttonNode.contains(event.target)) return; setTenantMenuOpen(false); }; const handleKeyDown = (event) => { if (event.key === 'Escape') { setTenantMenuOpen(false); } }; document.addEventListener('mousedown', handlePointer); document.addEventListener('touchstart', handlePointer); document.addEventListener('keydown', handleKeyDown); return () => { document.removeEventListener('mousedown', handlePointer); document.removeEventListener('touchstart', handlePointer); document.removeEventListener('keydown', handleKeyDown); }; }, [tenantMenuOpen]); const handleTenantSelect = useCallback( (tenant) => { const targetId = tenant?.tenant_id || tenant?.tenantId || tenant?.id || null; if (!targetId) { return; } setTenantMenuOpen(false); onSelectTenant?.(tenant); }, [onSelectTenant], ); const handleLogoutFromMenu = useCallback(() => { setTenantMenuOpen(false); onLogout?.(); }, [onLogout]); const renderNodes = useCallback( (ids, depth) => ids.map((id) => { const node = folderNodes.get(id); if (!node) return null; return ( onToggle(id)} onSelect={onSelect} onDrop={onDrop} onDragOver={onDragOver} onDragLeave={onDragLeave} onDelete={onDeleteFolder} onRename={onRenameFolder} renderChildren={renderNodes} onFolderDragStart={onFolderDragStart} onFolderDragEnd={onFolderDragEnd} draggingFolderId={draggedFolderId} /> ); }), [ folderNodes, selectedFolder, onToggle, onSelect, onDrop, onDragOver, onDragLeave, onDeleteFolder, onRenameFolder, onFolderDragStart, onFolderDragEnd, draggedFolderId, ], ); const rootNode = folderNodes.get('root'); return ( ); }; export default Sidebar; export { FolderNode };