merge master
authorPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Tue, 28 Aug 2018 11:08:29 +0000 (13:08 +0200)
committerPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Tue, 28 Aug 2018 11:08:29 +0000 (13:08 +0200)
Feature #13858

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

1  2 
src/index.tsx
src/routes/routes.ts
src/views/process-panel/process-panel.tsx
src/views/workbench/workbench.tsx

diff --cc src/index.tsx
index c6a5da24a372f2f3adbb822435e69008de18a3bc,c7f76c68f8c2e971a7558122f50ba0602512fea9..d3115a6754bf70feb6731e8910e71b04859534f4
@@@ -27,7 -27,9 +27,10 @@@ import { collectionFilesActionSet } fro
  import { collectionFilesItemActionSet } from './views-components/context-menu/action-sets/collection-files-item-action-set';
  import { collectionActionSet } from './views-components/context-menu/action-sets/collection-action-set';
  import { collectionResourceActionSet } from './views-components/context-menu/action-sets/collection-resource-action-set';
 +import { processActionSet } from './views-components/context-menu/action-sets/process-action-set';
+ import { addRouteChangeHandlers } from './routes/routes';
+ import { loadWorkbench } from './store/workbench/workbench-actions';
+ import { Routes } from '~/routes/routes';
  
  const getBuildNumber = () => "BN-" + (process.env.REACT_APP_BUILD_NUMBER || "dev");
  const getGitCommit = () => "GIT-" + (process.env.REACT_APP_GIT_COMMIT || "latest").substr(0, 7);
