import debug from 'debug'; import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { Map as makeMap } from 'immutable'; import { noop } from 'lodash'; import { clickCloseDetails, clickShowTopologyForNode } from '../actions/request-actions'; import { brightenColor, getNeutralColor, getNodeColorDark } from '../utils/color-utils'; import { isGenericTable, isPropertyList } from '../utils/node-details-utils'; import { resetDocumentTitle, setDocumentTitle } from '../utils/title-utils'; import Overlay from './overlay'; import MatchedText from './matched-text'; import NodeDetailsControls from './node-details/node-details-controls'; import NodeDetailsGenericTable from './node-details/node-details-generic-table'; import NodeDetailsPropertyList from './node-details/node-details-property-list'; import NodeDetailsHealth from './node-details/node-details-health'; import NodeDetailsInfo from './node-details/node-details-info'; import NodeDetailsRelatives from './node-details/node-details-relatives'; import NodeDetailsTable from './node-details/node-details-table'; import Warning from './warning'; const log = debug('scope:node-details'); function getTruncationText(count) { return 'This section was too long to be handled efficiently and has been truncated' + ` (${count} extra entries not included). We are working to remove this limitation.`; } class NodeDetails extends React.Component { handleClickClose = (ev) => { ev.preventDefault(); this.props.clickCloseDetails(this.props.nodeId); } handleShowTopologyForNode = (ev) => { ev.preventDefault(); this.props.clickShowTopologyForNode(this.props.topologyId, this.props.nodeId); } componentDidMount() { this.updateTitle(); } componentWillUnmount() { resetDocumentTitle(); } renderTools() { const showSwitchTopology = this.props.nodeId !== this.props.selectedNodeId; const topologyTitle = `View ${this.props.label} in ${this.props.topologyId}`; return (
{showSwitchTopology && ( Show in {this.props.topologyId.replace(/-/g, ' ')} ) }
); } renderLoading() { const node = this.props.nodes.get(this.props.nodeId); const label = node ? node.get('label') : this.props.label; // NOTE: If we start the fa-spin animation before the node details panel has been // mounted, the spinner is displayed blurred the whole time in Chrome (possibly // caused by a bug having to do with animating the details panel). const spinnerClassName = classNames('fa fa-circle-notch', { 'fa-spin': this.props.mounted }); const nodeColor = (node ? getNodeColorDark(node.get('rank'), label, node.get('pseudo')) : getNeutralColor()); const tools = this.renderTools(); const styles = { header: { backgroundColor: nodeColor } }; return (
{tools}

{label}

Loading...
); } renderNotAvailable() { const tools = this.renderTools(); return (
{tools}

{this.props.label}

n/a

{this.props.label} {' '} not found!

); } render() { if (this.props.notFound) { return this.renderNotAvailable(); } if (this.props.details) { return this.renderDetails(); } return this.renderLoading(); } renderDetails() { const { details, nodeControlStatus, nodeMatches = makeMap(), topologyId } = this.props; const showControls = details.controls && details.controls.length > 0; const nodeColor = getNodeColorDark(details.rank, details.label, details.pseudo); const {error, pending} = nodeControlStatus ? nodeControlStatus.toJS() : {}; const tools = this.renderTools(); const styles = { controls: { backgroundColor: brightenColor(nodeColor) }, header: { backgroundColor: nodeColor } }; return (
{tools}

{details.parents && ( )}
{showControls && (
) }
{details.metrics && (
Status
) } {details.metadata && (
Info
) } {details.connections && details.connections.filter(cs => cs.connections.length > 0) .map(connections => (
))} {details.children && details.children.map(children => (
))} {details.tables && details.tables.length > 0 && details.tables.map((table) => { if (table.rows.length > 0) { return (
{table.label && table.label.length > 0 && table.label} {table.truncationCount > 0 && ( ) }
{this.renderTable(table)}
); } return null; })} {this.props.renderNodeDetailsExtras({ details, topologyId })}
); } renderTable(table) { const { nodeMatches = makeMap() } = this.props; if (isGenericTable(table)) { return ( ); } if (isPropertyList(table)) { return ( ); } log(`Undefined type '${table.type}' for table ${table.id}`); return null; } componentDidUpdate() { this.updateTitle(); } updateTitle() { setDocumentTitle(this.props.details && this.props.details.label); } } NodeDetails.propTypes = { renderNodeDetailsExtras: PropTypes.func, }; NodeDetails.defaultProps = { renderNodeDetailsExtras: noop, }; function mapStateToProps(state, ownProps) { const currentTopologyId = state.get('currentTopologyId'); return { nodeMatches: state.getIn(['searchNodeMatches', currentTopologyId, ownProps.id]), nodes: state.get('nodes'), selectedNodeId: state.get('selectedNodeId'), transitioning: state.get('pausedAt') !== ownProps.timestamp, }; } export default connect( mapStateToProps, { clickCloseDetails, clickShowTopologyForNode } )(NodeDetails);