add compute node middleware, data explorer and remove reducer
authorJanicki Artur <artur.janicki@contractors.roche.com>
Thu, 13 Dec 2018 15:32:28 +0000 (16:32 +0100)
committerJanicki Artur <artur.janicki@contractors.roche.com>
Thu, 13 Dec 2018 15:32:28 +0000 (16:32 +0100)
Feature #14602_admin_compute_node_paginations

Arvados-DCO-1.1-Signed-off-by: Janicki Artur <artur.janicki@contractors.roche.com>

src/store/advanced-tab/advanced-tab.ts
src/store/compute-nodes/compute-nodes-actions.ts
src/store/compute-nodes/compute-nodes-middleware-service.ts [new file with mode: 0644]
src/store/compute-nodes/compute-nodes-reducer.ts [deleted file]
src/store/context-menu/context-menu-actions.ts
src/store/link-panel/link-panel-actions.ts
src/store/store.ts
src/store/workbench/workbench-actions.ts
src/views-components/data-explorer/renderers.tsx
src/views/compute-node-panel/compute-node-panel-root.tsx
src/views/compute-node-panel/compute-node-panel.tsx

index 659b6e49fb7b805aa31d45e960c021808576f849..da3f5c9173f5ac1fb7a7a11af0436207f2b76587 100644 (file)
@@ -241,7 +241,8 @@ export const openAdvancedTabDialog = (uuid: string) =>
                 dispatch<any>(initAdvancedTabDialog(advanceDataUser));
                 break;
             case ResourceKind.NODE:
-                const dataComputeNode = getState().computeNodes.find(node => node.uuid === uuid);
+                const computeNodeResources = getState().resources;
+                const dataComputeNode = getResource<NodeResource>(uuid)(computeNodeResources);
                 const advanceDataComputeNode = advancedTabData({
                     uuid,
                     metadata: '',
index 659b1e8674c7c63138d76c106c3cc23941665e02..f2f6ad0741e6312f7f5e9cd0683288c13ae74d93 100644 (file)
@@ -3,21 +3,18 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import { Dispatch } from "redux";
-import { unionize, ofType, UnionOf } from "~/common/unionize";
 import { RootState } from '~/store/store';
 import { setBreadcrumbs } from '~/store/breadcrumbs/breadcrumbs-actions';
-import { ServiceRepository } from "~/services/services";
-import { NodeResource } from '~/models/node';
 import { dialogActions } from '~/store/dialog/dialog-actions';
 import { snackbarActions } from '~/store/snackbar/snackbar-actions';
 import { navigateToRootProject } from '~/store/navigation/navigation-action';
+import { bindDataExplorerActions } from '~/store/data-explorer/data-explorer-action';
+import { getResource } from '~/store/resources/resources';
+import { ServiceRepository } from "~/services/services";
+import { NodeResource } from '~/models/node';
 
-export const computeNodesActions = unionize({
-    SET_COMPUTE_NODES: ofType<NodeResource[]>(),
-    REMOVE_COMPUTE_NODE: ofType<string>()
-});
-
-export type ComputeNodesActions = UnionOf<typeof computeNodesActions>;
+export const COMPUTE_NODE_PANEL_ID = "computeNodeId";
+export const computeNodesActions = bindDataExplorerActions(COMPUTE_NODE_PANEL_ID);
 
 export const COMPUTE_NODE_REMOVE_DIALOG = 'computeNodeRemoveDialog';
 export const COMPUTE_NODE_ATTRIBUTES_DIALOG = 'computeNodeAttributesDialog';
@@ -28,8 +25,7 @@ export const loadComputeNodesPanel = () =>
         if (user && user.isAdmin) {
             try {
                 dispatch(setBreadcrumbs([{ label: 'Compute Nodes' }]));
-                const response = await services.nodeService.list();
-                dispatch(computeNodesActions.SET_COMPUTE_NODES(response.items));
+                dispatch(computeNodesActions.REQUEST_ITEMS());
             } catch (e) {
                 return;
             }
@@ -41,7 +37,8 @@ export const loadComputeNodesPanel = () =>
 
 export const openComputeNodeAttributesDialog = (uuid: string) =>
     (dispatch: Dispatch, getState: () => RootState) => {
-        const computeNode = getState().computeNodes.find(node => node.uuid === uuid);
+        const { resources } = getState();
+        const computeNode = getResource<NodeResource>(uuid)(resources);
         dispatch(dialogActions.OPEN_DIALOG({ id: COMPUTE_NODE_ATTRIBUTES_DIALOG, data: { computeNode } }));
     };
 
@@ -63,7 +60,7 @@ export const removeComputeNode = (uuid: string) =>
         dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing ...' }));
         try {
             await services.nodeService.delete(uuid);
-            dispatch(computeNodesActions.REMOVE_COMPUTE_NODE(uuid));
+            dispatch(computeNodesActions.REQUEST_ITEMS());
             dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Compute node has been successfully removed.', hideDuration: 2000 }));
         } catch (e) {
             return;
diff --git a/src/store/compute-nodes/compute-nodes-middleware-service.ts b/src/store/compute-nodes/compute-nodes-middleware-service.ts
new file mode 100644 (file)
index 0000000..792da7a
--- /dev/null
@@ -0,0 +1,70 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { ServiceRepository } from '~/services/services';
+import { MiddlewareAPI, Dispatch } from 'redux';
+import { DataExplorerMiddlewareService, dataExplorerToListParams, listResultsToDataExplorerItemsMeta } from '~/store/data-explorer/data-explorer-middleware-service';
+import { RootState } from '~/store/store';
+import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
+import { DataExplorer, getDataExplorer } from '~/store/data-explorer/data-explorer-reducer';
+import { updateResources } from '~/store/resources/resources-actions';
+import { getSortColumn } from "~/store/data-explorer/data-explorer-reducer";
+import { computeNodesActions } from '~/store/compute-nodes/compute-nodes-actions';
+import { OrderDirection, OrderBuilder } from '~/services/api/order-builder';
+import { ListResults } from '~/services/common-service/common-service';
+import { NodeResource } from '~/models/node';
+import { SortDirection } from '~/components/data-table/data-column';
+import { ComputeNodePanelColumnNames } from '~/views/compute-node-panel/compute-node-panel-root';
+
+export class ComputeNodeMiddlewareService extends DataExplorerMiddlewareService {
+    constructor(private services: ServiceRepository, id: string) {
+        super(id);
+    }
+
+    async requestItems(api: MiddlewareAPI<Dispatch, RootState>) {
+        const state = api.getState();
+        const dataExplorer = getDataExplorer(state.dataExplorer, this.getId());
+        try {
+            const response = await this.services.nodeService.list(getParams(dataExplorer));
+            api.dispatch(updateResources(response.items));
+            api.dispatch(setItems(response));
+        } catch {
+            api.dispatch(couldNotFetchLinks());
+        }
+    }
+}
+
+export const getParams = (dataExplorer: DataExplorer) => ({
+    ...dataExplorerToListParams(dataExplorer),
+    order: getOrder(dataExplorer)
+});
+
+const getOrder = (dataExplorer: DataExplorer) => {
+    const sortColumn = getSortColumn(dataExplorer);
+    const order = new OrderBuilder<NodeResource>();
+    if (sortColumn) {
+        const sortDirection = sortColumn && sortColumn.sortDirection === SortDirection.ASC
+            ? OrderDirection.ASC
+            : OrderDirection.DESC;
+
+        const columnName = sortColumn && sortColumn.name === ComputeNodePanelColumnNames.UUID ? "uuid" : "modifiedAt";
+        return order
+            .addOrder(sortDirection, columnName)
+            .getOrder();
+    } else {
+        return order.getOrder();
+    }
+};
+
+export const setItems = (listResults: ListResults<NodeResource>) =>
+    computeNodesActions.SET_ITEMS({
+        ...listResultsToDataExplorerItemsMeta(listResults),
+        items: listResults.items.map(resource => resource.uuid),
+    });
+
+const couldNotFetchLinks = () =>
+    snackbarActions.OPEN_SNACKBAR({
+        message: 'Could not fetch compute nodes.',
+        kind: SnackbarKind.ERROR
+    });
diff --git a/src/store/compute-nodes/compute-nodes-reducer.ts b/src/store/compute-nodes/compute-nodes-reducer.ts
deleted file mode 100644 (file)
index 44a3780..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { computeNodesActions, ComputeNodesActions } from '~/store/compute-nodes/compute-nodes-actions';
-import { NodeResource } from '~/models/node';
-
-export type ComputeNodesState = NodeResource[];
-
-const initialState: ComputeNodesState = [];
-
-export const computeNodesReducer = (state: ComputeNodesState = initialState, action: ComputeNodesActions): ComputeNodesState =>
-    computeNodesActions.match(action, {
-        SET_COMPUTE_NODES: nodes => nodes,
-        REMOVE_COMPUTE_NODE: (uuid: string) => state.filter((computeNode) => computeNode.uuid !== uuid),
-        default: () => state
-    });
\ No newline at end of file
index e9b08a8417afdb3d69adb32e8ba44847bb6d9a39..6a5e7b788d070e4ace9e27afb7ab730c45df030e 100644 (file)
@@ -17,7 +17,6 @@ import { RepositoryResource } from '~/models/repositories';
 import { SshKeyResource } from '~/models/ssh-key';
 import { VirtualMachinesResource } from '~/models/virtual-machines';
 import { KeepServiceResource } from '~/models/keep-services';
-import { NodeResource } from '~/models/node';
 import { ApiClientAuthorization } from '~/models/api-client-authorization';
 
 export const contextMenuActions = unionize({
@@ -111,12 +110,12 @@ export const openKeepServiceContextMenu = (event: React.MouseEvent<HTMLElement>,
         }));
     };
 
-export const openComputeNodeContextMenu = (event: React.MouseEvent<HTMLElement>, computeNode: NodeResource) =>
+export const openComputeNodeContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) =>
     (dispatch: Dispatch) => {
         dispatch<any>(openContextMenu(event, {
             name: '',
-            uuid: computeNode.uuid,
-            ownerUuid: computeNode.ownerUuid,
+            uuid: resourceUuid,
+            ownerUuid: '',
             kind: ResourceKind.NODE,
             menuKind: ContextMenuKind.NODE
         }));
index 944c1bd17703004742f7b5bc16212bf7982ba84a..7cbc507342cd3b301035852f71b989a36837d4be 100644 (file)
@@ -18,6 +18,12 @@ export const linkPanelActions = bindDataExplorerActions(LINK_PANEL_ID);
 export const LINK_REMOVE_DIALOG = 'linkRemoveDialog';
 export const LINK_ATTRIBUTES_DIALOG = 'linkAttributesDialog';
 
+export const loadLinkPanel = () =>
+    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch(setBreadcrumbs([{ label: 'Links' }]));
+        dispatch(linkPanelActions.REQUEST_ITEMS());
+    };
+
 export const openLinkAttributesDialog = (uuid: string) =>
     (dispatch: Dispatch, getState: () => RootState) => {
         const { resources } = getState();
@@ -38,12 +44,6 @@ export const openLinkRemoveDialog = (uuid: string) =>
         }));
     };
 
