"use client"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { useEffect, useState, useCallback, useRef } from "react"; import { Folder, FolderOpen, ChevronRight } from "lucide-react"; import { FOLDERS_CHANGED_EVENT, handleInternalDrop, hasInternalDrag, readFolderId } from "../files/dnd"; interface TreeFolder { id: string; name: string; sharedSpace?: { id: string; name: string } | null; children?: TreeFolder[]; _count?: { files: number }; } interface FolderTreeProps { onNavigate?: () => void; } const EXPANDED_KEY = "spb-tree-expanded"; export default function FolderTree({ onNavigate }: FolderTreeProps) { const pathname = usePathname(); const [tree, setTree] = useState([]); const [expanded, setExpanded] = useState>(new Set()); const [dragOverId, setDragOverId] = useState(null); const hoverTimer = useRef | null>(null); const load = useCallback(() => { fetch("/api/folders?tree=true") .then((r) => (r.ok ? r.json() : [])) .then((d) => setTree(Array.isArray(d) ? d : [])) .catch(() => {}); }, []); useEffect(() => { load(); try { const saved = JSON.parse(localStorage.getItem(EXPANDED_KEY) || "[]"); if (Array.isArray(saved)) setExpanded(new Set(saved)); } catch {} window.addEventListener(FOLDERS_CHANGED_EVENT, load); return () => window.removeEventListener(FOLDERS_CHANGED_EVENT, load); }, [load]); // Déplier automatiquement le chemin du dossier courant useEffect(() => { const m = pathname.match(/^\/dashboard\/folder\/([^/]+)/); if (!m || tree.length === 0) return; const target = m[1]; const path: string[] = []; const find = (nodes: TreeFolder[], acc: string[]): boolean => { for (const n of nodes) { if (n.id === target) { path.push(...acc); return true; } if (n.children?.length && find(n.children, [...acc, n.id])) return true; } return false; }; find(tree, []); if (path.length) setExpanded((prev) => new Set([...Array.from(prev), ...path])); }, [pathname, tree]); const toggle = useCallback((id: string) => { setExpanded((prev) => { const next = new Set(prev); next.has(id) ? next.delete(id) : next.add(id); try { localStorage.setItem(EXPANDED_KEY, JSON.stringify(Array.from(next))); } catch {} return next; }); }, []); const clearHover = () => { if (hoverTimer.current) { clearTimeout(hoverTimer.current); hoverTimer.current = null; } }; function renderNode(node: TreeFolder, depth: number): React.ReactNode { const kids = node.children ?? []; const hasKids = kids.length > 0; const isOpen = expanded.has(node.id); const active = pathname === `/dashboard/folder/${node.id}`; const over = dragOverId === node.id; return (
{depth > 0 && }
{ if (!hasInternalDrag(e.dataTransfer)) return; e.preventDefault(); setDragOverId(node.id); clearHover(); if (hasKids && !isOpen) hoverTimer.current = setTimeout(() => setExpanded((p) => new Set([...Array.from(p), node.id])), 600); }} onDragOver={(e) => { if (!hasInternalDrag(e.dataTransfer)) return; e.preventDefault(); e.dataTransfer.dropEffect = "move"; if (dragOverId !== node.id) setDragOverId(node.id); }} onDragLeave={(e) => { const next = e.relatedTarget as Node | null; if (next && e.currentTarget.contains(next)) return; clearHover(); setDragOverId((cur) => (cur === node.id ? null : cur)); }} onDrop={async (e) => { if (!hasInternalDrag(e.dataTransfer)) return; e.preventDefault(); e.stopPropagation(); clearHover(); setDragOverId(null); if (readFolderId(e.dataTransfer) === node.id) return; const moved = await handleInternalDrop(e.dataTransfer, node.id); if (moved) { window.dispatchEvent(new Event(FOLDERS_CHANGED_EVENT)); window.dispatchEvent(new Event("spb:files-changed")); } }} > {(isOpen && hasKids) || over ? ( ) : ( )} {node.name} {node._count?.files ? {node._count.files} : null}
{hasKids && isOpen &&
{kids.map((c) => renderNode(c, depth + 1))}
}
); } if (tree.length === 0) return

Aucun dossier

; // Regroupement des racines par espace partagé (dossiers personnels d'abord) const groups = new Map(); for (const n of tree) { const key = n.sharedSpace?.id ?? "__perso"; if (!groups.has(key)) groups.set(key, { label: n.sharedSpace?.name ?? null, nodes: [] }); groups.get(key)!.nodes.push(n); } const ordered = Array.from(groups.entries()).sort(([a, ga], [b, gb]) => (a === "__perso" ? -1 : b === "__perso" ? 1 : (ga.label ?? "").localeCompare(gb.label ?? "", "fr"))); if (ordered.length <= 1) return
{tree.map((n) => renderNode(n, 0))}
; return (
{ordered.map(([key, g]) => (
{g.label ?? "Mes dossiers"}
{g.nodes.map((n) => renderNode(n, 0))}
))}
); }