15768: collection copy works except dialog Arvados-DCO-1.1-Signed-off-by: Lisa Knox...
authorLisa Knox <lisaknox83@gmail.com>
Wed, 6 Sep 2023 21:08:25 +0000 (17:08 -0400)
committerLisa Knox <lisaknox83@gmail.com>
Wed, 6 Sep 2023 21:08:25 +0000 (17:08 -0400)
src/components/collection-panel-files/collection-panel-files.tsx
src/components/data-explorer/data-explorer.tsx
src/components/data-table/data-table.tsx
src/components/multiselect-toolbar/MultiselectToolbar.tsx
src/store/collections/collection-copy-actions.ts
src/store/copy-dialog/copy-dialog.ts
src/store/workbench/workbench-actions.ts
src/views-components/context-menu/action-sets/collection-action-set.ts
src/views-components/data-explorer/data-explorer.tsx
src/views-components/dialog-copy/dialog-copy.tsx
src/views-components/dialog-forms/copy-collection-dialog.ts

index 95dd4d4c43321fbaaec9a82e8d7a25774ca96432..0fe933f5738cfaca30ac7b816c873644a0382062 100644 (file)
@@ -278,7 +278,6 @@ export const CollectionPanelFiles = withStyles(styles)(
                             }
 
                             pathPromise[key] = true;
-                            console.log(key);
 
                             return webdavClient.propfind(`c=${key}`, webDAVRequestConfig);
                         }