-export const loadLinkPanel = () =>
-    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        dispatch(setBreadcrumbs([{ label: 'Links' }]));
-        dispatch(linkPanelActions.REQUEST_ITEMS());
-    };
-
 export const removeLink = (uuid: string) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing ...' }));
index 792224d240f7434595f409db117a85744fbf528a..d196e632d24f8fe3a7f8bc3062d8a7c2bd8cb01f 100644 (file)
@@ -48,10 +48,11 @@ import { repositoriesReducer } from '~/store/repositories/repositories-reducer';
 import { keepServicesReducer } from '~/store/keep-services/keep-services-reducer';
 import { UserMiddlewareService } from '~/store/users/user-panel-middleware-service';
 import { USERS_PANEL_ID } from '~/store/users/users-actions';
-import { computeNodesReducer } from '~/store/compute-nodes/compute-nodes-reducer';
 import { apiClientAuthorizationsReducer } from '~/store/api-client-authorizations/api-client-authorizations-reducer';
 import { LINK_PANEL_ID } from '~/store/link-panel/link-panel-actions';
 import { LinkMiddlewareService } from '~/store/link-panel/link-panel-middleware-service';
+import { COMPUTE_NODE_PANEL_ID } from '~/store/compute-nodes/compute-nodes-actions';
+import { ComputeNodeMiddlewareService } from '~/store/compute-nodes/compute-nodes-middleware-service';
 
 const composeEnhancers =
     (process.env.NODE_ENV === 'development' &&
@@ -89,6 +90,9 @@ export function configureStore(history: History, services: ServiceRepository): R
     const linkPanelMiddleware = dataExplorerMiddleware(
         new LinkMiddlewareService(services, LINK_PANEL_ID)
     );
+    const computeNodeMiddleware = dataExplorerMiddleware(
+        new ComputeNodeMiddlewareService(services, COMPUTE_NODE_PANEL_ID)
+    );
     const middlewares: Middleware[] = [
         routerMiddleware(history),
         thunkMiddleware.withExtraArgument(services),
@@ -99,7 +103,8 @@ export function configureStore(history: History, services: ServiceRepository): R
         sharedWithMePanelMiddleware,
         workflowPanelMiddleware,
         userPanelMiddleware,
-        linkPanelMiddleware
+        linkPanelMiddleware,
+        computeNodeMiddleware
     ];
     const enhancer = composeEnhancers(applyMiddleware(...middlewares));
     return createStore(rootReducer, enhancer);
@@ -131,6 +136,5 @@ const createRootReducer = (services: ServiceRepository) => combineReducers({
     virtualMachines: virtualMachinesReducer,
     repositories: repositoriesReducer,
     keepServices: keepServicesReducer,
-    computeNodes: computeNodesReducer,
     apiClientAuthorizations: apiClientAuthorizationsReducer
 });
index 85540f0b434ac90826b93aba3561f726d3325578..e42e6c3ea1a8520f1961e0d5751bf83bb6e22e44 100644 (file)
@@ -60,9 +60,10 @@ import { loadRepositoriesPanel } from '~/store/repositories/repositories-actions
 import { loadKeepServicesPanel } from '~/store/keep-services/keep-services-actions';
 import { loadUsersPanel, userBindedActions } from '~/store/users/users-actions';
 import { loadLinkPanel, linkPanelActions } from '~/store/link-panel/link-panel-actions';
+import { loadComputeNodesPanel, computeNodesActions } from '~/store/compute-nodes/compute-nodes-actions';
 import { linkPanelColumns } from '~/views/link-panel/link-panel-root';
 import { userPanelColumns } from '~/views/user-panel/user-panel';
-import { loadComputeNodesPanel } from '~/store/compute-nodes/compute-nodes-actions';
+import { computeNodePanelColumns } from '~/views/compute-node-panel/compute-node-panel-root';
 import { loadApiClientAuthorizationsPanel } from '~/store/api-client-authorizations/api-client-authorizations-actions';
 
 export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen';
@@ -99,6 +100,7 @@ export const loadWorkbench = () =>
                 dispatch(searchResultsPanelActions.SET_COLUMNS({ columns: searchResultsPanelColumns }));
                 dispatch(userBindedActions.SET_COLUMNS({ columns: userPanelColumns }));
                 dispatch(linkPanelActions.SET_COLUMNS({ columns: linkPanelColumns }));
+                dispatch(computeNodesActions.SET_COLUMNS({ columns: computeNodePanelColumns }));
                 dispatch<any>(initSidePanelTree());
                 if (router.location) {
                     const match = matchRootRoute(router.location.pathname);
index a4713c8dc2337a8df20bb27d2094beff6fab32c5..9b9a5bb5c371a1a34195b82879eea901dc13f223 100644 (file)
@@ -26,6 +26,8 @@ import { toggleIsActive, toggleIsAdmin } from '~/store/users/users-actions';
 import { LinkResource } from '~/models/link';
 import { navigateTo } from '~/store/navigation/navigation-action';
 import { Link } from 'react-router-dom';
+import { NodeResource } from '../../models/node';
+import { NodeInfo } from '~/models/node';
 
 const renderName = (item: { name: string; uuid: string, kind: string }) =>
     <Grid container alignItems="center" wrap="nowrap" spacing={16}>
@@ -191,6 +193,60 @@ export const ResourceUsername = connect(
         return resource || { username: '' };
     })(renderUsername);
 
+// Compute Node Resources
+const renderNodeDate = (date?: string) =>
+    <Typography noWrap>{formatDate(date) || '(none)'}</Typography>;
+
+const renderNodeData = (property?: string) => 
+    <Typography noWrap>{property || '(none)'}</Typography>;
+
+const renderNodeInfo = (item: { info: NodeInfo }) =>
+    <Typography>
+        {JSON.stringify(item.info, null, 4)}
+    </Typography>;
+
+export const ResourceNodeInfo = connect(
+    (state: RootState, props: { uuid: string }) => {
+        const resource = getResource<NodeResource>(props.uuid)(state.resources);
+        return resource || { info: {} };
+    })(renderNodeInfo);
+
+export const ResourceNodeDomain = connect(
+    (state: RootState, props: { uuid: string }) => {
+        const resource = getResource<NodeResource>(props.uuid)(state.resources);
+        return { property: resource ? resource.domain : '' };
+    })((props: { property: string }) => renderNodeData(props.property));
+
+export const ResourceNodeFirstPingAt = connect(
+    (state: RootState, props: { uuid: string }) => {
+        const resource = getResource<NodeResource>(props.uuid)(state.resources);
+        return { date: resource ? resource.firstPingAt : '' };
+    })((props: { date: string }) => renderNodeDate(props.date));
+
+export const ResourceNodeHostname = connect(
+    (state: RootState, props: { uuid: string }) => {
+        const resource = getResource<NodeResource>(props.uuid)(state.resources);
+        return { property: resource ? resource.hostname : '' };
+    })((props: { property: string }) => renderNodeData(props.property));
+
+export const ResourceNodeIpAddress = connect(
+    (state: RootState, props: { uuid: string }) => {
+        const resource = getResource<NodeResource>(props.uuid)(state.resources);
+        return { property: resource ? resource.ipAddress : '' };
+    })((props: { property: string }) => renderNodeData(props.property));
+
+export const ResourceNodeJobUuid = connect(
+    (state: RootState, props: { uuid: string }) => {
+        const resource = getResource<NodeResource>(props.uuid)(state.resources);
+        return { property: resource ? resource.jobUuid : '' };
+    })((props: { property: string }) => renderNodeData(props.property));
+
+export const ResourceNodeLastPingAt = connect(
+    (state: RootState, props: { uuid: string }) => {
+        const resource = getResource<NodeResource>(props.uuid)(state.resources);
+        return { date: resource ? resource.lastPingAt : '' };
+    })((props: { date: string }) => renderNodeDate(props.date));
+
 // Links Resources
 const renderLinkName = (item: { name: string }) =>
     <Typography noWrap>{item.name || '(none)'}</Typography>;
index 2d325b514a8bb20897d3f4ca5959b4b5496af14e..1a525d8667ad1112e92ee3dc8b24456956c0183b 100644 (file)
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
+import { ShareMeIcon } from '~/components/icon/icon';
+import { DataExplorer } from '~/views-components/data-explorer/data-explorer';
+import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
+import { COMPUTE_NODE_PANEL_ID } from '~/store/compute-nodes/compute-nodes-actions';
+import { DataColumns } from '~/components/data-table/data-table';
+import { SortDirection } from '~/components/data-table/data-column';
+import { createTree } from '~/models/tree';
 import { 
-    StyleRulesCallback, WithStyles, withStyles, Card, CardContent, Grid, Table, 
-    TableHead, TableRow, TableCell, TableBody, Tooltip, IconButton 
-} from '@material-ui/core';
-import { ArvadosTheme } from '~/common/custom-theme';
-import { MoreOptionsIcon } from '~/components/icon/icon';
-import { NodeResource } from '~/models/node';
-import { formatDate } from '~/common/formatters';
+    ResourceUuid, ResourceNodeInfo, ResourceNodeDomain, ResourceNodeHostname, ResourceNodeJobUuid,
+    ResourceNodeFirstPingAt, ResourceNodeLastPingAt, ResourceNodeIpAddress
+} from '~/views-components/data-explorer/renderers';
+import { ResourcesState } from '~/store/resources/resources';
 
