workflow-view-middleware-service
authorPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Fri, 21 Sep 2018 12:04:06 +0000 (14:04 +0200)
committerPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Fri, 21 Sep 2018 12:04:06 +0000 (14:04 +0200)
Feature #13857

Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>

1  2 
src/store/store.ts
src/store/workbench/workbench-actions.ts
src/store/workflow-panel/workflow-middleware-service.ts
src/views-components/data-explorer/data-explorer.tsx
src/views-components/main-content-bar/main-content-bar.tsx
src/views/workbench/workbench.tsx
src/views/workflow-panel/workflow-description-card.tsx
src/views/workflow-panel/workflow-panel.tsx

index 97be93d868927822df3129b8e60a681de2abb5dc,012b747425b72e714472a5b2a0cfe89d03dc2546..16d0d055e30d9c100f8985e071ceee1e63768dd8
@@@ -73,6 -68,6 +73,7 @@@ export function configureStore(history
          favoritePanelMiddleware,
          trashPanelMiddleware,
          sharedWithMePanelMiddleware,
++        workflowPanelMiddleware
      ];
      const enhancer = composeEnhancers(applyMiddleware(...middlewares));
      return createStore(rootReducer, enhancer);
index 9124c660ddf4c4c69c459dc2e7ac4039459d9d01,99f5bc70f47ba2ebe5297ef777ddbf5b2cecc8c2..5440fc902561868ac7a72f0ddc0b9636e166ebd6
@@@ -39,8 -39,27 +39,29 @@@ import { loadProcessPanel } from '~/sto
  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';
+ import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions';
+ import { getProgressIndicator } from '../progress-indicator/progress-indicator-reducer';
+ export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen';
+ export const isWorkbenchLoading = (state: RootState) => {
+     const progress = getProgressIndicator(WORKBENCH_LOADING_SCREEN)(state.progressIndicator);
+     return progress ? progress.working : false;
+ };
+ const handleFirstTimeLoad = (action: any) =>
+     async (dispatch: Dispatch<any>, getState: () => RootState) => {
+         try {
+             await dispatch(action);
+         } finally {
+             if (isWorkbenchLoading(getState())) {
+                 dispatch(progressIndicatorActions.STOP_WORKING(WORKBENCH_LOADING_SCREEN));
+             }
+         }
+     };
  
  export const loadWorkbench = () =>
      async (dispatch: Dispatch, getState: () => RootState) => {
@@@ -53,7 -73,6 +75,7 @@@
                  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(workflowPanelActions.SET_COLUMNS({ columns: workflowPanelColumns }));
                  dispatch<any>(initSidePanelTree());
                  if (router.location) {
                      const match = matchRootRoute(router.location.pathname);
@@@ -273,14 -297,8 +300,14 @@@ export const reloadProjectMatchingUuid 
          }
      };
  
- export const loadSharedWithMe = (dispatch: Dispatch) => {
-     dispatch<any>(activateSidePanelTreeItem(SidePanelTreeCategory.SHARED_WITH_ME));
+ export const loadSharedWithMe = handleFirstTimeLoad(async (dispatch: Dispatch) => {
      dispatch<any>(loadSharedWithMePanel());
-     dispatch<any>(setSidePanelBreadcrumbs(SidePanelTreeCategory.SHARED_WITH_ME));
- };
+     await dispatch<any>(activateSidePanelTreeItem(SidePanelTreeCategory.SHARED_WITH_ME));
+     await dispatch<any>(setSidePanelBreadcrumbs(SidePanelTreeCategory.SHARED_WITH_ME));
+ });
 +
- export const loadWorkflow = (dispatch: Dispatch<any>) => {
++export const loadWorkflow = handleFirstTimeLoad(async (dispatch: Dispatch<any>) => {
 +    dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.WORKFLOWS));
-     dispatch(loadWorkflowPanel());
++    await dispatch(loadWorkflowPanel());
 +    dispatch(setSidePanelBreadcrumbs(SidePanelTreeCategory.WORKFLOWS));
- };
++});
index 2ca5337e2f39d7ed7d8a525d8fd12c71f89243ea,0000000000000000000000000000000000000000..7fa2dd4f819422b96e9acc9489c8e62e5635ca7e
mode 100644,000000..100644
--- /dev/null
@@@ -1,75 -1,0 +1,77 @@@
- import { DataExplorer } from '~/store/data-explorer/data-explorer-reducer';
 +// 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';
