[ARVADOS-WORKBENCH2] created: 1.2.0-421-geda5e30
Git user
git at public.curoverse.com
Thu Sep 20 10:40:18 EDT 2018
at eda5e30d786d5d1224a552e962b6b711efab7369 (commit)
commit eda5e30d786d5d1224a552e962b6b711efab7369
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date: Thu Sep 20 16:39:53 2018 +0200
workflow-view-without-working-services
Feature #13857
Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
diff --git a/src/models/resource.ts b/src/models/resource.ts
index 698bcf7..7e40c73 100644
--- a/src/models/resource.ts
+++ b/src/models/resource.ts
@@ -40,6 +40,7 @@ export enum ResourceObjectType {
GROUP = 'j7d0g',
LOG = '57u5n',
USER = 'tpzed',
+ WORKFLOW = '7fd4e'
}
export const RESOURCE_UUID_PATTERN = '.{5}-.{5}-.{15}';
@@ -70,6 +71,8 @@ export const extractUuidKind = (uuid: string = '') => {
return ResourceKind.CONTAINER;
case ResourceObjectType.LOG:
return ResourceKind.LOG;
+ case ResourceObjectType.WORKFLOW:
+ return ResourceKind.WORKFLOW;
default:
return undefined;
}
diff --git a/src/routes/route-change-handlers.ts b/src/routes/route-change-handlers.ts
index 33e0bef..9761314 100644
--- a/src/routes/route-change-handlers.ts
+++ b/src/routes/route-change-handlers.ts
@@ -4,11 +4,10 @@
import { History, Location } from 'history';
import { RootStore } from '~/store/store';
-import { matchProcessRoute, matchProcessLogRoute, matchProjectRoute, matchCollectionRoute, matchFavoritesRoute, matchTrashRoute, matchRootRoute, matchSharedWithMeRoute } from './routes';
+import { matchProcessRoute, matchProcessLogRoute, matchProjectRoute, matchCollectionRoute, matchFavoritesRoute, matchTrashRoute, matchRootRoute, matchSharedWithMeRoute, matchWorkflowRoute } from './routes';
import { loadProject, loadCollection, loadFavorites, loadTrash, loadProcess, loadProcessLog } from '~/store/workbench/workbench-actions';
import { navigateToRootProject } from '~/store/navigation/navigation-action';
-import { navigateToSharedWithMe } from '../store/navigation/navigation-action';
-import { loadSharedWithMe } from '../store/workbench/workbench-actions';
+import { loadSharedWithMe, loadWorkflow } from '~/store/workbench/workbench-actions';
export const addRouteChangeHandlers = (history: History, store: RootStore) => {
const handler = handleLocationChange(store);
@@ -25,6 +24,7 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
const processMatch = matchProcessRoute(pathname);
const processLogMatch = matchProcessLogRoute(pathname);
const sharedWithMeMatch = matchSharedWithMeRoute(pathname);
+ const workflowMatch = matchWorkflowRoute(pathname);
if (projectMatch) {
store.dispatch(loadProject(projectMatch.params.id));
@@ -42,5 +42,7 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
store.dispatch(navigateToRootProject);
} else if (sharedWithMeMatch) {
store.dispatch(loadSharedWithMe);
+ } else if (workflowMatch) {
+ store.dispatch(loadWorkflow);
}
};
diff --git a/src/routes/routes.ts b/src/routes/routes.ts
index fb28bd0..34b15e1 100644
--- a/src/routes/routes.ts
+++ b/src/routes/routes.ts
@@ -17,6 +17,7 @@ export const Routes = {
TRASH: '/trash',
PROCESS_LOGS: `/process-logs/:id(${RESOURCE_UUID_PATTERN})`,
SHARED_WITH_ME: '/shared-with-me',
+ WORKFLOWS: '/workflows'
};
export const getResourceUrl = (uuid: string) => {
@@ -64,3 +65,6 @@ export const matchProcessLogRoute = (route: string) =>
export const matchSharedWithMeRoute = (route: string) =>
matchPath(route, { path: Routes.SHARED_WITH_ME });
+
+export const matchWorkflowRoute = (route: string) =>
+ matchPath<ResourceRouteParams>(route, { path: Routes.WORKFLOWS });
diff --git a/src/services/services.ts b/src/services/services.ts
index 9c764b0..738b69d 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -21,6 +21,7 @@ import { ContainerRequestService } from './container-request-service/container-r
import { ContainerService } from './container-service/container-service';
import { LogService } from './log-service/log-service';
import { ApiActions } from "~/services/api/api-actions";
+import { WorkflowService } from './workflow-service/workflow-service';
export type ServiceRepository = ReturnType<typeof createServices>;
@@ -39,6 +40,7 @@ export const createServices = (config: Config, actions: ApiActions) => {
const logService = new LogService(apiClient, actions);
const projectService = new ProjectService(apiClient, actions);
const userService = new UserService(apiClient, actions);
+ const workflowService = new WorkflowService(apiClient, actions);
const ancestorsService = new AncestorService(groupsService, userService);
const authService = new AuthService(apiClient, config.rootUrl, actions);
@@ -64,6 +66,7 @@ export const createServices = (config: Config, actions: ApiActions) => {
tagService,
userService,
webdavClient,
+ workflowService
};
};
diff --git a/src/services/workflow-service/workflow-service.ts b/src/services/workflow-service/workflow-service.ts
new file mode 100644
index 0000000..60f898f
--- /dev/null
+++ b/src/services/workflow-service/workflow-service.ts
@@ -0,0 +1,14 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { AxiosInstance } from "axios";
+import { WorkflowResource } from '~/models/workflow';
+import { CommonResourceService } from "~/services/common-service/common-resource-service";
+import { ApiActions } from "~/services/api/api-actions";
+
+export class WorkflowService extends CommonResourceService<WorkflowResource> {
+ constructor(serverApi: AxiosInstance, actions: ApiActions) {
+ super(serverApi, "workflows", actions);
+ }
+}
diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts
index c68c539..d332e0f 100644
--- a/src/store/navigation/navigation-action.ts
+++ b/src/store/navigation/navigation-action.ts
@@ -24,8 +24,10 @@ export const navigateTo = (uuid: string) =>
}
if (uuid === SidePanelTreeCategory.FAVORITES) {
dispatch<any>(navigateToFavorites);
- } else if(uuid === SidePanelTreeCategory.SHARED_WITH_ME){
+ } else if (uuid === SidePanelTreeCategory.SHARED_WITH_ME) {
dispatch(navigateToSharedWithMe);
+ } else if (uuid === SidePanelTreeCategory.WORKFLOWS) {
+ dispatch(navigateToWorkflows);
}
};
@@ -33,6 +35,8 @@ export const navigateToFavorites = push(Routes.FAVORITES);
export const navigateToTrash = push(Routes.TRASH);
+export const navigateToWorkflows = push(Routes.WORKFLOWS);
+
export const navigateToProject = compose(push, getProjectUrl);
export const navigateToCollection = compose(push, getCollectionUrl);
diff --git a/src/store/shared-with-me-panel/shared-with-me-middleware-service.ts b/src/store/shared-with-me-panel/shared-with-me-middleware-service.ts
index 1ebb13e..c26a7a5 100644
--- a/src/store/shared-with-me-panel/shared-with-me-middleware-service.ts
+++ b/src/store/shared-with-me-panel/shared-with-me-middleware-service.ts
@@ -37,7 +37,6 @@ export class SharedWithMeMiddlewareService extends DataExplorerMiddlewareService
} catch (e) {
api.dispatch(couldNotFetchSharedItems());
}
-
}
}
diff --git a/src/store/side-panel-tree/side-panel-tree-actions.ts b/src/store/side-panel-tree/side-panel-tree-actions.ts
index 23c5ea2..073de22 100644
--- a/src/store/side-panel-tree/side-panel-tree-actions.ts
+++ b/src/store/side-panel-tree/side-panel-tree-actions.ts
@@ -13,7 +13,6 @@ import { getTreePicker, TreePicker } from '../tree-picker/tree-picker';
import { TreeItemStatus } from "~/components/tree/tree";
import { getNodeAncestors, getNodeValue, getNodeAncestorsIds, getNode } from '~/models/tree';
import { ProjectResource } from '~/models/project';
-import { progressIndicatorActions } from '../progress-indicator/progress-indicator-actions';
export enum SidePanelTreeCategory {
PROJECTS = 'Projects',
diff --git a/src/store/side-panel/side-panel-action.ts b/src/store/side-panel/side-panel-action.ts
index 2a5fdd0..fd08ee1 100644
--- a/src/store/side-panel/side-panel-action.ts
+++ b/src/store/side-panel/side-panel-action.ts
@@ -4,7 +4,7 @@
import { Dispatch } from 'redux';
import { isSidePanelTreeCategory, SidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree-actions';
-import { navigateToFavorites, navigateTo, navigateToTrash, navigateToSharedWithMe } from '../navigation/navigation-action';
+import { navigateToFavorites, navigateTo, navigateToTrash, navigateToSharedWithMe, navigateToWorkflows } from '../navigation/navigation-action';
import { snackbarActions } from '~/store/snackbar/snackbar-actions';
export const navigateFromSidePanel = (id: string) =>
@@ -24,6 +24,8 @@ const getSidePanelTreeCategoryAction = (id: string) => {
return navigateToTrash;
case SidePanelTreeCategory.SHARED_WITH_ME:
return navigateToSharedWithMe;
+ case SidePanelTreeCategory.WORKFLOWS:
+ return navigateToWorkflows;
default:
return sidePanelTreeCategoryNotAvailable(id);
}
diff --git a/src/store/store.ts b/src/store/store.ts
index 012b747..97be93d 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -35,6 +35,8 @@ import { processPanelReducer } from '~/store/process-panel/process-panel-reducer
import { SHARED_WITH_ME_PANEL_ID } from '~/store/shared-with-me-panel/shared-with-me-panel-actions';
import { SharedWithMeMiddlewareService } from './shared-with-me-panel/shared-with-me-middleware-service';
import { progressIndicatorReducer } from './progress-indicator/progress-indicator-reducer';
+import { WorkflowMiddlewareService } from './workflow-panel/workflow-middleware-service';
+import { WORKFLOW_PANEL_ID } from './workflow-panel/workflow-panel-actions';
const composeEnhancers =
(process.env.NODE_ENV === 'development' &&
@@ -60,6 +62,9 @@ export function configureStore(history: History, services: ServiceRepository): R
const sharedWithMePanelMiddleware = dataExplorerMiddleware(
new SharedWithMeMiddlewareService(services, SHARED_WITH_ME_PANEL_ID)
);
+ const workflowPanelMiddleware = dataExplorerMiddleware(
+ new WorkflowMiddlewareService(services, WORKFLOW_PANEL_ID)
+ );
const middlewares: Middleware[] = [
routerMiddleware(history),
diff --git a/src/store/workbench/workbench-actions.ts b/src/store/workbench/workbench-actions.ts
index c79dc48..9124c66 100644
--- a/src/store/workbench/workbench-actions.ts
+++ b/src/store/workbench/workbench-actions.ts
@@ -38,8 +38,9 @@ import { initProcessLogsPanel } from '../process-logs-panel/process-logs-panel-a
import { loadProcessPanel } from '~/store/process-panel/process-panel-actions';
import { sharedWithMePanelActions } from '~/store/shared-with-me-panel/shared-with-me-panel-actions';
import { loadSharedWithMePanel } from '../shared-with-me-panel/shared-with-me-panel-actions';
-
import { CopyFormDialogData } from '~/store/copy-dialog/copy-dialog';
+import { loadWorkflowPanel, workflowPanelActions } from '~/store/workflow-panel/workflow-panel-actions';
+import { workflowPanelColumns } from '~/views/workflow-panel/workflow-panel';
export const loadWorkbench = () =>
async (dispatch: Dispatch, getState: () => RootState) => {
@@ -52,6 +53,7 @@ export const loadWorkbench = () =>
dispatch(favoritePanelActions.SET_COLUMNS({ columns: favoritePanelColumns }));
dispatch(trashPanelActions.SET_COLUMNS({ columns: trashPanelColumns }));
dispatch(sharedWithMePanelActions.SET_COLUMNS({ columns: projectPanelColumns }));
+ dispatch(workflowPanelActions.SET_COLUMNS({ columns: workflowPanelColumns}));
dispatch<any>(initSidePanelTree());
if (router.location) {
const match = matchRootRoute(router.location.pathname);
@@ -276,3 +278,9 @@ export const loadSharedWithMe = (dispatch: Dispatch) => {
dispatch<any>(loadSharedWithMePanel());
dispatch<any>(setSidePanelBreadcrumbs(SidePanelTreeCategory.SHARED_WITH_ME));
};
+
+export const loadWorkflow = (dispatch: Dispatch<any>) => {
+ dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.WORKFLOWS));
+ dispatch(loadWorkflowPanel());
+ dispatch(setSidePanelBreadcrumbs(SidePanelTreeCategory.WORKFLOWS));
+};
diff --git a/src/store/workflow-panel/workflow-middleware-service.ts b/src/store/workflow-panel/workflow-middleware-service.ts
new file mode 100644
index 0000000..2ca5337
--- /dev/null
+++ b/src/store/workflow-panel/workflow-middleware-service.ts
@@ -0,0 +1,75 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { ServiceRepository } from '~/services/services';
+import { MiddlewareAPI, Dispatch } from 'redux';
+import { DataExplorerMiddlewareService, dataExplorerToListParams, listResultsToDataExplorerItemsMeta } from '~/store/data-explorer/data-explorer-middleware-service';
+import { RootState } from '~/store/store';
+import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
+import { DataExplorer } from '~/store/data-explorer/data-explorer-reducer';
+import { updateResources } from '~/store/resources/resources-actions';
+import { FilterBuilder } from '~/services/api/filter-builder';
+import { SortDirection } from '~/components/data-table/data-column';
+import { WorkflowPanelColumnNames } from '~/views/workflow-panel/workflow-panel';
+import { OrderDirection, OrderBuilder } from '~/services/api/order-builder';
+import { WorkflowResource } from '~/models/workflow';
+import { ListResults } from '~/services/common-service/common-resource-service';
+import { workflowPanelActions } from './workflow-panel-actions';
+
+export class WorkflowMiddlewareService extends DataExplorerMiddlewareService {
+ constructor(private services: ServiceRepository, id: string) {
+ super(id);
+ }
+
+ async requestItems(api: MiddlewareAPI<Dispatch, RootState>) {
+ try {
+ const response = await this.services.workflowService;
+ api.dispatch(updateResources([]));
+ api.dispatch(setItems({ kind: '', offset: 4, limit: 4, items: [], itemsAvailable: 4 }));
+ } catch {
+ api.dispatch(couldNotFetchWorkflows());
+ }
+ }
+}
+
+export const getParams = (dataExplorer: DataExplorer) => ({
+ ...dataExplorerToListParams(dataExplorer),
+ order: getOrder(dataExplorer),
+ filters: getFilters(dataExplorer),
+});
+
+export const getFilters = (dataExplorer: DataExplorer) => {
+ const filters = new FilterBuilder()
+ .addILike("name", dataExplorer.searchValue)
+ .getFilters();
+ return `[${filters}]`;
+};
+
+export const getOrder = (dataExplorer: DataExplorer) => {
+ const sortColumn = dataExplorer.columns.find(c => c.sortDirection !== SortDirection.NONE);
+ const order = new OrderBuilder<WorkflowResource>();
+ if (sortColumn) {
+ const sortDirection = sortColumn && sortColumn.sortDirection === SortDirection.ASC
+ ? OrderDirection.ASC
+ : OrderDirection.DESC;
+ const columnName = sortColumn && sortColumn.name === WorkflowPanelColumnNames.NAME ? "name" : "modifiedAt";
+ return order
+ .addOrder(sortDirection, columnName)
+ .getOrder();
+ } else {
+ return order.getOrder();
+ }
+};
+
+export const setItems = (listResults: ListResults<WorkflowResource>) =>
+ workflowPanelActions.SET_ITEMS({
+ ...listResultsToDataExplorerItemsMeta(listResults),
+ items: listResults.items.map(resource => resource.uuid),
+ });
+
+const couldNotFetchWorkflows = () =>
+ snackbarActions.OPEN_SNACKBAR({
+ message: 'Could not fetch workflows.',
+ kind: SnackbarKind.ERROR
+ });
\ No newline at end of file
diff --git a/src/store/workflow-panel/workflow-panel-actions.ts b/src/store/workflow-panel/workflow-panel-actions.ts
new file mode 100644
index 0000000..733fab1
--- /dev/null
+++ b/src/store/workflow-panel/workflow-panel-actions.ts
@@ -0,0 +1,16 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from 'redux';
+import { RootState } from '~/store/store';
+import { ServiceRepository } from '~/services/services';
+import { bindDataExplorerActions } from '~/store/data-explorer/data-explorer-action';
+
+export const WORKFLOW_PANEL_ID = "workflowPanel";
+export const workflowPanelActions = bindDataExplorerActions(WORKFLOW_PANEL_ID);
+
+export const loadWorkflowPanel = () =>
+ (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ dispatch(workflowPanelActions.REQUEST_ITEMS());
+ };
\ No newline at end of file
diff --git a/src/views-components/data-explorer/renderers.tsx b/src/views-components/data-explorer/renderers.tsx
index b9cc63c..b9475f5 100644
--- a/src/views-components/data-explorer/renderers.tsx
+++ b/src/views-components/data-explorer/renderers.tsx
@@ -6,7 +6,7 @@ import * as React from 'react';
import { Grid, Typography, withStyles } from '@material-ui/core';
import { FavoriteStar } from '../favorite-star/favorite-star';
import { ResourceKind, TrashableResource } from '~/models/resource';
-import { ProjectIcon, CollectionIcon, ProcessIcon, DefaultIcon } from '~/components/icon/icon';
+import { ProjectIcon, CollectionIcon, ProcessIcon, DefaultIcon, WorkflowIcon } from '~/components/icon/icon';
import { formatDate, formatFileSize } from '~/common/formatters';
import { resourceLabel } from '~/common/labels';
import { connect } from 'react-redux';
@@ -48,6 +48,8 @@ export const renderIcon = (item: { kind: string }) => {
return <CollectionIcon />;
case ResourceKind.PROCESS:
return <ProcessIcon />;
+ case ResourceKind.WORKFLOW:
+ return <WorkflowIcon />;
default:
return <DefaultIcon />;
}
diff --git a/src/views-components/main-content-bar/main-content-bar.tsx b/src/views-components/main-content-bar/main-content-bar.tsx
index 071b986..41442bb 100644
--- a/src/views-components/main-content-bar/main-content-bar.tsx
+++ b/src/views-components/main-content-bar/main-content-bar.tsx
@@ -8,25 +8,36 @@ import { DetailsIcon } from "~/components/icon/icon";
import { Breadcrumbs } from "~/views-components/breadcrumbs/breadcrumbs";
import { detailsPanelActions } from "~/store/details-panel/details-panel-action";
import { connect } from 'react-redux';
+import { RootState } from '~/store/store';
+import { matchWorkflowRoute } from '~/routes/routes';
interface MainContentBarProps {
onDetailsPanelToggle: () => void;
+ buttonVisible: boolean;
}
-export const MainContentBar = connect(undefined, {
- onDetailsPanelToggle: detailsPanelActions.TOGGLE_DETAILS_PANEL
-})((props: MainContentBarProps) =>
- <Toolbar>
- <Grid container>
- <Grid container item xs alignItems="center">
- <Breadcrumbs />
- </Grid>
- <Grid item>
- <Tooltip title="Additional Info">
- <IconButton color="inherit" onClick={props.onDetailsPanelToggle}>
- <DetailsIcon />
- </IconButton>
- </Tooltip>
+const isButtonVisible = ({ router }: RootState) => {
+ const pathname = router.location ? router.location.pathname : '';
+ const match = !matchWorkflowRoute(pathname);
+ return !!match;
+};
+
+export const MainContentBar = connect((state: RootState) => ({
+ buttonVisible: isButtonVisible(state)
+}), {
+ onDetailsPanelToggle: detailsPanelActions.TOGGLE_DETAILS_PANEL
+ })((props: MainContentBarProps) =>
+ <Toolbar>
+ <Grid container>
+ <Grid container item xs alignItems="center">
+ <Breadcrumbs />
+ </Grid>
+ <Grid item>
+ {props.buttonVisible ? <Tooltip title="Additional Info">
+ <IconButton color="inherit" onClick={props.onDetailsPanelToggle}>
+ <DetailsIcon />
+ </IconButton>
+ </Tooltip> : null}
+ </Grid>
</Grid>
- </Grid>
- </Toolbar>);
+ </Toolbar>);
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index ad1a266..5433657 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -46,6 +46,7 @@ import { SharedWithMePanel } from '../shared-with-me-panel/shared-with-me-panel'
import SplitterLayout from 'react-splitter-layout';
import { ProcessCommandDialog } from '~/views-components/process-command-dialog/process-command-dialog';
import { isSystemWorking } from "~/store/progress-indicator/progress-indicator-reducer";
+import { WorkflowPanel } from '~/views/workflow-panel/workflow-panel';
type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content' | 'appBar';
@@ -125,8 +126,8 @@ export const Workbench = withStyles(styles)(
{this.props.user &&
<Grid container item xs alignItems="stretch" wrap="nowrap">
<Grid container item className={classes.container}>
- <SplitterLayout customClassName={classes.splitter} percentage={true}
- primaryIndex={0} primaryMinSize={20} secondaryInitialSize={80} secondaryMinSize={40}>
+ <SplitterLayout customClassName={classes.splitter} percentage={true}
+ primaryIndex={0} primaryMinSize={20} secondaryInitialSize={80} secondaryMinSize={40}>
<Grid container item xs component='aside' direction='column' className={classes.asidePanel}>
<SidePanel />
</Grid>
@@ -143,6 +144,7 @@ export const Workbench = withStyles(styles)(
<Route path={Routes.TRASH} component={TrashPanel} />
<Route path={Routes.PROCESS_LOGS} component={ProcessLogPanel} />
<Route path={Routes.SHARED_WITH_ME} component={SharedWithMePanel} />
+ <Route path={Routes.WORKFLOWS} component={WorkflowPanel} />
</Switch>
</Grid>
</Grid>
diff --git a/src/views/workflow-panel/workflow-description-card.tsx b/src/views/workflow-panel/workflow-description-card.tsx
new file mode 100644
index 0000000..7aa26e6
--- /dev/null
+++ b/src/views/workflow-panel/workflow-description-card.tsx
@@ -0,0 +1,35 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { StyleRulesCallback, WithStyles, withStyles, Card, CardHeader, Typography } from '@material-ui/core';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { WorkflowIcon } from '~/components/icon/icon';
+import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
+
+export type CssRules = 'card';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+ card: {
+ height: '100%'
+ }
+});
+
+interface WorkflowDescriptionCardDataProps {
+}
+
+type WorkflowDescriptionCardProps = WorkflowDescriptionCardDataProps & WithStyles<CssRules>;
+
+export const WorkflowDescriptionCard = withStyles(styles)(
+ ({ classes }: WorkflowDescriptionCardProps) => {
+ return <Card className={classes.card}>
+ <CardHeader
+ title={<Typography noWrap variant="body2">
+ Workflow description:
+ </Typography>} />
+ <DataTableDefaultView
+ icon={WorkflowIcon}
+ messages={['Please select a workflow to see its description.']} />
+ </Card>;
+ });
\ No newline at end of file
diff --git a/src/views/workflow-panel/workflow-panel.tsx b/src/views/workflow-panel/workflow-panel.tsx
new file mode 100644
index 0000000..ef0b5fb
--- /dev/null
+++ b/src/views/workflow-panel/workflow-panel.tsx
@@ -0,0 +1,142 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { DataExplorer } from "~/views-components/data-explorer/data-explorer";
+import { connect, DispatchProp } from 'react-redux';
+import { RootState } from '~/store/store';
+import { WorkflowIcon } from '~/components/icon/icon';
+import { ResourcesState, getResource } from '~/store/resources/resources';
+import { navigateTo } from "~/store/navigation/navigation-action";
+import { loadDetailsPanel } from "~/store/details-panel/details-panel-action";
+import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
+import { WORKFLOW_PANEL_ID } from '~/store/workflow-panel/workflow-panel-actions';
+import { openContextMenu } from '~/store/context-menu/context-menu-actions';
+import { GroupResource } from '~/models/group';
+import { ContextMenuKind } from '~/views-components/context-menu/context-menu';
+import {
+ ResourceLastModifiedDate,
+ ResourceName,
+} from "~/views-components/data-explorer/renderers";
+import { SortDirection } from '~/components/data-table/data-column';
+import { DataColumns } from '~/components/data-table/data-table';
+import { DataTableFilterItem } from '~/components/data-table-filters/data-table-filters';
+import { Grid } from '@material-ui/core';
+import { WorkflowDescriptionCard } from './workflow-description-card';
+
+export enum WorkflowPanelColumnNames {
+ NAME = "Name",
+ AUTHORISATION = "Authorisation",
+ LAST_MODIFIED = "Last modified",
+}
+
+interface WorkflowPanelDataProps {
+ resources: ResourcesState;
+}
+
+export enum ResourceStatus {
+ PUBLIC = 'public',
+ PRIVATE = 'private',
+ SHARED = 'shared'
+}
+
+const resourceStatus = (type: string) => {
+ switch (type) {
+ case ResourceStatus.PUBLIC:
+ return "Public";
+ case ResourceStatus.PRIVATE:
+ return "Private";
+ case ResourceStatus.SHARED:
+ return "Shared";
+ default:
+ return "Unknown";
+ }
+};
+
+export const workflowPanelColumns: DataColumns<string, DataTableFilterItem> = [
+ {
+ name: WorkflowPanelColumnNames.NAME,
+ selected: true,
+ configurable: true,
+ sortDirection: SortDirection.ASC,
+ filters: [],
+ render: (uuid: string) => <ResourceName uuid={uuid} />
+ },
+ {
+ name: WorkflowPanelColumnNames.AUTHORISATION,
+ selected: true,
+ configurable: true,
+ sortDirection: SortDirection.NONE,
+ filters: [
+ {
+ name: resourceStatus(ResourceStatus.PUBLIC),
+ selected: true,
+ },
+ {
+ name: resourceStatus(ResourceStatus.PRIVATE),
+ selected: true,
+ },
+ {
+ name: resourceStatus(ResourceStatus.SHARED),
+ selected: true,
+ }
+ ],
+ render: (uuid: string) => <ResourceName uuid={uuid} />,
+ },
+ {
+ name: WorkflowPanelColumnNames.LAST_MODIFIED,
+ selected: true,
+ configurable: true,
+ sortDirection: SortDirection.NONE,
+ filters: [],
+ render: (uuid: string) => <ResourceLastModifiedDate uuid={uuid} />
+ }
+];
+
+type WorkflowPanelProps = WorkflowPanelDataProps & DispatchProp;
+
+export const WorkflowPanel = connect((state: RootState) => ({
+ resources: state.resources
+}))(
+ class extends React.Component<WorkflowPanelProps> {
+ render() {
+ return <Grid container>
+ <Grid item xs={6} style={{ paddingRight: '24px', display: 'grid' }}>
+ <DataExplorer
+ id={WORKFLOW_PANEL_ID}
+ onRowClick={this.handleRowClick}
+ onRowDoubleClick={this.handleRowDoubleClick}
+ onContextMenu={this.handleContextMenu}
+ contextMenuColumn={false}
+ dataTableDefaultView={<DataTableDefaultView icon={WorkflowIcon} />} />
+ </Grid>
+ <Grid item xs={6}>
+ <WorkflowDescriptionCard />
+ </Grid>
+ </Grid>;
+ }
+
+ handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
+ const resource = getResource<GroupResource>(resourceUuid)(this.props.resources);
+ if (resource) {
+ this.props.dispatch<any>(openContextMenu(event, {
+ name: '',
+ uuid: resource.uuid,
+ ownerUuid: resource.ownerUuid,
+ isTrashed: resource.isTrashed,
+ kind: resource.kind,
+ menuKind: ContextMenuKind.PROJECT,
+ }));
+ }
+ }
+
+ handleRowDoubleClick = (uuid: string) => {
+ this.props.dispatch<any>(navigateTo(uuid));
+ }
+
+ handleRowClick = (uuid: string) => {
+ this.props.dispatch(loadDetailsPanel(uuid));
+ }
+ }
+);
\ No newline at end of file
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list