-type CssRules = 'root' | 'tableRow';
+export enum ComputeNodePanelColumnNames {
+    INFO = 'Info',
+    UUID = 'UUID',
+    DOMAIN = 'Domain',
+    FIRST_PING_AT = 'First ping at',
+    HOSTNAME = 'Hostname',
+    IP_ADDRESS = 'IP Address',
+    JOB = 'Job',
+    LAST_PING_AT = 'Last ping at'
+}
 
-const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
-    root: {
-        width: '100%',
-        overflow: 'auto'
+export const computeNodePanelColumns: DataColumns<string> = [
+    {
+        name: ComputeNodePanelColumnNames.INFO,
+        selected: true,
+        configurable: true,
+        filters: createTree(),
+        render: uuid => <ResourceNodeInfo uuid={uuid} />
+    },
+    {
+        name: ComputeNodePanelColumnNames.UUID,
+        selected: true,
+        configurable: true,
+        sortDirection: SortDirection.NONE,
+        filters: createTree(),
+        render: uuid => <ResourceUuid uuid={uuid} />
+    },
+    {
+        name: ComputeNodePanelColumnNames.DOMAIN,
+        selected: true,
+        configurable: true,
+        filters: createTree(),
+        render: uuid => <ResourceNodeDomain uuid={uuid} />
+    },
+    {
+        name: ComputeNodePanelColumnNames.FIRST_PING_AT,
+        selected: true,
+        configurable: true,
+        filters: createTree(),
+        render: uuid => <ResourceNodeFirstPingAt uuid={uuid} />
+    },
+    {
+        name: ComputeNodePanelColumnNames.HOSTNAME,
+        selected: true,
+        configurable: true,
+        filters: createTree(),
+        render: uuid => <ResourceNodeHostname uuid={uuid} />
+    },
+    {
+        name: ComputeNodePanelColumnNames.IP_ADDRESS,
+        selected: true,
+        configurable: true,
+        filters: createTree(),
+        render: uuid => <ResourceNodeIpAddress uuid={uuid} />
+    },
+    {
+        name: ComputeNodePanelColumnNames.JOB,
+        selected: true,
+        configurable: true,
+        filters: createTree(),
+        render: uuid => <ResourceNodeJobUuid uuid={uuid} />
     },
-    tableRow: {
-        '& th': {
-            whiteSpace: 'nowrap'
-        }
+    {
+        name: ComputeNodePanelColumnNames.LAST_PING_AT,
+        selected: true,
+        configurable: true,
+        filters: createTree(),
+        render: uuid => <ResourceNodeLastPingAt uuid={uuid} />
     }
-});
+];
 
 export interface ComputeNodePanelRootActionProps {
-    openRowOptions: (event: React.MouseEvent<HTMLElement>, computeNode: NodeResource) => void;
+    onItemClick: (item: string) => void;
+    onContextMenu: (event: React.MouseEvent<HTMLElement>, item: string) => void;
+    onItemDoubleClick: (item: string) => void;
 }
 
 export interface ComputeNodePanelRootDataProps {
-    computeNodes: NodeResource[];
-    hasComputeNodes: boolean;
+    resources: ResourcesState;
 }
 
