cy.goToPath(path);
// then
- cy.get('[data-cy=not-found-page]').should('not.exist');
- cy.get('[data-cy=not-found-content]').should('exist');
+ cy.get('[data-cy=not-found-view]').should('exist');
});
});
- })
+ })
return { ...next, ...prev };
}, {});
setPathData(state => ({ ...state, ...newState }));
+ }, () => {
+ // Nothing to do
})
.finally(() => {
setIsLoading(false);
runningProcessResourceAdminActionSet,
readOnlyProcessResourceActionSet,
} from "views-components/context-menu/action-sets/process-resource-action-set";
- import { progressIndicatorActions } from "store/progress-indicator/progress-indicator-actions";
import { trashedCollectionActionSet } from "views-components/context-menu/action-sets/trashed-collection-action-set";
import { setBuildInfo } from "store/app-info/app-info-actions";
import { getBuildInfo } from "common/app-info";
} from "views-components/context-menu/action-sets/project-admin-action-set";
import { permissionEditActionSet } from "views-components/context-menu/action-sets/permission-edit-action-set";
import { workflowActionSet, readOnlyWorkflowActionSet } from "views-components/context-menu/action-sets/workflow-action-set";
- import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions";
- import { openNotFoundDialog } from "./store/not-found-panel/not-found-panel-action";
import { storeRedirects } from "./common/redirect-to";
import { searchResultsActionSet } from "views-components/context-menu/action-sets/search-results-action-set";
const services = createServices(config, {
progressFn: (id, working) => {
- //store.dispatch(progressIndicatorActions.TOGGLE_WORKING({ id, working }));
},
errorFn: (id, error, showSnackBar: boolean) => {
if (showSnackBar) {
console.error("Backend error:", error);
-
- if (error.status === 404) {
- store.dispatch(openNotFoundDialog());
- } else if (error.status === 401 && error.errors[0].indexOf("Not logged in") > -1) {
+ if (error.status === 401 && error.errors[0].indexOf("Not logged in") > -1) {
// Catch auth errors when navigating and redirect to login preserving url location
store.dispatch(logout(false, true));
- } else {
- store.dispatch(
- snackbarActions.OPEN_SNACKBAR({
- message: `${error.errors ? error.errors[0] : error.message}`,
- kind: SnackbarKind.ERROR,
- hideDuration: 8000,
- })
- );
}
}
},
}
async files(uuid: string) {
- const request = await this.keepWebdavClient.propfind(`c=${uuid}`);
- if (request.responseXML != null) {
- return extractFilesData(request.responseXML);
+ try {
+ const request = await this.keepWebdavClient.propfind(`c=${uuid}`);
+ if (request.responseXML != null) {
+ return extractFilesData(request.responseXML);
+ }
+ } catch (e) {
+ return Promise.reject(e);
}
-
return Promise.reject();
}
import { navigateTo } from 'store/navigation/navigation-action';
import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
import { CollectionFile, CollectionFileType } from "models/collection-file";
- import { ContainerRequestResource } from "models/container-request";
+ import { ContainerRequestResource, ContainerRequestState } from "models/container-request";
const SNIPLINE = `================ ✀ ================ ✀ ========= Some log(s) were skipped ========= ✀ ================ ✀ ================`;
const LOG_TIMESTAMP_PATTERN = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{9}Z/;
export const initProcessLogsPanel = (processUuid: string) =>
async (dispatch: Dispatch, getState: () => RootState, { logService }: ServiceRepository) => {
+ let process: Process | undefined;
try {
dispatch(processLogsPanelActions.RESET_PROCESS_LOGS_PANEL());
- const process = getProcess(processUuid)(getState().resources);
+ process = getProcess(processUuid)(getState().resources);
if (process?.containerRequest?.uuid) {
// Get log file size info
const logFiles = await loadContainerLogFileList(process.containerRequest, logService);
// Populate lastbyte 0 for each file
- const filesWithProgress = logFiles.map((file) => ({file, lastByte: 0}));
+ const filesWithProgress = logFiles.map((file) => ({ file, lastByte: 0 }));
// Fetch array of LogFragments
const logLines = await loadContainerLogFileContents(filesWithProgress, logService, process);
const initialState = createInitialLogPanelState(logFiles, logLines);
dispatch(processLogsPanelActions.INIT_PROCESS_LOGS_PANEL(initialState));
}
- } catch(e) {
+ } catch (e) {
// On error, populate empty state to allow polling to start
const initialState = createInitialLogPanelState([], []);
dispatch(processLogsPanelActions.INIT_PROCESS_LOGS_PANEL(initialState));
// Only show toast on errors other than 404 since 404 is expected when logs do not exist yet
if (e.status !== 404) {
- dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not load process logs', hideDuration: 2000, kind: SnackbarKind.ERROR }));
+ dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Error loading process logs', hideDuration: 4000, kind: SnackbarKind.ERROR }));
+ }
+ if (e.status === 404 && process?.containerRequest.state === ContainerRequestState.FINAL) {
+ dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Log collection was trashed or deleted.', hideDuration: 4000, kind: SnackbarKind.WARNING }));
}
}
};
const isChanged = !isNew && currentStateLogLastByte < updatedFile.size;
if (isNew || isChanged) {
- return acc.concat({file: updatedFile, lastByte: currentStateLogLastByte});
+ return acc.concat({ file: updatedFile, lastByte: currentStateLogLastByte });
} else {
return acc;
}
* @returns LogFragment[] containing a single LogFragment corresponding to each input file
*/
const loadContainerLogFileContents = async (logFilesWithProgress: FileWithProgress[], logService: LogService, process: Process) => (
- (await Promise.allSettled(logFilesWithProgress.filter(({file}) => file.size > 0).map(({file, lastByte}) => {
+ (await Promise.allSettled(logFilesWithProgress.filter(({ file }) => file.size > 0).map(({ file, lastByte }) => {
const requestSize = file.size - lastByte;
if (requestSize > maxLogFetchSize) {
const chunkSize = Math.floor(maxLogFetchSize / 2);
- const firstChunkEnd = lastByte+chunkSize-1;
+ const firstChunkEnd = lastByte + chunkSize - 1;
return Promise.all([
logService.getLogFileContents(process.containerRequest, file, lastByte, firstChunkEnd),
- logService.getLogFileContents(process.containerRequest, file, file.size-chunkSize, file.size-1)
+ logService.getLogFileContents(process.containerRequest, file, file.size - chunkSize, file.size - 1)
] as Promise<(LogFragment)>[]);
} else {
- return Promise.all([logService.getLogFileContents(process.containerRequest, file, lastByte, file.size-1)]);
+ return Promise.all([logService.getLogFileContents(process.containerRequest, file, lastByte, file.size - 1)]);
}
})).then((res) => {
if (res.length && res.every(promiseResult => (promiseResult.status === 'rejected'))) {
// error if every request failed
const error = res.find(
(promiseResult): promiseResult is PromiseRejectedResult => promiseResult.status === 'rejected'
- )?.reason;
+ )?.reason;
return Promise.reject(error);
}
return res.filter((promiseResult): promiseResult is PromiseFulfilledResult<LogFragment[]> => (
// (prevent incorrect snipline generation or an un-resumable situation)
!!promiseResult.value.every(logFragment => logFragment.contents.length)
)).map(one => one.value)
- })).map((logResponseSet)=> {
+ })).map((logResponseSet) => {
// For any multi fragment response set, modify the last line of non-final chunks to include a line break and snip line
// Don't add snip line as a separate line so that sorting won't reorder it
for (let i = 1; i < logResponseSet.length; i++) {
- const fragment = logResponseSet[i-1];
- const lastLineIndex = fragment.contents.length-1;
+ const fragment = logResponseSet[i - 1];
+ const lastLineIndex = fragment.contents.length - 1;
const lastLineContents = fragment.contents[lastLineIndex];
const newLastLine = `${lastLineContents}\n${SNIPLINE}`;
- logResponseSet[i-1].contents[lastLineIndex] = newLastLine;
+ logResponseSet[i - 1].contents[lastLineIndex] = newLastLine;
}
// Merge LogFragment Array (representing multiple log line arrays) into single LogLine[] / LogFragment
})
);
- const createInitialLogPanelState = (logFiles: CollectionFile[], logFragments: LogFragment[]): {filters: string[], logs: ProcessLogs} => {
+ const createInitialLogPanelState = (logFiles: CollectionFile[], logFragments: LogFragment[]): { filters: string[], logs: ProcessLogs } => {
const logs = groupLogs(logFiles, logFragments);
const filters = Object.keys(logs);
return { filters, logs };
const groupedLogs = logFragments.reduce((grouped, fragment) => ({
...grouped,
- [fragment.logType as string]: {lastByte: fetchLastByteNumber(logFiles, fragment.logType), contents: fragment.contents}
+ [fragment.logType as string]: { lastByte: fetchLastByteNumber(logFiles, fragment.logType), contents: fragment.contents }
}), {});
return {
- [MAIN_FILTER_TYPE]: {lastByte: undefined, contents: mainLogs},
- [ALL_FILTER_TYPE]: {lastByte: undefined, contents: allLogs},
+ [MAIN_FILTER_TYPE]: { lastByte: undefined, contents: mainLogs },
+ [ALL_FILTER_TYPE]: { lastByte: undefined, contents: allLogs },
...groupedLogs,
}
};
// Partial line without timestamp detected
if (i > 0) {
// If not first line, copy line to previous line
- const previousLineContents = fragmentCopy.contents[i-1];
+ const previousLineContents = fragmentCopy.contents[i - 1];
const newPreviousLineContents = `${previousLineContents}\n${lineContents}`;
- fragmentCopy.contents[i-1] = newPreviousLineContents;
+ fragmentCopy.contents[i - 1] = newPreviousLineContents;
}
// Delete the current line and prevent iterating
fragmentCopy.contents.splice(i, 1);
await services.collectionService.get(uuid);
dispatch<any>(navigateTo(uuid));
} catch {
- dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not request collection', hideDuration: 2000, kind: SnackbarKind.ERROR }));
+ dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Log collection was trashed or deleted.', hideDuration: 4000, kind: SnackbarKind.WARNING }));
}
};
import { ProcessStatus } from "store/processes/process";
import { RootState } from "store/store";
import { ServiceRepository } from "services/services";
- import { navigateTo, navigateToWorkflows } from "store/navigation/navigation-action";
+ import { navigateTo } from "store/navigation/navigation-action";
import { snackbarActions } from "store/snackbar/snackbar-actions";
import { SnackbarKind } from "../snackbar/snackbar-actions";
- import { showWorkflowDetails } from "store/workflow-panel/workflow-panel-actions";
import { loadSubprocessPanel, subprocessPanelActions } from "../subprocess-panel/subprocess-panel-actions";
import { initProcessLogsPanel, processLogsPanelActions } from "store/process-logs-panel/process-logs-panel-actions";
import { CollectionFile } from "models/collection-file";
await services.collectionService.get(uuid);
dispatch<any>(navigateTo(uuid));
} catch {
- dispatch(snackbarActions.OPEN_SNACKBAR({ message: "This collection does not exists!", hideDuration: 2000, kind: SnackbarKind.ERROR }));
+ dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Output collection was trashed or deleted.", hideDuration: 4000, kind: SnackbarKind.WARNING }));
}
};
};
export const openWorkflow = (uuid: string) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- dispatch<any>(navigateToWorkflows);
- dispatch<any>(showWorkflowDetails(uuid));
+ dispatch<any>(navigateTo(uuid));
};
export const initProcessPanelFilters = processPanelActions.SET_PROCESS_PANEL_FILTERS([
rowsPerPage: dataExplorer.rowsPerPage,
})
);
- api.dispatch(couldNotFetchProjectContents());
+ if (e.status === 404) {
+ // It'll just show up as not found
+ }
+ else {
+ api.dispatch(couldNotFetchProjectContents());
+ }
} finally {
if (!background) { api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId())); }
}
import { trashPanelActions } from "store/trash-panel/trash-panel-action";
import { activateSidePanelTreeItem, loadSidePanelTreeProjects } from "store/side-panel-tree/side-panel-tree-actions";
import { projectPanelActions } from "store/project-panel/project-panel-action-bind";
+ import { sharedWithMePanelActions } from "store/shared-with-me-panel/shared-with-me-panel-actions";
import { ResourceKind } from "models/resource";
import { navigateTo, navigateToTrash } from "store/navigation/navigation-action";
- import { matchCollectionRoute } from "routes/routes";
+ import { matchCollectionRoute, matchSharedWithMeRoute } from "routes/routes";
export const toggleProjectTrashed =
(uuid: string, ownerUuid: string, isTrashed: boolean, isMulti: boolean) =>
- async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<any> => {
- let errorMessage = "";
- let successMessage = "";
- let untrashedResource;
- try {
- if (isTrashed) {
- errorMessage = "Could not restore project from trash";
- successMessage = "Restored project from trash";
- untrashedResource = await services.groupsService.untrash(uuid);
- dispatch<any>(isMulti || !untrashedResource ? navigateToTrash : navigateTo(uuid));
- dispatch<any>(activateSidePanelTreeItem(uuid));
- } else {
- errorMessage = "Could not move project to trash";
- successMessage = "Added project to trash";
- await services.groupsService.trash(uuid);
- dispatch<any>(loadSidePanelTreeProjects(ownerUuid));
- dispatch<any>(navigateTo(ownerUuid));
- }
- if (untrashedResource) {
- dispatch(
- snackbarActions.OPEN_SNACKBAR({
- message: successMessage,
- hideDuration: 2000,
- kind: SnackbarKind.SUCCESS,
- })
- );
- }
- } catch (e) {
- if (e.status === 422) {
- dispatch(
- snackbarActions.OPEN_SNACKBAR({
- message: "Could not restore project from trash: Duplicate name at destination",
- kind: SnackbarKind.ERROR,
- })
- );
- } else {
- dispatch(
- snackbarActions.OPEN_SNACKBAR({
- message: errorMessage,
- kind: SnackbarKind.ERROR,
- })
- );
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<any> => {
+ let errorMessage = "";
+ let successMessage = "";
+ let untrashedResource;
+ try {
+ if (isTrashed) {
+ errorMessage = "Could not restore project from trash";
+ successMessage = "Restored project from trash";
+ untrashedResource = await services.groupsService.untrash(uuid);
+ dispatch<any>(isMulti || !untrashedResource ? navigateToTrash : navigateTo(uuid));
+ dispatch<any>(activateSidePanelTreeItem(uuid));
+ } else {
+ errorMessage = "Could not move project to trash";
+ successMessage = "Added project to trash";
+ await services.groupsService.trash(uuid);
+ dispatch<any>(loadSidePanelTreeProjects(ownerUuid));
+
+ const { location } = getState().router;
+ if (matchSharedWithMeRoute(location ? location.pathname : "")) {
+ dispatch(sharedWithMePanelActions.REQUEST_ITEMS());
+ }
+ else {
+ dispatch<any>(navigateTo(ownerUuid));
+ }
+ }
+ if (untrashedResource) {
+ dispatch(
+ snackbarActions.OPEN_SNACKBAR({
+ message: successMessage,
+ hideDuration: 2000,
+ kind: SnackbarKind.SUCCESS,
+ })
+ );
+ }
+ } catch (e) {
+ if (e.status === 422) {
+ dispatch(
+ snackbarActions.OPEN_SNACKBAR({
+ message: "Could not restore project from trash: Duplicate name at destination",
+ kind: SnackbarKind.ERROR,
+ })
+ );
+ } else {
+ dispatch(
+ snackbarActions.OPEN_SNACKBAR({
+ message: errorMessage,
+ kind: SnackbarKind.ERROR,
+ })
+ );
+ }
}
- }
- };
+ };
export const toggleCollectionTrashed =
(uuid: string, isTrashed: boolean) =>
- async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<any> => {
- let errorMessage = "";
- let successMessage = "";
- try {
- if (isTrashed) {
- const { location } = getState().router;
- errorMessage = "Could not restore collection from trash";
- successMessage = "Restored from trash";
- await services.collectionService.untrash(uuid);
- if (matchCollectionRoute(location ? location.pathname : "")) {
- dispatch(navigateToTrash);
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<any> => {
+ let errorMessage = "";
+ let successMessage = "";
+ try {
+ if (isTrashed) {
+ const { location } = getState().router;
+ errorMessage = "Could not restore collection from trash";
+ successMessage = "Restored from trash";
+ await services.collectionService.untrash(uuid);
+ if (matchCollectionRoute(location ? location.pathname : "")) {
+ dispatch(navigateToTrash);
+ }
+ dispatch(trashPanelActions.REQUEST_ITEMS());
+ } else {
+ errorMessage = "Could not move collection to trash";
+ successMessage = "Added to trash";
+ await services.collectionService.trash(uuid);
+ dispatch(projectPanelActions.REQUEST_ITEMS());
}
- dispatch(trashPanelActions.REQUEST_ITEMS());
- } else {
- errorMessage = "Could not move collection to trash";
- successMessage = "Added to trash";
- await services.collectionService.trash(uuid);
- dispatch(projectPanelActions.REQUEST_ITEMS());
- }
- dispatch(
- snackbarActions.OPEN_SNACKBAR({
- message: successMessage,
- hideDuration: 2000,
- kind: SnackbarKind.SUCCESS,
- })
- );
- } catch (e) {
- if (e.status === 422) {
dispatch(
snackbarActions.OPEN_SNACKBAR({
- message: "Could not restore collection from trash: Duplicate name at destination",
- kind: SnackbarKind.ERROR,
- })
- );
- } else {
- dispatch(
- snackbarActions.OPEN_SNACKBAR({
- message: errorMessage,
- kind: SnackbarKind.ERROR,
+ message: successMessage,
+ hideDuration: 2000,
+ kind: SnackbarKind.SUCCESS,
})
);
+ } catch (e) {
+ if (e.status === 422) {
+ dispatch(
+ snackbarActions.OPEN_SNACKBAR({
+ message: "Could not restore collection from trash: Duplicate name at destination",
+ kind: SnackbarKind.ERROR,
+ })
+ );
+ } else {
+ dispatch(
+ snackbarActions.OPEN_SNACKBAR({
+ message: errorMessage,
+ kind: SnackbarKind.ERROR,
+ })
+ );
+ }
}
- }
- };
+ };
export const toggleTrashed = (kind: ResourceKind, uuid: string, ownerUuid: string, isTrashed: boolean) => (dispatch: Dispatch) => {
if (kind === ResourceKind.PROJECT) {
export const handleFirstTimeLoad = (action: any) => async (dispatch: Dispatch<any>, getState: () => RootState) => {
try {
await dispatch(action);
+ } catch (e) {
+ snackbarActions.OPEN_SNACKBAR({
+ message: "Error " + e,
+ hideDuration: 8000,
+ kind: SnackbarKind.WARNING,
+ })
} finally {
if (isWorkbenchLoading(getState())) {
dispatch(progressIndicatorActions.STOP_WORKING(WORKBENCH_LOADING_SCREEN));
import { ResourceWithName, ResponsiblePerson } from 'views-components/data-explorer/renderers';
import { MPVContainer, MPVPanelContent, MPVPanelState } from 'components/multi-panel-view/multi-panel-view';
import { resourceIsFrozen } from 'common/frozen-resources';
+ import { NotFoundView } from 'views/not-found-panel/not-found-panel';
type CssRules = 'root'
| 'button'
</Card>
</MPVPanelContent>
</MPVContainer >
- : null;
+ : <NotFoundView
+ icon={CollectionIcon}
+ messages={["Collection not found"]}
+ />
+ ;
}
handleContextMenu = (event: React.MouseEvent<any>) => {
// SPDX-License-Identifier: AGPL-3.0
import { RootState } from 'store/store';
+ import React from 'react';
import { connect } from 'react-redux';
import { NotFoundPanelRoot, NotFoundPanelRootDataProps } from 'views/not-found-panel/not-found-panel-root';
+ import { Grid } from '@material-ui/core';
+ import { DefaultView } from "components/default-view/default-view";
+ import { IconType } from 'components/icon/icon';
const mapStateToProps = (state: RootState): NotFoundPanelRootDataProps => {
return {
export const NotFoundPanel = connect(mapStateToProps, mapDispatchToProps)
(NotFoundPanelRoot) as any;
+
+ export interface NotFoundViewDataProps {
+ messages: string[];
+ icon?: IconType;
+ }
+
+ // TODO: optionally pass in the UUID and check if the
+ // reason the item is not found is because
+ // it or a parent project is actually in the trash.
+ // If so, offer to untrash the item or the parent project.
+ export const NotFoundView =
+ ({ messages, icon: Icon }: NotFoundViewDataProps) =>
+ <Grid
+ container
+ alignItems="center"
+ justify="center"
+ style={{ minHeight: "100%" }}
+ data-cy="not-found-view">
+ <DefaultView
+ icon={Icon}
+ messages={messages}
+ />
+ </Grid>;
// SPDX-License-Identifier: AGPL-3.0
import React from "react";
- import { Grid, StyleRulesCallback, WithStyles, withStyles } from "@material-ui/core";
- import { DefaultView } from "components/default-view/default-view";
+ import { StyleRulesCallback, WithStyles, withStyles } from "@material-ui/core";
import { ProcessIcon } from "components/icon/icon";
import { Process } from "store/processes/process";
import { SubprocessPanel } from "views/subprocess-panel/subprocess-panel";
import { ProcessCmdCard } from "./process-cmd-card";
import { ContainerRequestResource } from "models/container-request";
import { OutputDetails, NodeInstanceType } from "store/process-panel/process-panel";
+ import { NotFoundView } from 'views/not-found-panel/not-found-panel';
type CssRules = "root";
</MPVPanelContent>
</MPVContainer>
) : (
- <Grid
- container
- alignItems="center"
- justify="center"
- style={{ minHeight: "100%" }}>
- <DefaultView
- icon={ProcessIcon}
- messages={["Process not found"]}
- />
- </Grid>
+ <NotFoundView
+ icon={ProcessIcon}
+ messages={["Process not found"]}
+ />
);
}
);
import { CollectionResource } from 'models/collection';
import { resourceIsFrozen } from 'common/frozen-resources';
import { ProjectResource } from 'models/project';
+ import { NotFoundView } from 'views/not-found-panel/not-found-panel';
type CssRules = 'root' | 'button';
interface ProjectPanelDataProps {
currentItemId: string;
resources: ResourcesState;
+ project: GroupResource;
isAdmin: boolean;
userUuid: string;
dataExplorerItems: any;
type ProjectPanelProps = ProjectPanelDataProps & DispatchProp & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
- export const ProjectPanel = withStyles(styles)(
- connect((state: RootState) => ({
- currentItemId: getProperty(PROJECT_PANEL_CURRENT_UUID)(state.properties),
+ const mapStateToProps = (state: RootState) => {
+ const currentItemId = getProperty<string>(PROJECT_PANEL_CURRENT_UUID)(state.properties);
+ const project = getResource<GroupResource>(currentItemId || "")(state.resources);
+ return {
+ currentItemId,
+ project,
resources: state.resources,
userUuid: state.auth.user!.uuid,
- }))(
+ };
+ }
+
+ export const ProjectPanel = withStyles(styles)(
+ connect(mapStateToProps)(
class extends React.Component<ProjectPanelProps> {
render() {
const { classes } = this.props;
- return (
+ return this.props.project ?
<div data-cy='project-panel' className={classes.root}>
<DataExplorer
id={PROJECT_PANEL_ID}
defaultViewMessages={DEFAULT_VIEW_MESSAGES}
/>
</div>
- );
+ :
+ <NotFoundView
+ icon={ProjectIcon}
+ messages={["Project not found"]}
+ />
}
isCurrentItemChild = (resource: Resource) => {
Card,
CardHeader,
CardContent,
- IconButton,
+ IconButton
} from '@material-ui/core';
import { connect, DispatchProp } from "react-redux";
import { RouteComponentProps } from 'react-router';
import { openContextMenu, resourceUuidToContextMenuKind } from 'store/context-menu/context-menu-actions';
import { MPVContainer, MPVPanelContent, MPVPanelState } from 'components/multi-panel-view/multi-panel-view';
import { ProcessIOCard, ProcessIOCardType } from 'views/process-panel/process-io-card';
+ import { NotFoundView } from 'views/not-found-panel/not-found-panel';
type CssRules = 'root'
| 'button'
</Card>
</MPVPanelContent>
</MPVContainer>
- : null;
+ :
+ <NotFoundView
+ icon={WorkflowIcon}
+ messages={["Workflow not found"]}
+ />
}
handleContextMenu = (event: React.MouseEvent<any>) => {