width: '60%',
color: theme.palette.primary.main,
textDecoration: 'none',
- overflowWrap: 'break-word'
+ overflowWrap: 'break-word',
+ cursor: 'pointer'
}
});
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';
addMenuActionSet(ContextMenuKind.COLLECTION_FILES_ITEM, collectionFilesItemActionSet);
addMenuActionSet(ContextMenuKind.COLLECTION, collectionActionSet);
addMenuActionSet(ContextMenuKind.COLLECTION_RESOURCE, collectionResourceActionSet);
+addMenuActionSet(ContextMenuKind.PROCESS, processActionSet);
fetchConfig()
.then((config) => {
TOKEN: '/token',
PROJECTS: `/projects/:id(${RESOURCE_UUID_PATTERN})`,
COLLECTIONS: `/collections/:id(${RESOURCE_UUID_PATTERN})`,
+ PROCESS: `/processes/:id(${RESOURCE_UUID_PATTERN})`,
FAVORITES: '/favorites',
};
}
};
+export const openProcessContextMenu = (event: React.MouseEvent<HTMLElement>) =>
+ (dispatch: Dispatch, getState: () => RootState) => {
+ const resource = {
+ uuid: '',
+ name: '',
+ description: '',
+ kind: ContextMenuKind.PROCESS
+ };
+ dispatch<any>(openContextMenu(event, resource));
+ };
+
export const resourceKindToContextMenuKind = (uuid: string) => {
const kind = extractUuidKind(uuid);
switch (kind) {
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { ContextMenuActionSet } from "../context-menu-action-set";
+import { ToggleFavoriteAction } from "../actions/favorite-action";
+import { toggleFavorite } from "~/store/favorites/favorites-actions";
+import {
+ RenameIcon, ShareIcon, MoveToIcon, CopyIcon, DetailsIcon, ProvenanceGraphIcon,
+ AdvancedIcon, RemoveIcon, ReRunProcessIcon, LogIcon
+} from "~/components/icon/icon";
+import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action";
+
+export const processActionSet: ContextMenuActionSet = [[
+ {
+ icon: RenameIcon,
+ name: "Edit process",
+ execute: (dispatch, resource) => {
+ // add code
+ }
+ },
+ {
+ icon: ShareIcon,
+ name: "Share",
+ execute: (dispatch, resource) => {
+ // add code
+ }
+ },
+ {
+ icon: MoveToIcon,
+ name: "Move to",
+ execute: (dispatch, resource) => {
+ // add code
+ }
+ },
+ {
+ component: ToggleFavoriteAction,
+ execute: (dispatch, resource) => {
+ dispatch<any>(toggleFavorite(resource)).then(() => {
+ dispatch<any>(favoritePanelActions.REQUEST_ITEMS());
+ });
+ }
+ },
+ {
+ icon: CopyIcon,
+ name: "Copy to project",
+ execute: (dispatch, resource) => {
+ // add code
+ }
+ },
+ {
+ icon: ReRunProcessIcon,
+ name: "Re-run process",
+ execute: (dispatch, resource) => {
+ // add code
+ }
+ },
+ {
+ name: "Inputs",
+ execute: (dispatch, resource) => {
+ // add code
+ }
+ },
+ {
+ name: "Outputs",
+ execute: (dispatch, resource) => {
+ // add code
+ }
+ },
+ {
+ name: "Command",
+ execute: (dispatch, resource) => {
+ // add code
+ }
+ },
+ {
+ icon: DetailsIcon,
+ name: "View details",
+ execute: (dispatch, resource) => {
+ // add code
+ }
+ },
+ {
+ icon: LogIcon,
+ name: "Log",
+ execute: (dispatch, resource) => {
+ // add code
+ }
+ },
+ {
+ icon: ProvenanceGraphIcon,
+ name: "Provenance graph",
+ execute: (dispatch, resource) => {
+ // add code
+ }
+ },
+ {
+ icon: AdvancedIcon,
+ name: "Advanced",
+ execute: (dispatch, resource) => {
+ // add code
+ }
+ },
+ {
+ icon: RemoveIcon,
+ name: "Remove",
+ execute: (dispatch, resource) => {
+ // add code
+ }
+ }
+]];
COLLECTION_FILES = "CollectionFiles",
COLLECTION_FILES_ITEM = "CollectionFilesItem",
COLLECTION = 'Collection',
- COLLECTION_RESOURCE = 'CollectionResource'
+ COLLECTION_RESOURCE = 'CollectionResource',
+ PROCESS = "Process"
}
+++ /dev/null
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import * as React from "react";
-import { InjectedFormProps, Field, WrappedFieldProps } from "redux-form";
-import { Dialog, DialogTitle, DialogContent, DialogActions, Button, CircularProgress } from "@material-ui/core";
-import { WithDialogProps } from "~/store/dialog/with-dialog";
-import { TextField } from "~/components/text-field/text-field";
-import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION, COLLECTION_PROJECT_VALIDATION } from "~/validators/validators";
-import { ProjectTreePicker } from "../project-tree-picker/project-tree-picker";
-
-export const DialogCollectionCreateWithSelected = (props: WithDialogProps<string> & InjectedFormProps<{ name: string }>) =>
- <form>
- <Dialog open={props.open}
- disableBackdropClick={true}
- disableEscapeKeyDown={true}>
- <DialogTitle>Create a collection</DialogTitle>
- <DialogContent style={{ display: 'flex' }}>
- <div>
- <Field
- name='name'
- component={TextField}
- validate={COLLECTION_NAME_VALIDATION}
- label="Collection Name" />
- <Field
- name='description'
- component={TextField}
- validate={COLLECTION_DESCRIPTION_VALIDATION}
- label="Description - optional" />
- </div>
- <Field
- name="projectUuid"
- component={Picker}
- validate={COLLECTION_PROJECT_VALIDATION} />
- </DialogContent>
- <DialogActions>
- <Button
- variant='flat'
- color='primary'
- disabled={props.submitting}
- onClick={props.closeDialog}>
- Cancel
- </Button>
- <Button
- variant='contained'
- color='primary'
- type='submit'
- onClick={props.handleSubmit}
- disabled={props.pristine || props.invalid || props.submitting}>
- {props.submitting ? <CircularProgress size={20} /> : 'Create a collection'}
- </Button>
- </DialogActions>
- </Dialog>
- </form>;
-
-const Picker = (props: WrappedFieldProps) =>
- <div style={{ width: '400px', height: '144px', display: 'flex', flexDirection: 'column' }}>
- <ProjectTreePicker onChange={projectUuid => props.input.onChange(projectUuid)} />
- </div>;
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from 'redux';
+import { openProcessContextMenu } from '~/store/context-menu/context-menu-actions';
+import { connect } from 'react-redux';
+import { RootState } from '~/store/store';
+import { ProcessInformationCard as InformationCardComponent, ProcessInformationCardDataProps } from '~/views/process-panel/process-information-card';
+
+type InformationCardActionProps = Pick<ProcessInformationCardDataProps, 'onContextMenu'>;
+
+const mapStateToProps = (state: RootState) => ({
+ // todo: change for processPanel
+ item: state.collectionPanel.item
+});
+
+const mapDispatchToProps = (dispatch: Dispatch): InformationCardActionProps => ({
+ onContextMenu: (event: React.MouseEvent<HTMLElement>) => {
+ dispatch<any>(openProcessContextMenu(event));
+ }
+});
+
+export const ProcessInformationCard = connect(mapStateToProps, mapDispatchToProps)(InformationCardComponent);
\ No newline at end of file
import * as React from 'react';
import { compose } from 'redux';
-import { reduxForm, reset, startSubmit, stopSubmit, InjectedFormProps, Field } from 'redux-form';
+import { reduxForm, InjectedFormProps, Field } from 'redux-form';
import { withDialog, WithDialogProps } from '~/store/dialog/with-dialog';
import { FormDialog } from '~/components/form-dialog/form-dialog';
import { DialogContentText } from '@material-ui/core';
--- /dev/null
+// 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, Typography, Tooltip
+} from '@material-ui/core';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { MoreOptionsIcon, ProcessIcon } from '~/components/icon/icon';
+import { DetailsAttribute } from '~/components/details-attribute/details-attribute';
+
+type CssRules = 'card' | 'iconHeader' | 'label' | 'value' | 'chip' | 'headerText' | 'link' | 'content' | 'title' | 'avatar';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+ card: {
+ marginBottom: theme.spacing.unit * 2
+ },
+ iconHeader: {
+ fontSize: '1.875rem',
+ color: theme.customs.colors.green700,
+ },
+ avatar: {
+ alignSelf: 'flex-start'
+ },
+ label: {
+ display: 'flex',
+ justifyContent: 'flex-end',
+ fontSize: '0.875rem',
+ marginRight: theme.spacing.unit * 3,
+ paddingRight: theme.spacing.unit
+ },
+ value: {
+ textTransform: 'none',
+ fontSize: '0.875rem',
+ },
+ link: {
+ fontSize: '0.875rem',
+ color: theme.palette.primary.main,
+ '&:hover': {
+ cursor: 'pointer'
+ }
+ },
+ chip: {
+ height: theme.spacing.unit * 3,
+ 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',
+ marginLeft: theme.spacing.unit * 3,
+ },
+ content: {
+ '&:last-child': {
+ paddingBottom: theme.spacing.unit * 2,
+ paddingTop: '0px'
+ }
+ },
+ title: {
+ overflow: 'hidden'
+ }
+});
+
+export interface ProcessInformationCardDataProps {
+ item: any;
+ onContextMenu: (event: React.MouseEvent<HTMLElement>) => void;
+}
+
+type ProcessInformationCardProps = ProcessInformationCardDataProps & WithStyles<CssRules>;
+
+export const ProcessInformationCard = withStyles(styles)(
+ ({ classes, onContextMenu }: ProcessInformationCardProps) =>
+ <Card className={classes.card}>
+ <CardHeader
+ classes={{
+ content: classes.title,
+ avatar: classes.avatar
+ }}
+ avatar={<ProcessIcon className={classes.iconHeader} />}
+ action={
+ <div>
+ <Chip label="Complete" className={classes.chip} />
+ <IconButton
+ aria-label="More options"
+ onClick={event => onContextMenu(event)}>
+ <MoreOptionsIcon />
+ </IconButton>
+ </div>
+ }
+ title={
+ <Tooltip title="Pipeline template that generates a config file from a template">
+ <Typography noWrap variant="title">
+ Pipeline template that generates a config file from a template
+ </Typography>
+ </Tooltip>
+ }
+ subheader="(no-description)" />
+ <CardContent className={classes.content}>
+ <Grid container>
+ <Grid item xs={6}>
+ <DetailsAttribute classLabel={classes.label} classValue={classes.value}
+ label='From' value="1:25 PM 3/23/2018" />
+ <DetailsAttribute classLabel={classes.label} classValue={classes.value}
+ label='To' value='1:25 PM 3/23/2018' />
+ <DetailsAttribute classLabel={classes.label} classValue={classes.link}
+ label='Workflow' value='FastQC MultiQC' />
+ </Grid>
+ <Grid item xs={6}>
+ <DetailsAttribute classLabel={classes.link} label='Outputs' />
+ <DetailsAttribute classLabel={classes.link} label='Inputs' />
+ </Grid>
+ </Grid>
+ </CardContent>
+ </Card>
+);
\ No newline at end of file
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { ProcessInformationCard } from '~/views-components/process-information-card/process-information-card';
+import { Grid } from '@material-ui/core';
+
+export class ProcessPanel extends React.Component {
+ render() {
+ return <div>
+ <Grid container>
+ <Grid item xs={7}>
+ <ProcessInformationCard />
+ </Grid>
+ </Grid>
+ </div>;
+ }
+}
\ No newline at end of file
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 { ProcessPanel } from '~/views/process-panel/process-panel';
import { UploadCollectionFilesDialog } from '~/views-components/dialog-forms/upload-collection-files-dialog';
import { PartialCopyCollectionDialog } from '~/views-components/dialog-forms/partial-copy-collection-dialog';
<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 />}