import type { CSSProperties, DragEvent as ReactDragEvent, MouseEvent as ReactMouseEvent, ReactNode } from "react"; import { Copy, Download, FolderOpen, Home, MoveRight, Plus, Trash2, UploadCloud } from "lucide-react"; import { Button, Dialog, ExplorerTree, type ExplorerTreeNodeContext, i18nMessage } from "@govoplan/core-webui"; import type { ConflictAction, FileSpace, RenameResponse } from "../../../api/files"; import type { ConflictDialogState, FileActionTarget, FileConflictItem, FolderNode, ContextMenuState } from "../types"; import { isPathUnderOrSame, normalizeFolder, treeNodeKey } from "../utils/fileManagerUtils"; export function TransferFolderSelector({ space, nodes, selectedFolder, onSelect, disabled }: {space: FileSpace | null;nodes: FolderNode[];selectedFolder: string;onSelect: (folderPath: string) => void;disabled?: boolean;}) { return (
{nodes.length === 0 && i18n:govoplan-files.no_folders_yet_choose_the_root_folder.6430304d}
); } export function TransferFolderSelectorNodes({ nodes, selectedFolder, onSelect, disabled, depth = 1 }: {nodes: FolderNode[];selectedFolder: string;onSelect: (folderPath: string) => void;disabled?: boolean;depth?: number;}) { if (nodes.length === 0) return null; return (
{nodes.map((node) =>
)}
); } export function FolderTree({ nodes, activeSpaceId, spaceId, currentFolder, dropTargetKey = "", expandedKeys, onOpen, onToggle, onContextMenu, onDragOverTarget, onDropOnTarget, onClearDropState, onRequestDragExpand, onDragStartFolder, onDragEndFolder, dragDropEnabled = true, contextMenuEnabled = true, disabled, depth = 1 }: {nodes: FolderNode[];activeSpaceId: string;spaceId: string;currentFolder: string;dropTargetKey?: string;expandedKeys: Set;onOpen: (spaceId: string, path: string) => void;onToggle: (spaceId: string, path: string) => void;onContextMenu?: (event: ReactMouseEvent, spaceId: string, path: string) => void;onDragOverTarget?: (event: ReactDragEvent, target: FileActionTarget) => void;onDropOnTarget?: (event: ReactDragEvent, target: FileActionTarget) => Promise;onClearDropState?: () => void;onRequestDragExpand?: (spaceId: string, path: string) => void;onDragStartFolder?: (spaceId: string, path: string, event: ReactDragEvent) => void;onDragEndFolder?: () => void;dragDropEnabled?: boolean;contextMenuEnabled?: boolean;disabled?: boolean;depth?: number;}) { return ( treeNodeKey(spaceId, node.path)} getNodeLabel={(node) => node.name} getNodeChildren={(node) => node.children} activeId={activeSpaceId === spaceId ? treeNodeKey(spaceId, currentFolder) : ""} expandedIds={expandedKeys} onOpen={(node) => onOpen(spaceId, node.path)} onToggle={(node) => onToggle(spaceId, node.path)} disabled={disabled} depth={depth} getNodeWrapClassName={(node) => { const target = { spaceId, folderPath: node.path }; return dragDropEnabled && dropTargetKey === `${target.spaceId}:${normalizeFolder(target.folderPath)}` ? "is-drop-target" : undefined; }} getNodeWrapStyle={(_node, context: ExplorerTreeNodeContext) => ({ paddingLeft: `${Math.min(context.depth * 14, 56)}px` })} getNodeDraggable={() => dragDropEnabled && !disabled} onContextMenu={contextMenuEnabled && onContextMenu ? (event, node) => onContextMenu(event, spaceId, node.path) : undefined} onDragStart={dragDropEnabled && onDragStartFolder ? (event, node) => onDragStartFolder(spaceId, node.path, event) : undefined} onDragEnd={dragDropEnabled && onDragEndFolder ? () => onDragEndFolder() : undefined} onDragOver={dragDropEnabled && onDragOverTarget ? (event, node, context) => { const target = { spaceId, folderPath: node.path }; onDragOverTarget(event, target); if (context.hasChildren && !context.expanded) onRequestDragExpand?.(spaceId, node.path); } : undefined} onDragLeave={dragDropEnabled && onClearDropState ? () => onClearDropState() : undefined} onDrop={dragDropEnabled && onDropOnTarget ? (event, node) => void onDropOnTarget(event, { spaceId, folderPath: node.path }) : undefined} />); } export function RenamePreviewList({ response, selectedFileIds, selectedFolderPaths, recursive, visibleCount, onShowMore, onShowFewer }: {response: RenameResponse;selectedFileIds: Set;selectedFolderPaths: Set;recursive: boolean;visibleCount: number;onShowMore: () => void;onShowFewer: () => void;}) { const selectedFolderRoots = Array.from(selectedFolderPaths).map(normalizeFolder); const hiddenNonRecursiveItems = !recursive ? response.items.filter((item) => { if (item.kind === "file" && selectedFileIds.has(item.id)) return false; if (item.kind === "folder" && selectedFolderRoots.includes(normalizeFolder(item.old_path))) return false; return selectedFolderRoots.some((folder) => isPathUnderOrSame(item.old_path, folder)); }).length : 0; const visibleItems = response.items.filter((item) => { if (recursive) return true; if (item.kind === "file") { if (selectedFileIds.has(item.id)) return true; return !selectedFolderRoots.some((folder) => isPathUnderOrSame(item.old_path, folder)); } return selectedFolderRoots.includes(normalizeFolder(item.old_path)); }); const safeVisibleCount = Math.max(20, visibleCount); const shownItems = visibleItems.slice(0, safeVisibleCount); const remaining = visibleItems.length - shownItems.length; return (
{hiddenNonRecursiveItems > 0 && {hiddenNonRecursiveItems} i18n:govoplan-files.contained_path_s_will_move_with_the_selected_fol.b786f1a1} {shownItems.map((item) => {item.old_path}{item.new_path})} {remaining > 0 && } {shownItems.length > 20 && }
); } export function FileDialog({ title, onClose, children }: {title: string;onClose: () => void;children: ReactNode;}) { return ( {children} ); } export function FileContextMenu({ menu, hasSelection, canCreateFolder, canUpload, canDownload, canOrganize, canDelete, downloadLabel, onCreateFolder, onUpload, onDownload, onMove, onCopy, onDelete }: {menu: ContextMenuState;hasSelection: boolean;canCreateFolder: boolean;canUpload: boolean;canDownload: boolean;canOrganize: boolean;canDelete: boolean;downloadLabel: string;onCreateFolder: () => void;onUpload: () => void;onDownload: () => void;onMove: () => void;onCopy: () => void;onDelete: () => void;}) { const showNewFolder = true; const showDelete = menu.target !== "empty"; const viewportWidth = typeof window === "undefined" ? 1024 : window.innerWidth; const viewportHeight = typeof window === "undefined" ? 768 : window.innerHeight; const estimatedWidth = 220; const estimatedHeight = 260; const left = Math.max(8, Math.min(menu.x, viewportWidth - estimatedWidth - 8)); const openUp = menu.y + estimatedHeight > viewportHeight; const style: CSSProperties = openUp ? { left, bottom: Math.max(8, viewportHeight - menu.y) } : { left, top: Math.max(8, menu.y) }; return (
event.stopPropagation()}> {showNewFolder && } {showDelete && }
); } export function FileConflictDialog({ state, busy, onClose, onCancel, onOverwrite, onRename, onReview, onApplyReview, onUpdateItem }: {state: ConflictDialogState;busy: boolean;onClose: () => void;onCancel: () => void;onOverwrite: () => void;onRename: () => void;onReview: () => void;onApplyReview: () => void;onUpdateItem: (id: string, patch: Partial) => void;}) { return (

{state.message}

{state.items.length} i18n:govoplan-files.conflict_s.60cea6d5 {state.items.length > 1 ? "i18n:govoplan-files.choose_the_same_action_for_all_conflicts_or_revi.319cfe5f" : "i18n:govoplan-files.choose_how_to_resolve_this_conflict.c303fcc6"}
{state.review &&
{state.items.map((item) =>
{item.label} {item.kind === "folder" ? "i18n:govoplan-files.folder.30baa249" : "i18n:govoplan-files.file.2c3cafa4"} i18n:govoplan-files.conflict_at.0a91052f {item.targetPath}
onUpdateItem(item.id, { newPath: event.target.value })} disabled={busy || item.action !== "rename"} aria-label={i18nMessage("i18n:govoplan-files.new_path_for_value.a9a30c1c", { value0: item.label })} />
)}
} {!state.review &&
{state.items.slice(0, 8).map((item) => {item.targetPath})} {state.items.length > 8 && i18n:govoplan-files.and.a0d93385 {state.items.length - 8} more}
}
{!state.review && state.items.length > 1 && } {state.review && }
); }