-             const response = await this.services.workflowService;
-             api.dispatch(updateResources([]));
-             api.dispatch(setItems({ kind: '', offset: 4, limit: 4, items: [], itemsAvailable: 4 }));
++import { DataExplorer, getDataExplorer } 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>) {
++        const state = api.getState();
++        const dataExplorer = getDataExplorer(state.dataExplorer, this.getId());
 +        try {
++            const response = await this.services.workflowService.list({ order: getOrder(dataExplorer) });
++            api.dispatch(updateResources(response.items));
++            api.dispatch(setItems(response));
 +        } 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
 +    });
index 74c3e64aaacf1139d5a90cff0c5ada69f40b14b2,74c3e64aaacf1139d5a90cff0c5ada69f40b14b2..17f2c77b0a44fdd3b235b1625c9d0ddf39ba430f
@@@ -15,7 -15,7 +15,7 @@@ import { DataColumns } from "~/componen
  interface Props {
      id: string;
      onRowClick: (item: any) => void;
--    onContextMenu: (event: React.MouseEvent<HTMLElement>, item: any) => void;
++    onContextMenu?: (event: React.MouseEvent<HTMLElement>, item: any) => void;
      onRowDoubleClick: (item: any) => void;
      extractKey?: (item: any) => React.Key;
  }
index 41442bba0d395e0f02f43d4ae6b3adb3bab00cbb,071b986ab57352050ef411e5cd6db7ecc3bd61ea..6fb419e36710aa187e0f28a79b46fba82ed5f0d7
@@@ -13,31 -11,22 +13,31 @@@ import { matchWorkflowRoute } from '~/r
  
  interface MainContentBarProps {
      onDetailsPanelToggle: () => void;
 +    buttonVisible: boolean;
  }
  
