merge master
authorPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Tue, 4 Sep 2018 12:16:44 +0000 (14:16 +0200)
committerPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Tue, 4 Sep 2018 12:16:44 +0000 (14:16 +0200)
Feature #13860

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

17 files changed:
src/components/data-table/data-column.ts
src/components/subprocess-filter/subprocess-filter.tsx
src/index.tsx
src/routes/route-change-handlers.ts [new file with mode: 0644]
src/routes/routes.ts
src/store/breadcrumbs/breadcrumbs-actions.ts
src/store/context-menu/context-menu-actions.ts
src/store/favorite-panel/favorite-panel-middleware-service.ts
src/store/process-panel/process-panel-actions.ts
src/store/processes/process.ts
src/store/project-panel/project-panel-middleware-service.ts
src/store/workbench/workbench-actions.ts
src/views-components/data-explorer/renderers.tsx
src/views/favorite-panel/favorite-panel.tsx
src/views/process-panel/process-panel-root.tsx
src/views/process-panel/subprocesses-card.tsx
src/views/project-panel/project-panel.tsx

index 90e87a88b0a7225a9cbea0d7b842e5146a593d4c..8dbdf0cc7ad0a1f1d13d2d773a760bda3e289f7a 100644 (file)
@@ -10,7 +10,7 @@ export interface DataColumn<T, F extends DataTableFilterItem = DataTableFilterIt
     name: string;
     selected: boolean;
     configurable: boolean;
-    sortDirection: SortDirection;
+    sortDirection?: SortDirection;
     filters: F[];
     render: (item: T) => React.ReactElement<any>;
     renderHeader?: () => React.ReactElement<any>;
index 23c1e65819e371424900a60c0e3c9809e5a6a80f..36be50d96aff7bb7fa6ff718c05baaf503bf53ba 100644 (file)
@@ -7,11 +7,13 @@ import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/st
 import { ArvadosTheme } from '~/common/custom-theme';
 import { Typography, Switch } from '@material-ui/core';
 
-type CssRules = 'container' | 'label' | 'value' | 'switch';
+type CssRules = 'container' | 'label' | 'value';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     container: {
         display: 'flex',
+        alignItems: 'center',
+        height: '20px'
     },
     label: {
         width: '86px',
@@ -21,12 +23,6 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     value: {
         width: '24px',
         paddingLeft: theme.spacing.unit,
-    },
-    switch: {
-        height: '20px',
-        '& span:first-child': {
-            height: '18px'
-        }
     }
 });
 
