diff --git a/react/src/components/AssetDetail/AssetButton.tsx b/react/src/components/AssetDetail/AssetButton.tsx index 72fbb035..f9f546dd 100644 --- a/react/src/components/AssetDetail/AssetButton.tsx +++ b/react/src/components/AssetDetail/AssetButton.tsx @@ -1,9 +1,15 @@ -import React from 'react'; +import React, { useState } from 'react'; import DOMPurify from 'dompurify'; import { Button } from '@tacc/core-components'; import { Feature, FeatureType } from '@hazmapper/types'; -import { getFeatureType, IFileImportRequest } from '@hazmapper/types'; -import { useImportFeatureAsset } from '@hazmapper/hooks'; +import { + getFeatureType, + IFileImportRequest, + TapisFilePath, +} from '@hazmapper/types'; +import { useImportFeatureAsset, useNotification } from '@hazmapper/hooks'; +import FileBrowserModal from '../FileBrowserModal/FileBrowserModal'; +import { IMPORTABLE_FEATURE_ASSET_TYPES } from '@hazmapper/utils/fileUtils'; type AssetButtonProps = { selectedFeature: Feature; @@ -19,27 +25,35 @@ const AssetButton: React.FC = ({ onQuestionnaireClick, }) => { const pointCloudURL = DOMPurify.sanitize(featureSource + '/index.html'); - + const [isModalOpen, setIsModalOpen] = useState(false); const featureType = getFeatureType(selectedFeature); const projectId = selectedFeature.project_id; + const notification = useNotification(); const featureId = selectedFeature.id; - const { - mutate: importFeatureAsset, - isPending: isImporting, - isSuccess: isImportingSuccess, - } = useImportFeatureAsset(projectId, featureId); + const { mutate: importFeatureAsset, isPending: isImporting } = + useImportFeatureAsset(projectId, featureId); - const handleImportFeatureAsset = (importData: IFileImportRequest) => { - importFeatureAsset(importData); - }; - const handleSubmit = () => { - const importData: IFileImportRequest = { - /*TODO Replace with passed in values from - FileBrowserModal. These are hardcoded to test.*/ - system_id: 'project-4072868216578445806-242ac117-0001-012', - path: 'images_good/image.jpg', - }; - handleImportFeatureAsset(importData); + const handleSubmit = (files: TapisFilePath[]) => { + for (const file of files) { + const importData: IFileImportRequest = { + system_id: file.system, + path: file.path, + }; + importFeatureAsset(importData, { + onSuccess: () => { + setIsModalOpen(false); + notification.success({ + description: `Your asset import for feature ${selectedFeature.id} was successful.`, + }); + }, + onError: (error) => { + setIsModalOpen(false); + notification.success({ + description: `There was an error importing your asset for feature ${selectedFeature.id}. Error: ${error}`, + }); + }, + }); + } }; return ( @@ -61,13 +75,23 @@ const AssetButton: React.FC = ({ //TODO )} + {isModalOpen && ( + setIsModalOpen(false)} + onImported={handleSubmit} + allowedFileExtensions={IMPORTABLE_FEATURE_ASSET_TYPES} + isSingleSelectMode={true} + singleSelectErrorMessage="Adding multiple assets to a feature is not supported." + /> + )} ); }; diff --git a/react/src/components/AssetDetail/AssetDetail.tsx b/react/src/components/AssetDetail/AssetDetail.tsx index 0292cf3b..776dcd01 100644 --- a/react/src/components/AssetDetail/AssetDetail.tsx +++ b/react/src/components/AssetDetail/AssetDetail.tsx @@ -53,18 +53,24 @@ const AssetDetail: React.FC = ({
}> -
- -
- + {featureType == FeatureType.Collection ? ( +
Collections of assets not supported
+ ) : ( + <> +
+ +
+ + + )}
{featureType !== FeatureType.Questionnaire && ( diff --git a/react/src/components/FileBrowserModal/FileBrowserModal.tsx b/react/src/components/FileBrowserModal/FileBrowserModal.tsx index 04eca824..4e94e1de 100644 --- a/react/src/components/FileBrowserModal/FileBrowserModal.tsx +++ b/react/src/components/FileBrowserModal/FileBrowserModal.tsx @@ -1,14 +1,17 @@ import React, { useState } from 'react'; -import { Modal, Button, Layout, Typography } from 'antd'; +import { Modal, Button, Layout, Typography, Flex } from 'antd'; import { FileListing } from '../Files'; import { File, TapisFilePath } from '@hazmapper/types'; import { convertFilesToTapisPaths } from '@hazmapper/utils/fileUtils'; +import { SectionMessage } from '@tacc/core-components'; type FileBrowserModalProps = { isOpen: boolean; toggle: () => void; onImported?: (files: TapisFilePath[]) => void; allowedFileExtensions: string[]; + singleSelectErrorMessage?: string; + isSingleSelectMode?: boolean; }; const { Content, Header } = Layout; @@ -19,9 +22,10 @@ const FileBrowserModal = ({ toggle: parentToggle, onImported, allowedFileExtensions = [], + isSingleSelectMode = false, + singleSelectErrorMessage = '', }: FileBrowserModalProps) => { const [selectedFiles, setSelectedFiles] = useState([]); - const handleClose = () => { parentToggle(); }; @@ -40,12 +44,27 @@ const FileBrowserModal = ({ return ( Select Files} + title={ +
+ Select File{!isSingleSelectMode && 's'} +
+ } open={isOpen} onCancel={handleClose} footer={[ - {selectedFiles.length > 0 && `${selectedFiles.length} files selected`} + + {isSingleSelectMode && selectedFiles.length > 1 && ( + + {singleSelectErrorMessage} + + )} + + {isSingleSelectMode &&
You may only import one file.
} + {selectedFiles.length > 0 && + `${selectedFiles.length} files selected`} +
+
, , @@ -71,7 +93,7 @@ const FileBrowserModal = ({
Note: Only files are selectable, not folders. Double-click on a folder - to navigate into it. + to navigate into it.{' '}
{ + const queryClient = useQueryClient(); const endpoint = `/projects/${projectId}/features/${featureId}/assets/`; return usePost({ endpoint, apiService: ApiService.Geoapi, + options: { + onSuccess: () => { + // Invalidate the main features query to get updated feature list + // This isn't done in a celery task, so onSuccess works to invalidate + queryClient.invalidateQueries({ + queryKey: [KEY_USE_FEATURES], + }); + }, + }, }); };