mirror of https://github.com/linkerd/linkerd2.git
UI updates, graph removals (#319)
UI cleanups. Remove repetitive labels in the UI, remove unused elements, remove graphs until we improve their utility. - remove “Deployment” from the headers of the Deployment Detail Page - remove Routes in sidebar - kill leftmost 100px of sidebear - remove word controller from service mesh page first table - add twitter and GitHub and slack links - kill the graphs, replace with one large header (request rate, success rate, latency top bar) put upstream/downstream diagram before upstream downstream tables * Clean up DeploymentList page (#321) - remove "Most active deployments" graphs from the Deployments List page - remove the scatterplot sections of the page as I don't think we'll be using them for a while
This commit is contained in:
parent
261586b862
commit
1f6aa27922
|
@ -1,6 +1,7 @@
|
|||
@import 'styles.css';
|
||||
|
||||
.entity-health {
|
||||
margin-top: 32px;
|
||||
margin-bottom: 32px;
|
||||
|
||||
& .metric-title {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
@import 'styles.css';
|
||||
|
||||
.metric-summary {
|
||||
& .metric {
|
||||
padding-left: var(--base-width);
|
||||
border-left: 4px solid var(--royalblue);
|
||||
|
||||
&.metric-large {
|
||||
& .metric-title {
|
||||
font-size: 12px;
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
& .metric-value {
|
||||
font-size: 18px;
|
||||
font-weight: var(--font-weight-extra-bold);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
@import 'styles.css';
|
||||
|
||||
.sidebar {
|
||||
padding: 0px 0px 0px 20%;
|
||||
padding: 0px 10% 0px 10%;
|
||||
background-color: #091B39;
|
||||
color: white;
|
||||
min-height: 100vh;
|
||||
|
@ -55,6 +55,17 @@
|
|||
& .ant-menu-item-active, & .ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
|
||||
background: #007EFF;
|
||||
}
|
||||
|
||||
& .social-links {
|
||||
padding: 0 0 0 9px;
|
||||
position: fixed;
|
||||
height: 40px;
|
||||
bottom: calc(var(--base-width)*16);
|
||||
|
||||
& a {
|
||||
margin-right: calc(var(--base-width)*2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.call-to-action .action {
|
||||
|
|
|
@ -2,12 +2,12 @@ import _ from 'lodash';
|
|||
import ConduitSpinner from "./ConduitSpinner.jsx";
|
||||
import ErrorBanner from './ErrorBanner.jsx';
|
||||
import { incompleteMeshMessage } from './util/CopyUtils.jsx';
|
||||
import MetricsSummary from './MetricsSummary.jsx';
|
||||
import PageHeader from './PageHeader.jsx';
|
||||
import React from 'react';
|
||||
import ResourceHealthOverview from './ResourceHealthOverview.jsx';
|
||||
import ResourceMetricsOverview from './ResourceMetricsOverview.jsx';
|
||||
import UpstreamDownstream from './UpstreamDownstream.jsx';
|
||||
import { getPodsByDeployment, processRollupMetrics, processTimeseriesMetrics } from './util/MetricUtils.js';
|
||||
import { getPodsByDeployment, processRollupMetrics } from './util/MetricUtils.js';
|
||||
import './../../css/deployment.css';
|
||||
import 'whatwg-fetch';
|
||||
|
||||
|
@ -61,7 +61,7 @@ export default class DeploymentDetail extends React.Component {
|
|||
let urls = this.api.urlsForResource;
|
||||
|
||||
let podListFetch = this.api.fetchPods();
|
||||
let deployMetricsUrl = urls["deployment"].url(this.state.deploy).ts;
|
||||
let deployMetricsUrl = urls["deployment"].url(this.state.deploy).rollup;
|
||||
let upstreamRollupUrl = urls["upstream_deployment"].url(this.state.deploy).rollup;
|
||||
let downstreamRollupUrl = urls["downstream_deployment"].url(this.state.deploy).rollup;
|
||||
|
||||
|
@ -70,11 +70,9 @@ export default class DeploymentDetail extends React.Component {
|
|||
let downstreamFetch = this.api.fetchMetrics(downstreamRollupUrl);
|
||||
|
||||
// expose serverPromise for testing
|
||||
this.serverPromise = Promise.all([
|
||||
deployFetch, upstreamFetch, downstreamFetch, podListFetch
|
||||
])
|
||||
this.serverPromise = Promise.all([deployFetch, upstreamFetch, downstreamFetch, podListFetch])
|
||||
.then(([deployMetrics, upstreamRollup, downstreamRollup, podList]) => {
|
||||
let tsByDeploy = processTimeseriesMetrics(deployMetrics.metrics, "targetDeploy");
|
||||
let deployRollup = processRollupMetrics(deployMetrics.metrics, "targetDeploy");
|
||||
let upstreamMetrics = processRollupMetrics(upstreamRollup.metrics, "sourceDeploy");
|
||||
let downstreamMetrics = processRollupMetrics(downstreamRollup.metrics, "targetDeploy");
|
||||
|
||||
|
@ -83,7 +81,8 @@ export default class DeploymentDetail extends React.Component {
|
|||
this.setState({
|
||||
added: deploy.added,
|
||||
pods: deploy.pods,
|
||||
deployTs: _.get(tsByDeploy, this.state.deploy, {}),
|
||||
deployMetrics: _.get(deployRollup, 0, {}),
|
||||
deployTs: {},
|
||||
upstreamMetrics: upstreamMetrics,
|
||||
downstreamMetrics: downstreamMetrics,
|
||||
lastUpdated: Date.now(),
|
||||
|
@ -114,6 +113,9 @@ export default class DeploymentDetail extends React.Component {
|
|||
let currentSuccessRate = _.get(_.last(srTs), "value");
|
||||
|
||||
return [
|
||||
<MetricsSummary
|
||||
key="metrics-summary"
|
||||
metrics={this.state.deployMetrics} />,
|
||||
<ResourceHealthOverview
|
||||
key="deploy-health-pane"
|
||||
resourceName={this.state.deploy}
|
||||
|
@ -122,13 +124,6 @@ export default class DeploymentDetail extends React.Component {
|
|||
upstreamMetrics={this.state.upstreamMetrics}
|
||||
downstreamMetrics={this.state.downstreamMetrics}
|
||||
deploymentAdded={this.state.added} />,
|
||||
_.isEmpty(this.state.deployTs) ? null :
|
||||
<ResourceMetricsOverview
|
||||
key="stat-pane"
|
||||
resourceType="deployment"
|
||||
lastUpdated={this.state.lastUpdated}
|
||||
timeseries={this.state.deployTs}
|
||||
window={this.api.getMetricsWindow()} />,
|
||||
<UpstreamDownstream
|
||||
key="deploy-upstream-downstream"
|
||||
resourceType="deployment"
|
||||
|
|
|
@ -1,42 +1,24 @@
|
|||
import _ from 'lodash';
|
||||
import CallToAction from './CallToAction.jsx';
|
||||
import ConduitSpinner from "./ConduitSpinner.jsx";
|
||||
import DeploymentSummary from './DeploymentSummary.jsx';
|
||||
import ErrorBanner from './ErrorBanner.jsx';
|
||||
import PageHeader from './PageHeader.jsx';
|
||||
import React from 'react';
|
||||
import ScatterPlot from './ScatterPlot.jsx';
|
||||
import TabbedMetricsTable from './TabbedMetricsTable.jsx';
|
||||
import { Col, Row } from 'antd';
|
||||
import { emptyMetric, getPodsByDeployment, processRollupMetrics, processTimeseriesMetrics } from './util/MetricUtils.js';
|
||||
import { metricToFormatter, rowGutter } from './util/Utils.js';
|
||||
import { emptyMetric, getPodsByDeployment, processRollupMetrics } from './util/MetricUtils.js';
|
||||
import './../../css/deployments.css';
|
||||
import 'whatwg-fetch';
|
||||
|
||||
const maxTsToFetch = 15; // Beyond this, stop showing sparklines in table
|
||||
let nodeStats = (description, node) => (
|
||||
<div>
|
||||
<div className="title">{description}:</div>
|
||||
<div>
|
||||
{node.name} ({metricToFormatter["LATENCY"](_.get(node, ["latency", "P99"]))})
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default class DeploymentsList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.api = this.props.api;
|
||||
this.handleApiError = this.handleApiError.bind(this);
|
||||
this.loadFromServer = this.loadFromServer.bind(this);
|
||||
this.loadTimeseriesFromServer = this.loadTimeseriesFromServer.bind(this);
|
||||
|
||||
this.state = {
|
||||
pollingInterval: 10000, // TODO: poll based on metricsWindow size
|
||||
metrics: [],
|
||||
timeseriesByDeploy: {},
|
||||
lastUpdated: 0,
|
||||
limitSparklineData: false,
|
||||
pendingRequests: false,
|
||||
loaded: false,
|
||||
error: ''
|
||||
|
@ -79,40 +61,14 @@ export default class DeploymentsList extends React.Component {
|
|||
let meshDeploys = processRollupMetrics(rollup.metrics, "targetDeploy");
|
||||
let combinedMetrics = this.addDeploysWithNoMetrics(poByDeploy, meshDeploys);
|
||||
|
||||
this.loadTimeseriesFromServer(meshDeploys, combinedMetrics);
|
||||
})
|
||||
.catch(this.handleApiError);
|
||||
}
|
||||
|
||||
loadTimeseriesFromServer(meshDeployMetrics, combinedMetrics) {
|
||||
// fetch only the timeseries for the 3 deployments we display at the top of the page
|
||||
let limitSparklineData = _.size(meshDeployMetrics) > maxTsToFetch;
|
||||
|
||||
let resourceInfo = this.api.urlsForResource["deployment"];
|
||||
let mostActiveDeployments = this.getMostActiveDeployments(meshDeployMetrics);
|
||||
|
||||
let tsPromises = _.map(mostActiveDeployments, dep => {
|
||||
let tsPathForDeploy = resourceInfo.url(dep.name).ts;
|
||||
return this.api.fetchMetrics(tsPathForDeploy);
|
||||
});
|
||||
|
||||
Promise.all(tsPromises)
|
||||
.then(tsMetrics => {
|
||||
let mostActiveTs = _.reduce(tsMetrics, (mem, ea) => {
|
||||
mem = mem.concat(ea.metrics);
|
||||
return mem;
|
||||
}, []);
|
||||
let tsByDeploy = processTimeseriesMetrics(mostActiveTs, resourceInfo.groupBy);
|
||||
this.setState({
|
||||
timeseriesByDeploy: tsByDeploy,
|
||||
lastUpdated: Date.now(),
|
||||
metrics: combinedMetrics,
|
||||
limitSparklineData: limitSparklineData,
|
||||
loaded: true,
|
||||
pendingRequests: false,
|
||||
error: ''
|
||||
});
|
||||
}).catch(this.handleApiError);
|
||||
})
|
||||
.catch(this.handleApiError);
|
||||
}
|
||||
|
||||
handleApiError(e) {
|
||||
|
@ -122,87 +78,6 @@ export default class DeploymentsList extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
getMostActiveDeployments(deployMetrics, limit = 3) {
|
||||
return _(deployMetrics)
|
||||
.filter('added')
|
||||
.orderBy('requestRate', 'desc')
|
||||
.take(limit)
|
||||
.value();
|
||||
}
|
||||
|
||||
renderPageContents() {
|
||||
let mostActiveDeployments = this.getMostActiveDeployments(this.state.metrics);
|
||||
|
||||
return (
|
||||
<div className="clearfix">
|
||||
{_.isEmpty(mostActiveDeployments) ? null :
|
||||
<div className="subsection-header">Most active deployments</div>}
|
||||
<Row gutter={rowGutter}>
|
||||
{
|
||||
_.map(mostActiveDeployments, deployment => {
|
||||
return (<Col span={8} key={`col-${deployment.name}`}>
|
||||
<DeploymentSummary
|
||||
key={deployment.name}
|
||||
lastUpdated={this.state.lastUpdated}
|
||||
data={deployment}
|
||||
api = {this.api}
|
||||
requestTs={_.get(this.state.timeseriesByDeploy,
|
||||
[deployment.name, "REQUEST_RATE"], [])} />
|
||||
</Col>);
|
||||
})
|
||||
}
|
||||
</Row>
|
||||
{/* <Row gutter={rowGutter}>
|
||||
{ this.renderScatterplot() }
|
||||
</Row> */}
|
||||
<div className="deployments-list">
|
||||
<TabbedMetricsTable
|
||||
resource="deployment"
|
||||
metrics={this.state.metrics}
|
||||
api={this.api} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderScatterplot() {
|
||||
let scatterplotData = _.reduce(this.state.metrics, (mem, datum) => {
|
||||
if (!_.isNil(datum.successRate) && !_.isNil(datum.latency)) {
|
||||
mem.push(datum);
|
||||
}
|
||||
return mem;
|
||||
}, []);
|
||||
|
||||
let slowestNode = _.maxBy(scatterplotData, 'latency.P99');
|
||||
let fastestNode = _.minBy(scatterplotData, 'latency.P99');
|
||||
|
||||
if (_.isEmpty(scatterplotData)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (<div className="deployments-scatterplot">
|
||||
<div className="scatterplot-info">
|
||||
<div className="subsection-header">Success rate vs p99 latency</div>
|
||||
</div>
|
||||
<Row gutter={rowGutter}>
|
||||
<Col span={8}>
|
||||
<div className="scatterplot-display">
|
||||
<div className="extremal-latencies">
|
||||
{ !fastestNode ? null : nodeStats("Least latency", fastestNode) }
|
||||
{ !slowestNode ? null : nodeStats("Most latency", slowestNode) }
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={16}><div className="scatterplot-chart">
|
||||
<ScatterPlot
|
||||
data={scatterplotData}
|
||||
lastUpdated={this.state.lastUpdated}
|
||||
containerClassName="scatterplot-chart" />
|
||||
</div></Col>
|
||||
</Row>
|
||||
</div>);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="page-content">
|
||||
|
@ -212,7 +87,12 @@ export default class DeploymentsList extends React.Component {
|
|||
<PageHeader header="Deployments" api={this.api} />
|
||||
{ _.isEmpty(this.state.metrics) ?
|
||||
<CallToAction numDeployments={_.size(this.state.metrics)} /> :
|
||||
this.renderPageContents()
|
||||
<div className="deployments-list">
|
||||
<TabbedMetricsTable
|
||||
resource="deployment"
|
||||
metrics={this.state.metrics}
|
||||
api={this.api} />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import _ from 'lodash';
|
||||
import { metricToFormatter } from './util/Utils.js';
|
||||
import React from 'react';
|
||||
import { Col, Row } from 'antd';
|
||||
import './../../css/metric-summary.css';
|
||||
|
||||
export default class MetricsSummary extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Row className="metric-summary">
|
||||
<Col span={4} className="metric metric-large">
|
||||
<div className="metric-title">Request rate</div>
|
||||
<div className="metric-value">
|
||||
{metricToFormatter["REQUEST_RATE"](this.props.metrics.requestRate)}
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={4} className="metric metric-large">
|
||||
<div className="metric-title">Success rate</div>
|
||||
<div className="metric-value">
|
||||
{metricToFormatter["SUCCESS_RATE"](this.props.metrics.successRate)}
|
||||
</div>
|
||||
</Col>
|
||||
{
|
||||
_.map(['P50', 'P95', 'P99'], label => {
|
||||
let latency = _.get(this.props.metrics, ['latency', label]);
|
||||
|
||||
return (
|
||||
<Col span={4} className="metric metric-large" key={`latency${label}`}>
|
||||
<div className="metric-title">{label} latency</div>
|
||||
<div className="metric-value">{metricToFormatter["LATENCY"](latency)}</div>
|
||||
</Col>
|
||||
);
|
||||
})
|
||||
}
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
export default class Routes extends React.Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="page-content">
|
||||
<div className="page-header">
|
||||
<h1>Routes</h1>
|
||||
</div>
|
||||
<div>Coming soon!</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -28,11 +28,11 @@ const serviceMeshDetailsColumns = [
|
|||
];
|
||||
const componentNames = {
|
||||
"prometheus": "Prometheus",
|
||||
"destination": "Controller Destination",
|
||||
"proxy-api": "Controller Proxy API",
|
||||
"public-api": "Controller Public API",
|
||||
"tap": "Controller Tap",
|
||||
"telemetry": "Controller Telemetry",
|
||||
"destination": "Destination",
|
||||
"proxy-api": "Proxy API",
|
||||
"public-api": "Public API",
|
||||
"tap": "Tap",
|
||||
"telemetry": "Telemetry",
|
||||
"web": "Web UI"
|
||||
};
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import _ from 'lodash';
|
|||
import { getPodsByDeployment } from './util/MetricUtils.js';
|
||||
import logo from './../../img/reversed_logo.png';
|
||||
import React from 'react';
|
||||
import SocialLinks from './SocialLinks.jsx';
|
||||
import Version from './Version.jsx';
|
||||
import { AutoComplete, Menu } from 'antd';
|
||||
import './../../css/sidebar.css';
|
||||
|
@ -82,14 +83,13 @@ export default class Sidebar extends React.Component {
|
|||
<Menu.Item className="sidebar-menu-item" key="/deployments">
|
||||
<ConduitLink to="/deployments">Deployments</ConduitLink>
|
||||
</Menu.Item>
|
||||
<Menu.Item className="sidebar-menu-item" key="/routes">
|
||||
<ConduitLink to="/routes">Routes</ConduitLink>
|
||||
</Menu.Item>
|
||||
<Menu.Item className="sidebar-menu-item" key="/docs">
|
||||
<ConduitLink to="https://conduit.io/docs/" absolute="true">Documentation</ConduitLink>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
|
||||
<SocialLinks />
|
||||
|
||||
<Version
|
||||
releaseVersion={this.props.releaseVersion}
|
||||
uuid={this.props.uuid} />
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import { Link } from 'react-router-dom';
|
||||
import React from 'react';
|
||||
|
||||
const Twitter = () => {
|
||||
return (
|
||||
<svg
|
||||
width="32" height="32"
|
||||
version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
||||
viewBox="0 0 400 400" xmlSpace="preserve">
|
||||
<g><circle className="st0" cx="200" cy="200" r="200" fill="#1DA1F2" /></g>
|
||||
<g>
|
||||
<path className="st1" d="M163.4,305.5c88.7,0,137.2-73.5,137.2-137.2c0-2.1,0-4.2-0.1-6.2c9.4-6.8,17.6-15.3,24.1-25 c-8.6,3.8-17.9,6.4-27.7,7.6c10-6,17.6-15.4,21.2-26.7c-9.3,5.5-19.6,9.5-30.6,11.7c-8.8-9.4-21.3-15.2-35.2-15.2 c-26.6,0-48.2,21.6-48.2,48.2c0,3.8,0.4,7.5,1.3,11c-40.1-2-75.6-21.2-99.4-50.4c-4.1,7.1-6.5,15.4-6.5,24.2 c0,16.7,8.5,31.5,21.5,40.1c-7.9-0.2-15.3-2.4-21.8-6c0,0.2,0,0.4,0,0.6c0,23.4,16.6,42.8,38.7,47.3c-4,1.1-8.3,1.7-12.7,1.7 c-3.1,0-6.1-0.3-9.1-0.9c6.1,19.2,23.9,33.1,45,33.5c-16.5,12.9-37.3,20.6-59.9,20.6c-3.9,0-7.7-0.2-11.5-0.7 C110.8,297.5,136.2,305.5,163.4,305.5" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
const Slack = () => {
|
||||
return (<svg width="32" height="32" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid">
|
||||
<path d="M165.964 15.838c-3.89-11.975-16.752-18.528-28.725-14.636-11.975 3.89-18.528 16.752-14.636 28.725l58.947 181.365c4.048 11.187 16.132 17.473 27.732 14.135 12.1-3.483 19.475-16.334 15.614-28.217L165.964 15.838" fill="#DFA22F" />
|
||||
<path d="M74.626 45.516C70.734 33.542 57.873 26.989 45.9 30.879 33.924 34.77 27.37 47.631 31.263 59.606l58.948 181.366c4.047 11.186 16.132 17.473 27.732 14.132 12.099-3.481 19.474-16.332 15.613-28.217L74.626 45.516" fill="#3CB187" />
|
||||
<path d="M240.162 166.045c11.975-3.89 18.526-16.75 14.636-28.726-3.89-11.973-16.752-18.527-28.725-14.636L44.708 181.632c-11.187 4.046-17.473 16.13-14.135 27.73 3.483 12.099 16.334 19.475 28.217 15.614l181.372-58.93" fill="#CE1E5B" />
|
||||
<path d="M82.508 217.27l43.347-14.084-14.086-43.352-43.35 14.09 14.089 43.347" fill="#392538" />
|
||||
<path d="M173.847 187.591c16.388-5.323 31.62-10.273 43.348-14.084l-14.088-43.36-43.35 14.09 14.09 43.354" fill="#BB242A" />
|
||||
<path d="M210.484 74.706c11.974-3.89 18.527-16.751 14.637-28.727-3.89-11.973-16.752-18.526-28.727-14.636L15.028 90.293C3.842 94.337-2.445 106.422.896 118.022c3.481 12.098 16.332 19.474 28.217 15.613l181.371-58.93" fill="#72C5CD" />
|
||||
<path d="M52.822 125.933c11.805-3.836 27.025-8.782 43.354-14.086-5.323-16.39-10.273-31.622-14.084-43.352l-43.36 14.092 14.09 43.346" fill="#248C73" />
|
||||
<path d="M144.16 96.256l43.356-14.088a546179.21 546179.21 0 0 0-14.089-43.36L130.07 52.9l14.09 43.356" fill="#62803A" />
|
||||
</svg>);
|
||||
};
|
||||
|
||||
const Github = () => {
|
||||
return <svg className="octicon-mark-github" fill="#ffffff" height="32" version="1.1" viewBox="0 0 16 16" width="32"><path fillRule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z" /></svg>;
|
||||
};
|
||||
|
||||
export default class SocialLinks extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="social-links">
|
||||
<Link target="_blank" to="https://github.com/runconduit/conduit"><Github /></Link>
|
||||
<Link target="_blank" to="https://slack.linkerd.io/"><Slack /></Link>
|
||||
<Link target="_blank" to="https://twitter.com/runconduit"><Twitter /></Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -15,9 +15,7 @@ export default class UpstreamDownstreamTables extends React.Component {
|
|||
numUpstreams === 0 ? null :
|
||||
<div className="upstream-downstream-list">
|
||||
<div className="border-container border-neutral subsection-header">
|
||||
<div className="border-container-content subsection-header">
|
||||
Upstream {this.props.resourceType}s: {numUpstreams}
|
||||
</div>
|
||||
<div className="border-container-content subsection-header">Upstreams</div>
|
||||
</div>
|
||||
<TabbedMetricsTable
|
||||
resource={`upstream_${this.props.resourceType}`}
|
||||
|
@ -30,9 +28,7 @@ export default class UpstreamDownstreamTables extends React.Component {
|
|||
numDownstreams === 0 ? null :
|
||||
<div className="upstream-downstream-list">
|
||||
<div className="border-container border-neutral subsection-header">
|
||||
<div className="border-container-content subsection-header">
|
||||
Downstream {this.props.resourceType}s: {numDownstreams}
|
||||
</div>
|
||||
<div className="border-container-content subsection-header">Downstreams</div>
|
||||
</div>
|
||||
<TabbedMetricsTable
|
||||
resource={`downstream_${this.props.resourceType}`}
|
||||
|
|
|
@ -5,7 +5,6 @@ import { Layout } from 'antd';
|
|||
import NoMatch from './components/NoMatch.jsx';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Routes from './components/Routes.jsx';
|
||||
import ServiceMesh from './components/ServiceMesh.jsx';
|
||||
import Sidebar from './components/Sidebar.jsx';
|
||||
import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom';
|
||||
|
@ -36,7 +35,6 @@ ReactDOM.render((
|
|||
<Route path={`${pathPrefix}/servicemesh`} render={() => <ServiceMesh api={api} releaseVersion={appData.releaseVersion} />} />
|
||||
<Route path={`${pathPrefix}/deployments`} render={() => <DeploymentsList api={api} />} />
|
||||
<Route path={`${pathPrefix}/deployment`} render={props => <DeploymentDetail api={api} location={props.location} />} />
|
||||
<Route path={`${pathPrefix}/routes`} render={() => <Routes />} />
|
||||
<Route component={NoMatch} />
|
||||
</Switch>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue