You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ea...@apache.org on 2019/11/02 14:36:48 UTC
[qpid-dispatch] branch eallen-DISPATCH-1385 updated: Progress on
details pages
This is an automated email from the ASF dual-hosted git repository.
eallen pushed a commit to branch eallen-DISPATCH-1385
in repository https://gitbox.apache.org/repos/asf/qpid-dispatch.git
The following commit(s) were added to refs/heads/eallen-DISPATCH-1385 by this push:
new 679c5f4 Progress on details pages
679c5f4 is described below
commit 679c5f418809e029ac99760f5b2093a36d615d0a
Author: Ernest Allen <ea...@redhat.com>
AuthorDate: Sat Nov 2 10:36:31 2019 -0400
Progress on details pages
---
console/react/src/App.css | 25 ++-
.../react/src/details/dataSources/addressData.js | 1 +
.../src/details/dataSources/connectionData.js | 130 --------------
.../react/src/details/dataSources/defaultData.js | 32 +++-
console/react/src/details/dataSources/linkData.js | 196 ++++-----------------
.../{addressData.js => listenerData.js} | 37 ++--
console/react/src/details/enitiesPage.js | 79 +++++++--
console/react/src/details/entityData.js | 10 +-
console/react/src/details/entityListTable.js | 92 ++++++----
.../react/src/{overview => }/detailsTablePage.js | 98 ++++++-----
console/react/src/layout.js | 10 +-
console/react/src/overview/overviewTable.js | 3 +-
console/react/src/qdrService.js | 2 +
console/react/src/tableToolbar.jsx | 51 ++++--
14 files changed, 343 insertions(+), 423 deletions(-)
diff --git a/console/react/src/App.css b/console/react/src/App.css
index 386930b..758ca39 100644
--- a/console/react/src/App.css
+++ b/console/react/src/App.css
@@ -2,9 +2,12 @@
height: 100vh;
}
+/*
#main-content-page-layout-manual-nav {
overflow-y: hidden;
}
+*/
+
.App {
text-align: center;
height: 100%;
@@ -1107,6 +1110,7 @@ div.details-table ul.entities-list {
.entities-list li {
padding: 0.25em 2em;
border-left: 2px solid white;
+ margin-top: 8px;
}
.entities-list li:hover {
background-color: #eaeaea;
@@ -1117,10 +1121,12 @@ div.details-table ul.entities-list {
background-color: #eaeaea;
}
+/*
#entityList {
max-height: calc(100vh - 140px);
padding-top: 0.5em;
}
+ */
.split-left {
text-align: left;
@@ -1135,7 +1141,7 @@ span.entity-type i {
padding-right: 1em;
font-style: normal;
}
-span.entity-type i:before {
+span.entity-type i.address-local:before {
font-family: FontAwesome;
content: "\f0ac";
}
@@ -1148,8 +1154,25 @@ span.entity-type i.address-router:before {
content: "\f047";
}
+span.entity-type i.link-type-endpoint:before {
+ content: "\f109";
+}
+span.entity-type i.link-type-inter-router:before {
+ content: "\f07e";
+}
+span.entity-type i.link-type-router-control:before {
+ content: "\f013";
+}
+
.details-table .pf-l-toolbar {
margin: 0 !important;
padding: 1em 1em;
border-bottom: 1px solid #eaeaea;
}
+
+.details-table table {
+ margin-left: 0.25em;
+}
+.details-header {
+ border-bottom: 1px solid #eaeaea;
+}
diff --git a/console/react/src/details/dataSources/addressData.js b/console/react/src/details/dataSources/addressData.js
index 528c286..13c7e97 100644
--- a/console/react/src/details/dataSources/addressData.js
+++ b/console/react/src/details/dataSources/addressData.js
@@ -38,6 +38,7 @@ class AddressData extends DefaultData {
constructor(service, schema) {
super(service, schema);
this.typeFormatter = AddressType;
+ this.detailName = "router.address";
}
}
diff --git a/console/react/src/details/dataSources/connectionData.js b/console/react/src/details/dataSources/connectionData.js
deleted file mode 100644
index b126e31..0000000
--- a/console/react/src/details/dataSources/connectionData.js
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements. See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership. The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied. See the License for the
-specific language governing permissions and limitations
-under the License.
-*/
-
-class ConnectionData {
- constructor(service) {
- this.service = service;
- this.fields = [
- {
- title: "Host",
- field: "name"
- },
- { title: "Container", field: "container" },
- { title: "Role", field: "role" },
- { title: "Dir", field: "dir" },
- { title: "Security", field: "security" },
- {
- title: "Authentication",
- field: "authentication"
- },
- {
- title: "",
- noSort: true
- }
- ];
- this.detailEntity = "connection";
- this.detailName = "Connection";
- }
-
- hasType = () => {
- return true;
- };
-
- fetchRecord = (currentRecord, schema) => {
- return new Promise(resolve => {
- this.service.management.topology.fetchEntities(
- currentRecord.nodeId,
- [{ entity: "connection" }],
- data => {
- const record = data[currentRecord.nodeId]["connection"];
- const identityIndex = record.attributeNames.indexOf("identity");
- const result = record.results.find(
- r => r[identityIndex] === currentRecord.identity
- );
- let connection = this.service.utilities.flatten(
- record.attributeNames,
- result
- );
- connection = this.service.utilities.formatAttributes(
- connection,
- schema.entityTypes["connection"]
- );
- resolve(connection);
- }
- );
- });
- };
-
- doFetch = (page, perPage) => {
- return new Promise(resolve => {
- this.service.management.topology.fetchAllEntities(
- { entity: "connection" },
- nodes => {
- // we have all the data now in the nodes object
- let connectionFields = [];
- for (let node in nodes) {
- const response = nodes[node]["connection"];
- for (let i = 0; i < response.results.length; i++) {
- const result = response.results[i];
- const connection = this.service.utilities.flatten(
- response.attributeNames,
- result
- );
- let auth = "no_auth";
- let sasl = connection.sasl;
- if (connection.isAuthenticated) {
- auth = sasl;
- if (sasl === "ANONYMOUS") auth = "anonymous-user";
- else {
- if (sasl === "GSSAPI") sasl = "Kerberos";
- if (sasl === "EXTERNAL") sasl = "x.509";
- auth = connection.user + "(" + connection.sslCipher + ")";
- }
- }
-
- let sec = "no-security";
- if (connection.isEncrypted) {
- if (sasl === "GSSAPI") sec = "Kerberos";
- else
- sec = connection.sslProto + "(" + connection.sslCipher + ")";
- }
-
- let host = connection.host;
- let connField = {
- host: host,
- security: sec,
- authentication: auth,
- nodeId: node,
- uid: host + connection.container + connection.identity,
- identity: connection.identity
- };
- response.attributeNames.forEach(function(attribute, i) {
- connField[attribute] = result[i];
- });
- connectionFields.push(connField);
- }
- }
- resolve({ data: connectionFields, page, perPage });
- }
- );
- });
- };
-}
-
-export default ConnectionData;
diff --git a/console/react/src/details/dataSources/defaultData.js b/console/react/src/details/dataSources/defaultData.js
index aa7ed8e..0ed5235 100644
--- a/console/react/src/details/dataSources/defaultData.js
+++ b/console/react/src/details/dataSources/defaultData.js
@@ -31,23 +31,47 @@ class DefaultData {
actions = entity => {
return this.schema.entityTypes[entity].operations.filter(
- action => action !== "READ"
+ action => action !== "READ" && action !== "UPDATE"
);
};
- fetchRecord = (currentRecord, schema) => {
+ // called by detailsTablePage to display a single record
+ fetchRecord = (currentRecord, schema, entity) => {
return new Promise(resolve => {
- resolve(null);
+ this.service.management.topology.fetchEntities(
+ currentRecord.routerId,
+ [{ entity: entity }],
+ data => {
+ const record = data[currentRecord.routerId][entity];
+ const identityIndex = record.attributeNames.indexOf("identity");
+ const result = record.results.find(
+ r => r[identityIndex] === currentRecord.identity
+ );
+ let object = this.service.utilities.flatten(
+ record.attributeNames,
+ result
+ );
+ object = this.service.utilities.formatAttributes(
+ object,
+ schema.entityTypes[entity]
+ );
+ resolve(object);
+ }
+ );
});
};
+ // called by entityListTable to get the list of records
doFetch = (page, perPage, routerId, entity) => {
return new Promise(resolve => {
this.service.management.topology.fetchEntities(
routerId,
{ entity },
results => {
- const data = utils.flattenAll(results[routerId][entity]);
+ const data = utils.flattenAll(results[routerId][entity], f => {
+ f.routerId = routerId;
+ return f;
+ });
resolve({ data, page, perPage });
}
);
diff --git a/console/react/src/details/dataSources/linkData.js b/console/react/src/details/dataSources/linkData.js
index 89ee4bc..47bee54 100644
--- a/console/react/src/details/dataSources/linkData.js
+++ b/console/react/src/details/dataSources/linkData.js
@@ -18,175 +18,41 @@ under the License.
*/
import React from "react";
-
-const LinkDir = ({ value }) => (
- <span>
- <i
- className={`link-dir-${value} fa fa-arrow-circle-${
- value === "in" ? "right" : "left"
- }`}
- ></i>
- {value}
- </span>
-);
-
-class LinkData {
- constructor(service) {
+import DefaultData from "./defaultData";
+
+const LinkDir = ({ value, extraInfo }) => {
+ const dir = extraInfo.rowData.data.linkDir;
+ return (
+ <span className="entity-type">
+ <i
+ className={`link-dir-${dir} fa fa-arrow-circle-${
+ dir === "in" ? "right" : "left"
+ }`}
+ ></i>
+ {dir}
+ </span>
+ );
+};
+
+const LinkType = ({ value, extraInfo }) => {
+ const type = extraInfo.rowData.data.linkType;
+ return (
+ <span className="entity-type">
+ <i className={`link-type-${type} fa`}></i>
+ {type}
+ </span>
+ );
+};
+
+class LinkData extends DefaultData {
+ constructor(service, schema) {
+ super(service, schema);
this.service = service;
- this.fields = [
- { title: "Link", field: "link", noWrap: true },
- { title: "Type", field: "linkType", noWrap: true },
- { title: "Dir", field: "linkDir", formatter: LinkDir },
- { title: "Admin status", field: "adminStatus" },
- { title: "Oper status", field: "operStatus" },
- {
- title: "Deliveries",
- field: "deliveryCount",
- noWrap: true,
- numeric: true
- },
- { title: "Rate", field: "rate", numeric: true },
- {
- title: "Delayed 1 sec",
- field: "delayed1Sec",
- numeric: true
- },
- {
- title: "Delayed 10 secs",
- field: "delayed10Sec",
- numeric: true
- },
- {
- title: "Outstanding",
- field: "outstanding",
- numeric: true
- },
- { title: "Address", field: "owningAddr" }
- ];
+ this.extraFields = [{ title: "Dir", field: "linkDir", formatter: LinkDir }];
this.detailEntity = "router.link";
this.detailName = "Link";
+ this.typeFormatter = LinkType;
}
- hasType = () => {
- return true;
- };
-
- fetchRecord = (currentRecord, schema) => {
- return new Promise(resolve => {
- this.service.management.topology.fetchEntities(
- currentRecord.nodeId,
- [{ entity: "router.link" }],
- data => {
- const record = data[currentRecord.nodeId]["router.link"];
- const identityIndex = record.attributeNames.indexOf("identity");
- const result = record.results.find(
- r => r[identityIndex] === currentRecord.identity
- );
- let link = this.service.utilities.flatten(
- record.attributeNames,
- result
- );
- link = this.service.utilities.formatAttributes(
- link,
- schema.entityTypes["router.link"]
- );
- resolve(link);
- }
- );
- });
- };
-
- doFetch = (page, perPage) => {
- return new Promise(resolve => {
- this.service.management.topology.fetchAllEntities(
- { entity: "router.link" },
- nodes => {
- // we have all the data now in the nodes object
- let linkFields = [];
- var getLinkName = (node, link) => {
- let namestr = this.service.utilities.nameFromId(node);
- return `${namestr}:${link.identity}`;
- };
- var fixAddress = link => {
- let addresses = [];
- let owningAddr = link.owningAddr || "";
- let rawAddress = owningAddr;
- /*
- - "L*" => "* (local)"
- - "M0*" => "* (direct)"
- - "M1*" => "* (dequeue)"
- - "MX*" => "* (phase X)"
- */
- let address = undefined;
- let starts = { L: "(local)", M0: "(direct)", M1: "(dequeue)" };
- for (let start in starts) {
- if (owningAddr.startsWith(start)) {
- let ends = owningAddr.substr(start.length);
- address = ends + " " + starts[start];
- rawAddress = ends;
- break;
- }
- }
- if (!address) {
- // check for MX*
- if (owningAddr.length > 3) {
- if (owningAddr[0] === "M") {
- let phase = parseInt(owningAddr.substr(1));
- if (phase && !isNaN(phase)) {
- let phaseStr = phase + "";
- let star = owningAddr.substr(2 + phaseStr.length);
- address = `${star} (phase ${phaseStr})`;
- }
- }
- }
- }
- addresses[0] = address || owningAddr;
- addresses[1] = rawAddress;
- return addresses;
- };
- for (let node in nodes) {
- const response = nodes[node]["router.link"];
- for (let i = 0; i < response.results.length; i++) {
- const result = response.results[i];
- const link = this.service.utilities.flatten(
- response.attributeNames,
- result
- );
- let linkName = getLinkName(node, link);
- let addresses = fixAddress(link);
-
- linkFields.push({
- link: linkName,
- linkType: link.linkType,
- linkDir: link.linkDir,
- adminStatus: link.adminStatus,
- operStatus: link.operStatus,
- deliveryCount: link.deliveryCount,
- rate: link.settleRate,
- delayed1Sec: link.deliveriesDelayed1Sec,
- delayed10Sec: link.deliveriesDelayed10Sec,
- outstanding: link.undeliveredCount + link.unsettledCount,
- owningAddr: addresses[0],
-
- capacity: link.capacity,
- undeliveredCount: link.undeliveredCount,
- unsettledCount: link.unsettledCount,
-
- rawAddress: addresses[1],
- name: link.name,
- connectionId: link.connectionId,
- peer: link.peer,
- type: link.type,
-
- nodeId: node,
- identity: link.identity
- });
- }
- }
- resolve({ data: linkFields, page, perPage });
- }
- );
- });
- };
}
export default LinkData;
diff --git a/console/react/src/details/dataSources/addressData.js b/console/react/src/details/dataSources/listenerData.js
similarity index 57%
copy from console/react/src/details/dataSources/addressData.js
copy to console/react/src/details/dataSources/listenerData.js
index 528c286..f23b099 100644
--- a/console/react/src/details/dataSources/addressData.js
+++ b/console/react/src/details/dataSources/listenerData.js
@@ -19,26 +19,33 @@ under the License.
import React from "react";
import DefaultData from "./defaultData";
-import { utils } from "../../amqp/utilities";
-const AddressType = ({ value, extraInfo }) => {
- const data = extraInfo.rowData.data;
- const identity = utils.identity_clean(data.identity);
- const cls = utils.addr_class(identity);
-
- return (
- <span className="entity-type">
- <i className={`address-${cls}`}></i>
- {cls}
- </span>
- );
+const HostPort = ({ value, extraInfo }) => {
+ const host = extraInfo.rowData.data.host;
+ const port = extraInfo.rowData.data.port;
+ return <span className="entity-type">{`${host}:${port}`}</span>;
};
-class AddressData extends DefaultData {
+class ListenerData extends DefaultData {
constructor(service, schema) {
super(service, schema);
- this.typeFormatter = AddressType;
+ this.service = service;
+ this.extraFields = [
+ { title: "Role", field: "role" },
+ {
+ title: "Host:Port",
+ field: "host",
+ formatter: HostPort,
+ filter: this.hostPortFilter
+ }
+ ];
+ this.detailEntity = "listener";
}
+
+ hostPortFilter = (data, filterValue) => {
+ const hostport = `${data.host}${data.port}`;
+ return hostport.includes(filterValue);
+ };
}
-export default AddressData;
+export default ListenerData;
diff --git a/console/react/src/details/enitiesPage.js b/console/react/src/details/enitiesPage.js
index 038ea6d..6392968 100644
--- a/console/react/src/details/enitiesPage.js
+++ b/console/react/src/details/enitiesPage.js
@@ -22,6 +22,7 @@ import { PageSection, PageSectionVariants } from "@patternfly/react-core";
import { Stack, StackItem } from "@patternfly/react-core";
import { Split, SplitItem } from "@patternfly/react-core";
+import DetailsTablePage from "../detailsTablePage";
import EntityListTable from "./entityListTable";
import EntityList from "./entityList";
import RouterSelect from "./routerSelect";
@@ -43,22 +44,78 @@ class EntitiesPage extends React.Component {
this.setState({ lastUpdated });
};
+ // called from entityList to change entity summary
+ handleSwitchEntity = entity => {
+ if (this.listTableRef) this.listTableRef.reset();
+ this.setState({ entity, showDetails: false, detailsState: {} });
+ };
+
+ // called from breadcrumb on entityListTable to return to current entity summary
handleSelectEntity = entity => {
- this.setState({ entity });
+ this.setState({ entity, showDetails: false });
};
handleRouterSelected = routerId => {
- this.setState({ routerId });
+ this.setState({ routerId, showDetails: false });
+ };
+
+ handleDetailClick = (value, extraInfo, stateInfo) => {
+ this.setState({
+ detailsState: {
+ value: extraInfo.rowData.cells[extraInfo.columnIndex],
+ currentRecord: extraInfo.rowData.data,
+ entity: this.props.entity,
+ page: stateInfo.page,
+ sortBy: stateInfo.sortBy,
+ filterBy: stateInfo.filterBy,
+ perPage: stateInfo.perPage,
+ property: extraInfo.property
+ },
+ showDetails: true
+ });
};
render() {
+ const entityTable = () => {
+ if (this.state.entity) {
+ if (!this.state.showDetails) {
+ return (
+ <EntityListTable
+ ref={el => (this.listTableRef = el)}
+ service={this.props.service}
+ entity={this.state.entity}
+ schema={this.schema}
+ routerId={this.state.routerId}
+ lastUpdated={this.lastUpdated}
+ handleDetailClick={this.handleDetailClick}
+ detailsState={this.state.detailsState}
+ />
+ );
+ } else {
+ return (
+ <DetailsTablePage
+ details={true}
+ locationState={this.state.detailsState}
+ entity={this.state.entity}
+ service={this.props.service}
+ lastUpdated={this.lastUpdated}
+ schema={this.schema}
+ handleSelectEntity={this.handleSelectEntity}
+ />
+ );
+ }
+ } else {
+ return null;
+ }
+ };
+
return (
<PageSection
variant={PageSectionVariants.light}
className="details-table-page"
>
<Stack>
- <StackItem className="details-header pf-u-box-shadow-sm-bottom">
+ <StackItem className="details-header">
<Split>
<SplitItem isFilled className="split-left">
<span className="prompt">Router</span>{" "}
@@ -80,22 +137,10 @@ class EntitiesPage extends React.Component {
<SplitItem id="entityList">
<EntityList
schema={this.schema}
- handleSelectEntity={this.handleSelectEntity}
+ handleSelectEntity={this.handleSwitchEntity}
/>
</SplitItem>
- <SplitItem isFilled>
- {this.state.entity ? (
- <EntityListTable
- {...this.props}
- entity={this.state.entity}
- schema={this.schema}
- routerId={this.state.routerId}
- lastUpdated={this.lastUpdated}
- />
- ) : (
- <React.Fragment />
- )}
- </SplitItem>
+ <SplitItem isFilled>{entityTable()}</SplitItem>
</Split>
</StackItem>
</Stack>
diff --git a/console/react/src/details/entityData.js b/console/react/src/details/entityData.js
index 35f99c2..8ab6fc2 100644
--- a/console/react/src/details/entityData.js
+++ b/console/react/src/details/entityData.js
@@ -17,16 +17,16 @@ specific language governing permissions and limitations
under the License.
*/
-//import RouterData from "./dataSources/routerData";
import AddressData from "./dataSources/addressData";
-//import LinkData from "./dataSources/linkData";
-//import ConnectionData from "./dataSources/connectionData";
-//import LogsData from "./dataSources/logsData";
+import LinkData from "./dataSources/linkData";
+import ListenerData from "./dataSources/listenerData";
import DefaultData from "./dataSources/defaultData";
const dataMap = {
- "router.address": AddressData
+ "router.address": AddressData,
+ "router.link": LinkData,
+ listener: ListenerData
};
const defaultData = DefaultData;
diff --git a/console/react/src/details/entityListTable.js b/console/react/src/details/entityListTable.js
index f079c2d..287d872 100644
--- a/console/react/src/details/entityListTable.js
+++ b/console/react/src/details/entityListTable.js
@@ -26,20 +26,20 @@ import {
TableBody,
TableVariant
} from "@patternfly/react-table";
-import { Button, Checkbox, Pagination } from "@patternfly/react-core";
+import { Button, Pagination } from "@patternfly/react-core";
import { Redirect } from "react-router-dom";
import TableToolbar from "../tableToolbar";
import { dataMap, defaultData } from "./entityData";
// If the breadcrumb on the details page was used to return to this page,
// we will have saved state info in props.location.state
-const propFromLocation = (props, which, defaultValue) =>
- props &&
- props.location &&
- props.location.state &&
- typeof props.location.state[which] !== "undefined"
- ? props.location.state[which]
+const propFromLocation = (props, which, defaultValue) => {
+ return props &&
+ props.detailsState &&
+ typeof props.detailsState[which] !== "undefined"
+ ? props.detailsState[which]
: defaultValue;
+};
class EntityListTable extends React.Component {
constructor(props) {
@@ -56,7 +56,8 @@ class EntityListTable extends React.Component {
allRows: [],
rows: [],
redirect: false,
- redirectState: {}
+ redirectState: {},
+ hasChecked: false
};
this.initDataSource();
this.columns = [];
@@ -93,6 +94,9 @@ class EntityListTable extends React.Component {
formatter: this.dataSource.typeFormatter
});
}
+ if (this.dataSource.extraFields) {
+ this.dataSource.fields.push(...this.dataSource.extraFields);
+ }
};
setupFields = () => {
@@ -125,24 +129,29 @@ class EntityListTable extends React.Component {
update = () => {
if (this.props.entity && this.props.routerId) {
- this.fetch(
- this.state.page,
- this.state.perPage,
- this.props.routerId,
- this.props.entity
- );
+ this.fetch(this.state.page, this.state.perPage);
}
};
- fetch = (page, perPage, routerId, entity) => {
+ fetch = (page, perPage) => {
// get the data. Note: The current page number might change if
// the number of rows is less than before
+ const routerId = this.props.routerId;
+ const entity = this.props.entity;
this.dataSource.doFetch(page, perPage, routerId, entity).then(results => {
const sliced = this.slice(results.data, results.page, results.perPage);
// if fetch was called and the component was unmounted before
// the results arrived, don't call setState
if (!this.mounted) return;
const { rows, page, total, allRows } = sliced;
+ allRows.forEach(row => {
+ const prevRow = this.state.allRows.find(
+ r => r.data.name === row.data.name
+ );
+ if (prevRow && prevRow.selected) {
+ row.selected = true;
+ }
+ });
this.setState({
rows,
page,
@@ -166,19 +175,14 @@ class EntityListTable extends React.Component {
};
detailClick = (value, extraInfo) => {
- this.setState({
- redirect: true,
- redirectState: {
- value: extraInfo.rowData.cells[extraInfo.columnIndex],
- currentRecord: extraInfo.rowData.data,
- entity: this.props.entity,
- page: this.state.page,
- sortBy: this.state.sortBy,
- filterBy: this.state.filterBy,
- perPage: this.state.perPage,
- property: extraInfo.property
- }
- });
+ const stateInfo = {
+ page: this.state.page,
+ perPage: this.state.perPage,
+ routerId: this.props.routerId,
+ entity: this.props.entity,
+ filterBy: this.state.filterBy
+ };
+ this.props.handleDetailClick(value, extraInfo, stateInfo);
};
// cell formatter
@@ -260,6 +264,9 @@ class EntityListTable extends React.Component {
) {
const cellIndex = this.cellIndex(filterField);
rows = rows.filter(r => {
+ if (this.dataSource.fields[cellIndex].filter) {
+ return this.dataSource.fields[cellIndex].filter(r.data, filterValue);
+ }
return r.cells[cellIndex].includes(filterValue);
});
}
@@ -315,11 +322,32 @@ class EntityListTable extends React.Component {
rows = [...this.state.rows];
rows[rowId].selected = isSelected;
}
+ const hasChecked = this.state.rows.some(row => row.selected);
this.setState({
- rows
+ rows,
+ hasChecked
});
};
+ // called from entitiesPage when a new entity is selected from the list.
+ // we need to reset the page, sortBy, and filterBy for the new entity
+ reset = () => {
+ this.setState(
+ {
+ page: 1,
+ sortBy: { index: 0, direction: SortByDirection.asc },
+ filterBy: {}
+ },
+ () => {
+ if (this.toolbarRef) {
+ this.toolbarRef.reset();
+ }
+ }
+ );
+ };
+
+ handleAction = action => {};
+
render() {
const tableProps = {
cells: this.columns,
@@ -329,7 +357,7 @@ class EntityListTable extends React.Component {
onSort: this.onSort,
variant: TableVariant.compact
};
- if (this.dataSource.actions(this.props.entity).includes("DELETE") > 0) {
+ if (this.dataSource.actions(this.props.entity).includes("DELETE")) {
tableProps.onSelect = this.onSelect;
tableProps.canSelectAll = true;
}
@@ -348,15 +376,19 @@ class EntityListTable extends React.Component {
return (
<React.Fragment>
<TableToolbar
+ ref={el => (this.toolbarRef = el)}
total={this.state.total}
page={this.state.page}
perPage={this.state.perPage}
onSetPage={this.onSetPage}
onPerPageSelect={this.onPerPageSelect}
fields={this.dataSource.fields}
+ filterBy={this.state.filterBy}
handleChangeFilterValue={this.handleChangeFilterValue}
hidePagination={true}
actions={this.dataSource.actions(this.props.entity)}
+ hasChecked={this.state.hasChecked}
+ handleAction={this.handleAction}
/>
<Table {...tableProps}>
<TableHeader />
diff --git a/console/react/src/overview/detailsTablePage.js b/console/react/src/detailsTablePage.js
similarity index 70%
rename from console/react/src/overview/detailsTablePage.js
rename to console/react/src/detailsTablePage.js
index f296427..c253ca6 100644
--- a/console/react/src/overview/detailsTablePage.js
+++ b/console/react/src/detailsTablePage.js
@@ -38,8 +38,9 @@ import {
} from "@patternfly/react-table";
import { Card, CardBody } from "@patternfly/react-core";
import { Redirect } from "react-router-dom";
-import { dataMap } from "./entityData";
-import Updated from "../updated";
+import { dataMap } from "./overview/entityData";
+import { dataMap as detailsDataMap, defaultData } from "./details/entityData";
+import Updated from "./updated";
class DetailTablesPage extends React.Component {
constructor(props) {
@@ -63,23 +64,36 @@ class DetailTablesPage extends React.Component {
// then redirect back to the dashboard.
// this can happen if we get here from a bookmark or browser refresh
this.entity =
- this.props &&
- this.props.location &&
- this.props.location.state &&
- this.props.location.state.entity;
- if (!dataMap[this.entity]) {
+ this.props.entity ||
+ (this.props &&
+ this.props.location &&
+ this.props.location.state &&
+ this.props.location.state.entity);
+
+ if (!this.entity) {
this.state.redirect = true;
} else {
- this.dataSource = new dataMap[this.entity](this.props.service);
+ if (this.props.details) {
+ this.dataSource = !detailsDataMap[this.entity]
+ ? new defaultData(this.props.service, this.props.schema)
+ : new detailsDataMap[this.entity](
+ this.props.service,
+ this.props.schema
+ );
+ this.locationState = this.props.locationState;
+ } else {
+ this.dataSource = new dataMap[this.entity](
+ this.props.service,
+ this.props.schema
+ );
+ this.locationState = this.props.location.state;
+ }
}
}
componentDidMount = () => {
- this.props.service.management.getSchema().then(schema => {
- this.schema = schema;
- this.timer = setInterval(this.update, 5000);
- this.update();
- });
+ this.timer = setInterval(this.update, 5000);
+ this.update();
};
componentWillUnmount = () => {
@@ -91,7 +105,11 @@ class DetailTablesPage extends React.Component {
update = () => {
this.mapRows().then(
rows => {
- this.setState({ rows, lastUpdated: new Date() });
+ this.setState({ rows, lastUpdated: new Date() }, () => {
+ if (this.props.details) {
+ this.props.lastUpdated(this.state.lastUpdated);
+ }
+ });
},
error => {
console.log(`detailsTablePage: ${error}`);
@@ -111,7 +129,11 @@ class DetailTablesPage extends React.Component {
reject("no data source");
}
this.dataSource
- .fetchRecord(this.props.location.state.currentRecord, this.schema)
+ .fetchRecord(
+ this.locationState.currentRecord,
+ this.props.schema,
+ this.entity
+ )
.then(data => {
for (const attribute in data) {
if (
@@ -130,24 +152,19 @@ class DetailTablesPage extends React.Component {
icap = s => s.charAt(0).toUpperCase() + s.slice(1);
- parentItem = () => {
- // if we have a specific field that should be used
- // as the record's title, return it
- if (this.dataSource.detailField) {
- return this.props.location.state.currentRecord[
- this.dataSource.detailField
- ];
- }
- // otherwise return the 1st field
- return this.props.location.state.value;
- };
+ parentItem = () =>
+ this.locationState.currentRecord[this.locationState.property];
breadcrumbSelected = () => {
- this.setState({
- redirect: true,
- redirectPath: `/overview/${this.entity}`,
- redirectState: this.props.location.state
- });
+ if (this.props.details) {
+ this.props.handleSelectEntity(this.entity);
+ } else {
+ this.setState({
+ redirect: true,
+ redirectPath: `/overview/${this.entity}`,
+ redirectState: this.locationState
+ });
+ }
};
render() {
@@ -173,25 +190,22 @@ class DetailTablesPage extends React.Component {
<Breadcrumb>
<BreadcrumbItem
className="link-button"
- onClick={() =>
- this.breadcrumbSelected(`/overview/${this.entity}`)
- }
+ onClick={this.breadcrumbSelected}
>
{this.icap(this.entity)}
</BreadcrumbItem>
- <BreadcrumbItem isActive>{this.parentItem()}</BreadcrumbItem>
</Breadcrumb>
<TextContent>
<Text className="overview-title" component={TextVariants.h1}>
- {`${
- this.dataSource.detailName
- } ${this.parentItem()} attributes`}
+ {this.parentItem()}
</Text>
- <Updated
- service={this.props.service}
- lastUpdated={this.state.lastUpdated}
- />
+ {!this.props.details && (
+ <Updated
+ service={this.props.service}
+ lastUpdated={this.state.lastUpdated}
+ />
+ )}
</TextContent>
</StackItem>
<StackItem className="overview-table">
diff --git a/console/react/src/layout.js b/console/react/src/layout.js
index c56278b..82c9534 100644
--- a/console/react/src/layout.js
+++ b/console/react/src/layout.js
@@ -51,7 +51,7 @@ import DropdownMenu from "./DropdownMenu";
import ConnectPage from "./connectPage";
import DashboardPage from "./overview/dashboard/dashboardPage";
import OverviewTablePage from "./overview/overviewTablePage";
-import DetailsTablePage from "./overview/detailsTablePage";
+import DetailsTablePage from "./detailsTablePage";
import EntitiesPage from "./details/enitiesPage";
import TopologyPage from "./topology/topologyPage";
import MessageFlowPage from "./chord/chordPage";
@@ -95,6 +95,7 @@ class PageLayout extends React.Component {
};
}
+ // the connection to the routers was lost
setLocation = whatHappened => {
if (whatHappened === "disconnect") {
this.setState({ connected: false });
@@ -137,6 +138,7 @@ class PageLayout extends React.Component {
this.service.connect(connectOptions).then(
r => {
+ this.schema = this.service.schema;
if (connectPath === "/") connectPath = "/dashboard";
const activeItem = connectPath.split("/").pop();
// find the active group for this item
@@ -391,7 +393,11 @@ class PageLayout extends React.Component {
path="/overview/:entity"
component={OverviewTablePage}
/>
- <PrivateRoute path="/details" component={DetailsTablePage} />
+ <PrivateRoute
+ path="/details"
+ schema={this.schema}
+ component={DetailsTablePage}
+ />
<PrivateRoute path="/topology" component={TopologyPage} />
<PrivateRoute path="/flow" component={MessageFlowPage} />
<PrivateRoute path="/logs" component={LogDetails} />
diff --git a/console/react/src/overview/overviewTable.js b/console/react/src/overview/overviewTable.js
index 7a42778..98f1a8a 100644
--- a/console/react/src/overview/overviewTable.js
+++ b/console/react/src/overview/overviewTable.js
@@ -282,7 +282,8 @@ class OverviewTable extends React.Component {
return (
<Redirect
to={{
- pathname: this.dataSource.detailPath || "/details",
+ pathname:
+ (this.dataSource && this.dataSource.detailPath) || "/details",
state: this.state.redirectState
}}
/>
diff --git a/console/react/src/qdrService.js b/console/react/src/qdrService.js
index 1771a6a..4f8c1b3 100644
--- a/console/react/src/qdrService.js
+++ b/console/react/src/qdrService.js
@@ -28,6 +28,7 @@ export class QDRService {
this.management = new dm(url.protocol, DEFAULT_INTERVAL);
this.utilities = utils;
this.hooks = hooks;
+ this.schema = null;
}
onReconnect() {
@@ -50,6 +51,7 @@ export class QDRService {
);
self.management.getSchema().then(schema => {
+ self.schema = schema;
//console.log("got schema after connection");
//console.log(schema);
self.management.topology.setUpdateEntities([]);
diff --git a/console/react/src/tableToolbar.jsx b/console/react/src/tableToolbar.jsx
index 5d76eef..69824d5 100644
--- a/console/react/src/tableToolbar.jsx
+++ b/console/react/src/tableToolbar.jsx
@@ -36,13 +36,17 @@ class TableToolbar extends React.Component {
super(props);
this.state = {
isDropDownOpen: false,
- searchValue: "",
- filterBy: this.props.fields[0].title
+ searchValue:
+ this.props.filterBy && this.props.filterBy.value
+ ? this.props.filterBy.value
+ : "",
+ filterField: this.props.fields[0].title
};
+
this.handleTextInputChange = value => {
this.setState({ searchValue: value }, () => {
this.props.handleChangeFilterValue(
- this.state.filterBy,
+ this.state.filterField,
this.state.searchValue
);
});
@@ -58,12 +62,12 @@ class TableToolbar extends React.Component {
this.setState(
{
isDropDownOpen: !this.state.isDropDownOpen,
- filterBy: event.target.text,
+ filterField: event.target.text,
searchValue: ""
},
() =>
this.props.handleChangeFilterValue(
- this.state.filterBy,
+ this.state.filterField,
this.state.searchValue
)
);
@@ -89,7 +93,7 @@ class TableToolbar extends React.Component {
position={DropdownPosition.right}
toggle={
<DropdownToggle onToggle={this.onDropDownToggle}>
- {this.state.filterBy}
+ {this.state.filterField}
</DropdownToggle>
}
isOpen={isDropDownOpen}
@@ -103,12 +107,37 @@ class TableToolbar extends React.Component {
};
}
+ reset = () => {
+ this.setState({
+ isDropDownOpen: false,
+ searchValue: "",
+ filterField: this.props.fields[0].title
+ });
+ };
+
render() {
- const actions = this.props.actions.map(action => (
- <ToolbarItem className="pf-u-mx-md">
- <Button aria-label={action}>{action}</Button>
- </ToolbarItem>
- ));
+ const actions =
+ this.props.actions &&
+ this.props.actions.map(action => {
+ let variant = "primary";
+ let isDisabled = false;
+ if (action === "DELETE" && !this.props.hasChecked) {
+ variant = "tertiary";
+ isDisabled = true;
+ }
+ return (
+ <ToolbarItem className="pf-u-mx-md" key={action}>
+ <Button
+ aria-label={action}
+ onClick={() => this.props.handleAction(action)}
+ variant={variant}
+ isDisabled={isDisabled}
+ >
+ {action}
+ </Button>
+ </ToolbarItem>
+ );
+ });
return (
<Toolbar className="pf-l-toolbar pf-u-mx-xl pf-u-my-md table-toolbar">
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org