diff --git a/frontend/src/components/CsvFileBrowser.jsx b/frontend/src/components/CsvFileBrowser.jsx index 9ad74f2..112ef6e 100644 --- a/frontend/src/components/CsvFileBrowser.jsx +++ b/frontend/src/components/CsvFileBrowser.jsx @@ -59,7 +59,8 @@ import { FaCalendar, FaDatabase, FaSync, - FaCog + FaCog, + FaCopy } from 'react-icons/fa' import * as api from '../services/api' @@ -348,6 +349,15 @@ function FileTree({ tree, selectedFiles, onFileToggle, expandedItems, onToggleEx onClick={() => onFileToggle(fileNode, 'excel')} /> + + } + colorScheme="purple" + variant="outline" + onClick={() => onFileToggle(fileNode, 'copy')} + /> + @@ -457,6 +467,40 @@ export default function CsvFileBrowser() { duration: 3000 }) } + } else if (action === 'copy') { + // Copy path to clipboard + try { + await navigator.clipboard.writeText(fileNode.path) + toast({ + title: '📋 Path copied', + description: `File path copied to clipboard`, + status: 'success', + duration: 2000 + }) + } catch (error) { + // Fallback for older browsers + try { + const textArea = document.createElement('textarea') + textArea.value = fileNode.path + document.body.appendChild(textArea) + textArea.select() + document.execCommand('copy') + document.body.removeChild(textArea) + toast({ + title: '📋 Path copied', + description: `File path copied to clipboard`, + status: 'success', + duration: 2000 + }) + } catch (fallbackError) { + toast({ + title: '❌ Failed to copy path', + description: 'Clipboard access not available', + status: 'error', + duration: 3000 + }) + } + } } else { // Toggle selection const newSelected = new Set(selectedFiles) @@ -469,6 +513,56 @@ export default function CsvFileBrowser() { } } + // Copy selected file paths + const copySelectedPaths = async () => { + if (selectedFiles.size === 0) { + toast({ + title: '⚠️ No files selected', + description: 'Please select files to copy their paths', + status: 'warning', + duration: 2000 + }) + return + } + + try { + const filePaths = Array.from(selectedFiles) + const pathsText = filePaths.join('\n') + await navigator.clipboard.writeText(pathsText) + toast({ + title: '📋 Paths copied', + description: `${filePaths.length} file path(s) copied to clipboard`, + status: 'success', + duration: 2000 + }) + } catch (error) { + // Fallback for older browsers + try { + const filePaths = Array.from(selectedFiles) + const pathsText = filePaths.join('\n') + const textArea = document.createElement('textarea') + textArea.value = pathsText + document.body.appendChild(textArea) + textArea.select() + document.execCommand('copy') + document.body.removeChild(textArea) + toast({ + title: '📋 Paths copied', + description: `${filePaths.length} file path(s) copied to clipboard`, + status: 'success', + duration: 2000 + }) + } catch (fallbackError) { + toast({ + title: '❌ Failed to copy paths', + description: 'Clipboard access not available', + status: 'error', + duration: 3000 + }) + } + } + } + // Launch PlotJuggler with selected files const launchSelectedFiles = async () => { if (selectedFiles.size === 0) { @@ -692,6 +786,14 @@ export default function CsvFileBrowser() { > Open in PlotJuggler +