index f0f30c81a842ad820812bfdbdcd88add3a4a5de2..91d7dc77418d0b026e23711172ab58e7311be60e 100644 (file)
@@ -83,6 +83,7 @@ interface DataExplorerDataProps<T> {
     currentItemUuid: string;
     elementPath?: string;
     isMSToolbarVisible: boolean;
+    checkedList: TCheckedList;
 }
 
 interface DataExplorerActionProps<T> {
@@ -188,6 +189,7 @@ export const DataExplorer = withStyles(styles)(
                 elementPath,
                 toggleMSToolbar,
                 setCheckedListOnStore,
+                checkedList,
             } = this.props;
             return (
                 <Paper
@@ -288,6 +290,7 @@ export const DataExplorer = withStyles(styles)(
                                 currentRoute={paperKey}
                                 toggleMSToolbar={toggleMSToolbar}
                                 setCheckedListOnStore={setCheckedListOnStore}
+                                checkedList={checkedList}
                             />
                         </Grid>
                         <Grid
index ae68a6bcf2ecbde4d552a420cb1447205bcd3ba9..c82f2ce36ee55a6f5ce46d757b8fffae81615640 100644 (file)
@@ -53,6 +53,7 @@ export interface DataTableDataProps<I> {
     currentRoute?: string;
     toggleMSToolbar: (isVisible: boolean) => void;
     setCheckedListOnStore: (checkedList: TCheckedList) => void;
+    checkedList: TCheckedList;
 }
 
 type CssRules =
@@ -141,7 +142,7 @@ export const DataTable = withStyles(styles)(
     class Component<T> extends React.Component<DataTableProps<T>> {
         state: DataTableState = {
             isSelected: false,
-            checkedList: {},
+            checkedList: this.props.checkedList,
         };
 
         componentDidMount(): void {
index b726cfa08aceb2d61701bfd8581402874a454cd9..8c36f60f84d064c3681099bcab3871610fe57efe 100644 (file)
@@ -176,6 +176,7 @@ function selectActionsByKind(currentResourceKinds: Array<string>, filterSet: TMu
 
 function mapStateToProps(state: RootState) {
     const { isVisible, checkedList } = state.multiselect;
+    // console.log("checkedList", checkedList); //here
     return {
         isVisible: isVisible,
         checkedList: checkedList as TCheckedList,
@@ -201,7 +202,6 @@ function mapDispatchToProps(dispatch: Dispatch) {
                 default:
                     for (const kind in kindGroups) {
                         const action = findActionByName(selectedAction.name as string, kindToActionSet[kind]);
-                        console.log(action, kindGroups[kind]);
                         if (action) action.execute(dispatch, kindGroups[kind]);
                     }
                     break;
index eb9c64fdcc576a6cdb7a44b5af051b99b85977cb..fe204aadfb27bb4a9ba3c35963e9cbb57c8fb559 100644 (file)
@@ -4,52 +4,56 @@
 
 import { Dispatch } from "redux";
 import { dialogActions } from "store/dialog/dialog-actions";
-import { FormErrors, initialize, startSubmit, stopSubmit } from 'redux-form';
-import { resetPickerProjectTree } from 'store/project-tree-picker/project-tree-picker-actions';
-import { RootState } from 'store/store';
-import { ServiceRepository } from 'services/services';
-import { getCommonResourceServiceError, CommonResourceServiceError } from 'services/common-service/common-resource-service';
-import { CopyFormDialogData } from 'store/copy-dialog/copy-dialog';
+import { FormErrors, initialize, startSubmit, stopSubmit } from "redux-form";
+import { resetPickerProjectTree } from "store/project-tree-picker/project-tree-picker-actions";
+import { RootState } from "store/store";
+import { ServiceRepository } from "services/services";
+import { getCommonResourceServiceError, CommonResourceServiceError } from "services/common-service/common-resource-service";
+import { CopyFormDialogData } from "store/copy-dialog/copy-dialog";
 import { progressIndicatorActions } from "store/progress-indicator/progress-indicator-actions";
-import { initProjectsTreePicker } from 'store/tree-picker/tree-picker-actions';
+import { initProjectsTreePicker } from "store/tree-picker/tree-picker-actions";
 import { getResource } from "store/resources/resources";
 import { CollectionResource } from "models/collection";
 
-export const COLLECTION_COPY_FORM_NAME = 'collectionCopyFormName';
+export const COLLECTION_COPY_FORM_NAME = "collectionCopyFormName";
 
-export const openCollectionCopyDialog = (resource: { name: string, uuid: string }) =>
-    (dispatch: Dispatch) => {
-        dispatch<any>(resetPickerProjectTree());
-        dispatch<any>(initProjectsTreePicker(COLLECTION_COPY_FORM_NAME));
-        const initialData: CopyFormDialogData = { name: `Copy of: ${resource.name}`, ownerUuid: '', uuid: resource.uuid };
-        dispatch<any>(initialize(COLLECTION_COPY_FORM_NAME, initialData));
-        dispatch(dialogActions.OPEN_DIALOG({ id: COLLECTION_COPY_FORM_NAME, data: {} }));
-    };
+export const openCollectionCopyDialog = (resource: { name: string; uuid: string; isSingle?: boolean }) => (dispatch: Dispatch) => {
+    dispatch<any>(resetPickerProjectTree());
+    dispatch<any>(initProjectsTreePicker(COLLECTION_COPY_FORM_NAME));
+    const initialData: CopyFormDialogData = { name: `Copy of: ${resource.name}`, ownerUuid: "", uuid: resource.uuid, isSingle: resource.isSingle };
+    dispatch<any>(initialize(COLLECTION_COPY_FORM_NAME, initialData));
+    dispatch(dialogActions.OPEN_DIALOG({ id: COLLECTION_COPY_FORM_NAME, data: {} }));
+};
 
-export const copyCollection = (resource: CopyFormDialogData) =>
-    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+export const copyCollection =
+    (resource: CopyFormDialogData) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         dispatch(startSubmit(COLLECTION_COPY_FORM_NAME));
         let collection = getResource<CollectionResource>(resource.uuid)(getState().resources);
         try {
             if (!collection) {
                 collection = await services.collectionService.get(resource.uuid);
             }
-            const collManifestText = await services.collectionService.get(resource.uuid, undefined, ['manifestText']);
+            const collManifestText = await services.collectionService.get(resource.uuid, undefined, ["manifestText"]);
             collection.manifestText = collManifestText.manifestText;
-            const {href, ...collectionRecord} = collection;
-            const newCollection = await services.collectionService.create({ ...collectionRecord, ownerUuid: resource.ownerUuid, name: resource.name });
+            const { href, ...collectionRecord } = collection;
+            const newCollection = await services.collectionService.create({
+                ...collectionRecord,
+                ownerUuid: resource.ownerUuid,
+                name: resource.name,
+            });
             dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_COPY_FORM_NAME }));
             return newCollection;
         } catch (e) {
             const error = getCommonResourceServiceError(e);
             if (error === CommonResourceServiceError.UNIQUE_NAME_VIOLATION) {
-                dispatch(stopSubmit(
-                    COLLECTION_COPY_FORM_NAME,
-                    { ownerUuid: 'A collection with the same name already exists in the target project.' } as FormErrors
-                ));
+                dispatch(
+                    stopSubmit(COLLECTION_COPY_FORM_NAME, {
+                        ownerUuid: "A collection with the same name already exists in the target project.",
+                    } as FormErrors)
+                );
             } else {
                 dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_COPY_FORM_NAME }));
-                throw new Error('Could not copy the collection.');
+                throw new Error("Could not copy the collection.");
             }
             return;
         } finally {
index 4450cfc6bf5b0f5ae5a481b71a9a5f7eea3d8e7d..af4d7ace95944915b99182b93a0b0e367fcd5662 100644 (file)
@@ -6,4 +6,5 @@ export interface CopyFormDialogData {
     name: string;
     uuid: string;
     ownerUuid: string;
-}
\ No newline at end of file
+    isSingle?: boolean;
+}
index 0de438c5f4b5e0b6fd9f4bb634bd3f0828ec4cc6..e79df2394747dc7a5578d78015e172316a28f900 100644 (file)
@@ -281,7 +281,6 @@ export const createProject = (data: projectCreateActions.ProjectCreateFormDialog
 export const moveProject =
     (data: MoveToFormDialogData, isSecondaryMove = false) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        console.log(data);
         const checkedList = getState().multiselect.checkedList;
         const uuidsToMove: string[] = data.isSingle ? [data.uuid] : selectedToArray(checkedList);
 
@@ -427,28 +426,44 @@ export const createCollection = (data: collectionCreateActions.CollectionCreateF
 };
 
 export const copyCollection = (data: CopyFormDialogData) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-    try {
-        const copyToProject = getResource(data.ownerUuid)(getState().resources);
-        const collection = await dispatch<any>(collectionCopyActions.copyCollection(data));
-        if (copyToProject && collection) {
-            dispatch<any>(reloadProjectMatchingUuid([copyToProject.uuid]));
+    const checkedList = getState().multiselect.checkedList;
+    const uuidsToCopy: string[] = data.isSingle ? [data.uuid] : selectedToArray(checkedList);
+
+    //if no items in checkedlist && no items passed in, default to normal context menu behavior
+    if (!uuidsToCopy.length) uuidsToCopy.push(data.uuid);
+
+    const collectionsToCopy: Resource[] = uuidsToCopy
+        .map(uuid => getResource(uuid)(getState().resources) as any)
+        .filter(resource => resource.kind === ResourceKind.COLLECTION);
+
+    for (const collection of collectionsToCopy) {
+        await copySingleCollection(collection as Resource & { name: string });
+    }
+
+    async function copySingleCollection(copyToProject: Resource & { name: string }) {
+        const newName = data.isSingle ? data.name : `Copy of: ${copyToProject.name}`;
+        try {
+            const collection = await dispatch<any>(collectionCopyActions.copyCollection({ ...copyToProject, name: newName }));
+            if (copyToProject && collection) {
+                await dispatch<any>(reloadProjectMatchingUuid([copyToProject.uuid]));
+                dispatch(
+                    snackbarActions.OPEN_SNACKBAR({
+                        message: "Collection has been copied.",
+                        hideDuration: 3000,
+                        kind: SnackbarKind.SUCCESS,
+                        link: collection.ownerUuid,
+                    })
+                );
+            }
+        } catch (e) {
             dispatch(
                 snackbarActions.OPEN_SNACKBAR({
-                    message: "Collection has been copied.",
-                    hideDuration: 3000,
-                    kind: SnackbarKind.SUCCESS,
-                    link: collection.ownerUuid,
+                    message: e.message,
+                    hideDuration: 2000,
+                    kind: SnackbarKind.ERROR,
                 })
             );
         }
-    } catch (e) {
-        dispatch(
-            snackbarActions.OPEN_SNACKBAR({
-                message: e.message,
-                hideDuration: 2000,
-                kind: SnackbarKind.ERROR,
-            })
-        );
     }
 };
 
index f960571e8c4d7f0f1fce5ae6909308528298aca3..f1250582c1b7c64b8e76f58d5221494fb7afba81 100644 (file)
@@ -66,7 +66,7 @@ const commonActionSet: ContextMenuActionSet = [
             icon: CopyIcon,
             name: "Make a copy",
             execute: (dispatch, resources) => {
-                resources.forEach(resource => dispatch<any>(openCollectionCopyDialog(resource))); //here
+                dispatch<any>(openCollectionCopyDialog(resources[0]));
             },
         },
         {
index 871389d4160ece2392846afe68ef74b2e295844f..d885fd5acb349090f9b11b6214a587936fb250de 100644 (file)
@@ -2,17 +2,17 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { connect } from 'react-redux';
-import { RootState } from 'store/store';
-import { DataExplorer as DataExplorerComponent } from 'components/data-explorer/data-explorer';
-import { getDataExplorer } from 'store/data-explorer/data-explorer-reducer';
-import { Dispatch } from 'redux';
-import { dataExplorerActions } from 'store/data-explorer/data-explorer-action';
-import { DataColumn } from 'components/data-table/data-column';
-import { DataColumns, TCheckedList } from 'components/data-table/data-table';
-import { DataTableFilters } from 'components/data-table-filters/data-table-filters-tree';
-import { LAST_REFRESH_TIMESTAMP } from 'components/refresh-button/refresh-button';
-import { toggleMSToolbar, setCheckedListOnStore } from 'store/multiselect/multiselect-actions';
+import { connect } from "react-redux";
+import { RootState } from "store/store";
+import { DataExplorer as DataExplorerComponent } from "components/data-explorer/data-explorer";
+import { getDataExplorer } from "store/data-explorer/data-explorer-reducer";
+import { Dispatch } from "redux";
+import { dataExplorerActions } from "store/data-explorer/data-explorer-action";
+import { DataColumn } from "components/data-table/data-column";
+import { DataColumns, TCheckedList } from "components/data-table/data-table";
+import { DataTableFilters } from "components/data-table-filters/data-table-filters-tree";
+import { LAST_REFRESH_TIMESTAMP } from "components/refresh-button/refresh-button";
+import { toggleMSToolbar, setCheckedListOnStore } from "store/multiselect/multiselect-actions";
 
 interface Props {
     id: string;
@@ -23,11 +23,11 @@ interface Props {
 }
 
 const mapStateToProps = (state: RootState, { id }: Props) => {
-    const progress = state.progressIndicator.find((p) => p.id === id);
+    const progress = state.progressIndicator.find(p => p.id === id);
     const dataExplorerState = getDataExplorer(state.dataExplorer, id);
-    const currentRoute = state.router.location ? state.router.location.pathname : '';
-    const currentRefresh = localStorage.getItem(LAST_REFRESH_TIMESTAMP) || '';
-    const currentItemUuid = currentRoute === '/workflows' ? state.properties.workflowPanelDetailsUuid : state.detailsPanel.resourceUuid;
+    const currentRoute = state.router.location ? state.router.location.pathname : "";
+    const currentRefresh = localStorage.getItem(LAST_REFRESH_TIMESTAMP) || "";
+    const currentItemUuid = currentRoute === "/workflows" ? state.properties.workflowPanelDetailsUuid : state.detailsPanel.resourceUuid;
     const isMSToolbarVisible = state.multiselect.isVisible;
     return {
         ...dataExplorerState,
@@ -37,10 +37,11 @@ const mapStateToProps = (state: RootState, { id }: Props) => {
         paperKey: currentRoute,
         currentItemUuid,
         isMSToolbarVisible,
+        checkedList: state.multiselect.checkedList,
     };
 };
 
-const mapDispatchToProps = (dispatchFn) => {
+const mapDispatchToProps = dispatchFn => {
     return (dispatch: Dispatch, { id, onRowClick, onRowDoubleClick, onContextMenu }: Props) => ({
         onSetColumns: (columns: DataColumns<any, any>) => {
             dispatch(dataExplorerActions.SET_COLUMNS({ id, columns }));
index a3e301195493be96d99b9b0c3c96648b2b6828e8..aa6b6784d7eb55d7215127b12c6f172ff1b950f8 100644 (file)
@@ -3,37 +3,40 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import React from "react";
-import { memoize } from 'lodash/fp';
-import { InjectedFormProps, Field } from 'redux-form';
-import { WithDialogProps } from 'store/dialog/with-dialog';
-import { FormDialog } from 'components/form-dialog/form-dialog';
-import { ProjectTreePickerField } from 'views-components/projects-tree-picker/tree-picker-field';
-import { COPY_NAME_VALIDATION, COPY_FILE_VALIDATION } from 'validators/validators';
+import { memoize } from "lodash/fp";
+import { InjectedFormProps, Field } from "redux-form";
+import { WithDialogProps } from "store/dialog/with-dialog";
+import { FormDialog } from "components/form-dialog/form-dialog";
+import { ProjectTreePickerField } from "views-components/projects-tree-picker/tree-picker-field";
+import { COPY_NAME_VALIDATION, COPY_FILE_VALIDATION } from "validators/validators";
 import { TextField } from "components/text-field/text-field";
-import { CopyFormDialogData } from 'store/copy-dialog/copy-dialog';
-import { PickerIdProp } from 'store/tree-picker/picker-id';
+import { CopyFormDialogData } from "store/copy-dialog/copy-dialog";
+import { PickerIdProp } from "store/tree-picker/picker-id";
 
 type CopyFormDialogProps = WithDialogProps<string> & InjectedFormProps<CopyFormDialogData>;
 
-export const DialogCopy = (props: CopyFormDialogProps & PickerIdProp) =>
+export const DialogCopy = (props: CopyFormDialogProps & PickerIdProp) => (
     <FormDialog
-        dialogTitle='Make a copy'
+        dialogTitle="Make a copy"
         formFields={CopyDialogFields(props.pickerId)}
-        submitLabel='Copy'
+        submitLabel="Copy"
         {...props}
-    />;
+    />
+);
 
-const CopyDialogFields = memoize((pickerId: string) =>
-    () =>
-        <>
-            <Field
-                name='name'
-                component={TextField as any}
-                validate={COPY_NAME_VALIDATION}
-                label="Enter a new name for the copy" />
-            <Field
-                name="ownerUuid"
-                component={ProjectTreePickerField}
-                validate={COPY_FILE_VALIDATION}
-                pickerId={pickerId}/>
-        </>);
+const CopyDialogFields = memoize((pickerId: string) => () => (
+    <>
+        <Field
+            name="name"
+            component={TextField as any}
+            validate={COPY_NAME_VALIDATION}
+            label="Enter a new name for the copy"
+        />
+        <Field
+            name="ownerUuid"
+            component={ProjectTreePickerField}
+            validate={COPY_FILE_VALIDATION}
+            pickerId={pickerId}
+        />
+    </>
+));
index a1c822cf1f6a9d9e741b0788450d0a2f617b3f4a..eb1f26153fc27cd3986feded49730494f1ea9508 100644 (file)
@@ -4,12 +4,12 @@
 
 import { compose } from "redux";
 import { withDialog } from "store/dialog/with-dialog";
-import { reduxForm } from 'redux-form';
-import { COLLECTION_COPY_FORM_NAME } from 'store/collections/collection-copy-actions';
+import { reduxForm } from "redux-form";
+import { COLLECTION_COPY_FORM_NAME } from "store/collections/collection-copy-actions";
 import { DialogCopy } from "views-components/dialog-copy/dialog-copy";
-import { copyCollection } from 'store/workbench/workbench-actions';
-import { CopyFormDialogData } from 'store/copy-dialog/copy-dialog';
-import { pickerId } from 'store/tree-picker/picker-id';
+import { copyCollection } from "store/workbench/workbench-actions";
+import { CopyFormDialogData } from "store/copy-dialog/copy-dialog";
+import { pickerId } from "store/tree-picker/picker-id";
 
 export const CopyCollectionDialog = compose(
     withDialog(COLLECTION_COPY_FORM_NAME),
@@ -18,7 +18,7 @@ export const CopyCollectionDialog = compose(
         touchOnChange: true,
         onSubmit: (data, dispatch) => {
             dispatch(copyCollection(data));
-        }
+        },
     }),
-    pickerId(COLLECTION_COPY_FORM_NAME),
-)(DialogCopy);
\ No newline at end of file
+    pickerId(COLLECTION_COPY_FORM_NAME)
+)(DialogCopy);