- const isButtonVisible = ({ router }: RootState) => {
 -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 isWorkflowPath = ({ router }: RootState) => {
 +    const pathname = router.location ? router.location.pathname : '';
-     const match = !matchWorkflowRoute(pathname);
++    const match = matchWorkflowRoute(pathname);
 +    return !!match;
 +};
 +
 +export const MainContentBar = connect((state: RootState) => ({
-     buttonVisible: isButtonVisible(state)
++    buttonVisible: !isWorkflowPath(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>);
index 54336575742ea83c51a6eb7a1b4a35d2a5f82427,78918be0c65c687d5230fa622c215c9e04ca847b..692c40b819b8b68cea59fa8f59b9f34b9b5a30aa
@@@ -45,10 -38,8 +38,9 @@@ import { Grid } from '@material-ui/core
  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';
+ type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
  
  const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
      root: {
      }
  });
  
- interface WorkbenchDataProps {
-     user?: User;
-     currentToken?: string;
-     working: boolean;
- }
- interface WorkbenchGeneralProps {
-     authService: AuthService;
-     buildInfo: string;
- }
- type WorkbenchProps = WorkbenchDataProps & WorkbenchGeneralProps & DispatchProp<any> & WithStyles<CssRules>;
+ type WorkbenchPanelProps = WithStyles<CssRules>;
  
- interface WorkbenchState {
-     searchText: string;
- }
- export const Workbench = withStyles(styles)(
-     connect<WorkbenchDataProps>(
-         (state: RootState) => ({
-             user: state.auth.user,
-             currentToken: state.auth.apiToken,
-             working: isSystemWorking(state.progressIndicator)
-         })
-     )(
-         class extends React.Component<WorkbenchProps, WorkbenchState> {
-             state = {
-                 searchText: "",
-             };
-             render() {
-                 const { classes } = this.props;
-                 return <>
-                     <MainAppBar
-                         searchText={this.state.searchText}
-                         user={this.props.user}
-                         onSearch={this.onSearch}
-                         buildInfo={this.props.buildInfo}>
-                         {this.props.working ? <LinearProgress color="secondary" /> : null}
-                     </MainAppBar>
-                     <Grid container direction="column" className={classes.root}>
-                         {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}>
-                                         <Grid container item xs component='aside' direction='column' className={classes.asidePanel}>
-                                             <SidePanel />
-                                         </Grid>
-                                         <Grid container item xs component="main" direction="column" className={classes.contentWrapper}>
-                                             <Grid item>
-                                                 <MainContentBar />
-                                             </Grid>
-                                             <Grid item xs className={classes.content}>
-                                                 <Switch>
-                                                     <Route path={Routes.PROJECTS} component={ProjectPanel} />
-                                                     <Route path={Routes.COLLECTIONS} component={CollectionPanel} />
-                                                     <Route path={Routes.FAVORITES} component={FavoritePanel} />
-                                                     <Route path={Routes.PROCESSES} component={ProcessPanel} />
-                                                     <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>
-                                     </SplitterLayout>
-                                 </Grid>
-                                 <Grid item>
-                                     <DetailsPanel />
-                                 </Grid>
-                             </Grid>
-                         }
+ export const WorkbenchPanel = 
+     withStyles(styles)(({ classes }: WorkbenchPanelProps) => 
+         <Grid container item xs className={classes.root}>
+             <Grid container item xs className={classes.container}>
+                 <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>
-                     <ContextMenu />
-                     <CopyCollectionDialog />
-                     <CopyProcessDialog />
-                     <CreateCollectionDialog />
-                     <CreateProjectDialog />
-                     <CurrentTokenDialog />
-                     <FileRemoveDialog />
-                     <FileRemoveDialog />
-                     <FilesUploadCollectionDialog />
-                     <MoveCollectionDialog />
-                     <MoveProcessDialog />
-                     <MoveProjectDialog />
-                     <MultipleFilesRemoveDialog />
-                     <PartialCopyCollectionDialog />
-                     <ProcessCommandDialog />
-                     <RenameFileDialog />
-                     <Snackbar />
-                     <UpdateCollectionDialog />
-                     <UpdateProcessDialog />
-                     <UpdateProjectDialog />
-                 </>;
-             }
-             onSearch = (searchText: string) => {
-                 this.setState({ searchText });
-                 this.props.dispatch(push(`/search?q=${searchText}`));
-             }
-             toggleDetailsPanel = () => {
-                 this.props.dispatch(detailsPanelActions.TOGGLE_DETAILS_PANEL());
-             }
-         }
-     )
- );
+                     <Grid container item xs component="main" direction="column" className={classes.contentWrapper}>
+                         <Grid item>
+                             <MainContentBar />
+                         </Grid>
+                         <Grid item xs className={classes.content}>
+                             <Switch>
+                                 <Route path={Routes.PROJECTS} component={ProjectPanel} />
+                                 <Route path={Routes.COLLECTIONS} component={CollectionPanel} />
+                                 <Route path={Routes.FAVORITES} component={FavoritePanel} />
+                                 <Route path={Routes.PROCESSES} component={ProcessPanel} />
+                                 <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>
+                 </SplitterLayout>
+             </Grid>
+             <Grid item>
+                 <DetailsPanel />
+             </Grid>
+             <ContextMenu />
+             <CopyCollectionDialog />
+             <CopyProcessDialog />
+             <CreateCollectionDialog />
+             <CreateProjectDialog />
+             <CurrentTokenDialog />
+             <FileRemoveDialog />
+             <FileRemoveDialog />
+             <FilesUploadCollectionDialog />
+             <MoveCollectionDialog />
+             <MoveProcessDialog />
+             <MoveProjectDialog />
+             <MultipleFilesRemoveDialog />
+             <PartialCopyCollectionDialog />
+             <ProcessCommandDialog />
+             <RenameFileDialog />
+             <Snackbar />
+             <UpdateCollectionDialog />
+             <UpdateProcessDialog />
+             <UpdateProjectDialog />
+         </Grid>
+     );
index 7aa26e6a12efbf3bbbc1bdb16c46b928bebebcd4,0000000000000000000000000000000000000000..e2b0f295e8e4ba5cd99a29ded6d8dc48c2b39943
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,37 @@@
- import { StyleRulesCallback, WithStyles, withStyles, Card, CardHeader, Typography } from '@material-ui/core';
 +// Copyright (C) The Arvados Authors. All rights reserved.
 +//
 +// SPDX-License-Identifier: AGPL-3.0
 +
 +import * as React from 'react';
-             <DataTableDefaultView
-                 icon={WorkflowIcon}
-                 messages={['Please select a workflow to see its description.']} />
++import { StyleRulesCallback, WithStyles, withStyles, Card, CardHeader, Typography, CardContent } 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>} />
++            <CardContent>
++                <DataTableDefaultView
++                    icon={WorkflowIcon}
++                    messages={['Please select a workflow to see its description.']} />
++            </CardContent>
 +        </Card>;
 +    });
index ef0b5fbb6e4f2d1ef6ae1eaa335a953626dea30f,0000000000000000000000000000000000000000..92bbcad65a1159a088a58d03614e8e2979cc2d52
mode 100644,000000..100644
--- /dev/null
@@@ -1,142 -1,0 +1,143 @@@
-             return <Grid container>
-                 <Grid item xs={6} style={{ paddingRight: '24px', display: 'grid' }}>
 +// 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,
 +            }
 +        ],
++        // do zmiany na ResourceAuthorisation
 +        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 spacing={16}>
++                <Grid item xs={6}>
 +                    <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));
 +        }
 +    }
 +);