diff --git a/frontend/src/components/listeners/SubmitExtraction.tsx b/frontend/src/components/listeners/SubmitExtraction.tsx index af000be69..d3768c337 100644 --- a/frontend/src/components/listeners/SubmitExtraction.tsx +++ b/frontend/src/components/listeners/SubmitExtraction.tsx @@ -25,7 +25,7 @@ import { RootState } from "../../types/data"; import { EventListenerOut as Extractor } from "../../openapi/v2"; import { ClowderRjsfSelectWidget } from "../styledComponents/ClowderRjsfSelectWidget"; import { ClowderRjsfTextWidget } from "../styledComponents/ClowderRjsfTextWidget"; -import { ClowderFileSelector } from "../styledComponents/ClowderFileSelector"; +import { ClowderFileSystemSelector } from "../styledComponents/ClowderFileSystemSelector"; import { ClowderImageAnnotator } from "../styledComponents/ClowderImageAnnotator"; import ExtractorStatus from "./ExtractorStatus"; import CloseIcon from "@mui/icons-material/Close"; @@ -43,7 +43,7 @@ type SubmitExtractionProps = { const widgets = { TextWidget: ClowderRjsfTextWidget, SelectWidget: ClowderRjsfSelectWidget, - ClowderFile: ClowderFileSelector, + ClowderFile: ClowderFileSystemSelector, ImageAnnotator: ClowderImageAnnotator, }; diff --git a/frontend/src/components/navigation/FileSelector.tsx b/frontend/src/components/navigation/FileSystemSelector.tsx similarity index 59% rename from frontend/src/components/navigation/FileSelector.tsx rename to frontend/src/components/navigation/FileSystemSelector.tsx index 24ac657d2..cb5253616 100644 --- a/frontend/src/components/navigation/FileSelector.tsx +++ b/frontend/src/components/navigation/FileSystemSelector.tsx @@ -24,30 +24,35 @@ import TextIcon from "@mui/icons-material/Description"; import { fetchDatasets } from "../../actions/dataset"; import { V2 } from "../../openapi"; -// Define the file details -interface FileDetails { - fileId: string; - fileName: string; +interface SelectionDetails { + selectionID: string; + selectionName: string; + selectionType: string; + datasetId?: string; } -// Define the RecursiveComponent component with props type interface RecursiveComponentProps { item: FSItem; depth?: number; - onSelectFile: (fileId: string, fileName: string) => void; + onHighlightSelection: ( + selectionID: string, + selectionName: string, + datasetId: string, + selectionType: string + ) => void; + highlightedSelectionId: string; + selectFolder: boolean; } -// Define a type for items in the directory structure interface FSItem { datasetId: string; id?: string; label: string; children?: FSItem[] | undefined; - type: string; // A FSItem can be a folder or a file, + type: string; content_type?: string; } -// Function to fetch children of a folder async function fetchFolderFiles( datasetid: string, folderId: string | undefined @@ -57,7 +62,6 @@ async function fetchFolderFiles( await V2.DatasetsService.getDatasetFoldersAndFilesApiV2DatasetsDatasetIdFoldersAndFilesGet( datasetid, folderId, - // TODO: Remove hardcoded values 0, 3000 ); @@ -85,13 +89,15 @@ async function fetchFolderFiles( const RecursiveComponent: React.FC = ({ item, depth = 0, - onSelectFile, + onHighlightSelection, + highlightedSelectionId, + selectFolder, }) => { const [expanded, setExpanded] = useState(false); const [children, setChildren] = useState(item.children); const isFolderOrDataset = item.type === "folder" || item.type === "dataset"; + const isHighlighted = item.id === highlightedSelectionId; - // Function to generate Icon based on item type const getIcon = () => { if (item.type === "folder") { return ; @@ -112,32 +118,35 @@ const RecursiveComponent: React.FC = ({ } }; - // Function to handle selection of folder or file - const onSelect = () => { + const expandFolder = () => { if (isFolderOrDataset) { - if (!expanded) { - fetchFolderFiles(item.datasetId, item.id).then((data) => { - setChildren(data); - }); - } + fetchFolderFiles(item.datasetId, item.id).then((data) => { + setChildren(data); + }); setExpanded(!expanded); - } else { - if (item.id !== undefined) { - onSelectFile(item.id, item.label); - } + } + }; + + const onSelect = () => { + if (item.id !== undefined) { + onHighlightSelection(item.id, item.label, item.datasetId, item.type); } }; return ( - {/* Indentation of item proportional to depth */} = ({ = ({ key={child.id} item={child} depth={depth + 1} - onSelectFile={onSelectFile} + onHighlightSelection={onHighlightSelection} + highlightedSelectionId={highlightedSelectionId} + selectFolder={selectFolder} /> ))} @@ -177,22 +189,26 @@ const RecursiveComponent: React.FC = ({ }; const FileSystemViewer: React.FC<{ - onSelectFile: (fileId: string, fileName: string) => void; -}> = ({ onSelectFile }) => { + onHighlightSelection: ( + selectionID: string, + selectionName: string, + datasetId: string, + selectionType: string + ) => void; + highlightedSelectionId: string; + selectFolder: boolean; +}> = ({ onHighlightSelection, highlightedSelectionId, selectFolder }) => { const dispatch = useDispatch(); const datasets = useSelector((state: any) => state.dataset.datasets); const [FSItems, setFSItems] = useState([]); - // API function call to get Datasets const listDatasets = (skip?: number, limit?: number, mine?: boolean) => { dispatch(fetchDatasets(skip, limit, mine)); }; - // Fetch datasets on component mount useEffect(() => { - // TODO: Remove hardcoded values for skip and limit listDatasets(0, 3000, true); - }, []); // + }, []); useEffect(() => { if (datasets.data) { @@ -219,13 +235,15 @@ const FileSystemViewer: React.FC<{ }} > - File Selector + {selectFolder ? "Select a Folder" : "Select a File"} {FSItems.map((FSItem) => ( ))} @@ -233,12 +251,23 @@ const FileSystemViewer: React.FC<{ }; const DatasetFileViewer: React.FC<{ - onSelectFile: (fileId: string, fileName: string) => void; + onHighlightSelection: ( + selectionID: string, + selectionName: string, + datasetId: string, + selectionType: string + ) => void; + highlightedSelectionId: string; datasetId: string; -}> = ({ onSelectFile, datasetId }) => { + selectFolder: boolean; +}> = ({ + onHighlightSelection, + highlightedSelectionId, + datasetId, + selectFolder, +}) => { const [FSItems, setFSItems] = useState([]); - // Only display contents of the passed dataset useEffect(() => { fetchFolderFiles(datasetId, undefined).then((data) => { setFSItems(data); @@ -257,44 +286,88 @@ const DatasetFileViewer: React.FC<{ }} > - File Selector + {selectFolder ? "Select a Folder" : "Select a File"} {FSItems.map((FSItem) => ( ))} ) : null; }; -const FileSelector: React.FC<{ +const FileSystemSelector: React.FC<{ showOnlyDatasetFiles: boolean; + selectFolder: boolean; datasetId: string | undefined; - onChange: (fileId: string) => void; -}> = ({ showOnlyDatasetFiles, datasetId, onChange }) => { + onChange: (SelectionDetails: string) => void; +}> = ({ showOnlyDatasetFiles, selectFolder, datasetId, onChange }) => { const [open, setOpen] = useState(false); - const [selectedFile, setSelectedFile] = useState({ - fileId: "", - fileName: "", + const [selection, setSelection] = useState({ + selectionID: "", + selectionName: "", + datasetId: "", + selectionType: "", }); + const [highlightedSelection, setHighlightedFile] = useState( + { + selectionID: "", + selectionName: "", + datasetId: "", + selectionType: "", + } + ); const handleOpen = () => setOpen(true); - const handleClose = () => setOpen(false); + const handleClose = () => { + setHighlightedFile({ + selectionID: "", + selectionName: "", + selectionType: "", + }); + setOpen(false); + }; + + const handleHighlight = ( + selectionID: string, + selectionName: string, + datasetId: string, + selectionType: string + ) => { + setHighlightedFile({ + selectionID, + selectionName, + datasetId, + selectionType, + }); + }; - const handleFileSelect = (fileId: string, fileName: string) => { - setSelectedFile({ fileId: fileId, fileName: fileName }); - onChange(fileId); - handleClose(); + const handleConfirmSelection = () => { + if (highlightedSelection.selectionID) { + setSelection(highlightedSelection); + const selection = { + selectionID: highlightedSelection.selectionID, + selectionName: highlightedSelection.selectionName, + datasetId: highlightedSelection.datasetId, + selectionType: highlightedSelection.selectionType, + }; + // Convert to string + const selectionString = JSON.stringify(selection); + onChange(selectionString); + handleClose(); + } }; return ( - {selectedFile.fileName && ( + {selection.selectionName && ( - {selectedFile.fileName} + {selection.selectionName} )} {showOnlyDatasetFiles ? ( ) : ( - + )} + + + ); }; -export default FileSelector; +export default FileSystemSelector; diff --git a/frontend/src/components/styledComponents/ClowderFileSelector.jsx b/frontend/src/components/styledComponents/ClowderFileSystemSelector.jsx similarity index 65% rename from frontend/src/components/styledComponents/ClowderFileSelector.jsx rename to frontend/src/components/styledComponents/ClowderFileSystemSelector.jsx index 0f779979a..2efe4641c 100644 --- a/frontend/src/components/styledComponents/ClowderFileSelector.jsx +++ b/frontend/src/components/styledComponents/ClowderFileSystemSelector.jsx @@ -1,20 +1,22 @@ import React from "react"; -import FileSelectorButton from "../navigation/FileSelector"; +import FileSystemSelectorButton from "../navigation/FileSystemSelector"; import { ClowderInputLabel } from "./ClowderInputLabel"; -export const ClowderFileSelector = (item) => { +export const ClowderFileSystemSelector = (item) => { const handleChange = (value) => { item.onChange(value); }; const datasetId = item.options.datasetId; const showOnlyDatasetFiles = item.schema.showOnlyDatasetFiles ? true : false; + const selectFolder = item.schema.selectFolder ? true : false; return ( <> {item.schema.title} - );