@@ -45,7 +41,7 @@ export const SubprocessFilter = withStyles(styles)(
         <div className={classes.container} >
             <Typography component="span" className={classes.label}>{label}:</Typography>
             <Typography component="span" className={classes.value}>{value}</Typography>
-            {onToggle && <Switch classes={{ root: classes.switch }}
+            {onToggle && <Switch
                 checked={checked}
                 onChange={onToggle}
                 value={key}
index 4ce80d31e9d0f0360e6dd7051b113f9dbfe4d40c..a921b471383dbc399521b9298ee7f80bd3cb8a00 100644 (file)
@@ -28,13 +28,13 @@ import { collectionFilesItemActionSet } from './views-components/context-menu/ac
 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';
 import { trashActionSet } from "~/views-components/context-menu/action-sets/trash-action-set";
 import { ServiceRepository } from '~/services/services';
 import { initWebSocket } from '~/websocket/websocket';
 import { Config } from '~/common/config';
+import { addRouteChangeHandlers } from './routes/route-change-handlers';
 
 const getBuildNumber = () => "BN-" + (process.env.REACT_APP_BUILD_NUMBER || "dev");
 const getGitCommit = () => "GIT-" + (process.env.REACT_APP_GIT_COMMIT || "latest").substr(0, 7);
diff --git a/src/routes/route-change-handlers.ts b/src/routes/route-change-handlers.ts
new file mode 100644 (file)
index 0000000..b29e5d1
--- /dev/null
@@ -0,0 +1,37 @@
+// 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 {  matchProcessRoute, matchProcessLogRoute, matchProjectRoute, matchCollectionRoute, matchFavoritesRoute, matchTrashRoute } from './routes';
+import { loadProject, loadCollection, loadFavorites, loadTrash, loadProcess, loadProcessLog } from '~/store/workbench/workbench-actions';
+
+export const addRouteChangeHandlers = (history: History, store: RootStore) => {
+    const handler = handleLocationChange(store);
+    handler(history.location);
+    history.listen(handler);
+};
+
+const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
+    const projectMatch = matchProjectRoute(pathname);
+    const collectionMatch = matchCollectionRoute(pathname);
+    const favoriteMatch = matchFavoritesRoute(pathname);
+    const trashMatch = matchTrashRoute(pathname);
+    const processMatch = matchProcessRoute(pathname);
+    const processLogMatch = matchProcessLogRoute(pathname);
+    
+    if (projectMatch) {
+        store.dispatch(loadProject(projectMatch.params.id));
+    } else if (collectionMatch) {
+        store.dispatch(loadCollection(collectionMatch.params.id));
+    } else if (favoriteMatch) {
+        store.dispatch(loadFavorites());
+    } else if (trashMatch) {
+        store.dispatch(loadTrash());
+    } else if (processMatch) {
+        store.dispatch(loadProcess(processMatch.params.id));
+    } else if (processLogMatch) {
+        store.dispatch(loadProcessLog(processLogMatch.params.id));
+    }
+};
index ac02dbcfca175cd2b1750913b56b52114ea67518..f108e0b8be423592cb50578a8667e468c3251e55 100644 (file)
@@ -2,14 +2,10 @@
 //
 // 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, loadTrash, loadProcessLog } from '~/store/workbench/workbench-actions';
-import { loadProcessPanel } from '~/store/process-panel/process-panel-actions';
 
 export const Routes = {
     ROOT: '/',
@@ -38,12 +34,6 @@ export const getProcessUrl = (uuid: string) => `/processes/${uuid}`;
 
 export const getProcessLogUrl = (uuid: string) => `/process-logs/${uuid}`;
 
-export const addRouteChangeHandlers = (history: History, store: RootStore) => {
-    const handler = handleLocationChange(store);
-    handler(history.location);
-    history.listen(handler);
-};
-
 export interface ResourceRouteParams {
     id: string;
 }
@@ -68,26 +58,3 @@ export const matchProcessRoute = (route: string) =>
 
 export const matchProcessLogRoute = (route: string) =>
     matchPath<ResourceRouteParams>(route, { path: Routes.PROCESS_LOGS });
-
-const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
-    const projectMatch = matchProjectRoute(pathname);
-    const collectionMatch = matchCollectionRoute(pathname);
-    const favoriteMatch = matchFavoritesRoute(pathname);
-    const trashMatch = matchTrashRoute(pathname);
-    const processMatch = matchProcessRoute(pathname);
-    const processLogMatch = matchProcessLogRoute(pathname);
-    
-    if (projectMatch) {
-        store.dispatch(loadProject(projectMatch.params.id));
-    } else if (collectionMatch) {
-        store.dispatch(loadCollection(collectionMatch.params.id));
-    } else if (favoriteMatch) {
-        store.dispatch(loadFavorites());
-    } else if (trashMatch) {
-        store.dispatch(loadTrash());
-    } else if (processMatch) {
-        store.dispatch(loadProcessPanel(processMatch.params.id));
-    } else if (processLogMatch) {
-        store.dispatch(loadProcessLog(processLogMatch.params.id));
-    }
-};
index 254a8d3e16e93aa75d62657d4eecbde75dd587de..cc7bb1dd2d2d6f9237e448dfe0c1fbca4692a6f3 100644 (file)
@@ -9,6 +9,7 @@ import { getResource } from '~/store/resources/resources';
 import { TreePicker } from '../tree-picker/tree-picker';
 import { getSidePanelTreeBranch } from '../side-panel-tree/side-panel-tree-actions';
 import { propertiesActions } from '../properties/properties-actions';
+import { getProcess } from '~/store/processes/process';
 
 export const BREADCRUMBS = 'breadcrumbs';
 
@@ -44,3 +45,11 @@ export const setCollectionBreadcrumbs = (collectionUuid: string) =>
             dispatch<any>(setProjectBreadcrumbs(collection.ownerUuid));
         }
     };
