15768: remove process works Arvados-DCO-1.1-Signed-off-by: Lisa Knox <lisa.knox@curii...
authorLisa Knox <lisaknox83@gmail.com>
Fri, 1 Sep 2023 20:37:27 +0000 (16:37 -0400)
committerLisa Knox <lisaknox83@gmail.com>
Fri, 1 Sep 2023 20:37:27 +0000 (16:37 -0400)
src/components/multiselect-toolbar/MultiselectToolbar.tsx
src/components/multiselect-toolbar/ms-toolbar-action-filters.ts
src/store/processes/processes-actions.ts
src/store/workbench/workbench-actions.ts
src/views-components/context-menu/action-sets/process-resource-action-set.ts

index 22a9c7139c7abffde2d051c5140c4485573f1db1..b726cfa08aceb2d61701bfd8581402874a454cd9 100644 (file)
@@ -189,6 +189,7 @@ function mapDispatchToProps(dispatch: Dispatch) {
             const kindGroups = groupByKind(checkedList, resources);
             switch (selectedAction.name) {
                 case contextMenuActionConsts.MOVE_TO:
+                case contextMenuActionConsts.REMOVE:
                     const firstResource = getResource(selectedToArray(checkedList)[0])(resources) as Resource;
                     const action = findActionByName(selectedAction.name as string, kindToActionSet[firstResource.kind]);
                     if (action) action.execute(dispatch, kindGroups[firstResource.kind]);
@@ -200,6 +201,7 @@ 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 91e6d6761be69f0620bdbf34db99713ac50149a5..3c155a6a2f520189aae7cdb2fc5cfe9357668f87 100644 (file)
@@ -19,17 +19,17 @@ export const contextMenuActionConsts = {
     REMOVE: "Remove",
 } as const;
 
-const { MOVE_TO, TOGGLE_TRASH_ACTION, COPY_TO_CLIPBOARD, REMOVE } = contextMenuActionConsts;
+const { MOVE_TO, TOGGLE_TRASH_ACTION, COPY_TO_CLIPBOARD, REMOVE, MAKE_A_COPY } = contextMenuActionConsts;
 
 //these sets govern what actions are on the ms toolbar for each resource kind
-const collectionMSActionsFilter = new Set([COPY_TO_CLIPBOARD, MOVE_TO, TOGGLE_TRASH_ACTION]);
 const projectMSActionsFilter = new Set([COPY_TO_CLIPBOARD, MOVE_TO, TOGGLE_TRASH_ACTION]);
 const processResourceMSActionsFilter = new Set([MOVE_TO, REMOVE]);
+const collectionMSActionsFilter = new Set([MAKE_A_COPY, MOVE_TO, TOGGLE_TRASH_ACTION]);
 
 const { COLLECTION, PROJECT, PROCESS } = ResourceKind;
 
 export const multiselectActionsFilters: TMultiselectActionsFilters = {
-    [COLLECTION]: [collectionActionSet, collectionMSActionsFilter],
     [PROJECT]: [projectActionSet, projectMSActionsFilter],
     [PROCESS]: [processResourceActionSet, processResourceMSActionsFilter],
+    [COLLECTION]: [collectionActionSet, collectionMSActionsFilter],
 };
index 6d46efd08746a17046d91cab7072b9108efd4256..18d95911507fdf715a19e552c13ac40a7e83f75f 100644 (file)
@@ -2,28 +2,30 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { Dispatch } from 'redux';
-import { RootState } from 'store/store';
-import { ServiceRepository } from 'services/services';
-import { updateResources } from 'store/resources/resources-actions';
-import { Process } from './process';
-import { dialogActions } from 'store/dialog/dialog-actions';
-import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
-import { projectPanelActions } from 'store/project-panel/project-panel-action';
-import { navigateToRunProcess } from 'store/navigation/navigation-action';
-import { goToStep, runProcessPanelActions } from 'store/run-process-panel/run-process-panel-actions';
-import { getResource } from 'store/resources/resources';
-import { initialize } from 'redux-form';
-import { RUN_PROCESS_BASIC_FORM, RunProcessBasicFormData } from 'views/run-process-panel/run-process-basic-form';
-import { RunProcessAdvancedFormData, RUN_PROCESS_ADVANCED_FORM } from 'views/run-process-panel/run-process-advanced-form';
-import { MOUNT_PATH_CWL_WORKFLOW, MOUNT_PATH_CWL_INPUT } from 'models/process';
-import { CommandInputParameter, getWorkflow, getWorkflowInputs, getWorkflowOutputs, WorkflowInputsData } from 'models/workflow';
-import { ProjectResource } from 'models/project';
-import { UserResource } from 'models/user';
-import { CommandOutputParameter } from 'cwlts/mappings/v1.0/CommandOutputParameter';
-import { ContainerResource } from 'models/container';
-import { ContainerRequestResource, ContainerRequestState } from 'models/container-request';
-import { FilterBuilder } from 'services/api/filter-builder';
+import { Dispatch } from "redux";
+import { RootState } from "store/store";
+import { ServiceRepository } from "services/services";
+import { updateResources } from "store/resources/resources-actions";
+import { Process } from "./process";
+import { dialogActions } from "store/dialog/dialog-actions";
+import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions";
+import { projectPanelActions } from "store/project-panel/project-panel-action";
+import { navigateToRunProcess } from "store/navigation/navigation-action";
+import { goToStep, runProcessPanelActions } from "store/run-process-panel/run-process-panel-actions";
+import { getResource } from "store/resources/resources";
+import { initialize } from "redux-form";
+import { RUN_PROCESS_BASIC_FORM, RunProcessBasicFormData } from "views/run-process-panel/run-process-basic-form";
+import { RunProcessAdvancedFormData, RUN_PROCESS_ADVANCED_FORM } from "views/run-process-panel/run-process-advanced-form";
+import { MOUNT_PATH_CWL_WORKFLOW, MOUNT_PATH_CWL_INPUT } from "models/process";
+import { CommandInputParameter, getWorkflow, getWorkflowInputs, getWorkflowOutputs, WorkflowInputsData } from "models/workflow";
+import { ProjectResource } from "models/project";
+import { UserResource } from "models/user";
+import { CommandOutputParameter } from "cwlts/mappings/v1.0/CommandOutputParameter";
+import { ContainerResource } from "models/container";
+import { ContainerRequestResource, ContainerRequestState } from "models/container-request";
+import { FilterBuilder } from "services/api/filter-builder";
+import { selectedToArray } from "components/multiselect-toolbar/MultiselectToolbar";
+import { Resource, ResourceKind } from "models/resource";
 
 export const loadProcess =
     (containerRequestUuid: string) =>
@@ -66,7 +68,7 @@ export const loadContainers =
     (containerUuids: string[], loadMounts: boolean = true) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         let args: any = {
-            filters: new FilterBuilder().addIn('uuid', containerUuids).getFilters(),
+            filters: new FilterBuilder().addIn("uuid", containerUuids).getFilters(),
             limit: containerUuids.length,
         };
         if (!loadMounts) {
@@ -79,41 +81,41 @@ export const loadContainers =
 
 // Until the api supports unselecting fields, we need a list of all other fields to omit mounts
 const containerFieldsNoMounts = [
-    'auth_uuid',
-    'command',
-    'container_image',
-    'cost',
-    'created_at',
-    'cwd',
-    'environment',
-    'etag',
-    'exit_code',
-    'finished_at',
-    'gateway_address',
-    'href',
-    'interactive_session_started',
-    'kind',
-    'lock_count',
-    'locked_by_uuid',
-    'log',
-    'modified_at',
-    'modified_by_client_uuid',
-    'modified_by_user_uuid',
-    'output_path',
-    'output_properties',
-    'output_storage_classes',
-    'output',
-    'owner_uuid',
-    'priority',
-    'progress',
-    'runtime_auth_scopes',
-    'runtime_constraints',
-    'runtime_status',
-    'runtime_user_uuid',
-    'scheduling_parameters',
-    'started_at',
-    'state',
-    'uuid',
+    "auth_uuid",
+    "command",
+    "container_image",
+    "cost",
+    "created_at",
+    "cwd",
+    "environment",
+    "etag",
+    "exit_code",
+    "finished_at",
+    "gateway_address",
+    "href",
+    "interactive_session_started",
+    "kind",
+    "lock_count",
+    "locked_by_uuid",
+    "log",
+    "modified_at",
+    "modified_by_client_uuid",
+    "modified_by_user_uuid",
+    "output_path",
+    "output_properties",
+    "output_storage_classes",
+    "output",
+    "owner_uuid",
+    "priority",
+    "progress",
+    "runtime_auth_scopes",
+    "runtime_constraints",
+    "runtime_status",
+    "runtime_user_uuid",
+    "scheduling_parameters",
+    "started_at",
+    "state",
+    "uuid",
 ];
 
 export const cancelRunningWorkflow = (uuid: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
@@ -126,7 +128,7 @@ export const cancelRunningWorkflow = (uuid: string) => async (dispatch: Dispatch
         }
         return process;
     } catch (e) {
-        throw new Error('Could not cancel the process.');
+        throw new Error("Could not cancel the process.");
     }
 };
 
@@ -140,7 +142,7 @@ export const resumeOnHoldWorkflow = (uuid: string) => async (dispatch: Dispatch,
         }
         return process;
     } catch (e) {
-        throw new Error('Could not resume the process.');
+        throw new Error("Could not resume the process.");
     }
 };
 
@@ -149,7 +151,7 @@ export const startWorkflow = (uuid: string) => async (dispatch: Dispatch, getSta
         const process = await services.containerRequestService.update(uuid, { state: ContainerRequestState.COMMITTED });
         if (process) {
             dispatch<any>(updateResources([process]));
-            dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Process started', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
+            dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Process started", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
         } else {
             dispatch<any>(snackbarActions.OPEN_SNACKBAR({ message: `Failed to start process`, kind: SnackbarKind.ERROR }));
         }
@@ -162,7 +164,7 @@ export const reRunProcess =
     (processUuid: string, workflowUuid: string) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         const process = getResource<any>(processUuid)(getState().resources);
         const workflows = getState().runProcessPanel.searchWorkflows;
-        const workflow = workflows.find((workflow) => workflow.uuid === workflowUuid);
+        const workflow = workflows.find(workflow => workflow.uuid === workflowUuid);
         if (workflow && process) {
             const mainWf = getWorkflow(process.mounts[MOUNT_PATH_CWL_WORKFLOW]);
             if (mainWf) {
@@ -230,7 +232,7 @@ export const getInputs = (data: any): CommandInputParameter[] => {
               id: it.id,
               label: it.label,
               default: content[it.id],
-              value: content[it.id.split('/').pop()] || [],
+              value: content[it.id.split("/").pop()] || [],
               doc: it.doc,
           }))
         : [];
@@ -257,12 +259,12 @@ export const getInputCollectionMounts = (data: any): InputCollectionMount[] => {
         return [];
     }
     return Object.keys(data.mounts)
-        .map((key) => ({
+        .map(key => ({
             ...data.mounts[key],
             path: key,
         }))
-        .filter((mount) => mount.kind === 'collection' && mount.portable_data_hash && mount.path)
-        .map((mount) => ({
+        .filter(mount => mount.kind === "collection" && mount.portable_data_hash && mount.path)
+        .map(mount => ({
             path: mount.path,
             pdh: mount.portable_data_hash,
         }));
@@ -283,25 +285,41 @@ export const getOutputParameters = (data: any): CommandOutputParameter[] => {
         : [];
 };
 
-export const openRemoveProcessDialog = (uuid: string) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-    dispatch(
-        dialogActions.OPEN_DIALOG({
-            id: REMOVE_PROCESS_DIALOG,
-            data: {
-                title: 'Remove process permanently',
-                text: 'Are you sure you want to remove this process?',
-                confirmButtonLabel: 'Remove',
-                uuid,
-            },
-        })
-    );
-};
+export const openRemoveProcessDialog =
+    (uuid: string, numOfProcesses: Number) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        const confirmationText =
+            numOfProcesses === 1
+                ? "Are you sure you want to remove this process?"
+                : `Are you sure you want to remove these ${numOfProcesses} processes?`;
+        const titleText = numOfProcesses === 1 ? "Remove process permanently" : "Remove processes permanently";
+
+        dispatch(
+            dialogActions.OPEN_DIALOG({
+                id: REMOVE_PROCESS_DIALOG,
+                data: {
+                    title: titleText,
+                    text: confirmationText,
+                    confirmButtonLabel: "Remove",
+                    uuid,
+                },
+            })
+        );
+    };
 
-export const REMOVE_PROCESS_DIALOG = 'removeProcessDialog';
+export const REMOVE_PROCESS_DIALOG = "removeProcessDialog";
 
 export const removeProcessPermanently = (uuid: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-    dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing ...', kind: SnackbarKind.INFO }));
-    await services.containerRequestService.delete(uuid);
-    dispatch(projectPanelActions.REQUEST_ITEMS());
-    dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removed.', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
+    const checkedList = getState().multiselect.checkedList;
+    const uuidsToRemove: string[] = selectedToArray(checkedList);
+
+    const processesToRemove = uuidsToRemove
+        .map(uuid => getResource(uuid)(getState().resources) as Resource)
+        .filter(resource => resource.kind === ResourceKind.PROCESS);
+
+    for (const process of processesToRemove) {
+        dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Removing ...", kind: SnackbarKind.INFO }));
+        await services.containerRequestService.delete(process.uuid);
+        dispatch(projectPanelActions.REQUEST_ITEMS());
+        dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Removed.", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
+    }
 };
index f46deeba1177c9687a083092bbc81050341f6629..f33ddeab002d026621a7198042f071672375c1db 100644 (file)
@@ -281,10 +281,11 @@ export const createProject = (data: projectCreateActions.ProjectCreateFormDialog
 export const moveProject =
     (data: MoveToFormDialogData, isSecondaryMove = false) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        const selectedUuid = getState().detailsPanel.resourceUuid;
         const checkedList = getState().multiselect.checkedList;
         const uuidsToMove: string[] = selectedToArray(checkedList);
 
-        //if no items in checkedlist && no items passed in, default to normal context menu behavior
+        //if no items in checkedlist default to normal context menu behavior
         if (!isSecondaryMove && !uuidsToMove.length) uuidsToMove.push(data.uuid);
 
         const sourceUuid = getResource(data.uuid)(getState().resources)?.ownerUuid;
index d9ad424e3d34a09209ee88f8a66af5394891deae..b4674951a5b3bd36866b99c0fb7b35e6fa6343d3 100644 (file)
@@ -112,7 +112,7 @@ export const processResourceActionSet: ContextMenuActionSet = [
             name: "Remove",
             icon: RemoveIcon,
             execute: (dispatch, resources) => {
-                resources.forEach(resource => dispatch<any>(openRemoveProcessDialog(resource.uuid)));
+                dispatch<any>(openRemoveProcessDialog(resources[0].uuid, resources.length));
             },
         },
     ],