red900: string;
blue500: string;
grey500: string;
+ grey700: string;
}
const red900 = red["900"];
red900: red['900'],
blue500: blue['500'],
grey500,
+ grey700
}
},
overrides: {
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
grid: {
- display: 'flex'
+ display: 'flex',
+ height: '20px',
+ paddingTop: '0px!important',
+ paddingBottom: '0px!important',
+ marginBottom: theme.spacing.unit
},
label: {
width: '86px',
color: theme.palette.grey["500"],
- textAlign: 'right'
+ textAlign: 'right',
},
value: {
width: '24px',
- paddingLeft: theme.spacing.unit
+ paddingLeft: theme.spacing.unit,
},
switch: {
+ height: '20px',
'& span:first-child': {
height: '18px'
}
import { getProjectUrl } from '../models/project';
import { getCollectionUrl } from '~/models/collection';
import { loadProject, loadFavorites, loadCollection, loadProcessLog } from '~/store/workbench/workbench-actions';
-import { loadProcess } from '~/store/processes/processes-actions';
+import { loadProcessPanel } from '~/store/process-panel/process-panel-actions';
export const Routes = {
ROOT: '/',
} else if (favoriteMatch) {
store.dispatch(loadFavorites());
} else if (processMatch) {
- store.dispatch(loadProcess(processMatch.params.id));
+ store.dispatch(loadProcessPanel(processMatch.params.id));
} else if (processLogMatch) {
store.dispatch(loadProcessLog(processLogMatch.params.id));
}
import { UserResource } from '../../models/user';
import { isSidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree-actions';
import { extractUuidKind, ResourceKind } from '~/models/resource';
-import { matchProcessRoute } from '~/routes/routes';
export const contextMenuActions = unionize({
OPEN_CONTEXT_MENU: ofType<{ position: ContextMenuPosition, resource: ContextMenuResource }>(),
// SPDX-License-Identifier: AGPL-3.0
import { ProcessLogsPanel } from './process-logs-panel';
-import { RootState } from '~/store/store';
import { ProcessLogsPanelAction, processLogsPanelActions } from './process-logs-panel-actions';
const initialState: ProcessLogsPanel = {
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { unionize, ofType, UnionOf } from "~/common/unionize";
+import { loadProcess } from '~/store/processes/processes-actions';
+import { Dispatch } from 'redux';
+
+export const procesPanelActions = unionize({
+ INIT_PROCESS_PANEL_FILTERS: ofType<string[]>(),
+ TOGGLE_PROCESS_PANEL_FILTER: ofType<string>(),
+});
+
+export type ProcessPanelAction = UnionOf<typeof procesPanelActions>;
+
+export const toggleProcessPanelFilter = procesPanelActions.TOGGLE_PROCESS_PANEL_FILTER;
+
+export const loadProcessPanel = (uuid: string) =>
+ (dispatch: Dispatch) => {
+ dispatch<any>(loadProcess(uuid));
+ dispatch(initProcessPanelFilters);
+ };
+
+export const initProcessPanelFilters = procesPanelActions.INIT_PROCESS_PANEL_FILTERS([
+ 'Queued',
+ 'Complete',
+ 'Active',
+ 'Failed'
+]);
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { ProcessPanel } from '~/store/process-panel/process-panel';
+import { ProcessPanelAction, procesPanelActions } from '~/store/process-panel/process-panel-actions';
+
+const initialState: ProcessPanel = {
+ filters: {}
+};
+
+export const processPanelReducer = (state = initialState, action: ProcessPanelAction): ProcessPanel =>
+ procesPanelActions.match(action, {
+ INIT_PROCESS_PANEL_FILTERS: statuses => {
+ const filters = statuses.reduce((filters, status) => ({ ...filters, [status]: true }), {});
+ return { filters };
+ },
+ TOGGLE_PROCESS_PANEL_FILTER: status => {
+ const filters = { ...state.filters, [status]: !state.filters[status] };
+ return { filters };
+ },
+ default: () => state,
+ });
+
\ No newline at end of file
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+export interface ProcessPanel {
+ filters: { [status: string]: boolean };
+}
import { filterResources } from '../resources/resources';
import { ResourceKind, Resource } from '~/models/resource';
import { getTimeDiff } from '~/common/formatters';
-import { SubprocessesStatus } from '~/views/process-panel/process-subprocesses-card';
import { ArvadosTheme } from '~/common/custom-theme';
+import { groupBy } from 'lodash';
export interface Process {
containerRequest: ContainerRequestResource;
container?: ContainerResource;
}
+enum ProcessStatus {
+ ACTIVE = 'Active',
+ COMPLETED = 'Complete',
+ QUEUED = 'Queued',
+ FAILED = 'Failed',
+ CANCELED = 'Canceled'
+}
+
export const getProcess = (uuid: string) => (resources: ResourcesState): Process | undefined => {
const containerRequest = getResource<ContainerRequestResource>(uuid)(resources);
if (containerRequest) {
export const getProcessStatusColor = (status: string, { customs }: ArvadosTheme) => {
switch (status) {
- case SubprocessesStatus.COMPLETED:
+ case ProcessStatus.COMPLETED:
return customs.colors.green700;
- case SubprocessesStatus.CANCELED:
+ case ProcessStatus.CANCELED:
return customs.colors.red900;
- case SubprocessesStatus.QUEUED:
+ case ProcessStatus.QUEUED:
return customs.colors.grey500;
- case SubprocessesStatus.FAILED:
+ case ProcessStatus.FAILED:
return customs.colors.red900;
- case SubprocessesStatus.ACTIVE:
+ case ProcessStatus.ACTIVE:
return customs.colors.blue500;
default:
return customs.colors.grey500;
const isSubprocess = (containerUuid: string) => (resource: Resource) =>
resource.kind === ResourceKind.CONTAINER_REQUEST
&& (resource as ContainerRequestResource).requestingContainerUuid === containerUuid;
-
import { RootState } from './store';
import { fileUploaderReducer } from './file-uploader/file-uploader-reducer';
import { processLogsPanelReducer } from './process-logs-panel/process-logs-panel-reducer';
+import { processPanelReducer } from '~/store/process-panel/process-panel-reducer';
const composeEnhancers =
(process.env.NODE_ENV === 'development' &&
snackbar: snackbarReducer,
treePicker: treePickerReducer,
fileUploader: fileUploaderReducer,
+ processPanel: processPanelReducer
});
import { ProcessInformationCard } from './process-information-card';
import { DefaultView } from '~/components/default-view/default-view';
import { ProcessIcon } from '~/components/icon/icon';
-import { Process, getProcessStatus } from '~/store/processes/process';
+import { Process } from '~/store/processes/process';
import { SubprocessesCard } from './subprocesses-card';
-import { SubprocessFilterDataProps } from '~/components/subprocess-filter/subprocess-filter';
-import { groupBy } from 'lodash';
import { ProcessSubprocesses } from '~/views/process-panel/process-subprocesses';
+import { SubprocessFilterDataProps } from '~/components/subprocess-filter/subprocess-filter';
export interface ProcessPanelRootDataProps {
process?: Process;
subprocesses: Array<Process>;
+ filters: Array<SubprocessFilterDataProps>;
}
export interface ProcessPanelRootActionProps {
onContextMenu: (event: React.MouseEvent<HTMLElement>) => void;
+ onToggle: (status: string) => void;
}
export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRootActionProps;
<ProcessInformationCard
process={props.process}
onContextMenu={props.onContextMenu} />
- {console.log(props.subprocesses)}
</Grid>
<Grid item xs={5}>
<SubprocessesCard
subprocesses={props.subprocesses}
- filters={mapGroupedProcessesToFilters(groupSubprocessesByStatus(props.subprocesses))}
- onToggle={() => { return; }} />
+ filters={props.filters}
+ onToggle={props.onToggle}
+ />
</Grid>
<Grid item xs={12}>
<ProcessSubprocesses
messages={['Process not found']} />
</Grid>;
-const groupSubprocessesByStatus = (processes: Process[]) =>
- groupBy(processes, getProcessStatus);
-
-const mapGroupedProcessesToFilters = (groupedProcesses: { [status: string]: Process[] }): SubprocessFilterDataProps[] =>
- Object
- .keys(groupedProcesses)
- .map(status => ({
- label: status,
- key: status,
- value: groupedProcesses[status].length
- }));
//
// SPDX-License-Identifier: AGPL-3.0
-import * as React from 'react';
import { RootState } from '~/store/store';
import { connect } from 'react-redux';
-import { getProcess, getSubprocesses } from '~/store/processes/process';
+import { getProcess, getSubprocesses, Process, getProcessStatus } from '~/store/processes/process';
import { Dispatch } from 'redux';
import { openProcessContextMenu } from '~/store/context-menu/context-menu-actions';
import { matchProcessRoute } from '~/routes/routes';
import { ProcessPanelRootDataProps, ProcessPanelRootActionProps, ProcessPanelRoot } from './process-panel-root';
+import { ProcessPanel as ProcessPanelState} from '~/store/process-panel/process-panel';
+import { groupBy } from 'lodash';
+import { toggleProcessPanelFilter } from '~/store/process-panel/process-panel-actions';
-const mapStateToProps = ({ router, resources }: RootState): ProcessPanelRootDataProps => {
+const mapStateToProps = ({ router, resources, processPanel }: RootState): ProcessPanelRootDataProps => {
const pathname = router.location ? router.location.pathname : '';
const match = matchProcessRoute(pathname);
const uuid = match ? match.params.id : '';
+ const subprocesses = getSubprocesses(uuid)(resources);
return {
process: getProcess(uuid)(resources),
- subprocesses: getSubprocesses(uuid)(resources)
+ subprocesses: subprocesses.filter(subprocess => processPanel.filters[getProcessStatus(subprocess)]),
+ filters: getFilters(processPanel, subprocesses)
};
};
const mapDispatchToProps = (dispatch: Dispatch): ProcessPanelRootActionProps => ({
- onContextMenu: (event: React.MouseEvent<HTMLElement>) => {
+ onContextMenu: event => {
dispatch<any>(openProcessContextMenu(event));
+ },
+ onToggle: status => {
+ dispatch<any>(toggleProcessPanelFilter(status));
}
});
export const ProcessPanel = connect(mapStateToProps, mapDispatchToProps)(ProcessPanelRoot);
+
+export const getFilters = (processPanel: ProcessPanelState, processes: Process[]) => {
+ const grouppedProcesses = groupBy(processes, getProcessStatus);
+ return Object
+ .keys(processPanel.filters)
+ .map(filter => ({
+ label: filter,
+ value: (grouppedProcesses[filter] || []).length,
+ checked: processPanel.filters[filter],
+ key: filter,
+ }));
+ };
\ No newline at end of file
},
});
-export enum SubprocessesStatus {
- ACTIVE = 'Active',
- COMPLETED = 'Completed',
- QUEUED = 'Queued',
- FAILED = 'Failed',
- CANCELED = 'Canceled'
-}
-
export interface SubprocessItemProps {
title: string;
status: string;
return <Grid container spacing={16}>
{subprocesses.map(subprocess =>
<Grid item xs={2} key={subprocess.containerRequest.uuid}>
- <ProcessSubprocessesCard onContextMenu={onContextMenu} subprocess={subprocess}/>
+ <ProcessSubprocessesCard onContextMenu={onContextMenu} subprocess={subprocess} />
</Grid>
)}
</Grid>;
import * as React from 'react';
import { ArvadosTheme } from '~/common/custom-theme';
-import { StyleRulesCallback, withStyles, WithStyles, Card, CardHeader, CardContent, Grid, Switch } from '@material-ui/core';
+import { StyleRulesCallback, withStyles, WithStyles, Card, CardHeader, CardContent, Grid, Switch, Typography } from '@material-ui/core';
import { SubprocessFilter } from '~/components/subprocess-filter/subprocess-filter';
import { SubprocessFilterDataProps } from '~/components/subprocess-filter/subprocess-filter';
import { Process } from '~/store/processes/process';
-type CssRules = 'root';
+type CssRules = 'root' | 'subtitle' | 'title';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
root: {
fontSize: '0.875rem'
+ },
+ subtitle: {
+ paddingBottom: '28px!important'
+ },
+ title: {
+ color: theme.customs.colors.grey700
}
});
interface SubprocessesDataProps {
subprocesses: Array<Process>;
filters: SubprocessFilterDataProps[];
- onToggle: (filter: SubprocessFilterDataProps) => void;
+ onToggle: (status: string) => void;
}
type SubprocessesProps = SubprocessesDataProps & WithStyles<CssRules>;
export const SubprocessesCard = withStyles(styles)(
- ({ classes, filters, subprocesses, onToggle }: SubprocessesProps) =>
+ ({ classes, filters, subprocesses, onToggle }: SubprocessesProps) =>
<Card className={classes.root}>
- <CardHeader title="Subprocess and filters" />
+ <CardHeader
+ className={classes.title}
+ title={
+ <Typography noWrap variant="title" color='inherit'>
+ Subprocess and filters
+ </Typography>} />
<CardContent>
<Grid container direction="column" spacing={16}>
- <Grid item xs={12} container spacing={16}>
- <SubprocessFilter label='Subprocesses' value={subprocesses.length} />
+ <Grid item xs={12} container spacing={16} className={classes.subtitle}>
+ <SubprocessFilter label='Subprocesses' value={subprocesses.length} />
</Grid>
<Grid item xs={12} container spacing={16}>
{
- filters.map(filter =>
- <SubprocessFilter {...filter} key={filter.key} onToggle={() => onToggle(filter)} />
+ filters.map(filter =>
+ <SubprocessFilter {...filter} key={filter.key} onToggle={() => onToggle(filter.label)} />
)
}
</Grid>