1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

feat(archive): reuse Feature

This commit is contained in:
Corinne Krych 2018-03-03 21:34:38 +01:00
parent 7d7ca48259
commit 98bf15bdbf
5 changed files with 194 additions and 75 deletions

View File

@ -192,11 +192,11 @@ export default class App extends Component {
<FooterSection type="middle">
<FooterDropDownSection title="Menu">
<FooterLinkList>
{createListItem('/features', 'Feature Toggles')}
{createListItem('/strategies', 'Strategies')}
{createListItem('/history', 'Event History')}
{createListItem('/archive', 'Archived Toggles')}
{createListItem('/applications', 'Applications')}
{createListItem('/features', 'Feature Toggles', '')}
{createListItem('/strategies', 'Strategies', '')}
{createListItem('/history', 'Event History', '')}
{createListItem('/archive', 'Archived Toggles', '')}
{createListItem('/applications', 'Applications', '')}
<a href="/api/admin/user/logout">Sign out</a>
</FooterLinkList>
</FooterDropDownSection>

View File

@ -1,16 +1,21 @@
import React, { Component } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import Feature from './../feature/feature-list-item-component';
import { Icon, Card, List, ListItem, ListItemContent, ListItemAction, Chip } from 'react-mdl';
//import { Textfield, Menu, MenuItem, Card, CardActions, List, Chip, MenuItemWithIcon, DropdownButton } from 'react-mdl';
import { styles as commonStyles } from '../common';
import styles from './archive.scss';
class ArchiveList extends Component {
class ArchiveList extends React.PureComponent {
static propTypes = {
name: PropTypes.string,
archive: PropTypes.array,
archive: PropTypes.array.isRequired,
fetchArchive: PropTypes.func,
revive: PropTypes.func,
featureMetrics: PropTypes.object.isRequired,
updateSetting: PropTypes.func.isRequired,
settings: PropTypes.object,
revive: PropTypes.func.optional,
};
componentDidMount() {
@ -55,8 +60,86 @@ class ArchiveList extends Component {
}
return display;
}
// render() {
// const { archive, featureMetrics, settings, revive } = this.props;
// archive.forEach(e => {
// e.reviveName = e.name;
// });
// return (
// <div>
// <div className={styles.toolbar}>
// <Textfield
// floatingLabel
// value={settings.filter}
// onChange={e => {
// this.setFilter(e.target.value);
// }}
// label="Search"
// style={{ width: '100%' }}
// />
// </div>
// <Card shadow={0} className={commonStyles.fullwidth} style={{ overflow: 'visible' }}>
// <CardActions>
// <DropdownButton id="metric" label={`Last ${settings.showLastHour ? 'hour' : 'minute'}`} />
// <Menu target="metric" onClick={() => this.toggleMetrics()} style={{ width: '168px' }}>
// <MenuItemWithIcon
// icon="hourglass_empty"
// disabled={!settings.showLastHour}
// data-target="minute"
// label="Last minute"
// />
// <MenuItemWithIcon
// icon="hourglass_full"
// disabled={settings.showLastHour}
// data-target="hour"
// label="Last hour"
// />
// </Menu>
// <DropdownButton id="sorting" label={`By ${settings.sort}`} />
// <Menu
// target="sorting"
// onClick={e => this.setSort(e.target.getAttribute('data-target'))}
// style={{ width: '168px' }}
// >
// <MenuItem disabled={settings.sort === 'name'} data-target="name">
// Name
// </MenuItem>
// <MenuItem disabled={settings.sort === 'enabled'} data-target="enabled">
// Enabled
// </MenuItem>
// <MenuItem disabled={settings.sort === 'created'} data-target="created">
// Created
// </MenuItem>
// <MenuItem disabled={settings.sort === 'strategies'} data-target="strategies">
// Strategies
// </MenuItem>
// <MenuItem disabled={settings.sort === 'metrics'} data-target="metrics">
// Metrics
// </MenuItem>
// </Menu>
// </CardActions>
// <hr />
// <List>
// {archive.map((feature, i) => (
// <Feature
// key={i}
// settings={settings}
// metricsLastHour={featureMetrics.lastHour[feature.name]}
// metricsLastMinute={featureMetrics.lastMinute[feature.name]}
// feature={feature}
// revive={revive}
// />
// ))}
// </List>
// </Card>
// </div>
// );
// }
render() {
const { archive, revive } = this.props;
const { archive, featureMetrics, settings, revive } = this.props;
archive.forEach(e => {
e.reviveName = e.name;
});
@ -73,57 +156,14 @@ class ArchiveList extends Component {
<hr />
<List>
{archive.map((feature, i) => (
<ListItem key={i} twoLine>
<ListItemAction>
{this.props.name && feature.name === this.props.name ? (
<Icon name="keyboard_arrow_down" />
) : (
<Icon name="keyboard_arrow_right" />
)}
</ListItemAction>
<ListItemContent>
{this.props.name && feature.name === this.props.name ? (
<Link
to={`/archive`}
className={[commonStyles.listLink, commonStyles.truncate].join(
' '
)}
>
{this.renderStrategiesInList(feature).map((strategyChip, i) => (
<span key={i}>{strategyChip}</span>
))}
{feature.name}
<div className={'mdl-list__item-sub-title'}>
{feature.description}
</div>
<div className={'mdl-list__item-sub-title'}>
{this.renderStrategyDetail(feature)}
</div>
</Link>
) : (
<Link
to={`/archive/${feature.name}`}
className={[commonStyles.listLink, commonStyles.truncate].join(
' '
)}
>
{feature.name}
{this.renderStrategiesInList(feature).map((strategyChip, i) => (
<span key={i}>{strategyChip}</span>
))}
<div className={'mdl-list__item-sub-title'}>
{feature.description}
</div>
</Link>
)}
</ListItemContent>
<ListItemAction onClick={() => revive(feature.name)}>
<Icon name="undo" />
</ListItemAction>
</ListItem>
<Feature
key={i}
settings={settings}
metricsLastHour={featureMetrics.lastHour[feature.name]}
metricsLastMinute={featureMetrics.lastMinute[feature.name]}
feature={feature}
revive={revive}
/>
))}
</List>
</List>

View File

@ -1,15 +1,80 @@
import { connect } from 'react-redux';
import ListComponent from './archive-list-component';
import ArchiveList from './archive-list-component';
import { fetchArchive, revive } from './../../store/archive-actions';
import { updateSettingForGroup } from './../../store/settings/actions';
const mapStateToProps = state => {
const archive = state.archive.get('list').toArray();
const featureMetrics = state.featureMetrics.toJS();
const settings = state.settings.toJS().feature || {};
let features = state.archive.get('list').toArray();
if (settings.filter) {
try {
const regex = new RegExp(settings.filter, 'i');
features = features.filter(
feature =>
regex.test(feature.name) ||
regex.test(feature.description) ||
feature.strategies.some(s => s && s.name && regex.test(s.name))
);
} catch (e) {
// Invalid filter regex
}
}
if (!settings.sort) {
settings.sort = 'name';
}
if (settings.sort === 'enabled') {
features = features.sort(
(a, b) =>
// eslint-disable-next-line
a.enabled === b.enabled ? 0 : a.enabled ? -1 : 1
);
} else if (settings.sort === 'created') {
features = features.sort((a, b) => (new Date(a.createdAt) > new Date(b.createdAt) ? -1 : 1));
} else if (settings.sort === 'name') {
features = features.sort((a, b) => {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
});
} else if (settings.sort === 'strategies') {
features = features.sort((a, b) => (a.strategies.length > b.strategies.length ? -1 : 1));
} else if (settings.sort === 'metrics') {
const target = settings.showLastHour ? featureMetrics.lastHour : featureMetrics.lastMinute;
features = features.sort((a, b) => {
if (!target[a.name]) {
return 1;
}
if (!target[b.name]) {
return -1;
}
if (target[a.name].yes > target[b.name].yes) {
return -1;
}
return 1;
});
}
return {
archive,
archive: features,
featureMetrics,
settings,
};
};
const ArchiveListContainer = connect(mapStateToProps, { fetchArchive, revive })(ListComponent);
const mapDispatchToProps = {
fetchArchive,
revive,
updateSetting: updateSettingForGroup('feature'),
};
const ArchiveListContainer = connect(mapStateToProps, mapDispatchToProps)(ArchiveList);
export default ArchiveListContainer;

View File

@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import { Switch, Chip, ListItem } from 'react-mdl';
import { Switch, Chip, ListItem, ListItemAction, Icon } from 'react-mdl';
import Progress from './progress';
import { calc, styles as commonStyles } from '../common';
@ -13,6 +13,7 @@ const Feature = ({
settings,
metricsLastHour = { yes: 0, no: 0, isFallback: true },
metricsLastMinute = { yes: 0, no: 0, isFallback: true },
revive,
}) => {
const { name, description, enabled, strategies } = feature;
@ -42,14 +43,19 @@ const Feature = ({
<span className={styles.listItemMetric}>
<Progress strokeWidth={15} percentage={percent} isFallback={isStale} />
</span>
<span className={styles.listItemToggle}>
<Switch
title={`Toggle ${name}`}
key="left-actions"
onChange={() => toggleFeature(name)}
checked={enabled}
/>
</span>
{toggleFeature ? ( // display feature list
<span className={styles.listItemToggle}>
<Switch
title={`Toggle ${name}`}
key="left-actions"
onChange={() => toggleFeature(name)}
checked={enabled}
/>
</span>
) : (
// display archive
<span />
)}
<span className={['mdl-list__item-primary-content', styles.listItemLink].join(' ')}>
<Link
to={`/features/strategies/${name}`}
@ -63,6 +69,13 @@ const Feature = ({
{strategyChips}
{summaryChip}
</span>
{revive ? (
<ListItemAction onClick={() => revive(feature.name)}>
<Icon name="undo" />
</ListItemAction>
) : (
<span />
)}
</ListItem>
);
};
@ -73,6 +86,7 @@ Feature.propTypes = {
settings: PropTypes.object,
metricsLastHour: PropTypes.object,
metricsLastMinute: PropTypes.object,
revive: PropTypes.func,
};
export default Feature;

View File

@ -13,7 +13,7 @@ export default class FeatureListComponent extends React.PureComponent {
featureMetrics: PropTypes.object.isRequired,
fetchFeatureToggles: PropTypes.func.isRequired,
updateSetting: PropTypes.func.isRequired,
toggleFeature: PropTypes.func.isRequired,
toggleFeature: PropTypes.func,
settings: PropTypes.object,
};