mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +01:00
Merge pull request #121 from corinnekrych/archive.revisited
Archive.revisited
This commit is contained in:
commit
4b64762671
1
frontend/src/__mocks__/react-mdl.js
vendored
1
frontend/src/__mocks__/react-mdl.js
vendored
@ -1,5 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
Card: 'react-mdl-Card',
|
Card: 'react-mdl-Card',
|
||||||
|
CardActions: 'react-mdl-CardActions',
|
||||||
CardTitle: 'react-mdl-CardTitle',
|
CardTitle: 'react-mdl-CardTitle',
|
||||||
CardText: 'react-mdl-CardText',
|
CardText: 'react-mdl-CardText',
|
||||||
CardMenu: 'react-mdl-CardMenu',
|
CardMenu: 'react-mdl-CardMenu',
|
||||||
|
@ -192,11 +192,11 @@ export default class App extends Component {
|
|||||||
<FooterSection type="middle">
|
<FooterSection type="middle">
|
||||||
<FooterDropDownSection title="Menu">
|
<FooterDropDownSection title="Menu">
|
||||||
<FooterLinkList>
|
<FooterLinkList>
|
||||||
{createListItem('/features', 'Feature Toggles')}
|
{createListItem('/features', 'Feature Toggles', '')}
|
||||||
{createListItem('/strategies', 'Strategies')}
|
{createListItem('/strategies', 'Strategies', '')}
|
||||||
{createListItem('/history', 'Event History')}
|
{createListItem('/history', 'Event History', '')}
|
||||||
{createListItem('/archive', 'Archived Toggles')}
|
{createListItem('/archive', 'Archived Toggles', '')}
|
||||||
{createListItem('/applications', 'Applications')}
|
{createListItem('/applications', 'Applications', '')}
|
||||||
<a href="/api/admin/user/logout">Sign out</a>
|
<a href="/api/admin/user/logout">Sign out</a>
|
||||||
</FooterLinkList>
|
</FooterLinkList>
|
||||||
</FooterDropDownSection>
|
</FooterDropDownSection>
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"env": {
|
|
||||||
"jest": true
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,149 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`renders correctly with archived toggles 1`] = `
|
|
||||||
<react-mdl-Card
|
|
||||||
className="fullwidth"
|
|
||||||
shadow={0}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"position": "relative",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<react-mdl-List>
|
|
||||||
<react-mdl-ListItem
|
|
||||||
className="archiveList"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className="listItemToggle"
|
|
||||||
>
|
|
||||||
Toggle name
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className="listItemRevive"
|
|
||||||
>
|
|
||||||
Revive
|
|
||||||
</span>
|
|
||||||
</react-mdl-ListItem>
|
|
||||||
<hr />
|
|
||||||
<react-mdl-List>
|
|
||||||
<react-mdl-ListItem
|
|
||||||
twoLine={true}
|
|
||||||
>
|
|
||||||
<react-mdl-ListItemAction>
|
|
||||||
<react-mdl-Icon
|
|
||||||
name="keyboard_arrow_right"
|
|
||||||
/>
|
|
||||||
</react-mdl-ListItemAction>
|
|
||||||
<react-mdl-ListItemContent>
|
|
||||||
<a
|
|
||||||
className="listLink truncate"
|
|
||||||
onClick={[Function]}
|
|
||||||
style={Object {}}
|
|
||||||
>
|
|
||||||
adin-pay-confirm-disabled
|
|
||||||
<span>
|
|
||||||
<span
|
|
||||||
className="strategiesList hideLt920"
|
|
||||||
>
|
|
||||||
<react-mdl-Chip
|
|
||||||
className="strategyChip"
|
|
||||||
>
|
|
||||||
default
|
|
||||||
</react-mdl-Chip>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
className="mdl-list__item-sub-title"
|
|
||||||
>
|
|
||||||
Disables the confirm-functionality from API
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</react-mdl-ListItemContent>
|
|
||||||
<react-mdl-ListItemAction
|
|
||||||
onClick={[Function]}
|
|
||||||
>
|
|
||||||
<react-mdl-Icon
|
|
||||||
name="undo"
|
|
||||||
/>
|
|
||||||
</react-mdl-ListItemAction>
|
|
||||||
</react-mdl-ListItem>
|
|
||||||
<react-mdl-ListItem
|
|
||||||
twoLine={true}
|
|
||||||
>
|
|
||||||
<react-mdl-ListItemAction>
|
|
||||||
<react-mdl-Icon
|
|
||||||
name="keyboard_arrow_right"
|
|
||||||
/>
|
|
||||||
</react-mdl-ListItemAction>
|
|
||||||
<react-mdl-ListItemContent>
|
|
||||||
<a
|
|
||||||
className="listLink truncate"
|
|
||||||
onClick={[Function]}
|
|
||||||
style={Object {}}
|
|
||||||
>
|
|
||||||
adin-pay-platform-sch-payment
|
|
||||||
<span>
|
|
||||||
<span
|
|
||||||
className="strategiesList hideLt920"
|
|
||||||
>
|
|
||||||
<react-mdl-Chip
|
|
||||||
className="strategyChip"
|
|
||||||
>
|
|
||||||
default
|
|
||||||
</react-mdl-Chip>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
className="mdl-list__item-sub-title"
|
|
||||||
>
|
|
||||||
Enables use of schibsted payment from order-payment-management
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</react-mdl-ListItemContent>
|
|
||||||
<react-mdl-ListItemAction
|
|
||||||
onClick={[Function]}
|
|
||||||
>
|
|
||||||
<react-mdl-Icon
|
|
||||||
name="undo"
|
|
||||||
/>
|
|
||||||
</react-mdl-ListItemAction>
|
|
||||||
</react-mdl-ListItem>
|
|
||||||
</react-mdl-List>
|
|
||||||
</react-mdl-List>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</react-mdl-Card>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders correctly with no archived toggles 1`] = `
|
|
||||||
<react-mdl-Card
|
|
||||||
className="fullwidth"
|
|
||||||
shadow={0}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="emptyState"
|
|
||||||
>
|
|
||||||
<react-mdl-Icon
|
|
||||||
className="mdl-color-text--grey-300"
|
|
||||||
name="archive"
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"fontSize": "56px",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
No archived feature toggles, go see
|
|
||||||
<a
|
|
||||||
onClick={[Function]}
|
|
||||||
style={Object {}}
|
|
||||||
>
|
|
||||||
active toggles here
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</react-mdl-Card>
|
|
||||||
`;
|
|
@ -1,35 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import ArchiveList from '../archive-list-component';
|
|
||||||
import renderer from 'react-test-renderer';
|
|
||||||
|
|
||||||
jest.mock('react-mdl');
|
|
||||||
|
|
||||||
const archive = [
|
|
||||||
{
|
|
||||||
name: 'adin-pay-confirm-disabled',
|
|
||||||
description: 'Disables the confirm-functionality from API',
|
|
||||||
enabled: false,
|
|
||||||
strategies: [{ name: 'default', parameters: {} }],
|
|
||||||
createdAt: '2016-10-25T15:38:28.573Z',
|
|
||||||
reviveName: 'adin-pay-confirm-disabled',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'adin-pay-platform-sch-payment',
|
|
||||||
description: 'Enables use of schibsted payment from order-payment-management',
|
|
||||||
enabled: true,
|
|
||||||
strategies: [{ name: 'default', parameters: {} }],
|
|
||||||
createdAt: '2016-08-03T12:41:35.631Z',
|
|
||||||
reviveName: 'adin-pay-platform-sch-payment',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
test('renders correctly with no archived toggles', () => {
|
|
||||||
const tree = renderer.create(<ArchiveList fetchArchive={jest.fn()} archive={[]} />).toJSON();
|
|
||||||
expect(tree).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('renders correctly with archived toggles', () => {
|
|
||||||
const tree = renderer.create(<ArchiveList fetchArchive={jest.fn()} archive={archive} />).toJSON();
|
|
||||||
expect(tree).toMatchSnapshot();
|
|
||||||
});
|
|
@ -1,144 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Link } from 'react-router';
|
|
||||||
import { Icon, Card, List, ListItem, ListItemContent, ListItemAction, Chip } from 'react-mdl';
|
|
||||||
import { styles as commonStyles } from '../common';
|
|
||||||
import styles from './archive.scss';
|
|
||||||
|
|
||||||
class ArchiveList extends Component {
|
|
||||||
static propTypes = {
|
|
||||||
name: PropTypes.string,
|
|
||||||
archive: PropTypes.array,
|
|
||||||
fetchArchive: PropTypes.func,
|
|
||||||
revive: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.fetchArchive();
|
|
||||||
}
|
|
||||||
renderStrategyDetail(feature) {
|
|
||||||
let strategiesList = (
|
|
||||||
<span>
|
|
||||||
{feature.strategies.map((s, i) => (
|
|
||||||
<span style={{ marginLeft: `8px` }} key={i}>
|
|
||||||
<strong>{s.name}</strong>
|
|
||||||
{Object.keys(s.parameters).map((p, j) => <i key={j}> {s.parameters[p]}</i>)}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
return strategiesList;
|
|
||||||
}
|
|
||||||
renderStrategiesInList(feature) {
|
|
||||||
let display = [];
|
|
||||||
if (feature.strategies && feature.strategies.length > 0) {
|
|
||||||
const strategiesToShow = Math.min(feature.strategies.length, 3);
|
|
||||||
const remainingStrategies = feature.strategies.length - strategiesToShow;
|
|
||||||
|
|
||||||
const strategyChips =
|
|
||||||
feature.strategies &&
|
|
||||||
feature.strategies.slice(0, strategiesToShow).map((s, i) => (
|
|
||||||
<span key={i} className={[styles.strategiesList, commonStyles.hideLt920].join(' ')}>
|
|
||||||
<Chip className={styles.strategyChip}>{s.name}</Chip>
|
|
||||||
</span>
|
|
||||||
));
|
|
||||||
const remaining = (
|
|
||||||
<span className={[styles.strategiesList, commonStyles.hideLt920].join(' ')}>
|
|
||||||
<Chip className={styles.strategyChip}>+{remainingStrategies}</Chip>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
if (remainingStrategies > 0) {
|
|
||||||
display.push(remaining);
|
|
||||||
}
|
|
||||||
display.push(strategyChips);
|
|
||||||
}
|
|
||||||
return display;
|
|
||||||
}
|
|
||||||
render() {
|
|
||||||
const { archive, revive } = this.props;
|
|
||||||
archive.forEach(e => {
|
|
||||||
e.reviveName = e.name;
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<Card shadow={0} className={commonStyles.fullwidth}>
|
|
||||||
{archive && archive.length > 0 ? (
|
|
||||||
<div>
|
|
||||||
<div style={{ position: 'relative' }}>
|
|
||||||
<List>
|
|
||||||
<ListItem className={styles.archiveList}>
|
|
||||||
<span className={styles.listItemToggle}>Toggle name</span>
|
|
||||||
<span className={styles.listItemRevive}>Revive</span>
|
|
||||||
</ListItem>
|
|
||||||
<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>
|
|
||||||
))}
|
|
||||||
</List>
|
|
||||||
</List>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className={commonStyles.emptyState}>
|
|
||||||
<Icon name="archive" className="mdl-color-text--grey-300" style={{ fontSize: '56px' }} />
|
|
||||||
<br />
|
|
||||||
No archived feature toggles, go see <Link to="/features">active toggles here</Link>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ArchiveList;
|
|
@ -1,15 +1,16 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import ListComponent from './archive-list-component';
|
import FeatureListComponent from './../feature/list-component';
|
||||||
import { fetchArchive, revive } from './../../store/archive-actions';
|
import { fetchArchive, revive } from './../../store/archive-actions';
|
||||||
|
import { updateSettingForGroup } from './../../store/settings/actions';
|
||||||
|
import { mapStateToPropsConfigurable } from '../feature/list-container';
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = mapStateToPropsConfigurable(false);
|
||||||
const archive = state.archive.get('list').toArray();
|
const mapDispatchToProps = {
|
||||||
|
fetchArchive,
|
||||||
return {
|
revive,
|
||||||
archive,
|
updateSetting: updateSettingForGroup('feature'),
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ArchiveListContainer = connect(mapStateToProps, { fetchArchive, revive })(ListComponent);
|
const ArchiveListContainer = connect(mapStateToProps, mapDispatchToProps)(FeatureListComponent);
|
||||||
|
|
||||||
export default ArchiveListContainer;
|
export default ArchiveListContainer;
|
||||||
|
18
frontend/src/component/archive/view-container.js
Normal file
18
frontend/src/component/archive/view-container.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { fetchArchive, revive } from './../../store/archive-actions';
|
||||||
|
import ViewToggleComponent from './../feature/view-component';
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
(state, props) => ({
|
||||||
|
features: state.archive.get('list').toArray(),
|
||||||
|
featureToggle: state.archive
|
||||||
|
.get('list')
|
||||||
|
.toArray()
|
||||||
|
.find(toggle => toggle.name === props.featureToggleName),
|
||||||
|
activeTab: props.activeTab,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
fetchArchive,
|
||||||
|
revive,
|
||||||
|
}
|
||||||
|
)(ViewToggleComponent);
|
@ -22,6 +22,7 @@ exports[`renders correctly with one feature 1`] = `
|
|||||||
>
|
>
|
||||||
<react-mdl-Switch
|
<react-mdl-Switch
|
||||||
checked={false}
|
checked={false}
|
||||||
|
disabled={true}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
title="Toggle Another"
|
title="Toggle Another"
|
||||||
/>
|
/>
|
||||||
@ -51,5 +52,6 @@ exports[`renders correctly with one feature 1`] = `
|
|||||||
gradualRolloutRandom
|
gradualRolloutRandom
|
||||||
</react-mdl-Chip>
|
</react-mdl-Chip>
|
||||||
</span>
|
</span>
|
||||||
|
<span />
|
||||||
</react-mdl-ListItem>
|
</react-mdl-ListItem>
|
||||||
`;
|
`;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Link } from 'react-router';
|
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 Progress from './progress';
|
||||||
import { calc, styles as commonStyles } from '../common';
|
import { calc, styles as commonStyles } from '../common';
|
||||||
|
|
||||||
@ -13,12 +13,11 @@ const Feature = ({
|
|||||||
settings,
|
settings,
|
||||||
metricsLastHour = { yes: 0, no: 0, isFallback: true },
|
metricsLastHour = { yes: 0, no: 0, isFallback: true },
|
||||||
metricsLastMinute = { yes: 0, no: 0, isFallback: true },
|
metricsLastMinute = { yes: 0, no: 0, isFallback: true },
|
||||||
|
revive,
|
||||||
}) => {
|
}) => {
|
||||||
const { name, description, enabled, strategies } = feature;
|
const { name, description, enabled, strategies } = feature;
|
||||||
|
|
||||||
const { showLastHour = false } = settings;
|
const { showLastHour = false } = settings;
|
||||||
const isStale = showLastHour ? metricsLastHour.isFallback : metricsLastMinute.isFallback;
|
const isStale = showLastHour ? metricsLastHour.isFallback : metricsLastMinute.isFallback;
|
||||||
|
|
||||||
const percent =
|
const percent =
|
||||||
1 *
|
1 *
|
||||||
(showLastHour
|
(showLastHour
|
||||||
@ -27,7 +26,6 @@ const Feature = ({
|
|||||||
|
|
||||||
const strategiesToShow = Math.min(strategies.length, 3);
|
const strategiesToShow = Math.min(strategies.length, 3);
|
||||||
const remainingStrategies = strategies.length - strategiesToShow;
|
const remainingStrategies = strategies.length - strategiesToShow;
|
||||||
|
|
||||||
const strategyChips =
|
const strategyChips =
|
||||||
strategies &&
|
strategies &&
|
||||||
strategies.slice(0, strategiesToShow).map((s, i) => (
|
strategies.slice(0, strategiesToShow).map((s, i) => (
|
||||||
@ -36,7 +34,7 @@ const Feature = ({
|
|||||||
</Chip>
|
</Chip>
|
||||||
));
|
));
|
||||||
const summaryChip = remainingStrategies > 0 && <Chip className={styles.strategyChip}>+{remainingStrategies}</Chip>;
|
const summaryChip = remainingStrategies > 0 && <Chip className={styles.strategyChip}>+{remainingStrategies}</Chip>;
|
||||||
|
const featureUrl = toggleFeature === undefined ? `/archive/strategies/${name}` : `/features/strategies/${name}`;
|
||||||
return (
|
return (
|
||||||
<ListItem twoLine>
|
<ListItem twoLine>
|
||||||
<span className={styles.listItemMetric}>
|
<span className={styles.listItemMetric}>
|
||||||
@ -44,6 +42,7 @@ const Feature = ({
|
|||||||
</span>
|
</span>
|
||||||
<span className={styles.listItemToggle}>
|
<span className={styles.listItemToggle}>
|
||||||
<Switch
|
<Switch
|
||||||
|
disabled={toggleFeature !== undefined}
|
||||||
title={`Toggle ${name}`}
|
title={`Toggle ${name}`}
|
||||||
key="left-actions"
|
key="left-actions"
|
||||||
onChange={() => toggleFeature(name)}
|
onChange={() => toggleFeature(name)}
|
||||||
@ -51,10 +50,7 @@ const Feature = ({
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span className={['mdl-list__item-primary-content', styles.listItemLink].join(' ')}>
|
<span className={['mdl-list__item-primary-content', styles.listItemLink].join(' ')}>
|
||||||
<Link
|
<Link to={featureUrl} className={[commonStyles.listLink, commonStyles.truncate].join(' ')}>
|
||||||
to={`/features/strategies/${name}`}
|
|
||||||
className={[commonStyles.listLink, commonStyles.truncate].join(' ')}
|
|
||||||
>
|
|
||||||
{name}
|
{name}
|
||||||
<span className={['mdl-list__item-sub-title', commonStyles.truncate].join(' ')}>{description}</span>
|
<span className={['mdl-list__item-sub-title', commonStyles.truncate].join(' ')}>{description}</span>
|
||||||
</Link>
|
</Link>
|
||||||
@ -63,6 +59,13 @@ const Feature = ({
|
|||||||
{strategyChips}
|
{strategyChips}
|
||||||
{summaryChip}
|
{summaryChip}
|
||||||
</span>
|
</span>
|
||||||
|
{revive ? (
|
||||||
|
<ListItemAction onClick={() => revive(feature.name)}>
|
||||||
|
<Icon name="undo" />
|
||||||
|
</ListItemAction>
|
||||||
|
) : (
|
||||||
|
<span />
|
||||||
|
)}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -73,6 +76,7 @@ Feature.propTypes = {
|
|||||||
settings: PropTypes.object,
|
settings: PropTypes.object,
|
||||||
metricsLastHour: PropTypes.object,
|
metricsLastHour: PropTypes.object,
|
||||||
metricsLastMinute: PropTypes.object,
|
metricsLastMinute: PropTypes.object,
|
||||||
|
revive: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Feature;
|
export default Feature;
|
||||||
|
@ -45,7 +45,7 @@ const prepare = (methods, dispatch) => {
|
|||||||
methods.onCancel = evt => {
|
methods.onCancel = evt => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
methods.clear();
|
methods.clear();
|
||||||
window.history.back();
|
hashHistory.push(`/features`);
|
||||||
};
|
};
|
||||||
|
|
||||||
methods.addStrategy = v => {
|
methods.addStrategy = v => {
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import StrategiesSection from './strategies-section-container';
|
||||||
|
import { Button, Icon } from 'react-mdl';
|
||||||
|
|
||||||
|
class ViewFeatureComponent extends Component {
|
||||||
|
render() {
|
||||||
|
const { input, onCancel } = this.props;
|
||||||
|
const configuredStrategies = input.strategies || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section style={{ padding: '16px' }}>
|
||||||
|
<StrategiesSection configuredStrategies={configuredStrategies} />
|
||||||
|
<br />
|
||||||
|
<Button type="cancel" ripple raised onClick={onCancel} style={{ float: 'right' }}>
|
||||||
|
<Icon name="cancel" /> Cancel
|
||||||
|
</Button>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewFeatureComponent.propTypes = {
|
||||||
|
input: PropTypes.object,
|
||||||
|
onCancel: PropTypes.func.isRequired,
|
||||||
|
initCallRequired: PropTypes.bool,
|
||||||
|
init: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ViewFeatureComponent;
|
@ -0,0 +1,40 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createMapper, createActions } from '../../input-helpers';
|
||||||
|
import ViewFeatureToggleComponent from './form-view-feature-component';
|
||||||
|
import { hashHistory } from 'react-router';
|
||||||
|
|
||||||
|
const ID = 'view-feature-toggle';
|
||||||
|
function getId(props) {
|
||||||
|
return [ID, props.featureToggle.name];
|
||||||
|
}
|
||||||
|
// TODO: need to scope to the active featureToggle
|
||||||
|
// best is to emulate the "input-storage"?
|
||||||
|
const mapStateToProps = createMapper({
|
||||||
|
id: getId,
|
||||||
|
getDefault: (state, ownProps) => {
|
||||||
|
ownProps.featureToggle.strategies.forEach((strategy, index) => {
|
||||||
|
strategy.id = Math.round(Math.random() * 1000000 * (1 + index));
|
||||||
|
});
|
||||||
|
return ownProps.featureToggle;
|
||||||
|
},
|
||||||
|
prepare: props => {
|
||||||
|
props.editmode = true;
|
||||||
|
return props;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const prepare = methods => {
|
||||||
|
methods.onCancel = evt => {
|
||||||
|
evt.preventDefault();
|
||||||
|
methods.clear();
|
||||||
|
hashHistory.push(`/archive`);
|
||||||
|
};
|
||||||
|
return methods;
|
||||||
|
};
|
||||||
|
|
||||||
|
const actions = createActions({
|
||||||
|
id: getId,
|
||||||
|
prepare,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, actions)(ViewFeatureToggleComponent);
|
@ -5,7 +5,7 @@ import { Menu, MenuItem, IconButton } from 'react-mdl';
|
|||||||
class AddStrategy extends React.Component {
|
class AddStrategy extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
strategies: PropTypes.array.isRequired,
|
strategies: PropTypes.array.isRequired,
|
||||||
addStrategy: PropTypes.func.isRequired,
|
addStrategy: PropTypes.func,
|
||||||
fetchStrategies: PropTypes.func.isRequired,
|
fetchStrategies: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ class StrategiesList extends React.Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
strategies: PropTypes.array.isRequired,
|
strategies: PropTypes.array.isRequired,
|
||||||
configuredStrategies: PropTypes.array.isRequired,
|
configuredStrategies: PropTypes.array.isRequired,
|
||||||
updateStrategy: PropTypes.func.isRequired,
|
updateStrategy: PropTypes.func,
|
||||||
removeStrategy: PropTypes.func.isRequired,
|
removeStrategy: PropTypes.func,
|
||||||
moveStrategy: PropTypes.func.isRequired,
|
moveStrategy: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -27,8 +27,8 @@ class StrategiesList extends React.Component {
|
|||||||
key={strategy.id}
|
key={strategy.id}
|
||||||
strategy={strategy}
|
strategy={strategy}
|
||||||
moveStrategy={moveStrategy}
|
moveStrategy={moveStrategy}
|
||||||
removeStrategy={removeStrategy.bind(null, i)}
|
removeStrategy={removeStrategy ? removeStrategy.bind(null, i) : null}
|
||||||
updateStrategy={updateStrategy.bind(null, i)}
|
updateStrategy={updateStrategy ? updateStrategy.bind(null, i) : null}
|
||||||
strategyDefinition={strategies.find(s => s.name === strategy.name)}
|
strategyDefinition={strategies.find(s => s.name === strategy.name)}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
@ -8,9 +8,9 @@ import { HeaderTitle } from '../../common';
|
|||||||
class StrategiesSectionComponent extends React.Component {
|
class StrategiesSectionComponent extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
strategies: PropTypes.array.isRequired,
|
strategies: PropTypes.array.isRequired,
|
||||||
addStrategy: PropTypes.func.isRequired,
|
addStrategy: PropTypes.func,
|
||||||
removeStrategy: PropTypes.func.isRequired,
|
removeStrategy: PropTypes.func,
|
||||||
updateStrategy: PropTypes.func.isRequired,
|
updateStrategy: PropTypes.func,
|
||||||
fetchStrategies: PropTypes.func.isRequired,
|
fetchStrategies: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -25,7 +25,11 @@ class StrategiesSectionComponent extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
{this.props.addStrategy ? (
|
||||||
<HeaderTitle title="Activation strategies" actions={<AddStrategy {...this.props} />} />
|
<HeaderTitle title="Activation strategies" actions={<AddStrategy {...this.props} />} />
|
||||||
|
) : (
|
||||||
|
<span />
|
||||||
|
)}
|
||||||
<StrategiesList {...this.props} />
|
<StrategiesList {...this.props} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -46,10 +46,10 @@ class StrategyConfigure extends React.Component {
|
|||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
strategy: PropTypes.object.isRequired,
|
strategy: PropTypes.object.isRequired,
|
||||||
strategyDefinition: PropTypes.object.isRequired,
|
strategyDefinition: PropTypes.object,
|
||||||
updateStrategy: PropTypes.func.isRequired,
|
updateStrategy: PropTypes.func,
|
||||||
removeStrategy: PropTypes.func.isRequired,
|
removeStrategy: PropTypes.func,
|
||||||
moveStrategy: PropTypes.func.isRequired,
|
moveStrategy: PropTypes.func,
|
||||||
isDragging: PropTypes.bool.isRequired,
|
isDragging: PropTypes.bool.isRequired,
|
||||||
connectDragPreview: PropTypes.func.isRequired,
|
connectDragPreview: PropTypes.func.isRequired,
|
||||||
connectDragSource: PropTypes.func.isRequired,
|
connectDragSource: PropTypes.func.isRequired,
|
||||||
@ -170,7 +170,11 @@ class StrategyConfigure extends React.Component {
|
|||||||
<Link title="View strategy" to={`/strategies/view/${name}`} className={styles.editLink}>
|
<Link title="View strategy" to={`/strategies/view/${name}`} className={styles.editLink}>
|
||||||
<Icon name="link" />
|
<Icon name="link" />
|
||||||
</Link>
|
</Link>
|
||||||
|
{this.props.removeStrategy ? (
|
||||||
<IconButton title="Remove strategy from toggle" name="delete" onClick={this.handleRemove} />
|
<IconButton title="Remove strategy from toggle" name="delete" onClick={this.handleRemove} />
|
||||||
|
) : (
|
||||||
|
<span />
|
||||||
|
)}
|
||||||
{connectDragSource(
|
{connectDragSource(
|
||||||
<span className={styles.reorderIcon}>
|
<span className={styles.reorderIcon}>
|
||||||
<Icon name="reorder" />
|
<Icon name="reorder" />
|
||||||
|
@ -7,13 +7,15 @@ import { Icon, FABButton, Textfield, Menu, MenuItem, Card, CardActions, List } f
|
|||||||
import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../common';
|
import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../common';
|
||||||
import styles from './feature.scss';
|
import styles from './feature.scss';
|
||||||
|
|
||||||
export default class FeatureListComponent extends React.PureComponent {
|
export default class FeatureListComponent extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
features: PropTypes.array.isRequired,
|
features: PropTypes.array.isRequired,
|
||||||
featureMetrics: PropTypes.object.isRequired,
|
featureMetrics: PropTypes.object.isRequired,
|
||||||
fetchFeatureToggles: PropTypes.func.isRequired,
|
fetchFeatureToggles: PropTypes.func,
|
||||||
|
fetchArchive: PropTypes.func,
|
||||||
|
revive: PropTypes.func,
|
||||||
updateSetting: PropTypes.func.isRequired,
|
updateSetting: PropTypes.func.isRequired,
|
||||||
toggleFeature: PropTypes.func.isRequired,
|
toggleFeature: PropTypes.func,
|
||||||
settings: PropTypes.object,
|
settings: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -22,7 +24,11 @@ export default class FeatureListComponent extends React.PureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
if (this.props.fetchFeatureToggles) {
|
||||||
this.props.fetchFeatureToggles();
|
this.props.fetchFeatureToggles();
|
||||||
|
} else {
|
||||||
|
this.props.fetchArchive();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleMetrics() {
|
toggleMetrics() {
|
||||||
@ -38,8 +44,10 @@ export default class FeatureListComponent extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { features, toggleFeature, featureMetrics, settings } = this.props;
|
const { features, toggleFeature, featureMetrics, settings, revive } = this.props;
|
||||||
|
features.forEach(e => {
|
||||||
|
e.reviveName = e.name;
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.toolbar}>
|
<div className={styles.toolbar}>
|
||||||
@ -108,6 +116,7 @@ export default class FeatureListComponent extends React.PureComponent {
|
|||||||
metricsLastMinute={featureMetrics.lastMinute[feature.name]}
|
metricsLastMinute={featureMetrics.lastMinute[feature.name]}
|
||||||
feature={feature}
|
feature={feature}
|
||||||
toggleFeature={toggleFeature}
|
toggleFeature={toggleFeature}
|
||||||
|
revive={revive}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
|
@ -4,10 +4,10 @@ import { updateSettingForGroup } from '../../store/settings/actions';
|
|||||||
|
|
||||||
import FeatureListComponent from './list-component';
|
import FeatureListComponent from './list-component';
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
export const mapStateToPropsConfigurable = isFeature => state => {
|
||||||
const featureMetrics = state.featureMetrics.toJS();
|
const featureMetrics = state.featureMetrics.toJS();
|
||||||
const settings = state.settings.toJS().feature || {};
|
const settings = state.settings.toJS().feature || {};
|
||||||
let features = state.features.toJS();
|
let features = isFeature ? state.features.toJS() : state.archive.get('list').toArray();
|
||||||
if (settings.filter) {
|
if (settings.filter) {
|
||||||
try {
|
try {
|
||||||
const regex = new RegExp(settings.filter, 'i');
|
const regex = new RegExp(settings.filter, 'i');
|
||||||
@ -69,7 +69,7 @@ const mapStateToProps = state => {
|
|||||||
settings,
|
settings,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
const mapStateToProps = mapStateToPropsConfigurable(true);
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
toggleFeature,
|
toggleFeature,
|
||||||
fetchFeatureToggles,
|
fetchFeatureToggles,
|
||||||
|
@ -6,6 +6,7 @@ import { hashHistory, Link } from 'react-router';
|
|||||||
import HistoryComponent from '../history/history-list-toggle-container';
|
import HistoryComponent from '../history/history-list-toggle-container';
|
||||||
import MetricComponent from './metric-container';
|
import MetricComponent from './metric-container';
|
||||||
import EditFeatureToggle from './form/form-update-feature-container';
|
import EditFeatureToggle from './form/form-update-feature-container';
|
||||||
|
import ViewFeatureToggle from './form/form-view-feature-container';
|
||||||
import { styles as commonStyles } from '../common';
|
import { styles as commonStyles } from '../common';
|
||||||
|
|
||||||
const TABS = {
|
const TABS = {
|
||||||
@ -15,24 +16,32 @@ const TABS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default class ViewFeatureToggleComponent extends React.Component {
|
export default class ViewFeatureToggleComponent extends React.Component {
|
||||||
|
isFeatureView;
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
this.isFeatureView = !!props.fetchFeatureToggles;
|
||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
activeTab: PropTypes.string.isRequired,
|
activeTab: PropTypes.string.isRequired,
|
||||||
featureToggleName: PropTypes.string.isRequired,
|
featureToggleName: PropTypes.string.isRequired,
|
||||||
features: PropTypes.array.isRequired,
|
features: PropTypes.array.isRequired,
|
||||||
toggleFeature: PropTypes.func.isRequired,
|
toggleFeature: PropTypes.func,
|
||||||
removeFeatureToggle: PropTypes.func.isRequired,
|
removeFeatureToggle: PropTypes.func,
|
||||||
fetchFeatureToggles: PropTypes.func.isRequired,
|
revive: PropTypes.func,
|
||||||
editFeatureToggle: PropTypes.func.isRequired,
|
fetchArchive: PropTypes.func,
|
||||||
|
fetchFeatureToggles: PropTypes.func,
|
||||||
|
editFeatureToggle: PropTypes.func,
|
||||||
featureToggle: PropTypes.object,
|
featureToggle: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
if (this.props.features.length === 0) {
|
if (this.props.features.length === 0) {
|
||||||
|
if (this.isFeatureView) {
|
||||||
this.props.fetchFeatureToggles();
|
this.props.fetchFeatureToggles();
|
||||||
|
} else {
|
||||||
|
this.props.fetchArchive();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,14 +51,18 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
if (TABS[activeTab] === TABS.history) {
|
if (TABS[activeTab] === TABS.history) {
|
||||||
return <HistoryComponent toggleName={featureToggleName} />;
|
return <HistoryComponent toggleName={featureToggleName} />;
|
||||||
} else if (TABS[activeTab] === TABS.strategies) {
|
} else if (TABS[activeTab] === TABS.strategies) {
|
||||||
|
if (this.isFeatureView) {
|
||||||
return <EditFeatureToggle featureToggle={featureToggle} />;
|
return <EditFeatureToggle featureToggle={featureToggle} />;
|
||||||
|
}
|
||||||
|
return <ViewFeatureToggle featureToggle={featureToggle} />;
|
||||||
} else {
|
} else {
|
||||||
return <MetricComponent featureToggle={featureToggle} />;
|
return <MetricComponent featureToggle={featureToggle} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
goToTab(tabName, featureToggleName) {
|
goToTab(tabName, featureToggleName) {
|
||||||
hashHistory.push(`/features/${tabName}/${featureToggleName}`);
|
let view = this.props.fetchFeatureToggles ? 'features' : 'archive';
|
||||||
|
hashHistory.push(`/${view}/${tabName}/${featureToggleName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -57,6 +70,7 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
featureToggle,
|
featureToggle,
|
||||||
features,
|
features,
|
||||||
activeTab,
|
activeTab,
|
||||||
|
revive,
|
||||||
// setValue,
|
// setValue,
|
||||||
featureToggleName,
|
featureToggleName,
|
||||||
toggleFeature,
|
toggleFeature,
|
||||||
@ -94,6 +108,10 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
hashHistory.push('/features');
|
hashHistory.push('/features');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const reviveToggle = () => {
|
||||||
|
revive(featureToggle.name);
|
||||||
|
hashHistory.push('/features');
|
||||||
|
};
|
||||||
const updateFeatureToggle = () => {
|
const updateFeatureToggle = () => {
|
||||||
let feature = { ...featureToggle };
|
let feature = { ...featureToggle };
|
||||||
if (Array.isArray(feature.strategies)) {
|
if (Array.isArray(feature.strategies)) {
|
||||||
@ -113,6 +131,7 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
<Card shadow={0} className={commonStyles.fullwidth} style={{ overflow: 'visible' }}>
|
<Card shadow={0} className={commonStyles.fullwidth} style={{ overflow: 'visible' }}>
|
||||||
<CardTitle style={{ paddingTop: '24px', wordBreak: 'break-all' }}>{featureToggle.name}</CardTitle>
|
<CardTitle style={{ paddingTop: '24px', wordBreak: 'break-all' }}>{featureToggle.name}</CardTitle>
|
||||||
<CardText>
|
<CardText>
|
||||||
|
{this.isFeatureView ? (
|
||||||
<Textfield
|
<Textfield
|
||||||
floatingLabel
|
floatingLabel
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
@ -123,6 +142,17 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
onChange={v => setValue('description', v)}
|
onChange={v => setValue('description', v)}
|
||||||
onBlur={updateFeatureToggle}
|
onBlur={updateFeatureToggle}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<Textfield
|
||||||
|
disabled
|
||||||
|
floatingLabel
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
rows={1}
|
||||||
|
label="Description"
|
||||||
|
required
|
||||||
|
value={featureToggle.description}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</CardText>
|
</CardText>
|
||||||
|
|
||||||
<CardActions
|
<CardActions
|
||||||
@ -135,6 +165,7 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
>
|
>
|
||||||
<span style={{ paddingRight: '24px' }}>
|
<span style={{ paddingRight: '24px' }}>
|
||||||
<Switch
|
<Switch
|
||||||
|
disabled={this.isFeatureView}
|
||||||
ripple
|
ripple
|
||||||
checked={featureToggle.enabled}
|
checked={featureToggle.enabled}
|
||||||
onChange={() => toggleFeature(featureToggle.name)}
|
onChange={() => toggleFeature(featureToggle.name)}
|
||||||
@ -142,9 +173,16 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
{featureToggle.enabled ? 'Enabled' : 'Disabled'}
|
{featureToggle.enabled ? 'Enabled' : 'Disabled'}
|
||||||
</Switch>
|
</Switch>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
{this.isFeatureView ? (
|
||||||
<Button onClick={removeToggle} style={{ flexShrink: 0 }}>
|
<Button onClick={removeToggle} style={{ flexShrink: 0 }}>
|
||||||
Archive
|
Archive
|
||||||
</Button>
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button onClick={reviveToggle} style={{ flexShrink: 0 }}>
|
||||||
|
Revive
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</CardActions>
|
</CardActions>
|
||||||
<hr />
|
<hr />
|
||||||
<Tabs
|
<Tabs
|
||||||
|
@ -23,6 +23,7 @@ import CreateStrategies from './page/strategies/create';
|
|||||||
import HistoryPage from './page/history';
|
import HistoryPage from './page/history';
|
||||||
import HistoryTogglePage from './page/history/toggle';
|
import HistoryTogglePage from './page/history/toggle';
|
||||||
import Archive from './page/archive';
|
import Archive from './page/archive';
|
||||||
|
import ShowArchive from './page/archive/show';
|
||||||
import Applications from './page/applications';
|
import Applications from './page/applications';
|
||||||
import ApplicationView from './page/applications/view';
|
import ApplicationView from './page/applications/view';
|
||||||
|
|
||||||
@ -68,7 +69,7 @@ ReactDOM.render(
|
|||||||
</Route>
|
</Route>
|
||||||
<Route pageTitle="Archived Toggles" link="/archive">
|
<Route pageTitle="Archived Toggles" link="/archive">
|
||||||
<Route pageTitle="Archived Toggles" path="/archive" component={Archive} />
|
<Route pageTitle="Archived Toggles" path="/archive" component={Archive} />
|
||||||
<Route pageTitle=":name" path="/archive/:name" component={Archive} />
|
<Route pageTitle=":name" path="/archive/:activeTab/:name" component={ShowArchive} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route pageTitle="Applications" link="/applications">
|
<Route pageTitle="Applications" link="/applications">
|
||||||
|
14
frontend/src/page/archive/show.js
Normal file
14
frontend/src/page/archive/show.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ViewFeatureToggle from './../../component/archive/view-container';
|
||||||
|
|
||||||
|
export default class Features extends PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
params: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { params } = this.props;
|
||||||
|
return <ViewFeatureToggle featureToggleName={params.name} activeTab={params.activeTab} />;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user