+export const setProcessBreadcrumbs = (processUuid: string) =>
+    (dispatch: Dispatch, getState: () => RootState) => {
+        const { resources } = getState();
+        const process = getProcess(processUuid)(resources);
+        if (process) {
+            dispatch<any>(setProjectBreadcrumbs(process.containerRequest.ownerUuid));
+        }
+    };
index 2b0e6f8f8bd2aad4192397459c79927862264166..bb404b88963b9fbd358db6cdba1aa82a6e7bbf20 100644 (file)
@@ -11,7 +11,8 @@ import { getResource } from '../resources/resources';
 import { ProjectResource } from '~/models/project';
 import { UserResource } from '~/models/user';
 import { isSidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree-actions';
-import { extractUuidKind, ResourceKind, TrashableResource } from '~/models/resource';
+import { extractUuidKind, ResourceKind } from '~/models/resource';
+import { matchProcessRoute } from '~/routes/routes';
 
 export const contextMenuActions = unionize({
     OPEN_CONTEXT_MENU: ofType<{ position: ContextMenuPosition, resource: ContextMenuResource }>(),
@@ -87,13 +88,10 @@ export const openProcessContextMenu = (event: React.MouseEvent<HTMLElement>) =>
     (dispatch: Dispatch, getState: () => RootState) => {
         const { location } = getState().router;
         const pathname = location ? location.pathname : '';
-        // ToDo: We get error from matchProcessRoute
-        // const match = matchProcessRoute(pathname); 
-        // console.log('match: ', match);
-        // const uuid = match ? match.params.id : '';
-        const uuid = pathname.split('/').slice(-1)[0];
+        const match = matchProcessRoute(pathname); 
+        const uuid = match ? match.params.id : '';
         const resource = {
-            uuid: '',
+            uuid,
             ownerUuid: '',
             kind: ResourceKind.PROCESS,
             name: '',
index f808b97e8835882a682694799a0b52cdc63155f9..c385309f71f28d3295e23b718089fab3e334b7f2 100644 (file)
@@ -18,17 +18,18 @@ import { GroupContentsResource, GroupContentsResourcePrefix } from "~/services/g
 import { resourcesActions } from "~/store/resources/resources-actions";
 import { snackbarActions } from '~/store/snackbar/snackbar-actions';
 import { getDataExplorer } from "~/store/data-explorer/data-explorer-reducer";
+import { loadMissingProcessesInformation } from "~/store/project-panel/project-panel-middleware-service";
 
 export class FavoritePanelMiddlewareService extends DataExplorerMiddlewareService {
     constructor(private services: ServiceRepository, id: string) {
         super(id);
     }
 
-    requestItems(api: MiddlewareAPI<Dispatch, RootState>) {
+    async requestItems(api: MiddlewareAPI<Dispatch, RootState>) {
         const dataExplorer = getDataExplorer(api.getState().dataExplorer, this.getId());
         if (!dataExplorer) {
             api.dispatch(favoritesPanelDataExplorerIsNotSet());
-         } else {
+        } else {
 
             const columns = dataExplorer.columns as DataColumns<string, FavoritePanelFilter>;
             const sortColumn = dataExplorer.columns.find(c => c.sortDirection !== SortDirection.NONE);
@@ -48,36 +49,35 @@ export class FavoritePanelMiddlewareService extends DataExplorerMiddlewareServic
                     .addOrder(direction, "name", GroupContentsResourcePrefix.PROCESS)
                     .addOrder(direction, "name", GroupContentsResourcePrefix.PROJECT);
             }
-
-            this.services.favoriteService
-                .list(this.services.authService.getUuid()!, {
-                    limit: dataExplorer.rowsPerPage,
-                    offset: dataExplorer.page * dataExplorer.rowsPerPage,
-                    linkOrder: linkOrder.getOrder(),
-                    contentOrder: contentOrder.getOrder(),
-                    filters: new FilterBuilder()
-                        .addIsA("headUuid", typeFilters.map(filter => filter.type))
-                        .addILike("name", dataExplorer.searchValue)
-                        .getFilters()
-                })
-                .then(response => {
-                    api.dispatch(resourcesActions.SET_RESOURCES(response.items));
-                    api.dispatch(favoritePanelActions.SET_ITEMS({
-                        items: response.items.map(resource => resource.uuid),
-                        itemsAvailable: response.itemsAvailable,
-                        page: Math.floor(response.offset / response.limit),
-                        rowsPerPage: response.limit
-                    }));
-                    api.dispatch<any>(updateFavorites(response.items.map(item => item.uuid)));
-                })
-                .catch(() => {
-                    api.dispatch(favoritePanelActions.SET_ITEMS({
-                        items: [],
-                        itemsAvailable: 0,
-                        page: 0,
-                        rowsPerPage: dataExplorer.rowsPerPage
-                    }));
-                });
+            try {
+                const response = await this.services.favoriteService
+                    .list(this.services.authService.getUuid()!, {
+                        limit: dataExplorer.rowsPerPage,
+                        offset: dataExplorer.page * dataExplorer.rowsPerPage,
+                        linkOrder: linkOrder.getOrder(),
+                        contentOrder: contentOrder.getOrder(),
+                        filters: new FilterBuilder()
+                            .addIsA("headUuid", typeFilters.map(filter => filter.type))
+                            .addILike("name", dataExplorer.searchValue)
+                            .getFilters()
+                    });
+                api.dispatch(resourcesActions.SET_RESOURCES(response.items));
+                await api.dispatch<any>(loadMissingProcessesInformation(response.items));
+                api.dispatch(favoritePanelActions.SET_ITEMS({
+                    items: response.items.map(resource => resource.uuid),
+                    itemsAvailable: response.itemsAvailable,
+                    page: Math.floor(response.offset / response.limit),
+                    rowsPerPage: response.limit
+                }));
+                api.dispatch<any>(updateFavorites(response.items.map(item => item.uuid)));
+            } catch (e) {
+                api.dispatch(favoritePanelActions.SET_ITEMS({
+                    items: [],
+                    itemsAvailable: 0,
+                    page: 0,
+                    rowsPerPage: dataExplorer.rowsPerPage
+                }));
+            }
         }
     }
 }
index 902a5c9c4e7ab7cf99fe258e7237ad0691126ec7..85a7314d2e7a1e2d84164a32cacd7fa651bf58ae 100644 (file)
@@ -5,6 +5,7 @@
 import { unionize, ofType, UnionOf } from "~/common/unionize";
 import { loadProcess } from '~/store/processes/processes-actions';
 import { Dispatch } from 'redux';
+import { ProcessStatus } from '~/store/processes/process';
 
 export const procesPanelActions = unionize({
     INIT_PROCESS_PANEL_FILTERS: ofType<string[]>(),
@@ -22,8 +23,10 @@ export const loadProcessPanel = (uuid: string) =>
     };
 
 export const initProcessPanelFilters = procesPanelActions.INIT_PROCESS_PANEL_FILTERS([
-    'Queued',
-    'Complete',
-    'Active',
-    'Failed'
+    ProcessStatus.QUEUED,
+    ProcessStatus.COMPLETED,
+    ProcessStatus.FAILED,
+    ProcessStatus.RUNNING,
+    ProcessStatus.LOCKED,
+    ProcessStatus.CANCELLED
 ]);
index 7253d9fcad285fb08d52cdf07e76838eae8451ed..c9e62f943455a2ebaae9b8d8b45c16ff8084a62f 100644 (file)
@@ -2,38 +2,42 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { ContainerRequestResource } from '../../models/container-request';
-import { ContainerResource } from '../../models/container';
+import { ContainerRequestResource, ContainerRequestState } from '../../models/container-request';
+import { ContainerResource, ContainerState } from '../../models/container';
 import { ResourcesState, getResource } from '~/store/resources/resources';
 import { filterResources } from '../resources/resources';
-import { ResourceKind, Resource } from '~/models/resource';
+import { ResourceKind, Resource, extractUuidKind } from '~/models/resource';
 import { getTimeDiff } from '~/common/formatters';
 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',
+export enum ProcessStatus {
+    CANCELLED = 'Cancelled',
+    COMPLETED = 'Completed',
+    DRAFT = 'Draft',
     FAILED = 'Failed',
-    CANCELED = 'Canceled'
+    LOCKED = 'Locked',
+    QUEUED = 'Queued',
+    RUNNING = 'Running',
+    UNKNOWN = 'Unknown',
 }
 
 export const getProcess = (uuid: string) => (resources: ResourcesState): Process | undefined => {
-    const containerRequest = getResource<ContainerRequestResource>(uuid)(resources);
-    if (containerRequest) {
-        if (containerRequest.containerUuid) {
-            const container = getResource<ContainerResource>(containerRequest.containerUuid)(resources);
-            if (container) {
-                return { containerRequest, container };
+    if (extractUuidKind(uuid) === ResourceKind.CONTAINER_REQUEST) {
+        const containerRequest = getResource<ContainerRequestResource>(uuid)(resources);
+        if (containerRequest) {
+            if (containerRequest.containerUuid) {
+                const container = getResource<ContainerResource>(containerRequest.containerUuid)(resources);
+                if (container) {
+                    return { containerRequest, container };
+                }
             }
+            return { containerRequest };
         }
-        return { containerRequest };
     }
     return;
 };
@@ -59,25 +63,46 @@ export const getProcessRuntime = ({ container }: Process) =>
 
 export const getProcessStatusColor = (status: string, { customs }: ArvadosTheme) => {
     switch (status) {
+        case ProcessStatus.RUNNING:
+            return customs.colors.blue500;
         case ProcessStatus.COMPLETED:
             return customs.colors.green700;
-        case ProcessStatus.CANCELED:
-            return customs.colors.red900;
-        case ProcessStatus.QUEUED:
-            return customs.colors.grey500;
+        case ProcessStatus.CANCELLED:
         case ProcessStatus.FAILED:
             return customs.colors.red900;
-        case ProcessStatus.ACTIVE:
-            return customs.colors.blue500;
         default:
             return customs.colors.grey500;
     }
 };
 
-export const getProcessStatus = (process: Process) =>
-    process.container
-        ? process.container.state
-        : process.containerRequest.state;
+export const getProcessStatus = ({ containerRequest, container }: Process): ProcessStatus => {
+    switch (true) {
+        case containerRequest.state === ContainerRequestState.UNCOMMITTED:
+            return ProcessStatus.DRAFT;
+
+        case containerRequest.priority === 0:
+        case container && container.state === ContainerState.CANCELLED:
+            return ProcessStatus.CANCELLED;
+
+        case container && container.state === ContainerState.QUEUED:
+            return ProcessStatus.QUEUED;
+
+        case container && container.state === ContainerState.LOCKED:
+            return ProcessStatus.LOCKED;
+
+        case container && container.state === ContainerState.RUNNING:
+            return ProcessStatus.RUNNING;
+
+        case container && container.state === ContainerState.COMPLETE && container.exitCode === 0:
+            return ProcessStatus.COMPLETED;
+
+        case container && container.state === ContainerState.COMPLETE && container.exitCode !== 0:
+            return ProcessStatus.FAILED;
+
+        default:
+            return ProcessStatus.UNKNOWN;
+    }
+};
 
 const isSubprocess = (containerUuid: string) => (resource: Resource) =>
     resource.kind === ResourceKind.CONTAINER_REQUEST
index aade4172f99201cc10205109bd8740c37d9f72ee..4b4ec2697989831e734f963eb8c872b144b1ebee 100644 (file)
@@ -20,6 +20,8 @@ import { getProperty } from "~/store/properties/properties";
 import { snackbarActions } from '../snackbar/snackbar-actions';
 import { DataExplorer, getDataExplorer } from '../data-explorer/data-explorer-reducer';
 import { ListResults } from '~/services/common-service/common-resource-service';
+import { loadContainers } from '../processes/processes-actions';
+import { ResourceKind } from '~/models/resource';
 
 export class ProjectPanelMiddlewareService extends DataExplorerMiddlewareService {
     constructor(private services: ServiceRepository, id: string) {
@@ -39,6 +41,7 @@ export class ProjectPanelMiddlewareService extends DataExplorerMiddlewareService
                 const response = await this.services.groupsService.contents(projectUuid, getParams(dataExplorer));
                 api.dispatch<any>(updateFavorites(response.items.map(item => item.uuid)));
                 api.dispatch(updateResources(response.items));
+                await api.dispatch<any>(loadMissingProcessesInformation(response.items));
                 api.dispatch(setItems(response));
             } catch (e) {
                 api.dispatch(couldNotFetchProjectContents());
@@ -47,6 +50,22 @@ export class ProjectPanelMiddlewareService extends DataExplorerMiddlewareService
     }
 }
 
+export const loadMissingProcessesInformation = (resources: GroupContentsResource[]) =>
+    async (dispatch: Dispatch) => {
+        const containerUuids = resources.reduce((uuids, resource) => {
+            return resource.kind === ResourceKind.CONTAINER_REQUEST
+                ? resource.containerUuid
+                    ? [...uuids, resource.containerUuid]
+                    : uuids
+                : uuids;
+        }, []);
+        if (containerUuids.length > 0) {
+            await dispatch<any>(loadContainers(
+                new FilterBuilder().addIn('uuid', containerUuids).getFilters()
+            ));
+        }
+    };
+
 const setItems = (listResults: ListResults<GroupContentsResource>) =>
     projectPanelActions.SET_ITEMS({
         ...listResultsToDataExplorerItemsMeta(listResults),
@@ -65,7 +84,6 @@ const getFilters = (dataExplorer: DataExplorer) => {
     const statusFilters = getDataExplorerColumnFilters(columns, ProjectPanelColumnNames.STATUS);
     return new FilterBuilder()
         .addIsA("uuid", typeFilters.map(f => f.type))
-        .addIn("state", statusFilters.map(f => f.type), GroupContentsResourcePrefix.PROCESS)
         .addILike("name", dataExplorer.searchValue, GroupContentsResourcePrefix.COLLECTION)
         .addILike("name", dataExplorer.searchValue, GroupContentsResourcePrefix.PROCESS)
         .addILike("name", dataExplorer.searchValue, GroupContentsResourcePrefix.PROJECT)
index 80f50fe153744382832c81aa34d63f8976ea2869..7077f414e5e2c1703c63f81107cae7a34aab9c65 100644 (file)
@@ -15,7 +15,7 @@ import { favoritePanelActions } from '~/store/favorite-panel/favorite-panel-acti
 import { projectPanelColumns } from '~/views/project-panel/project-panel';
 import { favoritePanelColumns } from '~/views/favorite-panel/favorite-panel';
 import { matchRootRoute } from '~/routes/routes';
-import { setCollectionBreadcrumbs, setProjectBreadcrumbs, setSidePanelBreadcrumbs } from '../breadcrumbs/breadcrumbs-actions';
+import { setCollectionBreadcrumbs, setProjectBreadcrumbs, setSidePanelBreadcrumbs, setProcessBreadcrumbs } from '../breadcrumbs/breadcrumbs-actions';
 import { navigateToProject } from '../navigation/navigation-action';
 import { MoveToFormDialogData } from '~/store/move-to-dialog/move-to-dialog';
 import { ServiceRepository } from '~/services/services';
@@ -29,10 +29,10 @@ import * as collectionCopyActions from '~/store/collections/collection-copy-acti
 import * as collectionUpdateActions from '~/store/collections/collection-update-actions';
 import * as collectionMoveActions from '~/store/collections/collection-move-actions';
 import * as processesActions from '../processes/processes-actions';
-import { getProcess } from '../processes/process';
 import { trashPanelColumns } from "~/views/trash-panel/trash-panel";
 import { loadTrashPanel, trashPanelActions } from "~/store/trash-panel/trash-panel-action";
 import { initProcessLogsPanel } from '../process-logs-panel/process-logs-panel-actions';
+import { loadProcessPanel } from '~/store/process-panel/process-panel-actions';
 
 
 export const loadWorkbench = () =>
@@ -186,17 +186,19 @@ export const moveCollection = (data: MoveToFormDialogData) =>
 
 export const loadProcess = (uuid: string) =>
     async (dispatch: Dispatch, getState: () => RootState) => {
-        await dispatch<any>(processesActions.loadProcess(uuid));
-        const process = getProcess(uuid)(getState().resources);
-        if (process) {
-            await dispatch<any>(activateSidePanelTreeItem(process.containerRequest.ownerUuid));
-            dispatch<any>(setCollectionBreadcrumbs(process.containerRequest.ownerUuid));
-            dispatch(loadDetailsPanel(uuid));
-        }
+        dispatch<any>(loadProcessPanel(uuid));
+        const process = await dispatch<any>(processesActions.loadProcess(uuid));
+        await dispatch<any>(activateSidePanelTreeItem(process.containerRequest.ownerUuid));
+        dispatch<any>(setProcessBreadcrumbs(uuid));
+        dispatch(loadDetailsPanel(uuid));
+        
     };
 
 export const loadProcessLog = (uuid: string) =>
     async (dispatch: Dispatch) => {
+        const process = await dispatch<any>(processesActions.loadProcess(uuid));
+        await dispatch<any>(activateSidePanelTreeItem(process.containerRequest.ownerUuid));
+        dispatch<any>(setProcessBreadcrumbs(uuid));
         dispatch<any>(initProcessLogsPanel(uuid));
     };
 
index c96813df95bb25f8b4f773804ed6eda5df7255b2..72644f93af2e9cea7c5a93e965cff53793f8f026 100644 (file)
@@ -3,7 +3,7 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { Grid, Typography } from '@material-ui/core';
+import { Grid, Typography, withStyles } from '@material-ui/core';
 import { FavoriteStar } from '../favorite-star/favorite-star';
 import { ResourceKind, TrashableResource } from '~/models/resource';
 import { ProjectIcon, CollectionIcon, ProcessIcon, DefaultIcon } from '~/components/icon/icon';
@@ -13,8 +13,9 @@ import { connect } from 'react-redux';
 import { RootState } from '~/store/store';
 import { getResource } from '~/store/resources/resources';
 import { GroupContentsResource } from '~/services/groups-service/groups-service';
-import { ProcessResource } from '~/models/process';
-
+import { getProcess, Process, getProcessStatus, getProcessStatusColor } from '~/store/processes/process';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { compose } from 'redux';
 
 export const renderName = (item: { name: string; uuid: string, kind: string }) =>
     <Grid container alignItems="center" wrap="nowrap" spacing={16}>
@@ -107,13 +108,17 @@ export const ResourceType = connect(
         return { type: resource ? resource.kind : '' };
     })((props: { type: string }) => renderType(props.type));
 
-export const renderStatus = (item: { status?: string }) =>
-    <Typography noWrap align="center" >
-        {item.status || "-"}
-    </Typography>;
-
-export const ProcessStatus = connect(
-    (state: RootState, props: { uuid: string }) => {
-        const resource = getResource<ProcessResource>(props.uuid)(state.resources);
-        return { status: resource ? resource.state : '-' };
-    })((props: { status: string }) => renderType(props.status));
+export const ProcessStatus = compose(
+    connect((state: RootState, props: { uuid: string }) => {
+        return { process: getProcess(props.uuid)(state.resources) };
+    }),
+    withStyles({}, { withTheme: true }))
+    ((props: { process?: Process, theme: ArvadosTheme }) => {
+        const status = props.process ? getProcessStatus(props.process) : "-";
+        return <Typography
+            noWrap
+            align="center"
+            style={{ color: getProcessStatusColor(status, props.theme) }} >
+            {status}
+        </Typography>;
+    });
index 2cb30198e7242c54fca8020bdf5579dc57535a66..803d8002a365105c2691648e2a5f000cbd3be254 100644 (file)
@@ -68,24 +68,7 @@ export const favoritePanelColumns: DataColumns<string, FavoritePanelFilter> = [
         name: "Status",
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
-        filters: [
-            {
-                name: ContainerRequestState.COMMITTED,
-                selected: true,
-                type: ContainerRequestState.COMMITTED
-            },
-            {
-                name: ContainerRequestState.FINAL,
-                selected: true,
-                type: ContainerRequestState.FINAL
-            },
-            {
-                name: ContainerRequestState.UNCOMMITTED,
-                selected: true,
-                type: ContainerRequestState.UNCOMMITTED
-            }
-        ],
+        filters: [],
         render: uuid => <ProcessStatus uuid={uuid} />,
         width: "75px"
     },
@@ -93,7 +76,6 @@ export const favoritePanelColumns: DataColumns<string, FavoritePanelFilter> = [
         name: FavoritePanelColumnNames.TYPE,
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
         filters: [
             {
                 name: resourceLabel(ResourceKind.COLLECTION),
@@ -118,7 +100,6 @@ export const favoritePanelColumns: DataColumns<string, FavoritePanelFilter> = [
         name: FavoritePanelColumnNames.OWNER,
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
         filters: [],
         render: uuid => <ResourceOwner uuid={uuid} />,
         width: "200px"
@@ -127,7 +108,6 @@ export const favoritePanelColumns: DataColumns<string, FavoritePanelFilter> = [
         name: FavoritePanelColumnNames.FILE_SIZE,
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
         filters: [],
         render: uuid => <ResourceFileSize uuid={uuid} />,
         width: "50px"
index f76efe0d993dd2b83cbdc5b87a952405b73b8c54..d12704bcf4b69c87ec59283dc824feba3e1dd308 100644 (file)
@@ -29,7 +29,7 @@ export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRoot
 export const ProcessPanelRoot = (props: ProcessPanelRootProps) =>
     props.process
         ? <Grid container spacing={16} alignItems="stretch">
-            <Grid item sm={12} md={7} alignItems="stretch">
+            <Grid item sm={12} md={7}>
                 <ProcessInformationCard
                     process={props.process}
                     onContextMenu={props.onContextMenu} />
index 6c5562db7cbff45d8f809b50a552c3d92e1e4b90..85de7033340f8bde984c41a4e0e751e24a20bf35 100644 (file)
@@ -55,7 +55,7 @@ export const SubprocessesCard = withStyles(styles)(
                         <Grid item md={12} lg={6}/>
                         {
                             filters.map(filter =>
-                                <Grid item md={12} lg={6} key={filter.key} spacing={0} className={classes.gridFilter}>
+                                <Grid item md={12} lg={6} key={filter.key} className={classes.gridFilter}>
                                     <SubprocessFilter {...filter} onToggle={() => onToggle(filter.label)} />
                                 </Grid>
                             )
index 1cb72a963b0ff709bff33203ad21859eda374c01..b75b37aab5e7fa99ac8c4bb2d738abb55d764b29 100644 (file)
@@ -72,24 +72,7 @@ export const projectPanelColumns: DataColumns<string, ProjectPanelFilter> = [
         name: "Status",
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
-        filters: [
-            {
-                name: ContainerRequestState.COMMITTED,
-                selected: true,
-                type: ContainerRequestState.COMMITTED
-            },
-            {
-                name: ContainerRequestState.FINAL,
-                selected: true,
-                type: ContainerRequestState.FINAL
-            },
-            {
-                name: ContainerRequestState.UNCOMMITTED,
-                selected: true,
-                type: ContainerRequestState.UNCOMMITTED
-            }
-        ],
+        filters: [],
         render: uuid => <ProcessStatus uuid={uuid} />,
         width: "75px"
     },
@@ -97,7 +80,6 @@ export const projectPanelColumns: DataColumns<string, ProjectPanelFilter> = [
         name: ProjectPanelColumnNames.TYPE,
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
         filters: [
             {
                 name: resourceLabel(ResourceKind.COLLECTION),
@@ -122,7 +104,6 @@ export const projectPanelColumns: DataColumns<string, ProjectPanelFilter> = [
         name: ProjectPanelColumnNames.OWNER,
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
         filters: [],
         render: uuid => <ResourceOwner uuid={uuid} />,
         width: "200px"
@@ -131,7 +112,6 @@ export const projectPanelColumns: DataColumns<string, ProjectPanelFilter> = [
         name: ProjectPanelColumnNames.FILE_SIZE,
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
         filters: [],
         render: uuid => <ResourceFileSize uuid={uuid} />,
         width: "50px"