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:
parent
7d7ca48259
commit
98bf15bdbf
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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,6 +43,7 @@ const Feature = ({
|
||||
<span className={styles.listItemMetric}>
|
||||
<Progress strokeWidth={15} percentage={percent} isFallback={isStale} />
|
||||
</span>
|
||||
{toggleFeature ? ( // display feature list
|
||||
<span className={styles.listItemToggle}>
|
||||
<Switch
|
||||
title={`Toggle ${name}`}
|
||||
@ -50,6 +52,10 @@ const Feature = ({
|
||||
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;
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user