mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
Merge pull request #32 from Unleash/strategies-in-metrics-view
Strategies in metrics view
This commit is contained in:
commit
5659567766
@ -64,7 +64,7 @@ export const TogglesLinkList = ({ toggles }) => (
|
|||||||
{toggles.length > 0 && toggles.map(({ name, description = '-', icon = 'toggle' }) => (
|
{toggles.length > 0 && toggles.map(({ name, description = '-', icon = 'toggle' }) => (
|
||||||
<ListItem twoLine key={name}>
|
<ListItem twoLine key={name}>
|
||||||
<ListItemContent avatar={icon} subtitle={description}>
|
<ListItemContent avatar={icon} subtitle={description}>
|
||||||
<Link key={name} to={`/features/edit/${name}`}>
|
<Link key={name} to={`/features/view/${name}`}>
|
||||||
{name}
|
{name}
|
||||||
</Link>
|
</Link>
|
||||||
</ListItemContent>
|
</ListItemContent>
|
||||||
|
@ -8,20 +8,7 @@ import { DragSource, DropTarget } from 'react-dnd';
|
|||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
import StrategyInputPercentage from './strategy-input-percentage';
|
import StrategyInputPercentage from './strategy-input-percentage';
|
||||||
import StrategyInputList from './strategy-input-list';
|
import StrategyInputList from './strategy-input-list';
|
||||||
|
import styles from './strategy.scss';
|
||||||
const style = {
|
|
||||||
flex: '1',
|
|
||||||
minWidth: '300px',
|
|
||||||
maxWidth: '100%',
|
|
||||||
margin: '5px 20px 15px 0px',
|
|
||||||
};
|
|
||||||
|
|
||||||
const helpText = {
|
|
||||||
color: 'rgba(0,0,0, 0.54)',
|
|
||||||
fontSize: '12px',
|
|
||||||
lineHeight: '14px',
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const dragSource = {
|
const dragSource = {
|
||||||
beginDrag (props) {
|
beginDrag (props) {
|
||||||
@ -105,7 +92,7 @@ class StrategyConfigure extends React.Component {
|
|||||||
name={name}
|
name={name}
|
||||||
onChange={this.handleConfigChange.bind(this, name)}
|
onChange={this.handleConfigChange.bind(this, name)}
|
||||||
value={1 * value} />
|
value={1 * value} />
|
||||||
{description && <p style={helpText}>{description}</p>}
|
{description && <p className={styles.helpText}>{description}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (type === 'list') {
|
} else if (type === 'list') {
|
||||||
@ -119,7 +106,7 @@ class StrategyConfigure extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div key={name}>
|
<div key={name}>
|
||||||
<StrategyInputList name={name} list={list} setConfig={this.setConfig} />
|
<StrategyInputList name={name} list={list} setConfig={this.setConfig} />
|
||||||
{description && <p style={helpText}>{description}</p>}
|
{description && <p className={styles.helpText}>{description}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (type === 'number') {
|
} else if (type === 'number') {
|
||||||
@ -136,7 +123,7 @@ class StrategyConfigure extends React.Component {
|
|||||||
onChange={this.handleConfigChange.bind(this, name)}
|
onChange={this.handleConfigChange.bind(this, name)}
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
{description && <p style={helpText}>{description}</p>}
|
{description && <p className={styles.helpText}>{description}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -152,7 +139,7 @@ class StrategyConfigure extends React.Component {
|
|||||||
onChange={this.handleConfigChange.bind(this, name)}
|
onChange={this.handleConfigChange.bind(this, name)}
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
{description && <p style={helpText}>{description}</p>}
|
{description && <p className={styles.helpText}>{description}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -169,8 +156,8 @@ class StrategyConfigure extends React.Component {
|
|||||||
const inputFields = this.renderInputFields(this.props.strategyDefinition);
|
const inputFields = this.renderInputFields(this.props.strategyDefinition);
|
||||||
const { name } = this.props.strategy;
|
const { name } = this.props.strategy;
|
||||||
item = (
|
item = (
|
||||||
<Card shadow={0} style={{ background: '#f2f9fc', width: '100%', display: 'block', opacity: isDragging ? '0.1' : '1' }}>
|
<Card shadow={0} className={styles.card} style={{ opacity: isDragging ? '0.1' : '1' }}>
|
||||||
<CardTitle style={{ color: '#fff', height: '65px', background: '#607d8b' }}>
|
<CardTitle className={styles.cardTitle}>
|
||||||
<Icon name="extension" /> {name}
|
<Icon name="extension" /> {name}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardText>
|
<CardText>
|
||||||
@ -182,27 +169,23 @@ class StrategyConfigure extends React.Component {
|
|||||||
</CardActions>
|
</CardActions>
|
||||||
}
|
}
|
||||||
|
|
||||||
<CardMenu style={{ color: '#fff' }}>
|
<CardMenu className="mdl-color-text--white">
|
||||||
<Link
|
<Link
|
||||||
title="View strategy"
|
title="View strategy"
|
||||||
to={`/strategies/view/${name}`}
|
to={`/strategies/view/${name}`}
|
||||||
style={{ color: '#fff', display: 'inline-block', verticalAlign: 'bottom', marginRight: '5px' }}>
|
className={styles.editLink}>
|
||||||
<Icon name="link" />
|
<Icon name="link" />
|
||||||
</Link>
|
</Link>
|
||||||
<IconButton title="Remove strategy from toggle" name="delete" onClick={this.handleRemove} />
|
<IconButton title="Remove strategy from toggle" name="delete" onClick={this.handleRemove} />
|
||||||
{connectDragSource(
|
{connectDragSource(
|
||||||
<span style={{
|
<span className={styles.reorderIcon}><Icon name="reorder" /></span>)}
|
||||||
cursor: 'pointer',
|
|
||||||
display: 'inline-block',
|
|
||||||
verticalAlign: 'bottom',
|
|
||||||
}}><Icon name="reorder" /></span>)}
|
|
||||||
</CardMenu>
|
</CardMenu>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const { name } = this.props.strategy;
|
const { name } = this.props.strategy;
|
||||||
item = (
|
item = (
|
||||||
<Card shadow={0} style={style}>
|
<Card shadow={0} className={styles.card}>
|
||||||
<CardTitle>"{name}" deleted?</CardTitle>
|
<CardTitle>"{name}" deleted?</CardTitle>
|
||||||
<CardText>
|
<CardText>
|
||||||
The strategy "{name}" does not exist on this server.
|
The strategy "{name}" does not exist on this server.
|
||||||
@ -217,7 +200,7 @@ class StrategyConfigure extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (connectDropTarget(connectDragPreview(
|
return (connectDropTarget(connectDragPreview(
|
||||||
<div style={style}>{item}</div>
|
<div className={styles.item}>{item}</div>
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
58
frontend/src/component/feature/form/strategy.scss
Normal file
58
frontend/src/component/feature/form/strategy.scss
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
.item {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 5px 0px 15px 35px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
.card {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
background-color: #f2f9fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item:not(:first-child):after {
|
||||||
|
content: " OR ";
|
||||||
|
position: absolute;
|
||||||
|
left: -30px;
|
||||||
|
top: 45%;
|
||||||
|
color: #ccc;
|
||||||
|
width: 25px;
|
||||||
|
line-height: 32px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardTitle {
|
||||||
|
color: #fff;
|
||||||
|
height: 65px;
|
||||||
|
background-color: #607d8b !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.helpText {
|
||||||
|
color: rgba(0,0,0, 0.54);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 14px;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
.editLink {
|
||||||
|
color: #fff;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: bottom;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reorderIcon {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
@ -1,8 +1,25 @@
|
|||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { Grid, Cell, Icon } from 'react-mdl';
|
import { Grid, Cell, Icon, Chip, ChipContact } from 'react-mdl';
|
||||||
import Progress from './progress';
|
import Progress from './progress';
|
||||||
|
import { Link } from 'react-router';
|
||||||
import { AppsLinkList, SwitchWithLabel, calc } from '../common';
|
import { AppsLinkList, SwitchWithLabel, calc } from '../common';
|
||||||
|
import styles from './metrics.scss';
|
||||||
|
|
||||||
|
const StrategyChipItem = ({ strategy }) => (
|
||||||
|
<Chip className={styles.chip}>
|
||||||
|
<ChipContact className="mdl-color--blue-grey mdl-color-text--white">
|
||||||
|
<Icon style={{ marginTop: '3px' }} name="link" />
|
||||||
|
</ChipContact>
|
||||||
|
<Link to={`/strategies/view/${strategy.name}`} className="mdl-color-text--blue-grey">{strategy.name}</Link>
|
||||||
|
</Chip>
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO what about "missing" strategies here?
|
||||||
|
const StrategiesList = ({ strategies }) => (
|
||||||
|
<div style={{ verticalAlign: 'middle' }}>With {strategies.length > 1 ? 'strategies' : 'strategy'} {
|
||||||
|
strategies.map((strategy, i) => <StrategyChipItem key={i} strategy={strategy} />)
|
||||||
|
}</div>
|
||||||
|
);
|
||||||
|
|
||||||
export default class MetricComponent extends React.Component {
|
export default class MetricComponent extends React.Component {
|
||||||
static propTypes () {
|
static propTypes () {
|
||||||
@ -39,16 +56,17 @@ export default class MetricComponent extends React.Component {
|
|||||||
const lastMinutePercent = 1 * calc(lastMinute.yes, lastMinute.yes + lastMinute.no, 0);
|
const lastMinutePercent = 1 * calc(lastMinute.yes, lastMinute.yes + lastMinute.no, 0);
|
||||||
|
|
||||||
return (<div>
|
return (<div>
|
||||||
|
<div style={{ paddingTop: '4px' }}>
|
||||||
<SwitchWithLabel
|
<SwitchWithLabel
|
||||||
checked={featureToggle.enabled}
|
checked={featureToggle.enabled}
|
||||||
onChange={() => toggleFeature(featureToggle)}>Toggle {featureToggle.name}</SwitchWithLabel>
|
onChange={() => toggleFeature(featureToggle)}>Toggle {featureToggle.name}</SwitchWithLabel>
|
||||||
<hr />
|
</div>
|
||||||
|
<hr style={{ borderColor: '#e0e0e0' }} />
|
||||||
<Grid style={{ textAlign: 'center' }}>
|
<Grid style={{ textAlign: 'center' }}>
|
||||||
<Cell tablet={4} col={3} phone={12}>
|
<Cell tablet={4} col={3} phone={12}>
|
||||||
{
|
{
|
||||||
lastMinute.isFallback ?
|
lastMinute.isFallback ?
|
||||||
<Icon style={{ width: '100px', height: '100px', fontSize: '100px', color: '#ccc' }}
|
<Icon className={styles.problemIcon} name="report problem" title="No metrics avaiable" /> :
|
||||||
name="report problem" title="No metrics avaiable" /> :
|
|
||||||
<div>
|
<div>
|
||||||
<Progress animatePercentageText strokeWidth={10} percentage={lastMinutePercent} width="50" />
|
<Progress animatePercentageText strokeWidth={10} percentage={lastMinutePercent} width="50" />
|
||||||
</div>
|
</div>
|
||||||
@ -58,8 +76,7 @@ export default class MetricComponent extends React.Component {
|
|||||||
<Cell col={3} tablet={4} phone={12}>
|
<Cell col={3} tablet={4} phone={12}>
|
||||||
{
|
{
|
||||||
lastHour.isFallback ?
|
lastHour.isFallback ?
|
||||||
<Icon style={{ width: '100px', height: '100px', fontSize: '100px', color: '#ccc' }}
|
<Icon className={styles.problemIcon} name="report problem" title="No metrics avaiable" /> :
|
||||||
name="report problem" title="No metrics avaiable" /> :
|
|
||||||
<div>
|
<div>
|
||||||
<Progress strokeWidth={10} percentage={lastHourPercent} width="50" />
|
<Progress strokeWidth={10} percentage={lastHourPercent} width="50" />
|
||||||
</div>
|
</div>
|
||||||
@ -70,8 +87,7 @@ export default class MetricComponent extends React.Component {
|
|||||||
{seenApps.length > 0 ?
|
{seenApps.length > 0 ?
|
||||||
(<div><strong>Seen in applications:</strong></div>) :
|
(<div><strong>Seen in applications:</strong></div>) :
|
||||||
<div>
|
<div>
|
||||||
<Icon style={{ width: '100px', height: '100px', fontSize: '100px', color: '#ccc' }}
|
<Icon className={styles.problemIcon} name="report problem" title="Not used in a app in the last hour" />
|
||||||
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>
|
<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>
|
This might be due to your client implementation is not reporting usage.</small></div>
|
||||||
</div>
|
</div>
|
||||||
@ -79,6 +95,8 @@ export default class MetricComponent extends React.Component {
|
|||||||
<AppsLinkList apps={seenApps} />
|
<AppsLinkList apps={seenApps} />
|
||||||
</Cell>
|
</Cell>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<hr style={{ borderColor: '#e0e0e0' }} />
|
||||||
|
<StrategiesList strategies={featureToggle.strategies}/>
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
37
frontend/src/component/feature/metrics.scss
Normal file
37
frontend/src/component/feature/metrics.scss
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
.chip {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: 30px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip:not(:first-child):after {
|
||||||
|
content: " OR ";
|
||||||
|
position: absolute;
|
||||||
|
left: -27px;
|
||||||
|
top: 0;
|
||||||
|
color: #ccc;
|
||||||
|
width: 25px;
|
||||||
|
line-height: 32px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip:first-child:before {
|
||||||
|
content: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
.problemIcon {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
font-size: 100px !important;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
@ -83,7 +83,7 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
Created {(new Date(featureToggle.createdAt)).toLocaleString('nb-NO')}
|
Created {(new Date(featureToggle.createdAt)).toLocaleString('nb-NO')}
|
||||||
</small>
|
</small>
|
||||||
</h4>
|
</h4>
|
||||||
<div>{featureToggle.description}</div>
|
<div className="mdl-color-text--grey"><small>{featureToggle.description}</small></div>
|
||||||
<Tabs activeTab={activeTabId} ripple style={{ marginBottom: '10px' }}>
|
<Tabs activeTab={activeTabId} ripple style={{ marginBottom: '10px' }}>
|
||||||
<Tab onClick={() => this.goToTab('view', featureToggleName)}>Metrics</Tab>
|
<Tab onClick={() => this.goToTab('view', featureToggleName)}>Metrics</Tab>
|
||||||
<Tab onClick={() => this.goToTab('edit', featureToggleName)}>Edit</Tab>
|
<Tab onClick={() => this.goToTab('edit', featureToggleName)}>Edit</Tab>
|
||||||
|
Loading…
Reference in New Issue
Block a user