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);
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);
--- /dev/null
+ // 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());
+ }
+ };
--- /dev/null
- 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));
++ }
+ }
+ )
+);
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: {
<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 />}