import type { Connection, Folder, Tag } from "../../lib/types"; import { Folder as FolderIcon, Check, Pencil, Trash2 } from "lucide-react"; import { useDroppable } from "@dnd-kit/core"; import { ConnectionCard } from "./ConnectionCard"; import { FolderBreadcrumb } from "../folders/FolderBreadcrumb"; import { getChildFolders, getDescendantFolderIds } from "../../lib/utils"; import { useUiStore } from "../../stores/uiStore"; import { useConnectionStore } from "../../stores/connectionStore"; import { TagBadge } from "../tags/TagBadge"; interface DroppableFolderCardProps { folder: Folder; isSelected: boolean; count: number; subfolderCount: number; folderTags: Tag[]; onFolderClick: (id: string) => void; onToggleSelection: (id: string) => void; } function DroppableFolderCard({ folder, isSelected, count, subfolderCount, folderTags, onFolderClick, onToggleSelection, }: DroppableFolderCardProps) { const { setNodeRef, isOver } = useDroppable({ id: `folder-${folder.id}`, data: { type: "folder", folder }, }); return (
); } interface ConnectionGridProps { connections: Connection[]; tags: Tag[]; folders?: Folder[]; activeFolderId?: string | null; onFolderSelect?: (id: string | null) => void; hasSearch?: boolean; onTagToggle?: (id: string) => void; onEditFolder?: (folder: Folder) => void; onDeleteFolder?: (folder: Folder) => void; onOpenDbViewer?: (connectionId: string) => void; onEditConnection?: (conn: Connection) => void; onDuplicateConnection?: (conn: Connection) => void; onDeleteConnection?: (conn: Connection) => void; } export function ConnectionGrid({ connections, tags, folders = [], activeFolderId = null, onFolderSelect, hasSearch = false, onTagToggle, onEditFolder, onDeleteFolder, onOpenDbViewer, onEditConnection, onDuplicateConnection, onDeleteConnection, }: ConnectionGridProps) { const selectedItemIds = useUiStore((s) => s.selectedItemIds); const toggleItemSelection = useUiStore((s) => s.toggleItemSelection); const clearSelection = useUiStore((s) => s.clearSelection); const activeTagIds = useUiStore((s) => s.activeTagIds); const activeDbTypes = useUiStore((s) => s.activeDbTypes); const activeEnvironment = useUiStore((s) => s.activeEnvironment); const currentFolderId = hasSearch ? null : activeFolderId !== null && folders.some((f) => f.id === activeFolderId) ? activeFolderId : null; const hasActiveFilters = activeTagIds.length > 0 || activeDbTypes.length > 0 || (activeEnvironment !== null && activeEnvironment !== undefined); const connectionMatchesFilters = (c: Connection) => { if (activeTagIds.length > 0 && !c.tag_ids.some((id) => activeTagIds.includes(id))) { return false; } if (activeDbTypes.length > 0 && !activeDbTypes.includes(c.db_type)) { return false; } if (activeEnvironment !== null && activeEnvironment !== undefined && c.environment !== activeEnvironment) { return false; } return true; }; const visibleFolders = hasSearch ? [] : getChildFolders(folders, currentFolderId).filter((f) => { if (!hasActiveFilters) return true; const folderMatchesTags = f.tag_ids.some((id) => activeTagIds.includes(id)); if (folderMatchesTags) return true; const subIds = new Set(getDescendantFolderIds(folders, f.id)); return connections.some( (c) => c.folder_id !== null && subIds.has(c.folder_id) && connectionMatchesFilters(c), ); }); const directConnections = hasSearch ? connections : connections.filter((c) => c.folder_id === currentFolderId); const allStoreConnections = useConnectionStore((s) => s.connections); const allStoreFolders = useConnectionStore((s) => s.folders); // Check if any direct connections OR any subfolder has connections anywhere below const hasItems = visibleFolders.length > 0 || directConnections.length > 0 || (currentFolderId && allStoreConnections.some((c) => { const allowed = new Set(getDescendantFolderIds(allStoreFolders, currentFolderId)); return c.folder_id !== null && allowed.has(c.folder_id); })); const isSelecting = selectedItemIds.length > 0; const activeFolder = currentFolderId ? (folders.find((f) => f.id === currentFolderId) ?? null) : null; const handleFolderClick = (folderId: string) => { if (isSelecting) { toggleItemSelection(folderId); } else { onFolderSelect?.(folderId); } }; const handleBreadcrumbNavigate = (folderId: string | null) => { clearSelection(); onFolderSelect?.(folderId); }; return (
useUiStore.getState().clearFilters()} /> {activeFolder && (
)}
{!hasItems ? (
{hasSearch ? "No connections match your search." : activeFolderId ? "This folder is empty. Add a connection or subfolder." : "No connections yet. Create one to get started."}
) : (
{visibleFolders.map((f) => { const isSelected = selectedItemIds.includes(f.id); // Count all connections in this subfolder (including nested descendants) const subIds = new Set(getDescendantFolderIds(folders, f.id)); const count = allStoreConnections.filter( (c) => c.folder_id !== null && subIds.has(c.folder_id), ).length; const subfolderCount = getChildFolders( folders, f.id, ).length; const tagMap = new Map(tags.map((t) => [t.id, t])); const folderTags = f.tag_ids .map((id) => tagMap.get(id)) .filter(Boolean) as Tag[]; return ( ); })} {directConnections.map((c) => ( ))}
)}
); }