@@@ -45,10 -47,9 +48,10 @@@ addMenuActionSet(ContextMenuKind.COLLEC
  addMenuActionSet(ContextMenuKind.COLLECTION_FILES_ITEM, collectionFilesItemActionSet);
  addMenuActionSet(ContextMenuKind.COLLECTION, collectionActionSet);
  addMenuActionSet(ContextMenuKind.COLLECTION_RESOURCE, collectionResourceActionSet);
 +addMenuActionSet(ContextMenuKind.PROCESS, processActionSet);
  
  fetchConfig()
-     .then(config => {
+     .then((config) => {
          const history = createBrowserHistory();
          const services = createServices(config);
          const store = configureStore(history, services);
index 0000000000000000000000000000000000000000,0279bb06f25f0a2372a848f7cc7cbb9585fcd36c..0bf7110126df7849f4571cfbe6e2c21dffa49fdd
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,71 +1,72 @@@
+ // Copyright (C) The Arvados Authors. All rights reserved.
+ //
+ // SPDX-License-Identifier: AGPL-3.0
+ import { History, Location } from 'history';
+ import { RootStore } from '../store/store';
+ import { matchPath } from 'react-router';
+ import { ResourceKind, RESOURCE_UUID_PATTERN, extractUuidKind } from '~/models/resource';
+ import { getProjectUrl } from '../models/project';
+ import { getCollectionUrl } from '~/models/collection';
+ import { loadProject, loadFavorites, loadCollection } from '../store/workbench/workbench-actions';
+ export const Routes = {
+     ROOT: '/',
+     TOKEN: '/token',
+     PROJECTS: `/projects/:id(${RESOURCE_UUID_PATTERN})`,
+     COLLECTIONS: `/collections/:id(${RESOURCE_UUID_PATTERN})`,
++    PROCESS: `/processes/:id(${RESOURCE_UUID_PATTERN})`,
+     FAVORITES: '/favorites',
+ };
+ export const getResourceUrl = (uuid: string) => {
+     const kind = extractUuidKind(uuid);
+     switch (kind) {
+         case ResourceKind.PROJECT:
+             return getProjectUrl(uuid);
+         case ResourceKind.COLLECTION:
+             return getCollectionUrl(uuid);
+         default:
+             return undefined;
+     }
+ };
+ export const addRouteChangeHandlers = (history: History, store: RootStore) => {
+     const handler = handleLocationChange(store);
+     handler(history.location);
+     history.listen(handler);
+ };
+ export const matchRootRoute = (route: string) =>
+     matchPath(route, { path: Routes.ROOT, exact: true });
+ export const matchFavoritesRoute = (route: string) =>
+     matchPath(route, { path: Routes.FAVORITES });
+ export interface ProjectRouteParams {
+     id: string;
+ }
+ export const matchProjectRoute = (route: string) =>
+     matchPath<ProjectRouteParams>(route, { path: Routes.PROJECTS });
+ export interface CollectionRouteParams {
+     id: string;
+ }
+ export const matchCollectionRoute = (route: string) =>
+     matchPath<CollectionRouteParams>(route, { path: Routes.COLLECTIONS });
+ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
+     const projectMatch = matchProjectRoute(pathname);
+     const collectionMatch = matchCollectionRoute(pathname);
+     const favoriteMatch = matchFavoritesRoute(pathname);
+     if (projectMatch) {
+         store.dispatch(loadProject(projectMatch.params.id));
+     } else if (collectionMatch) {
+         store.dispatch(loadCollection(collectionMatch.params.id));
+     } else if (favoriteMatch) {
+         store.dispatch(loadFavorites());
+     }
+ };
index 08bd37015d228837d3834952bacb4ed8041a40a2,0000000000000000000000000000000000000000..70a7a405ac8e83ee6c66ee18689bad107c46683c
mode 100644,000000..100644
--- /dev/null
@@@ -1,129 -1,0 +1,141 @@@
-                 const { classes, onContextMenu, item } = this.props;
 +// 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, IconButton, CardContent, Grid, Chip
 +} from '@material-ui/core';
 +import { ArvadosTheme } from '~/common/custom-theme';
 +import { ProcessResource } from '~/models/process';
 +import { DispatchProp, connect } from 'react-redux';
 +import { RouteComponentProps } from 'react-router';
 +import { MoreOptionsIcon, ProcessIcon } from '~/components/icon/icon';
 +import { DetailsAttribute } from '~/components/details-attribute/details-attribute';
 +import { RootState } from '~/store/store';
++import { ContextMenuKind } from '~/views-components/context-menu/context-menu';
++import { openContextMenu } from '~/store/context-menu/context-menu-actions';
 +
 +type CssRules = 'card' | 'iconHeader' | 'label' | 'value' | 'content' | 'chip' | 'headerText' | 'link';
 +
 +const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 +    card: {
 +        marginBottom: theme.spacing.unit * 2,
 +        width: '60%'
 +    },
 +    iconHeader: {
 +        fontSize: '1.875rem',
 +        color: theme.customs.colors.green700
 +    },
 +    label: {
 +        fontSize: '0.875rem',
 +    },
 +    value: {
 +        textTransform: 'none',
 +        fontSize: '0.875rem'
 +    },
 +    content: {
 +        display: 'flex',
 +        paddingBottom: '0px ',
 +        paddingTop: '0px',
 +        '&:last-child': {
 +            paddingBottom: '0px ',
 +        }
 +    },
 +    link: {
 +        fontSize: '0.875rem',
 +        '&:hover': {
 +            color: theme.palette.primary.main,
 +            cursor: 'pointer'
 +        }
 +    },
 +    chip: {
 +        height: theme.spacing.unit * 2.5,
 +        width: theme.spacing.unit * 12,
 +        backgroundColor: theme.customs.colors.green700,
 +        color: theme.palette.common.white,
 +        fontSize: '0.875rem',
 +        borderRadius: theme.spacing.unit * 0.625
 +    },
 +    headerText: {
 +        fontSize: '0.875rem',
 +        display: 'flex',
 +        position: 'relative',
 +        justifyContent: 'flex-start',
 +        top: -theme.spacing.unit * 4.5,
 +        left: theme.spacing.unit * 3,
 +    }
 +});
 +
 +interface ProcessPanelDataProps {
 +    item: ProcessResource;
 +}
 +
 +interface ProcessPanelActionProps {
 +    onContextMenu: (event: React.MouseEvent<HTMLElement>, item: ProcessResource) => void;
 +}
 +
 +type ProcessPanelProps = ProcessPanelDataProps & ProcessPanelActionProps & DispatchProp & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
 +
 +export const ProcessPanel = withStyles(styles)(
 +    connect((state: RootState) => ({
 +        item: state.collectionPanel.item,
 +        tags: state.collectionPanel.tags
 +    }))(
 +        class extends React.Component<ProcessPanelProps> {
 +            render() {
-                                     onClick={event => onContextMenu(event, item)}>
++                const { classes } = this.props;
 +
 +                return <div>
 +                    <Card className={classes.card}>
 +                        <CardHeader
 +                            avatar={<ProcessIcon className={classes.iconHeader} />}
 +                            action={
 +                                <IconButton
 +                                    aria-label="More options"
-                             title="Pipeline template that generates a config file from a template"
-                              />
++                                    onClick={this.handleContextMenu}>
 +                                    <MoreOptionsIcon />
 +                                </IconButton>
 +                            }
++                            title="Pipeline template that generates a config file from a template" />
 +                        <CardContent className={classes.content}>
 +                            <Grid container direction="column">
 +                                <Grid item xs={8}>
 +                                    <DetailsAttribute classLabel={classes.label} classValue={classes.value}
 +                                        label='Status' value={<Chip label="Complete" className={classes.chip} />} />
 +                                    <DetailsAttribute classLabel={classes.label} classValue={classes.value}
 +                                        label='Started at' value="1:25 PM 3/23/2018" />
 +                                    <DetailsAttribute classLabel={classes.label} classValue={classes.value}
 +                                        label='Finished at' value='1:25 PM 3/23/2018' />
 +                                </Grid>
 +                            </Grid>
 +                            <Grid container direction="column">
 +                                <Grid item xs={8}>
 +                                    <DetailsAttribute classLabel={classes.link} classValue={classes.value}
 +                                        label='Container output' />
 +                                    <DetailsAttribute classLabel={classes.link} classValue={classes.value}
 +                                        label='Show inputs' />
 +                                    <DetailsAttribute classLabel={classes.link} classValue={classes.value}
 +                                        label='Show command' />
 +                                </Grid>
 +                            </Grid>
 +                        </CardContent>
 +                        <span className={classes.headerText}>This container request was created from the workflow FastQC MultiQC</span>
 +                    </Card>
 +                </div>;
 +            }
++
++            handleContextMenu = (event: React.MouseEvent<any>) => {
++                // const { uuid, name, description } = this.props.item;
++                const resource = {
++                    uuid: '',
++                    name: '',
++                    description: '',
++                    kind: ContextMenuKind.PROCESS
++                };
++                this.props.dispatch<any>(openContextMenu(event, resource));
++            }
 +        }
 +    )
 +);
index d8daed58b6ce1a421dc724bf47e0fe097b1c08bc,12010acc0eb04733877c3ac2245a39ed35b227e0..27470fa43086c2ebdd78630eec1db39b01ecdefc
@@@ -52,14 -26,21 +26,21 @@@ import { FileRemoveDialog } from '~/vie
  import { MultipleFilesRemoveDialog } from '~/views-components/file-remove-dialog/multiple-files-remove-dialog';
  import { UploadCollectionFilesDialog } from '~/views-components/upload-collection-files-dialog/upload-collection-files-dialog';
  import { CollectionPartialCopyDialog } from '~/views-components/collection-partial-copy-dialog/collection-partial-copy-dialog';
+ import { SidePanel } from '~/views-components/side-panel/side-panel';
+ import { Routes } from '~/routes/routes';
+ import { Breadcrumbs } from '~/views-components/breadcrumbs/breadcrumbs';
+ import { CreateProjectDialog } from '~/views-components/dialog-forms/create-project-dialog';
+ import { CreateCollectionDialog } from '~/views-components/dialog-forms/create-collection-dialog';
+ import { CopyCollectionDialog } from '~/views-components/dialog-forms/copy-collection-dialog';
+ import { UpdateCollectionDialog } from '~/views-components/dialog-forms/update-collection-dialog';
+ import { UpdateProjectDialog } from '~/views-components/dialog-forms/update-project-dialog';
  import { MoveProjectDialog } from '~/views-components/dialog-forms/move-project-dialog';
  import { MoveCollectionDialog } from '~/views-components/dialog-forms/move-collection-dialog';
- import { CopyCollectionDialog } from '~/views-components/dialog-forms/copy-collection-dialog';
 -
++import { ProcessPanel } from '~/views/process-panel/process-panel';
  
- const DRAWER_WITDH = 240;
  const APP_BAR_HEIGHT = 100;
  
- type CssRules = 'root' | 'appBar' | 'drawerPaper' | 'content' | 'contentWrapper' | 'toolbar';
+ type CssRules = 'root' | 'appBar' | 'content' | 'contentWrapper';
  
  const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
      root: {
@@@ -235,11 -162,9 +162,10 @@@ export const Workbench = withStyles(sty
                          <main className={classes.contentWrapper}>
                              <div className={classes.content}>
                                  <Switch>
-                                     <Route path='/' exact render={() => <Redirect to={`/projects/${this.props.authService.getUuid()}`} />} />
-                                     <Route path="/projects/:id" render={this.renderProjectPanel} />
-                                     <Route path="/favorites" render={this.renderFavoritePanel} />
-                                     <Route path="/collections/:id" render={this.renderCollectionPanel} />
-                                     <Route path="/process/:id" render={this.renderProcessPanel} />
+                                     <Route path={Routes.PROJECTS} component={ProjectPanel} />
+                                     <Route path={Routes.COLLECTIONS} component={CollectionPanel} />
+                                     <Route path={Routes.FAVORITES} component={FavoritePanel} />
++                                    <Route path={Routes.PROCESS} component={ProcessPanel} />
                                  </Switch>
                              </div>
                              {user && <DetailsPanel />}