-type ComputeNodePanelRootProps = ComputeNodePanelRootActionProps & ComputeNodePanelRootDataProps & WithStyles<CssRules>;
+type ComputeNodePanelRootProps = ComputeNodePanelRootActionProps & ComputeNodePanelRootDataProps;
 
-export const ComputeNodePanelRoot = withStyles(styles)(
-    ({ classes, hasComputeNodes, computeNodes, openRowOptions }: ComputeNodePanelRootProps) =>
-        <Card className={classes.root}>
-            <CardContent>
-                {hasComputeNodes && <Grid container direction="row">
-                    <Grid item xs={12}>
-                        <Table>
-                            <TableHead>
-                                <TableRow className={classes.tableRow}>
-                                    <TableCell>Info</TableCell>
-                                    <TableCell>UUID</TableCell>
-                                    <TableCell>Domain</TableCell>
-                                    <TableCell>First ping at</TableCell>
-                                    <TableCell>Hostname</TableCell>
-                                    <TableCell>IP Address</TableCell>
-                                    <TableCell>Job</TableCell>
-                                    <TableCell>Last ping at</TableCell>
-                                    <TableCell />
-                                </TableRow>
-                            </TableHead>
-                            <TableBody>
-                                {computeNodes.map((computeNode, index) =>
-                                    <TableRow key={index} className={classes.tableRow}>
-                                        <TableCell>{JSON.stringify(computeNode.info, null, 4)}</TableCell>
-                                        <TableCell>{computeNode.uuid}</TableCell>
-                                        <TableCell>{computeNode.domain}</TableCell>
-                                        <TableCell>{formatDate(computeNode.firstPingAt) || '(none)'}</TableCell>
-                                        <TableCell>{computeNode.hostname || '(none)'}</TableCell>
-                                        <TableCell>{computeNode.ipAddress || '(none)'}</TableCell>
-                                        <TableCell>{computeNode.jobUuid || '(none)'}</TableCell>
-                                        <TableCell>{formatDate(computeNode.lastPingAt) || '(none)'}</TableCell>
-                                        <TableCell>
-                                            <Tooltip title="More options" disableFocusListener>
-                                                <IconButton onClick={event => openRowOptions(event, computeNode)}>
-                                                    <MoreOptionsIcon />
-                                                </IconButton>
-                                            </Tooltip>
-                                        </TableCell>
-                                    </TableRow>)}
-                            </TableBody>
-                        </Table>
-                    </Grid>
-                </Grid>}
-            </CardContent>
-        </Card>
-);
\ No newline at end of file
+export const ComputeNodePanelRoot = (props: ComputeNodePanelRootProps) => {
+    return <DataExplorer
+        id={COMPUTE_NODE_PANEL_ID}
+        onRowClick={props.onItemClick}
+        onRowDoubleClick={props.onItemDoubleClick}
+        onContextMenu={props.onContextMenu}
+        contextMenuColumn={true}
+        hideColumnSelector
+        hideSearchInput
+        dataTableDefaultView={
+            <DataTableDefaultView
+                icon={ShareMeIcon}
+                messages={['Your compute node list is empty.']} />
+        } />;
+};
\ No newline at end of file
index a4f22c808959cae960d606910411f1d540c7a11f..77a6abef45026fabf64dca423311ef13af31a7c3 100644 (file)
@@ -5,25 +5,25 @@
 import { RootState } from '~/store/store';
 import { Dispatch } from 'redux';
 import { connect } from 'react-redux';
