mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-14 00:19:16 +01:00
Split feature toggle view in smaller components
This commit is contained in:
parent
b42742e992
commit
d63e05abc0
66
frontend/src/component/feature/metric-component.jsx
Normal file
66
frontend/src/component/feature/metric-component.jsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
import { Grid, Cell, Icon } from 'react-mdl';
|
||||||
|
import Progress from './progress';
|
||||||
|
import { AppsLinkList, SwitchWithLabel, calc } from '../common';
|
||||||
|
|
||||||
|
const MetricComponent = ({ metrics, featureToggle, toggleFeature }) => {
|
||||||
|
const {
|
||||||
|
lastHour = { yes: 0, no: 0, isFallback: true },
|
||||||
|
lastMinute = { yes: 0, no: 0, isFallback: true },
|
||||||
|
seenApps = [],
|
||||||
|
} = metrics;
|
||||||
|
|
||||||
|
const lastHourPercent = 1 * calc(lastHour.yes, lastHour.yes + lastHour.no, 0);
|
||||||
|
const lastMinutePercent = 1 * calc(lastMinute.yes, lastMinute.yes + lastMinute.no, 0);
|
||||||
|
|
||||||
|
return (<div>
|
||||||
|
<SwitchWithLabel
|
||||||
|
checked={featureToggle.enabled}
|
||||||
|
onChange={() => toggleFeature(featureToggle)}>Toggle {featureToggle.name}</SwitchWithLabel>
|
||||||
|
<hr />
|
||||||
|
<Grid style={{ textAlign: 'center' }}>
|
||||||
|
<Cell tablet={4} col={3} phone={12}>
|
||||||
|
{
|
||||||
|
lastMinute.isFallback ?
|
||||||
|
<Icon style={{ width: '100px', height: '100px', fontSize: '100px', color: '#ccc' }}
|
||||||
|
name="report problem" title="No metrics avaiable" /> :
|
||||||
|
<div>
|
||||||
|
<Progress animatePercentageText strokeWidth={10} percentage={lastMinutePercent} width="50" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<p><strong>Last minute</strong><br /> Yes {lastMinute.yes}, No: {lastMinute.no}</p>
|
||||||
|
</Cell>
|
||||||
|
<Cell col={3} tablet={4} phone={12}>
|
||||||
|
{
|
||||||
|
lastHour.isFallback ?
|
||||||
|
<Icon style={{ width: '100px', height: '100px', fontSize: '100px', color: '#ccc' }}
|
||||||
|
name="report problem" title="No metrics avaiable" /> :
|
||||||
|
<div>
|
||||||
|
<Progress strokeWidth={10} percentage={lastHourPercent} width="50" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<p><strong>Last hour</strong><br /> Yes {lastHour.yes}, No: {lastHour.no}</p>
|
||||||
|
</Cell>
|
||||||
|
<Cell col={6} tablet={12}>
|
||||||
|
{seenApps.length > 0 ?
|
||||||
|
(<div><strong>Seen in applications:</strong></div>) :
|
||||||
|
<div>
|
||||||
|
<Icon style={{ width: '100px', height: '100px', fontSize: '100px', color: '#ccc' }}
|
||||||
|
name="report problem" title="Not used in a app in the last hour" />
|
||||||
|
<div><small><strong>Not used in a app in the last hour.</strong>
|
||||||
|
This might be due to your client implementation is not reporting usage.</small></div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<AppsLinkList apps={seenApps} />
|
||||||
|
</Cell>
|
||||||
|
</Grid>
|
||||||
|
</div>);
|
||||||
|
};
|
||||||
|
|
||||||
|
MetricComponent.propTypes = {
|
||||||
|
metrics: PropTypes.object,
|
||||||
|
featureToggle: PropTypes.object,
|
||||||
|
toggleFeature: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MetricComponent;
|
88
frontend/src/component/feature/view-component.jsx
Normal file
88
frontend/src/component/feature/view-component.jsx
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
import { Tabs, Tab, ProgressBar, List, ListItem, ListItemContent } from 'react-mdl';
|
||||||
|
import { Link } from 'react-router';
|
||||||
|
|
||||||
|
import HistoryComponent from '../history/history-list-toggle-container';
|
||||||
|
import MetricComponent from './metric-component';
|
||||||
|
import EditFeatureToggle from './form-edit-container.jsx';
|
||||||
|
import { getIcon } from '../common';
|
||||||
|
|
||||||
|
export default class ViewFeatureToggleComponent extends React.Component {
|
||||||
|
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = { activeTab: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes () {
|
||||||
|
return {
|
||||||
|
featureToggleName: PropTypes.string.isRequired,
|
||||||
|
features: PropTypes.array.isRequired,
|
||||||
|
fetchFeatureToggles: PropTypes.array.isRequired,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount () {
|
||||||
|
if (this.props.features.length === 0) {
|
||||||
|
this.props.fetchFeatureToggles();
|
||||||
|
}
|
||||||
|
this.props.fetchSeenApps();
|
||||||
|
this.props.fetchFeatureMetrics();
|
||||||
|
this.timer = setInterval(() => {
|
||||||
|
this.props.fetchFeatureMetrics();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount () {
|
||||||
|
clearInterval(this.timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const {
|
||||||
|
toggleFeature,
|
||||||
|
features,
|
||||||
|
featureToggleName,
|
||||||
|
metrics = {},
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const featureToggle = features.find(toggle => toggle.name === featureToggleName);
|
||||||
|
|
||||||
|
if (!featureToggle) {
|
||||||
|
if (features.length === 0 ) {
|
||||||
|
return <ProgressBar indeterminate />;
|
||||||
|
}
|
||||||
|
return <span>Could not find the toggle "{this.props.featureToggleName}"</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tabContent;
|
||||||
|
if (this.state.activeTab === 0) {
|
||||||
|
tabContent = <MetricComponent metrics={metrics} toggleFeature={toggleFeature} featureToggle={featureToggle} />;
|
||||||
|
} else if (this.state.activeTab === 1) {
|
||||||
|
tabContent = <EditFeatureToggle featureToggle={featureToggle} />;
|
||||||
|
} else {
|
||||||
|
tabContent = <HistoryComponent toggleName={featureToggleName} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4>{featureToggle.name} <small>{featureToggle.enabled ? 'is enabled' : 'is disabled'}</small>
|
||||||
|
<small style={{ float: 'right', lineHeight: '38px' }}>
|
||||||
|
Created {(new Date(featureToggle.createdAt)).toLocaleString('nb-NO')}
|
||||||
|
</small>
|
||||||
|
</h4>
|
||||||
|
<div>{featureToggle.description}</div>
|
||||||
|
<Tabs activeTab={this.state.activeTab}
|
||||||
|
onChange={(tabId) => this.setState({ activeTab: tabId })}
|
||||||
|
ripple
|
||||||
|
style={{ marginBottom: '10px' }}>
|
||||||
|
<Tab>Metrics</Tab>
|
||||||
|
<Tab>Edit</Tab>
|
||||||
|
<Tab>History</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
{tabContent}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,167 +1,11 @@
|
|||||||
import React, { PropTypes } from 'react';
|
|
||||||
import { Tabs, Tab, Grid, Cell, Icon, ProgressBar, List, ListItem, ListItemContent } from 'react-mdl';
|
|
||||||
import { Link } from 'react-router';
|
|
||||||
|
|
||||||
import Progress from './progress';
|
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import EditFeatureToggle from './form-edit-container.jsx';
|
|
||||||
import { fetchFeatureToggles, toggleFeature } from '../../store/feature-actions';
|
import { fetchFeatureToggles, toggleFeature } from '../../store/feature-actions';
|
||||||
import { fetchFeatureMetrics, fetchSeenApps } from '../../store/feature-metrics-actions';
|
import { fetchFeatureMetrics, fetchSeenApps } from '../../store/feature-metrics-actions';
|
||||||
import { fetchHistoryForToggle } from '../../store/history-actions';
|
import { fetchHistoryForToggle } from '../../store/history-actions';
|
||||||
|
|
||||||
import { AppsLinkList, SwitchWithLabel, getIcon, calc } from '../common';
|
import ViewToggleComponent from './view-component';
|
||||||
|
|
||||||
|
|
||||||
const MetricTab = ({ metrics, featureToggle, toggleFeature }) => {
|
|
||||||
const {
|
|
||||||
lastHour = { yes: 0, no: 0, isFallback: true },
|
|
||||||
lastMinute = { yes: 0, no: 0, isFallback: true },
|
|
||||||
seenApps = [],
|
|
||||||
} = metrics;
|
|
||||||
|
|
||||||
const lastHourPercent = 1 * calc(lastHour.yes, lastHour.yes + lastHour.no, 0);
|
|
||||||
const lastMinutePercent = 1 * calc(lastMinute.yes, lastMinute.yes + lastMinute.no, 0);
|
|
||||||
|
|
||||||
return (<div>
|
|
||||||
<SwitchWithLabel
|
|
||||||
checked={featureToggle.enabled}
|
|
||||||
onChange={() => toggleFeature(featureToggle)}>Toggle {featureToggle.name}</SwitchWithLabel>
|
|
||||||
<hr />
|
|
||||||
<Grid style={{ textAlign: 'center' }}>
|
|
||||||
<Cell tablet={4} col={3} phone={12}>
|
|
||||||
{
|
|
||||||
lastMinute.isFallback ?
|
|
||||||
<Icon style={{ width: '100px', height: '100px', fontSize: '100px', color: '#ccc' }}
|
|
||||||
name="report problem" title="No metrics avaiable" /> :
|
|
||||||
<div>
|
|
||||||
<Progress animatePercentageText strokeWidth={10} percentage={lastMinutePercent} width="50" />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<p><strong>Last minute</strong><br /> Yes {lastMinute.yes}, No: {lastMinute.no}</p>
|
|
||||||
</Cell>
|
|
||||||
<Cell col={3} tablet={4} phone={12}>
|
|
||||||
{
|
|
||||||
lastHour.isFallback ?
|
|
||||||
<Icon style={{ width: '100px', height: '100px', fontSize: '100px', color: '#ccc' }}
|
|
||||||
name="report problem" title="No metrics avaiable" /> :
|
|
||||||
<div>
|
|
||||||
<Progress strokeWidth={10} percentage={lastHourPercent} width="50" />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<p><strong>Last hour</strong><br /> Yes {lastHour.yes}, No: {lastHour.no}</p>
|
|
||||||
</Cell>
|
|
||||||
<Cell col={6} tablet={12}>
|
|
||||||
{seenApps.length > 0 ?
|
|
||||||
(<div><strong>Seen in applications:</strong></div>) :
|
|
||||||
<div>
|
|
||||||
<Icon style={{ width: '100px', height: '100px', fontSize: '100px', color: '#ccc' }}
|
|
||||||
name="report problem" title="Not used in a app in the last hour" />
|
|
||||||
<div><small><strong>Not used in a app in the last hour.</strong>
|
|
||||||
This might be due to your client implementation is not reporting usage.</small></div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<AppsLinkList apps={seenApps} />
|
|
||||||
</Cell>
|
|
||||||
</Grid>
|
|
||||||
</div>);
|
|
||||||
};
|
|
||||||
|
|
||||||
class EditFeatureToggleWrapper extends React.Component {
|
|
||||||
|
|
||||||
constructor (props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = { activeTab: 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes () {
|
|
||||||
return {
|
|
||||||
featureToggleName: PropTypes.string.isRequired,
|
|
||||||
features: PropTypes.array.isRequired,
|
|
||||||
fetchFeatureToggles: PropTypes.array.isRequired,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount () {
|
|
||||||
if (this.props.features.length === 0) {
|
|
||||||
this.props.fetchFeatureToggles();
|
|
||||||
}
|
|
||||||
this.props.fetchSeenApps();
|
|
||||||
this.props.fetchHistoryForToggle(this.props.featureToggleName);
|
|
||||||
this.props.fetchFeatureMetrics();
|
|
||||||
this.timer = setInterval(() => {
|
|
||||||
this.props.fetchFeatureMetrics();
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
clearInterval(this.timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const {
|
|
||||||
history,
|
|
||||||
toggleFeature,
|
|
||||||
features,
|
|
||||||
featureToggleName,
|
|
||||||
metrics = {},
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const featureToggle = features.find(toggle => toggle.name === featureToggleName);
|
|
||||||
|
|
||||||
if (!featureToggle) {
|
|
||||||
if (features.length === 0 ) {
|
|
||||||
return <ProgressBar indeterminate />;
|
|
||||||
}
|
|
||||||
return <span>Could not find the toggle "{this.props.featureToggleName}"</span>;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tabContent;
|
|
||||||
if (this.state.activeTab === 0) {
|
|
||||||
tabContent = <MetricTab metrics={metrics} toggleFeature={toggleFeature} featureToggle={featureToggle} />;
|
|
||||||
} else if (this.state.activeTab === 1) {
|
|
||||||
tabContent = <EditFeatureToggle featureToggle={featureToggle} />;
|
|
||||||
} else {
|
|
||||||
tabContent = (
|
|
||||||
<div>
|
|
||||||
<List style={{ textAlign: 'left' }}>
|
|
||||||
{history.map(({ createdAt, type, createdBy }, i) =>
|
|
||||||
<ListItem twoLine key={i}>
|
|
||||||
<ListItemContent title={type} avatar={getIcon(type)} subtitle={createdAt}>
|
|
||||||
{type} <small>{createdBy}</small>
|
|
||||||
</ListItemContent>
|
|
||||||
</ListItem>)}
|
|
||||||
</List>
|
|
||||||
<Link to={`/history/${featureToggleName}`}>
|
|
||||||
See all events.
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h4>{featureToggle.name} <small>{featureToggle.enabled ? 'is enabled' : 'is disabled'}</small>
|
|
||||||
<small style={{ float: 'right', lineHeight: '38px' }}>
|
|
||||||
Created {(new Date(featureToggle.createdAt)).toLocaleString('nb-NO')}
|
|
||||||
</small>
|
|
||||||
</h4>
|
|
||||||
<div>{featureToggle.description}</div>
|
|
||||||
<Tabs activeTab={this.state.activeTab}
|
|
||||||
onChange={(tabId) => this.setState({ activeTab: tabId })}
|
|
||||||
ripple
|
|
||||||
style={{ marginBottom: '10px' }}>
|
|
||||||
<Tab>Metrics</Tab>
|
|
||||||
<Tab>Edit</Tab>
|
|
||||||
<Tab>History</Tab>
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
{tabContent}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMetricsForToggle (state, toggleName) {
|
function getMetricsForToggle (state, toggleName) {
|
||||||
if (!toggleName) {
|
if (!toggleName) {
|
||||||
@ -210,4 +54,4 @@ export default connect((state, props) => ({
|
|||||||
toggleFeature,
|
toggleFeature,
|
||||||
fetchSeenApps,
|
fetchSeenApps,
|
||||||
fetchHistoryForToggle,
|
fetchHistoryForToggle,
|
||||||
})(EditFeatureToggleWrapper);
|
})(ViewToggleComponent);
|
||||||
|
Loading…
Reference in New Issue
Block a user