import { setTokenDialogApiHost } from "store/token-dialog/token-dialog-actions";
import {
processResourceActionSet,
+ runningProcessResourceActionSet,
processResourceAdminActionSet,
+ runningProcessResourceAdminActionSet,
readOnlyProcessResourceActionSet,
} from "views-components/context-menu/action-sets/process-resource-action-set";
import { progressIndicatorActions } from "store/progress-indicator/progress-indicator-actions";
addMenuActionSet(ContextMenuKind.OLD_VERSION_COLLECTION, oldCollectionVersionActionSet);
addMenuActionSet(ContextMenuKind.TRASHED_COLLECTION, trashedCollectionActionSet);
addMenuActionSet(ContextMenuKind.PROCESS_RESOURCE, processResourceActionSet);
+addMenuActionSet(ContextMenuKind.RUNNING_PROCESS_RESOURCE, runningProcessResourceActionSet);
addMenuActionSet(ContextMenuKind.READONLY_PROCESS_RESOURCE, readOnlyProcessResourceActionSet);
addMenuActionSet(ContextMenuKind.TRASH, trashActionSet);
addMenuActionSet(ContextMenuKind.REPOSITORY, repositoryActionSet);
addMenuActionSet(ContextMenuKind.GROUP_MEMBER, groupMemberActionSet);
addMenuActionSet(ContextMenuKind.COLLECTION_ADMIN, collectionAdminActionSet);
addMenuActionSet(ContextMenuKind.PROCESS_ADMIN, processResourceAdminActionSet);
+addMenuActionSet(ContextMenuKind.RUNNING_PROCESS_ADMIN, runningProcessResourceAdminActionSet);
addMenuActionSet(ContextMenuKind.PROJECT_ADMIN, projectAdminActionSet);
addMenuActionSet(ContextMenuKind.FROZEN_PROJECT, frozenActionSet);
addMenuActionSet(ContextMenuKind.FROZEN_PROJECT_ADMIN, frozenAdminActionSet);
import { UserResource } from "models/user";
import { isSidePanelTreeCategory } from "store/side-panel-tree/side-panel-tree-actions";
import { extractUuidKind, ResourceKind, EditableResource, Resource } from "models/resource";
-import { Process } from "store/processes/process";
+import { Process, isProcessCancelable } from "store/processes/process";
import { RepositoryResource } from "models/repositories";
import { SshKeyResource } from "models/ssh-key";
import { VirtualMachinesResource } from "models/virtual-machines";
import { LinkResource } from "models/link";
import { resourceIsFrozen } from "common/frozen-resources";
import { ProjectResource } from "models/project";
+import { getProcess } from "store/processes/process";
import { filterCollectionFilesBySelection } from "store/collection-panel/collection-panel-files/collection-panel-files-state";
export const contextMenuActions = unionize({
export const openCollectionFilesContextMenu =
(event: React.MouseEvent<HTMLElement>, isWritable: boolean) => (dispatch: Dispatch, getState: () => RootState) => {
+ const isCollectionFileSelected = JSON.stringify(getState().collectionPanelFiles).includes('"selected":true');
const selectedCount = filterCollectionFilesBySelection(getState().collectionPanelFiles, true).length;
const multiple = selectedCount > 1;
dispatch<any>(
}
};
+export const openRunningProcessContextMenu =
+ (event: React.MouseEvent<HTMLElement>, process: Process) => (dispatch: Dispatch, getState: () => RootState) => {
+ const res = getResource<ProcessResource>(process.containerRequest.uuid)(getState().resources);
+ if (res) {
+ dispatch<any>(
+ openContextMenu(event, {
+ uuid: res.uuid,
+ ownerUuid: res.ownerUuid,
+ kind: ResourceKind.PROCESS,
+ name: res.name,
+ description: res.description,
+ outputUuid: res.outputUuid || "",
+ workflowUuid: res.properties.template_uuid || "",
+ menuKind: ContextMenuKind.RUNNING_PROCESS_RESOURCE,
+ })
+ );
+ }
+ };
+
export const openPermissionEditContextMenu =
(event: React.MouseEvent<HTMLElement>, link: LinkResource) => (dispatch: Dispatch, getState: () => RootState) => {
if (link) {
: ContextMenuKind.READONLY_COLLECTION;
case ResourceKind.PROCESS:
return isAdminUser && isEditable
- ? ContextMenuKind.PROCESS_ADMIN
+ ? resource && isProcessCancelable(getProcess(resource.uuid)(getState().resources) as Process)
+ ? ContextMenuKind.RUNNING_PROCESS_ADMIN
+ : ContextMenuKind.PROCESS_ADMIN
: readonly
? ContextMenuKind.READONLY_PROCESS_RESOURCE
+ : resource && isProcessCancelable(getProcess(resource.uuid)(getState().resources) as Process)
+ ? ContextMenuKind.RUNNING_PROCESS_RESOURCE
: ContextMenuKind.PROCESS_RESOURCE;
case ResourceKind.USER:
return ContextMenuKind.ROOT_PROJECT;
OutputIcon,
AdvancedIcon,
OpenIcon,
+ StopIcon,
} from "components/icon/icon";
import { favoritePanelActions } from "store/favorite-panel/favorite-panel-action";
import { openMoveProcessDialog } from "store/processes/process-move-actions";
import { togglePublicFavorite } from "store/public-favorites/public-favorites-actions";
import { publicFavoritePanelActions } from "store/public-favorites-panel/public-favorites-action";
import { openInNewTabAction } from "store/open-in-new-tab/open-in-new-tab.actions";
+import { cancelRunningWorkflow } from "store/processes/processes-actions";
export const readOnlyProcessResourceActionSet: ContextMenuActionSet = [
[
],
];
+export const runningProcessResourceActionSet = [
+ [
+ ...processResourceActionSet.reduce((prev, next) => prev.concat(next), []),
+ {
+ name: "CANCEL",
+ icon: StopIcon,
+ execute: (dispatch, resource) => {
+ dispatch(cancelRunningWorkflow(resource.uuid));
+ },
+ },
+ ],
+];
+
export const processResourceAdminActionSet: ContextMenuActionSet = [
[
...processResourceActionSet.reduce((prev, next) => prev.concat(next), []),
},
],
];
+
+export const runningProcessResourceAdminActionSet: ContextMenuActionSet = [
+ [
+ ...processResourceActionSet.reduce((prev, next) => prev.concat(next), []),
+ {
+ component: TogglePublicFavoriteAction,
+ name: "Add to public favorites",
+ execute: (dispatch, resources) => {
+ dispatch<any>(togglePublicFavorite(resources[0])).then(() => {
+ dispatch<any>(publicFavoritePanelActions.REQUEST_ITEMS());
+ });
+ },
+ },
+ {
+ name: "CANCEL",
+ icon: StopIcon,
+ execute: (dispatch, resources) => {
+ dispatch<any>(cancelRunningWorkflow(resources[0].uuid));
+ },
+ },
+ ],
+];
OLD_VERSION_COLLECTION = "OldVersionCollection",
TRASHED_COLLECTION = "TrashedCollection",
PROCESS = "Process",
+ RUNNING_PROCESS_ADMIN = "RunningProcessAdmin",
PROCESS_ADMIN = "ProcessAdmin",
+ RUNNING_PROCESS_RESOURCE = "RunningProcessResource",
PROCESS_RESOURCE = "ProcessResource",
READONLY_PROCESS_RESOURCE = "ReadOnlyProcessResource",
PROCESS_LOGS = "ProcessLogs",
//
// SPDX-License-Identifier: AGPL-3.0
-import React from 'react';
-import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core';
+import React from "react";
+import { StyleRulesCallback, WithStyles, withStyles } from "@material-ui/core";
import { DataExplorer } from "views-components/data-explorer/data-explorer";
-import { connect, DispatchProp } from 'react-redux';
-import { DataColumns } from 'components/data-table/data-table';
-import { RouteComponentProps } from 'react-router';
-import { DataTableFilterItem } from 'components/data-table-filters/data-table-filters';
-import { SortDirection } from 'components/data-table/data-column';
-import { ResourceKind } from 'models/resource';
-import { ArvadosTheme } from 'common/custom-theme';
-import { ALL_PROCESSES_PANEL_ID } from 'store/all-processes-panel/all-processes-panel-action';
+import { connect, DispatchProp } from "react-redux";
+import { DataColumns } from "components/data-table/data-table";
+import { RouteComponentProps } from "react-router";
+import { DataTableFilterItem } from "components/data-table-filters/data-table-filters";
+import { SortDirection } from "components/data-table/data-column";
+import { ResourceKind } from "models/resource";
+import { ArvadosTheme } from "common/custom-theme";
+import { ALL_PROCESSES_PANEL_ID } from "store/all-processes-panel/all-processes-panel-action";
import {
ProcessStatus,
ResourceName,
ResourceOwnerWithName,
ResourceType,
ContainerRunTime,
- ResourceCreatedAtDate
-} from 'views-components/data-explorer/renderers';
-import { ProcessIcon } from 'components/icon/icon';
-import { openProcessContextMenu } from 'store/context-menu/context-menu-actions';
-import { loadDetailsPanel } from 'store/details-panel/details-panel-action';
-import { navigateTo } from 'store/navigation/navigation-action';
+ ResourceCreatedAtDate,
+} from "views-components/data-explorer/renderers";
+import { ProcessIcon } from "components/icon/icon";
+import { openProcessContextMenu, openRunningProcessContextMenu } from "store/context-menu/context-menu-actions";
+import { loadDetailsPanel } from "store/details-panel/details-panel-action";
+import { navigateTo } from "store/navigation/navigation-action";
import { ContainerRequestResource, ContainerRequestState } from "models/container-request";
-import { RootState } from 'store/store';
-import { createTree } from 'models/tree';
-import { getInitialProcessStatusFilters, getInitialProcessTypeFilters } from 'store/resource-type-filters/resource-type-filters';
-import { getProcess } from 'store/processes/process';
-import { ResourcesState } from 'store/resources/resources';
+import { RootState } from "store/store";
+import { createTree } from "models/tree";
+import { getInitialProcessStatusFilters, getInitialProcessTypeFilters } from "store/resource-type-filters/resource-type-filters";
+import { getProcess, isProcessCancelable } from "store/processes/process";
+import { ResourcesState } from "store/resources/resources";
type CssRules = "toolbar" | "button" | "root";
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
toolbar: {
paddingBottom: theme.spacing.unit * 3,
- textAlign: "right"
+ textAlign: "right",
},
button: {
- marginLeft: theme.spacing.unit
+ marginLeft: theme.spacing.unit,
},
root: {
- width: '100%',
- }
+ width: "100%",
+ },
});
export enum AllProcessesPanelColumnNames {
TYPE = "Type",
OWNER = "Owner",
CREATED_AT = "Created at",
- RUNTIME = "Run Time"
+ RUNTIME = "Run Time",
}
export interface AllProcessesPanelFilter extends DataTableFilterItem {
name: AllProcessesPanelColumnNames.NAME,
selected: true,
configurable: true,
- sort: {direction: SortDirection.NONE, field: "name"},
+ sort: { direction: SortDirection.NONE, field: "name" },
filters: createTree(),
- render: uuid => <ResourceName uuid={uuid} />
+ render: uuid => <ResourceName uuid={uuid} />,
},
{
name: AllProcessesPanelColumnNames.STATUS,
configurable: true,
mutuallyExclusiveFilters: true,
filters: getInitialProcessStatusFilters(),
- render: uuid => <ProcessStatus uuid={uuid} />
+ render: uuid => <ProcessStatus uuid={uuid} />,
},
{
name: AllProcessesPanelColumnNames.TYPE,
selected: true,
configurable: true,
filters: getInitialProcessTypeFilters(),
- render: uuid => <ResourceType uuid={uuid} />
+ render: uuid => <ResourceType uuid={uuid} />,
},
{
name: AllProcessesPanelColumnNames.OWNER,
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceOwnerWithName uuid={uuid} />
+ render: uuid => <ResourceOwnerWithName uuid={uuid} />,
},
{
name: AllProcessesPanelColumnNames.CREATED_AT,
selected: true,
configurable: true,
- sort: {direction: SortDirection.DESC, field: "createdAt"},
+ sort: { direction: SortDirection.DESC, field: "createdAt" },
filters: createTree(),
- render: uuid => <ResourceCreatedAtDate uuid={uuid} />
+ render: uuid => <ResourceCreatedAtDate uuid={uuid} />,
},
{
name: AllProcessesPanelColumnNames.RUNTIME,
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ContainerRunTime uuid={uuid} />
- }
+ render: uuid => <ContainerRunTime uuid={uuid} />,
+ },
];
interface AllProcessesPanelDataProps {
onDialogOpen: (ownerUuid: string) => void;
onItemDoubleClick: (item: string) => void;
}
-const mapStateToProps = (state : RootState): AllProcessesPanelDataProps => ({
- resources: state.resources
+const mapStateToProps = (state: RootState): AllProcessesPanelDataProps => ({
+ resources: state.resources,
});
-type AllProcessesPanelProps = AllProcessesPanelDataProps & AllProcessesPanelActionProps & DispatchProp
- & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
+type AllProcessesPanelProps = AllProcessesPanelDataProps &
+ AllProcessesPanelActionProps &
+ DispatchProp &
+ WithStyles<CssRules> &
+ RouteComponentProps<{ id: string }>;
export const AllProcessesPanel = withStyles(styles)(
connect(mapStateToProps)(
class extends React.Component<AllProcessesPanelProps> {
handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
const process = getProcess(resourceUuid)(this.props.resources);
- if (process) {
+ if (process && isProcessCancelable(process)) {
+ this.props.dispatch<any>(openRunningProcessContextMenu(event, process));
+ } else if (process) {
this.props.dispatch<any>(openProcessContextMenu(event, process));
}
this.props.dispatch<any>(loadDetailsPanel(resourceUuid));
- }
+ };
handleRowDoubleClick = (uuid: string) => {
this.props.dispatch<any>(navigateTo(uuid));
- }
+ };
handleRowClick = (uuid: string) => {
this.props.dispatch<any>(loadDetailsPanel(uuid));
- }
+ };
render() {
- return <div className={this.props.classes.root}><DataExplorer
- id={ALL_PROCESSES_PANEL_ID}
- onRowClick={this.handleRowClick}
- onRowDoubleClick={this.handleRowDoubleClick}
- onContextMenu={this.handleContextMenu}
- contextMenuColumn={true}
- defaultViewIcon={ProcessIcon}
- defaultViewMessages={['Processes list empty.']} />
- </div>
+ return (
+ <div className={this.props.classes.root}>
+ <DataExplorer
+ id={ALL_PROCESSES_PANEL_ID}
+ onRowClick={this.handleRowClick}
+ onRowDoubleClick={this.handleRowDoubleClick}
+ onContextMenu={this.handleContextMenu}
+ contextMenuColumn={true}
+ defaultViewIcon={ProcessIcon}
+ defaultViewMessages={["Processes list empty."]}
+ />
+ </div>
+ );
}
}
)
import { connect } from "react-redux";
import { getProcess, getSubprocesses, Process, getProcessStatus } from "store/processes/process";
import { Dispatch } from "redux";
-import { openProcessContextMenu } from "store/context-menu/context-menu-actions";
+import { openProcessContextMenu, openRunningProcessContextMenu } from "store/context-menu/context-menu-actions";
import { ProcessPanelRootDataProps, ProcessPanelRootActionProps, ProcessPanelRoot } from "./process-panel-root";
import { getProcessPanelCurrentUuid, ProcessPanel as ProcessPanelState } from "store/process-panel/process-panel";
import { groupBy } from "lodash";
loadNodeJson,
} from "store/process-panel/process-panel-actions";
import { cancelRunningWorkflow, resumeOnHoldWorkflow, startWorkflow } from "store/processes/processes-actions";
+import { isProcessCancelable } from "store/processes/process";
import { navigateToLogCollection, pollProcessLogs, setProcessLogsPanelFilter } from "store/process-logs-panel/process-logs-panel-actions";
import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions";
);
},
onContextMenu: (event, process) => {
- dispatch<any>(openProcessContextMenu(event, process));
+ // dispatch<any>(openProcessContextMenu(event, process));
+ if (process && isProcessCancelable(process)) {
+ dispatch<any>(openRunningProcessContextMenu(event, process));
+ } else if (process) {
+ dispatch<any>(openProcessContextMenu(event, process));
+ }
},
onToggle: status => {
dispatch<any>(toggleProcessPanelFilter(status));
import { Dispatch } from "redux";
import { connect } from "react-redux";
-import { openProcessContextMenu } from 'store/context-menu/context-menu-actions';
-import { SubprocessPanelRoot, SubprocessPanelActionProps, SubprocessPanelDataProps } from 'views/subprocess-panel/subprocess-panel-root';
+import { openProcessContextMenu, openRunningProcessContextMenu } from "store/context-menu/context-menu-actions";
+import { SubprocessPanelRoot, SubprocessPanelActionProps, SubprocessPanelDataProps } from "views/subprocess-panel/subprocess-panel-root";
import { RootState } from "store/store";
import { navigateTo } from "store/navigation/navigation-action";
import { loadDetailsPanel } from "store/details-panel/details-panel-action";
import { getProcess } from "store/processes/process";
+import { isProcessCancelable } from "store/processes/process";
const mapDispatchToProps = (dispatch: Dispatch): SubprocessPanelActionProps => ({
onContextMenu: (event, resourceUuid, resources) => {
const process = getProcess(resourceUuid)(resources);
- if (process) {
+ if (process && isProcessCancelable(process)) {
+ dispatch<any>(openRunningProcessContextMenu(event, process));
+ } else if (process) {
dispatch<any>(openProcessContextMenu(event, process));
}
},
},
onItemDoubleClick: uuid => {
dispatch<any>(navigateTo(uuid));
- }
+ },
});
const mapStateToProps = (state: RootState): SubprocessPanelDataProps => ({
- resources: state.resources
+ resources: state.resources,
});
-export const SubprocessPanel = connect(mapStateToProps, mapDispatchToProps)(SubprocessPanelRoot);
\ No newline at end of file
+export const SubprocessPanel = connect(mapStateToProps, mapDispatchToProps)(SubprocessPanelRoot);