-import { } from '~/store/compute-nodes/compute-nodes-actions';
 import {
     ComputeNodePanelRoot,
     ComputeNodePanelRootDataProps,
     ComputeNodePanelRootActionProps
 } from '~/views/compute-node-panel/compute-node-panel-root';
-import { openComputeNodeContextMenu } from '~/store/context-menu/context-menu-actions';
+import { openComputeNodeContextMenu, resourceKindToContextMenuKind } from '~/store/context-menu/context-menu-actions';
 
 const mapStateToProps = (state: RootState): ComputeNodePanelRootDataProps => {
     return {
-        computeNodes: state.computeNodes,
-        hasComputeNodes: state.computeNodes.length > 0
+        resources: state.resources
     };
 };
 
 const mapDispatchToProps = (dispatch: Dispatch): ComputeNodePanelRootActionProps => ({
-    openRowOptions: (event, computeNode) => {
-        dispatch<any>(openComputeNodeContextMenu(event, computeNode));
-    }
+    onContextMenu: (event, resourceUuid) => {
+        dispatch<any>(openComputeNodeContextMenu(event, resourceUuid));
+    },
+    onItemClick: (resourceUuid: string) => { return; },
+    onItemDoubleClick: uuid => { return; }
 });
 
 export const ComputeNodePanel = connect(mapStateToProps, mapDispatchToProps)(ComputeNodePanelRoot);
\ No newline at end of file