mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-20 00:08:02 +01:00
Use 120 as width in prettier (#90)
This commit is contained in:
parent
b176d63f56
commit
8ef9def08c
@ -5,6 +5,15 @@
|
||||
"finn-prettier"
|
||||
],
|
||||
"rules": {
|
||||
"no-shadow": 0
|
||||
"no-shadow": 0,
|
||||
"prettier/prettier": [
|
||||
2,
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"es6": true
|
||||
"es6": true
|
||||
},
|
||||
"globals": {
|
||||
"process": false
|
||||
@ -23,6 +23,15 @@
|
||||
},
|
||||
"rules": {
|
||||
"no-shadow": 0,
|
||||
"react/sort-comp": 0
|
||||
"react/sort-comp": 0,
|
||||
"prettier/prettier": [
|
||||
2,
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -89,18 +89,10 @@ export default class App extends Component {
|
||||
return (
|
||||
<span>
|
||||
{result.map((entry, index) => (
|
||||
<span
|
||||
key={entry.link + index}
|
||||
className={
|
||||
index > 0 ? 'mdl-layout--large-screen-only' : ''
|
||||
}
|
||||
>
|
||||
<span key={entry.link + index} className={index > 0 ? 'mdl-layout--large-screen-only' : ''}>
|
||||
{index > 0 ? ' › ' : null}
|
||||
<Link
|
||||
className={[
|
||||
styles.headerTitleLink,
|
||||
'mdl-color-text--primary-contrast',
|
||||
].join(' ')}
|
||||
className={[styles.headerTitleLink, 'mdl-color-text--primary-contrast'].join(' ')}
|
||||
to={entry.link}
|
||||
>
|
||||
{entry.name}
|
||||
@ -113,21 +105,13 @@ export default class App extends Component {
|
||||
|
||||
render() {
|
||||
const shouldUpdateScroll = (prevRouterProps, { location }) => {
|
||||
if (
|
||||
prevRouterProps &&
|
||||
location.pathname !== prevRouterProps.location.pathname
|
||||
) {
|
||||
if (prevRouterProps && location.pathname !== prevRouterProps.location.pathname) {
|
||||
return location.action === 'POP';
|
||||
} else {
|
||||
return [0, 0];
|
||||
}
|
||||
};
|
||||
const createListItem = (
|
||||
path,
|
||||
caption,
|
||||
icon,
|
||||
isDrawerNavigation = false
|
||||
) => {
|
||||
const createListItem = (path, caption, icon, isDrawerNavigation = false) => {
|
||||
const linkColor =
|
||||
isDrawerNavigation && this.context.router.isActive(path)
|
||||
? 'mdl-color-text--black'
|
||||
@ -137,20 +121,11 @@ export default class App extends Component {
|
||||
? 'mdl-color-text--black'
|
||||
: 'mdl-color-text--grey-600';
|
||||
return (
|
||||
<Link
|
||||
to={path}
|
||||
className={
|
||||
isDrawerNavigation &&
|
||||
[styles.navigationLink, linkColor].join(' ')
|
||||
}
|
||||
>
|
||||
<Link to={path} className={isDrawerNavigation && [styles.navigationLink, linkColor].join(' ')}>
|
||||
{icon && (
|
||||
<Icon
|
||||
name={icon}
|
||||
className={
|
||||
isDrawerNavigation &&
|
||||
[styles.navigationIcon, iconColor].join(' ')
|
||||
}
|
||||
className={isDrawerNavigation && [styles.navigationIcon, iconColor].join(' ')}
|
||||
/>
|
||||
)}
|
||||
{caption}
|
||||
@ -168,79 +143,32 @@ export default class App extends Component {
|
||||
</Navigation>
|
||||
</Header>
|
||||
<Drawer className="mdl-color--white">
|
||||
<span
|
||||
className={[
|
||||
styles.drawerTitle,
|
||||
'mdl-layout-title',
|
||||
].join(' ')}
|
||||
>
|
||||
<img
|
||||
src="public/logo.png"
|
||||
width="32"
|
||||
height="32"
|
||||
className={styles.drawerTitleLogo}
|
||||
/>
|
||||
<span className={styles.drawerTitleText}>
|
||||
Unleash
|
||||
</span>
|
||||
<span className={[styles.drawerTitle, 'mdl-layout-title'].join(' ')}>
|
||||
<img src="public/logo.png" width="32" height="32" className={styles.drawerTitleLogo} />
|
||||
<span className={styles.drawerTitleText}>Unleash</span>
|
||||
</span>
|
||||
<hr />
|
||||
<Navigation className={styles.navigation}>
|
||||
{createListItem(
|
||||
'/features',
|
||||
'Feature Toggles',
|
||||
'list',
|
||||
true
|
||||
)}
|
||||
{createListItem(
|
||||
'/strategies',
|
||||
'Strategies',
|
||||
'extension',
|
||||
true
|
||||
)}
|
||||
{createListItem(
|
||||
'/history',
|
||||
'Event History',
|
||||
'history',
|
||||
true
|
||||
)}
|
||||
{createListItem(
|
||||
'/archive',
|
||||
'Archived Toggles',
|
||||
'archive',
|
||||
true
|
||||
)}
|
||||
{createListItem(
|
||||
'/applications',
|
||||
'Applications',
|
||||
'apps',
|
||||
true
|
||||
)}
|
||||
{createListItem('/features', 'Feature Toggles', 'list', true)}
|
||||
{createListItem('/strategies', 'Strategies', 'extension', true)}
|
||||
{createListItem('/history', 'Event History', 'history', true)}
|
||||
{createListItem('/archive', 'Archived Toggles', 'archive', true)}
|
||||
{createListItem('/applications', 'Applications', 'apps', true)}
|
||||
</Navigation>
|
||||
<hr />
|
||||
<Navigation className={styles.navigation}>
|
||||
<a
|
||||
href="https://github.com/Unleash"
|
||||
target="_blank"
|
||||
className={[
|
||||
styles.navigationLink,
|
||||
'mdl-color-text--grey-900',
|
||||
].join(' ')}
|
||||
className={[styles.navigationLink, 'mdl-color-text--grey-900'].join(' ')}
|
||||
>
|
||||
<i
|
||||
className={[
|
||||
'material-icons',
|
||||
styles.navigationIcon,
|
||||
styles.iconGitHub,
|
||||
].join(' ')}
|
||||
className={['material-icons', styles.navigationIcon, styles.iconGitHub].join(' ')}
|
||||
/>GitHub
|
||||
</a>
|
||||
</Navigation>
|
||||
</Drawer>
|
||||
<ScrollContainer
|
||||
scrollKey="container"
|
||||
shouldUpdateScroll={shouldUpdateScroll}
|
||||
>
|
||||
<ScrollContainer scrollKey="container" shouldUpdateScroll={shouldUpdateScroll}>
|
||||
<Content className="mdl-color--grey-50">
|
||||
<Grid noSpacing className={styles.content}>
|
||||
<Cell col={12}>
|
||||
@ -252,54 +180,27 @@ 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')}
|
||||
</FooterLinkList>
|
||||
</FooterDropDownSection>
|
||||
<FooterDropDownSection title="Clients">
|
||||
<FooterLinkList>
|
||||
<a href="https://github.com/Unleash/unleash-client-node/">
|
||||
Node.js
|
||||
</a>
|
||||
<a href="https://github.com/Unleash/unleash-client-java/">
|
||||
Java
|
||||
</a>
|
||||
<a href="https://github.com/Unleash/unleash-client-go/">
|
||||
Go
|
||||
</a>
|
||||
<a href="https://github.com/Unleash/unleash-client-node/">Node.js</a>
|
||||
<a href="https://github.com/Unleash/unleash-client-java/">Java</a>
|
||||
<a href="https://github.com/Unleash/unleash-client-go/">Go</a>
|
||||
</FooterLinkList>
|
||||
</FooterDropDownSection>
|
||||
</FooterSection>
|
||||
<FooterSection type="bottom" logo="Unleash">
|
||||
<FooterLinkList>
|
||||
<a
|
||||
href="https://github.com/Unleash/unleash/"
|
||||
target="_blank"
|
||||
>
|
||||
<a href="https://github.com/Unleash/unleash/" target="_blank">
|
||||
GitHub
|
||||
</a>
|
||||
<a
|
||||
href="https://finn.no"
|
||||
target="_blank"
|
||||
>
|
||||
<a href="https://finn.no" target="_blank">
|
||||
<small>A product by</small> FINN.no
|
||||
</a>
|
||||
</FooterLinkList>
|
||||
|
@ -6,9 +6,7 @@ import renderer from 'react-test-renderer';
|
||||
jest.mock('react-mdl');
|
||||
|
||||
test('renders correctly if no application', () => {
|
||||
const tree = renderer
|
||||
.create(<ClientApplications fetchApplication={jest.fn()} />)
|
||||
.toJSON();
|
||||
const tree = renderer.create(<ClientApplications fetchApplication={jest.fn()} />).toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -61,16 +61,7 @@ class ClientApplications extends PureComponent {
|
||||
return <ProgressBar indeterminate />;
|
||||
}
|
||||
const { application, storeApplicationMetaData } = this.props;
|
||||
const {
|
||||
appName,
|
||||
instances,
|
||||
strategies,
|
||||
seenToggles,
|
||||
url,
|
||||
description,
|
||||
icon = 'apps',
|
||||
color,
|
||||
} = application;
|
||||
const { appName, instances, strategies, seenToggles, url, description, icon = 'apps', color } = application;
|
||||
|
||||
const content =
|
||||
this.state.activeTab === 0 ? (
|
||||
@ -83,17 +74,8 @@ class ClientApplications extends PureComponent {
|
||||
({ name, description, enabled, notFound }, i) =>
|
||||
notFound ? (
|
||||
<ListItem twoLine key={i}>
|
||||
<ListItemContent
|
||||
icon={'report'}
|
||||
subtitle={
|
||||
'Missing, want to create?'
|
||||
}
|
||||
>
|
||||
<Link
|
||||
to={`/features/create?name=${name}`}
|
||||
>
|
||||
{name}
|
||||
</Link>
|
||||
<ListItemContent icon={'report'} subtitle={'Missing, want to create?'}>
|
||||
<Link to={`/features/create?name=${name}`}>{name}</Link>
|
||||
</ListItemContent>
|
||||
</ListItem>
|
||||
) : (
|
||||
@ -101,22 +83,12 @@ class ClientApplications extends PureComponent {
|
||||
<ListItemContent
|
||||
icon={
|
||||
<span>
|
||||
<Switch
|
||||
disabled
|
||||
checked={!!enabled}
|
||||
/>
|
||||
<Switch disabled checked={!!enabled} />
|
||||
</span>
|
||||
}
|
||||
subtitle={shorten(
|
||||
description,
|
||||
60
|
||||
)}
|
||||
subtitle={shorten(description, 60)}
|
||||
>
|
||||
<Link
|
||||
to={`/features/view/${name}`}
|
||||
>
|
||||
{shorten(name, 50)}
|
||||
</Link>
|
||||
<Link to={`/features/view/${name}`}>{shorten(name, 50)}</Link>
|
||||
</ListItemContent>
|
||||
</ListItem>
|
||||
)
|
||||
@ -131,33 +103,14 @@ class ClientApplications extends PureComponent {
|
||||
({ name, description, notFound }, i) =>
|
||||
notFound ? (
|
||||
<ListItem twoLine key={`${name}-${i}`}>
|
||||
<ListItemContent
|
||||
icon={'report'}
|
||||
subtitle={
|
||||
'Missing, want to create?'
|
||||
}
|
||||
>
|
||||
<Link
|
||||
to={`/strategies/create?name=${name}`}
|
||||
>
|
||||
{name}
|
||||
</Link>
|
||||
<ListItemContent icon={'report'} subtitle={'Missing, want to create?'}>
|
||||
<Link to={`/strategies/create?name=${name}`}>{name}</Link>
|
||||
</ListItemContent>
|
||||
</ListItem>
|
||||
) : (
|
||||
<ListItem twoLine key={`${name}-${i}`}>
|
||||
<ListItemContent
|
||||
icon={'extension'}
|
||||
subtitle={shorten(
|
||||
description,
|
||||
60
|
||||
)}
|
||||
>
|
||||
<Link
|
||||
to={`/strategies/view/${name}`}
|
||||
>
|
||||
{shorten(name, 50)}
|
||||
</Link>
|
||||
<ListItemContent icon={'extension'} subtitle={shorten(description, 60)}>
|
||||
<Link to={`/strategies/view/${name}`}>{shorten(name, 50)}</Link>
|
||||
</ListItemContent>
|
||||
</ListItem>
|
||||
)
|
||||
@ -168,32 +121,20 @@ class ClientApplications extends PureComponent {
|
||||
<h6>{instances.length} Instances registered</h6>
|
||||
<hr />
|
||||
<List>
|
||||
{instances.map(
|
||||
(
|
||||
{
|
||||
instanceId,
|
||||
clientIp,
|
||||
lastSeen,
|
||||
sdkVersion,
|
||||
},
|
||||
i
|
||||
) => (
|
||||
<ListItem key={i} twoLine>
|
||||
<ListItemContent
|
||||
icon="timeline"
|
||||
subtitle={
|
||||
<span>
|
||||
{clientIp} last seen at{' '}
|
||||
<small>{formatFullDateTime(lastSeen)}</small>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
{instanceId}{' '}
|
||||
{sdkVersion ? `(${sdkVersion})` : ''}
|
||||
</ListItemContent>
|
||||
</ListItem>
|
||||
)
|
||||
)}
|
||||
{instances.map(({ instanceId, clientIp, lastSeen, sdkVersion }, i) => (
|
||||
<ListItem key={i} twoLine>
|
||||
<ListItemContent
|
||||
icon="timeline"
|
||||
subtitle={
|
||||
<span>
|
||||
{clientIp} last seen at <small>{formatFullDateTime(lastSeen)}</small>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
{instanceId} {sdkVersion ? `(${sdkVersion})` : ''}
|
||||
</ListItemContent>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Cell>
|
||||
</Grid>
|
||||
@ -206,46 +147,26 @@ class ClientApplications extends PureComponent {
|
||||
<StatefulTextfield
|
||||
value={url}
|
||||
label="URL"
|
||||
onBlur={e =>
|
||||
storeApplicationMetaData(
|
||||
appName,
|
||||
'url',
|
||||
e.target.value
|
||||
)}
|
||||
onBlur={e => storeApplicationMetaData(appName, 'url', e.target.value)}
|
||||
/>
|
||||
<br />
|
||||
<StatefulTextfield
|
||||
value={description}
|
||||
label="Description"
|
||||
rows={5}
|
||||
onBlur={e =>
|
||||
storeApplicationMetaData(
|
||||
appName,
|
||||
'description',
|
||||
e.target.value
|
||||
)}
|
||||
onBlur={e => storeApplicationMetaData(appName, 'description', e.target.value)}
|
||||
/>
|
||||
</Cell>
|
||||
<Cell col={6} tablet={12}>
|
||||
<StatefulTextfield
|
||||
value={icon}
|
||||
label="Select icon"
|
||||
onBlur={e =>
|
||||
storeApplicationMetaData(
|
||||
appName,
|
||||
'icon',
|
||||
e.target.value
|
||||
)}
|
||||
onBlur={e => storeApplicationMetaData(appName, 'icon', e.target.value)}
|
||||
/>
|
||||
<StatefulTextfield
|
||||
value={color}
|
||||
label="Select color"
|
||||
onBlur={e =>
|
||||
storeApplicationMetaData(
|
||||
appName,
|
||||
'color',
|
||||
e.target.value
|
||||
)}
|
||||
onBlur={e => storeApplicationMetaData(appName, 'color', e.target.value)}
|
||||
/>
|
||||
</Cell>
|
||||
</Grid>
|
||||
@ -253,13 +174,7 @@ class ClientApplications extends PureComponent {
|
||||
|
||||
return (
|
||||
<Card shadow={0} className={commonStyles.fullwidth}>
|
||||
<CardTitle
|
||||
style={{
|
||||
paddingTop: '24px',
|
||||
paddingRight: '64px',
|
||||
wordBreak: 'break-all',
|
||||
}}
|
||||
>
|
||||
<CardTitle style={{ paddingTop: '24px', paddingRight: '64px', wordBreak: 'break-all' }}>
|
||||
<Icon name={icon} /> {appName}
|
||||
</CardTitle>
|
||||
{description && <CardText>{description}</CardText>}
|
||||
|
@ -1,9 +1,6 @@
|
||||
import { connect } from 'react-redux';
|
||||
import ApplicationEdit from './application-edit-component';
|
||||
import {
|
||||
fetchApplication,
|
||||
storeApplicationMetaData,
|
||||
} from '../../store/application/actions';
|
||||
import { fetchApplication, storeApplicationMetaData } from '../../store/application/actions';
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
let application = state.applications.getIn(['apps', props.appName]);
|
||||
|
@ -2,9 +2,7 @@ import { connect } from 'react-redux';
|
||||
import ApplicationList from './application-list-component';
|
||||
import { fetchAll } from '../../store/application/actions';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
applications: state.applications.get('list').toJS(),
|
||||
});
|
||||
const mapStateToProps = state => ({ applications: state.applications.get('list').toJS() });
|
||||
|
||||
const Container = connect(mapStateToProps, { fetchAll })(ApplicationList);
|
||||
|
||||
|
@ -102,8 +102,7 @@ exports[`renders correctly with no archived toggles 1`] = `
|
||||
}
|
||||
/>
|
||||
<br />
|
||||
No archived feature toggles, go see
|
||||
|
||||
No archived feature toggles, go see
|
||||
<a
|
||||
onClick={[Function]}
|
||||
style={Object {}}
|
||||
|
@ -16,8 +16,7 @@ const archive = [
|
||||
},
|
||||
{
|
||||
name: 'adin-pay-platform-sch-payment',
|
||||
description:
|
||||
'Enables use of schibsted payment from order-payment-management',
|
||||
description: 'Enables use of schibsted payment from order-payment-management',
|
||||
enabled: true,
|
||||
strategies: [{ name: 'default', parameters: {} }],
|
||||
createdAt: '2016-08-03T12:41:35.631Z',
|
||||
@ -26,17 +25,13 @@ const archive = [
|
||||
];
|
||||
|
||||
test('renders correctly with no archived toggles', () => {
|
||||
const tree = renderer
|
||||
.create(<ArchiveList fetchArchive={jest.fn()} archive={[]} />)
|
||||
.toJSON();
|
||||
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();
|
||||
const tree = renderer.create(<ArchiveList fetchArchive={jest.fn()} archive={archive} />).toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -10,8 +10,6 @@ const mapStateToProps = state => {
|
||||
};
|
||||
};
|
||||
|
||||
const ArchiveListContainer = connect(mapStateToProps, { fetchArchive, revive })(
|
||||
ListComponent
|
||||
);
|
||||
const ArchiveListContainer = connect(mapStateToProps, { fetchArchive, revive })(ListComponent);
|
||||
|
||||
export default ArchiveListContainer;
|
||||
|
@ -17,20 +17,12 @@ class ArchiveList extends Component {
|
||||
<Card shadow={0} className={commonStyles.fullwidth}>
|
||||
{archive.length > 0 ? (
|
||||
<div className={commonStyles.horisontalScroll}>
|
||||
<DataTable
|
||||
rows={archive}
|
||||
className={commonStyles.fullwidth}
|
||||
style={{ border: 0 }}
|
||||
>
|
||||
<DataTable rows={archive} className={commonStyles.fullwidth} style={{ border: 0 }}>
|
||||
<TableHeader
|
||||
style={{ width: '25px' }}
|
||||
name="reviveName"
|
||||
cellFormatter={reviveName => (
|
||||
<IconButton
|
||||
colored
|
||||
name="undo"
|
||||
onClick={() => revive(reviveName)}
|
||||
/>
|
||||
<IconButton colored name="undo" onClick={() => revive(reviveName)} />
|
||||
)}
|
||||
>
|
||||
Revive
|
||||
@ -50,14 +42,9 @@ class ArchiveList extends Component {
|
||||
</div>
|
||||
) : (
|
||||
<div className={commonStyles.emptyState}>
|
||||
<Icon
|
||||
name="archive"
|
||||
className="mdl-color-text--grey-300"
|
||||
style={{ fontSize: '56px' }}
|
||||
/>
|
||||
<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>
|
||||
No archived feature toggles, go see <Link to="/features">active toggles here</Link>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
@ -6,14 +6,7 @@ import renderer from 'react-test-renderer';
|
||||
jest.mock('react-mdl');
|
||||
|
||||
test('renders correctly with no clientInstances', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<ClientStrategies
|
||||
fetchClientInstances={jest.fn()}
|
||||
clientInstances={[]}
|
||||
/>
|
||||
)
|
||||
.toJSON();
|
||||
const tree = renderer.create(<ClientStrategies fetchClientInstances={jest.fn()} clientInstances={[]} />).toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -16,11 +16,7 @@ class ClientStrategies extends Component {
|
||||
const source = this.props.clientInstances;
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
style={{ width: '100%' }}
|
||||
rows={source}
|
||||
selectable={false}
|
||||
>
|
||||
<DataTable style={{ width: '100%' }} rows={source} selectable={false}>
|
||||
<TableHeader name="instanceId">Instance ID</TableHeader>
|
||||
<TableHeader name="appName">Application name</TableHeader>
|
||||
<TableHeader name="clientIp">IP</TableHeader>
|
||||
|
@ -2,12 +2,8 @@ import { connect } from 'react-redux';
|
||||
import ClientInstances from './client-instance-component';
|
||||
import { fetchClientInstances } from '../../store/client-instance-actions';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
clientInstances: state.clientInstances.toJS(),
|
||||
});
|
||||
const mapStateToProps = state => ({ clientInstances: state.clientInstances.toJS() });
|
||||
|
||||
const StrategiesContainer = connect(mapStateToProps, { fetchClientInstances })(
|
||||
ClientInstances
|
||||
);
|
||||
const StrategiesContainer = connect(mapStateToProps, { fetchClientInstances })(ClientInstances);
|
||||
|
||||
export default StrategiesContainer;
|
||||
|
@ -1,44 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import {
|
||||
List,
|
||||
ListItem,
|
||||
ListItemContent,
|
||||
Button,
|
||||
Icon,
|
||||
Switch,
|
||||
MenuItem,
|
||||
} from 'react-mdl';
|
||||
import { List, ListItem, ListItemContent, Button, Icon, Switch, MenuItem } from 'react-mdl';
|
||||
import styles from './common.scss';
|
||||
|
||||
export { styles };
|
||||
|
||||
export const shorten = (str, len = 50) =>
|
||||
str && str.length > len ? `${str.substring(0, len)}...` : str;
|
||||
export const shorten = (str, len = 50) => (str && str.length > len ? `${str.substring(0, len)}...` : str);
|
||||
|
||||
export const AppsLinkList = ({ apps }) => (
|
||||
<List>
|
||||
{apps.length > 0 &&
|
||||
apps.map(({ appName, description = '-', icon = 'apps' }) => (
|
||||
<ListItem twoLine key={appName}>
|
||||
<span
|
||||
className="mdl-list__item-primary-content"
|
||||
style={{ minWidth: 0 }}
|
||||
>
|
||||
<span className="mdl-list__item-primary-content" style={{ minWidth: 0 }}>
|
||||
<Icon name={icon} className="mdl-list__item-avatar" />
|
||||
<Link
|
||||
to={`/applications/${appName}`}
|
||||
className={[styles.listLink, styles.truncate].join(
|
||||
' '
|
||||
)}
|
||||
>
|
||||
<Link to={`/applications/${appName}`} className={[styles.listLink, styles.truncate].join(' ')}>
|
||||
{appName}
|
||||
<span
|
||||
className={[
|
||||
'mdl-list__item-sub-title',
|
||||
styles.truncate,
|
||||
].join(' ')}
|
||||
>
|
||||
<span className={['mdl-list__item-sub-title', styles.truncate].join(' ')}>
|
||||
{description}
|
||||
</span>
|
||||
</Link>
|
||||
@ -62,9 +40,7 @@ export const HeaderTitle = ({ title, actions, subtitle }) => (
|
||||
{subtitle && <small>{subtitle}</small>}
|
||||
</div>
|
||||
|
||||
{actions && (
|
||||
<div style={{ flex: '1', textAlign: 'right' }}>{actions}</div>
|
||||
)}
|
||||
{actions && <div style={{ flex: '1', textAlign: 'right' }}>{actions}</div>}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -84,24 +60,13 @@ export const FormButtons = ({ submitText = 'Create', onCancel }) => (
|
||||
{submitText}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="cancel"
|
||||
ripple
|
||||
raised
|
||||
onClick={onCancel}
|
||||
style={{ float: 'right' }}
|
||||
>
|
||||
<Button type="cancel" ripple raised onClick={onCancel} style={{ float: 'right' }}>
|
||||
<Icon name="cancel" /> Cancel
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const SwitchWithLabel = ({
|
||||
onChange,
|
||||
checked,
|
||||
children,
|
||||
...switchProps
|
||||
}) => (
|
||||
export const SwitchWithLabel = ({ onChange, checked, children, ...switchProps }) => (
|
||||
<span className={styles.switchWithLabel}>
|
||||
<span className={styles.label}>{children}</span>
|
||||
<span className={styles.switch}>
|
||||
@ -141,12 +106,7 @@ export function getIcon(type) {
|
||||
}
|
||||
|
||||
export const IconLink = ({ url, icon }) => (
|
||||
<a
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
className="mdl-color-text--grey-600"
|
||||
>
|
||||
<a href={url} target="_blank" rel="noopener" className="mdl-color-text--grey-600">
|
||||
<Icon name={icon} />
|
||||
</a>
|
||||
);
|
||||
@ -158,17 +118,8 @@ export const DropdownButton = ({ label, id }) => (
|
||||
</Button>
|
||||
);
|
||||
|
||||
export const MenuItemWithIcon = ({
|
||||
icon,
|
||||
label,
|
||||
disabled,
|
||||
...menuItemProps
|
||||
}) => (
|
||||
<MenuItem
|
||||
disabled={disabled}
|
||||
style={{ display: 'flex', alignItems: 'center' }}
|
||||
{...menuItemProps}
|
||||
>
|
||||
export const MenuItemWithIcon = ({ icon, label, disabled, ...menuItemProps }) => (
|
||||
<MenuItem disabled={disabled} style={{ display: 'flex', alignItems: 'center' }} {...menuItemProps}>
|
||||
<Icon name={icon} style={{ paddingRight: '16px' }} />
|
||||
{label}
|
||||
</MenuItem>
|
||||
@ -176,11 +127,7 @@ export const MenuItemWithIcon = ({
|
||||
|
||||
const badNumbers = [NaN, Infinity, -Infinity];
|
||||
export function calc(value, total, decimal) {
|
||||
if (
|
||||
typeof value !== 'number' ||
|
||||
typeof total !== 'number' ||
|
||||
typeof decimal !== 'number'
|
||||
) {
|
||||
if (typeof value !== 'number' || typeof total !== 'number' || typeof decimal !== 'number') {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -7,5 +7,4 @@ const dateTimeOptions = {
|
||||
second: '2-digit',
|
||||
};
|
||||
|
||||
export const formatFullDateTime = v =>
|
||||
new Date(v).toLocaleString('nb-NO', dateTimeOptions);
|
||||
export const formatFullDateTime = v => new Date(v).toLocaleString('nb-NO', dateTimeOptions);
|
||||
|
@ -8,13 +8,7 @@ const ErrorComponent = ({ errors, ...props }) => {
|
||||
const error = showError ? errors[0] : undefined;
|
||||
const muteError = () => props.muteError(error);
|
||||
return (
|
||||
<Snackbar
|
||||
action="Dismiss"
|
||||
active={showError}
|
||||
onActionClick={muteError}
|
||||
onTimeout={muteError}
|
||||
timeout={10000}
|
||||
>
|
||||
<Snackbar action="Dismiss" active={showError} onActionClick={muteError} onTimeout={muteError} timeout={10000}>
|
||||
<Icon name="question_answer" /> {error}
|
||||
</Snackbar>
|
||||
);
|
||||
|
@ -17,23 +17,13 @@ const Feature = ({
|
||||
const { name, description, enabled, strategies } = feature;
|
||||
|
||||
const { showLastHour = false } = settings;
|
||||
const isStale = showLastHour
|
||||
? metricsLastHour.isFallback
|
||||
: metricsLastMinute.isFallback;
|
||||
const isStale = showLastHour ? metricsLastHour.isFallback : metricsLastMinute.isFallback;
|
||||
|
||||
const percent =
|
||||
1 *
|
||||
(showLastHour
|
||||
? calc(
|
||||
metricsLastHour.yes,
|
||||
metricsLastHour.yes + metricsLastHour.no,
|
||||
0
|
||||
)
|
||||
: calc(
|
||||
metricsLastMinute.yes,
|
||||
metricsLastMinute.yes + metricsLastMinute.no,
|
||||
0
|
||||
));
|
||||
? calc(metricsLastHour.yes, metricsLastHour.yes + metricsLastHour.no, 0)
|
||||
: calc(metricsLastMinute.yes, metricsLastMinute.yes + metricsLastMinute.no, 0));
|
||||
|
||||
const strategiesToShow = Math.min(strategies.length, 3);
|
||||
const remainingStrategies = strategies.length - strategiesToShow;
|
||||
@ -45,18 +35,12 @@ const Feature = ({
|
||||
{s.name}
|
||||
</Chip>
|
||||
));
|
||||
const summaryChip = remainingStrategies > 0 && (
|
||||
<Chip className={styles.strategyChip}>+{remainingStrategies}</Chip>
|
||||
);
|
||||
const summaryChip = remainingStrategies > 0 && <Chip className={styles.strategyChip}>+{remainingStrategies}</Chip>;
|
||||
|
||||
return (
|
||||
<ListItem twoLine>
|
||||
<span className={styles.listItemMetric}>
|
||||
<Progress
|
||||
strokeWidth={15}
|
||||
percentage={percent}
|
||||
isFallback={isStale}
|
||||
/>
|
||||
<Progress strokeWidth={15} percentage={percent} isFallback={isStale} />
|
||||
</span>
|
||||
<span className={styles.listItemToggle}>
|
||||
<Switch
|
||||
@ -66,36 +50,16 @@ const Feature = ({
|
||||
checked={enabled}
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className={[
|
||||
'mdl-list__item-primary-content',
|
||||
styles.listItemLink,
|
||||
].join(' ')}
|
||||
>
|
||||
<span className={['mdl-list__item-primary-content', styles.listItemLink].join(' ')}>
|
||||
<Link
|
||||
to={`/features/view/${name}`}
|
||||
className={[
|
||||
commonStyles.listLink,
|
||||
commonStyles.truncate,
|
||||
].join(' ')}
|
||||
className={[commonStyles.listLink, commonStyles.truncate].join(' ')}
|
||||
>
|
||||
{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>
|
||||
</span>
|
||||
<span
|
||||
className={[
|
||||
styles.listItemStrategies,
|
||||
commonStyles.hideLt920,
|
||||
].join(' ')}
|
||||
>
|
||||
<span className={[styles.listItemStrategies, commonStyles.hideLt920].join(' ')}>
|
||||
{strategyChips}
|
||||
{summaryChip}
|
||||
</span>
|
||||
|
@ -1,9 +1,6 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { hashHistory } from 'react-router';
|
||||
import {
|
||||
createFeatureToggles,
|
||||
validateName,
|
||||
} from '../../store/feature-actions';
|
||||
import { createFeatureToggles, validateName } from '../../store/feature-actions';
|
||||
import { createMapper, createActions } from '../input-helpers';
|
||||
import FormAddComponent from './form-add-component';
|
||||
|
||||
|
@ -92,10 +92,7 @@ class AddFeatureToggleComponent extends Component {
|
||||
/>
|
||||
|
||||
<br />
|
||||
<FormButtons
|
||||
submitText={editmode ? 'Update' : 'Create'}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
<FormButtons submitText={editmode ? 'Update' : 'Create'} onCancel={onCancel} />
|
||||
</section>
|
||||
</form>
|
||||
);
|
||||
|
@ -10,9 +10,7 @@ class AddStrategy extends React.Component {
|
||||
};
|
||||
|
||||
addStrategy = strategyName => {
|
||||
const selectedStrategy = this.props.strategies.find(
|
||||
s => s.name === strategyName
|
||||
);
|
||||
const selectedStrategy = this.props.strategies.find(s => s.name === strategyName);
|
||||
const parameters = {};
|
||||
|
||||
selectedStrategy.parameters.forEach(({ name }) => {
|
||||
@ -37,14 +35,7 @@ class AddStrategy extends React.Component {
|
||||
backgroundColor: 'rgb(247, 248, 255)',
|
||||
};
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
width: '25px',
|
||||
height: '25px',
|
||||
display: 'inline-block',
|
||||
}}
|
||||
>
|
||||
<div style={{ position: 'relative', width: '25px', height: '25px', display: 'inline-block' }}>
|
||||
<IconButton
|
||||
name="add"
|
||||
id="strategies-add"
|
||||
@ -53,20 +44,10 @@ class AddStrategy extends React.Component {
|
||||
title="Add Strategy"
|
||||
onClick={this.stopPropagation}
|
||||
/>
|
||||
<Menu
|
||||
target="strategies-add"
|
||||
valign="bottom"
|
||||
align="right"
|
||||
ripple
|
||||
style={menuStyle}
|
||||
>
|
||||
<Menu target="strategies-add" valign="bottom" align="right" ripple style={menuStyle}>
|
||||
<MenuItem disabled>Add Strategy:</MenuItem>
|
||||
{this.props.strategies.map(s => (
|
||||
<MenuItem
|
||||
key={s.name}
|
||||
title={s.description}
|
||||
onClick={() => this.addStrategy(s.name)}
|
||||
>
|
||||
<MenuItem key={s.name} title={s.description} onClick={() => this.addStrategy(s.name)}>
|
||||
{s.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
|
@ -15,13 +15,7 @@ class StrategiesList extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
strategies,
|
||||
configuredStrategies,
|
||||
moveStrategy,
|
||||
removeStrategy,
|
||||
updateStrategy,
|
||||
} = this.props;
|
||||
const { strategies, configuredStrategies, moveStrategy, removeStrategy, updateStrategy } = this.props;
|
||||
|
||||
if (!configuredStrategies || configuredStrategies.length === 0) {
|
||||
return <i style={{ color: 'red' }}>No strategies added</i>;
|
||||
@ -35,14 +29,10 @@ class StrategiesList extends React.Component {
|
||||
moveStrategy={moveStrategy}
|
||||
removeStrategy={removeStrategy.bind(null, i)}
|
||||
updateStrategy={updateStrategy.bind(null, i)}
|
||||
strategyDefinition={strategies.find(
|
||||
s => s.name === strategy.name
|
||||
)}
|
||||
strategyDefinition={strategies.find(s => s.name === strategy.name)}
|
||||
/>
|
||||
));
|
||||
return (
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap' }}>{blocks}</div>
|
||||
);
|
||||
return <div style={{ display: 'flex', flexWrap: 'wrap' }}>{blocks}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,10 +25,7 @@ class StrategiesSection extends React.Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<HeaderTitle
|
||||
title="Activation strategies"
|
||||
actions={<AddStrategy {...this.props} />}
|
||||
/>
|
||||
<HeaderTitle title="Activation strategies" actions={<AddStrategy {...this.props} />} />
|
||||
<StrategiesList {...this.props} />
|
||||
</div>
|
||||
);
|
||||
|
@ -1,16 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Textfield,
|
||||
Button,
|
||||
Card,
|
||||
CardTitle,
|
||||
CardText,
|
||||
CardActions,
|
||||
CardMenu,
|
||||
IconButton,
|
||||
Icon,
|
||||
} from 'react-mdl';
|
||||
import { Textfield, Button, Card, CardTitle, CardText, CardActions, CardMenu, IconButton, Icon } from 'react-mdl';
|
||||
import { DragSource, DropTarget } from 'react-dnd';
|
||||
import { Link } from 'react-router';
|
||||
import StrategyInputPercentage from './strategy-input-percentage';
|
||||
@ -91,25 +81,17 @@ class StrategyConfigure extends React.Component {
|
||||
return parameters.map(({ name, type, description, required }) => {
|
||||
let value = this.props.strategy.parameters[name];
|
||||
if (type === 'percentage') {
|
||||
if (
|
||||
value == null ||
|
||||
(typeof value === 'string' && value === '')
|
||||
) {
|
||||
if (value == null || (typeof value === 'string' && value === '')) {
|
||||
this.setConfig(name, 50);
|
||||
}
|
||||
return (
|
||||
<div key={name}>
|
||||
<StrategyInputPercentage
|
||||
name={name}
|
||||
onChange={this.handleConfigChange.bind(
|
||||
this,
|
||||
name
|
||||
)}
|
||||
onChange={this.handleConfigChange.bind(this, name)}
|
||||
value={1 * value}
|
||||
/>
|
||||
{description && (
|
||||
<p className={styles.helpText}>{description}</p>
|
||||
)}
|
||||
{description && <p className={styles.helpText}>{description}</p>}
|
||||
</div>
|
||||
);
|
||||
} else if (type === 'list') {
|
||||
@ -122,14 +104,8 @@ class StrategyConfigure extends React.Component {
|
||||
}
|
||||
return (
|
||||
<div key={name}>
|
||||
<StrategyInputList
|
||||
name={name}
|
||||
list={list}
|
||||
setConfig={this.setConfig}
|
||||
/>
|
||||
{description && (
|
||||
<p className={styles.helpText}>{description}</p>
|
||||
)}
|
||||
<StrategyInputList name={name} list={list} setConfig={this.setConfig} />
|
||||
{description && <p className={styles.helpText}>{description}</p>}
|
||||
</div>
|
||||
);
|
||||
} else if (type === 'number') {
|
||||
@ -143,15 +119,10 @@ class StrategyConfigure extends React.Component {
|
||||
style={{ width: '100%' }}
|
||||
name={name}
|
||||
label={name}
|
||||
onChange={this.handleConfigChange.bind(
|
||||
this,
|
||||
name
|
||||
)}
|
||||
onChange={this.handleConfigChange.bind(this, name)}
|
||||
value={value}
|
||||
/>
|
||||
{description && (
|
||||
<p className={styles.helpText}>{description}</p>
|
||||
)}
|
||||
{description && <p className={styles.helpText}>{description}</p>}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
@ -164,15 +135,10 @@ class StrategyConfigure extends React.Component {
|
||||
required={required}
|
||||
name={name}
|
||||
label={name}
|
||||
onChange={this.handleConfigChange.bind(
|
||||
this,
|
||||
name
|
||||
)}
|
||||
onChange={this.handleConfigChange.bind(this, name)}
|
||||
value={value}
|
||||
/>
|
||||
{description && (
|
||||
<p className={styles.helpText}>{description}</p>
|
||||
)}
|
||||
{description && <p className={styles.helpText}>{description}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -182,31 +148,18 @@ class StrategyConfigure extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
isDragging,
|
||||
connectDragPreview,
|
||||
connectDragSource,
|
||||
connectDropTarget,
|
||||
} = this.props;
|
||||
const { isDragging, connectDragPreview, connectDragSource, connectDropTarget } = this.props;
|
||||
|
||||
let item;
|
||||
if (this.props.strategyDefinition) {
|
||||
const inputFields = this.renderInputFields(
|
||||
this.props.strategyDefinition
|
||||
);
|
||||
const inputFields = this.renderInputFields(this.props.strategyDefinition);
|
||||
const { name } = this.props.strategy;
|
||||
item = (
|
||||
<Card
|
||||
shadow={0}
|
||||
className={styles.card}
|
||||
style={{ opacity: isDragging ? '0.1' : '1' }}
|
||||
>
|
||||
<Card shadow={0} className={styles.card} style={{ opacity: isDragging ? '0.1' : '1' }}>
|
||||
<CardTitle className={styles.cardTitle}>
|
||||
<Icon name="extension" /> {name}
|
||||
</CardTitle>
|
||||
<CardText>
|
||||
{this.props.strategyDefinition.description}
|
||||
</CardText>
|
||||
<CardText>{this.props.strategyDefinition.description}</CardText>
|
||||
{inputFields && (
|
||||
<CardActions border style={{ padding: '20px' }}>
|
||||
{inputFields}
|
||||
@ -214,18 +167,10 @@ class StrategyConfigure extends React.Component {
|
||||
)}
|
||||
|
||||
<CardMenu className="mdl-color-text--white">
|
||||
<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" />
|
||||
</Link>
|
||||
<IconButton
|
||||
title="Remove strategy from toggle"
|
||||
name="delete"
|
||||
onClick={this.handleRemove}
|
||||
/>
|
||||
<IconButton title="Remove strategy from toggle" name="delete" onClick={this.handleRemove} />
|
||||
{connectDragSource(
|
||||
<span className={styles.reorderIcon}>
|
||||
<Icon name="reorder" />
|
||||
@ -241,17 +186,10 @@ class StrategyConfigure extends React.Component {
|
||||
<CardTitle>"{name}" deleted?</CardTitle>
|
||||
<CardText>
|
||||
The strategy "{name}" does not exist on this server.
|
||||
<Link to={`/strategies/create?name=${name}`}>
|
||||
Want to create it now?
|
||||
</Link>
|
||||
<Link to={`/strategies/create?name=${name}`}>Want to create it now?</Link>
|
||||
</CardText>
|
||||
<CardActions>
|
||||
<Button
|
||||
onClick={this.handleRemove}
|
||||
label="remove strategy"
|
||||
accent
|
||||
raised
|
||||
>
|
||||
<Button onClick={this.handleRemove} label="remove strategy" accent raised>
|
||||
Remove
|
||||
</Button>
|
||||
</CardActions>
|
||||
@ -259,9 +197,7 @@ class StrategyConfigure extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
return connectDropTarget(
|
||||
connectDragPreview(<div className={styles.item}>{item}</div>)
|
||||
);
|
||||
return connectDropTarget(connectDragPreview(<div className={styles.item}>{item}</div>));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,10 +46,7 @@ export default class InputList extends Component {
|
||||
onClose(index) {
|
||||
const { name, list, setConfig } = this.props;
|
||||
list[index] = null;
|
||||
setConfig(
|
||||
name,
|
||||
list.length === 1 ? '' : list.filter(Boolean).join(',')
|
||||
);
|
||||
setConfig(name, list.length === 1 ? '' : list.filter(Boolean).join(','));
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -58,11 +55,7 @@ export default class InputList extends Component {
|
||||
<div>
|
||||
<p>{name}</p>
|
||||
{list.map((entryValue, index) => (
|
||||
<Chip
|
||||
key={index + entryValue}
|
||||
style={{ marginRight: '3px' }}
|
||||
onClose={() => this.onClose(index)}
|
||||
>
|
||||
<Chip key={index + entryValue} style={{ marginRight: '3px' }} onClose={() => this.onClose(index)}>
|
||||
{entryValue}
|
||||
</Chip>
|
||||
))}
|
||||
@ -79,11 +72,7 @@ export default class InputList extends Component {
|
||||
<IconButton
|
||||
name="add"
|
||||
raised
|
||||
style={{
|
||||
flex: 1,
|
||||
flexGrow: 0,
|
||||
margin: '20px 0 0 10px',
|
||||
}}
|
||||
style={{ flex: 1, flexGrow: 0, margin: '20px 0 0 10px' }}
|
||||
onClick={this.setValue}
|
||||
/>
|
||||
</div>
|
||||
|
@ -13,13 +13,6 @@ export default ({ name, value, onChange }) => (
|
||||
<div style={labelStyle}>
|
||||
{name}: {value}%
|
||||
</div>
|
||||
<Slider
|
||||
min={0}
|
||||
max={100}
|
||||
defaultValue={value}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
label={name}
|
||||
/>
|
||||
<Slider min={0} max={100} defaultValue={value} value={value} onChange={onChange} label={name} />
|
||||
</div>
|
||||
);
|
||||
|
@ -2,22 +2,9 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Feature from './feature-list-item-component';
|
||||
import { Link } from 'react-router';
|
||||
import {
|
||||
Icon,
|
||||
FABButton,
|
||||
Textfield,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Card,
|
||||
CardActions,
|
||||
List,
|
||||
} from 'react-mdl';
|
||||
import { Icon, FABButton, Textfield, Menu, MenuItem, Card, CardActions, List } from 'react-mdl';
|
||||
|
||||
import {
|
||||
MenuItemWithIcon,
|
||||
DropdownButton,
|
||||
styles as commonStyles,
|
||||
} from '../common';
|
||||
import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../common';
|
||||
import styles from './feature.scss';
|
||||
|
||||
export default class FeatureListComponent extends React.PureComponent {
|
||||
@ -47,10 +34,7 @@ export default class FeatureListComponent extends React.PureComponent {
|
||||
}
|
||||
|
||||
toggleMetrics() {
|
||||
this.props.updateSetting(
|
||||
'showLastHour',
|
||||
!this.props.settings.showLastHour
|
||||
);
|
||||
this.props.updateSetting('showLastHour', !this.props.settings.showLastHour);
|
||||
}
|
||||
|
||||
setFilter(v) {
|
||||
@ -62,12 +46,7 @@ export default class FeatureListComponent extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
features,
|
||||
toggleFeature,
|
||||
featureMetrics,
|
||||
settings,
|
||||
} = this.props;
|
||||
const { features, toggleFeature, featureMetrics, settings } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -81,32 +60,16 @@ export default class FeatureListComponent extends React.PureComponent {
|
||||
label="Search"
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
<Link
|
||||
to="/features/create"
|
||||
className={styles.toolbarButton}
|
||||
>
|
||||
<Link to="/features/create" className={styles.toolbarButton}>
|
||||
<FABButton accent title="Create feature toggle">
|
||||
<Icon name="add" />
|
||||
</FABButton>
|
||||
</Link>
|
||||
</div>
|
||||
<Card
|
||||
shadow={0}
|
||||
className={commonStyles.fullwidth}
|
||||
style={{ overflow: 'visible' }}
|
||||
>
|
||||
<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' }}
|
||||
>
|
||||
<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}
|
||||
@ -120,46 +83,25 @@ export default class FeatureListComponent extends React.PureComponent {
|
||||
label="Last hour"
|
||||
/>
|
||||
</Menu>
|
||||
<DropdownButton
|
||||
id="sorting"
|
||||
label={`By ${settings.sort}`}
|
||||
/>
|
||||
<DropdownButton id="sorting" label={`By ${settings.sort}`} />
|
||||
<Menu
|
||||
target="sorting"
|
||||
onClick={e =>
|
||||
this.setSort(
|
||||
e.target.getAttribute('data-target')
|
||||
)}
|
||||
onClick={e => this.setSort(e.target.getAttribute('data-target'))}
|
||||
style={{ width: '168px' }}
|
||||
>
|
||||
<MenuItem
|
||||
disabled={settings.sort === 'name'}
|
||||
data-target="name"
|
||||
>
|
||||
<MenuItem disabled={settings.sort === 'name'} data-target="name">
|
||||
Name
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
disabled={settings.sort === 'enabled'}
|
||||
data-target="enabled"
|
||||
>
|
||||
<MenuItem disabled={settings.sort === 'enabled'} data-target="enabled">
|
||||
Enabled
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
disabled={settings.sort === 'created'}
|
||||
data-target="created"
|
||||
>
|
||||
<MenuItem disabled={settings.sort === 'created'} data-target="created">
|
||||
Created
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
disabled={settings.sort === 'strategies'}
|
||||
data-target="strategies"
|
||||
>
|
||||
<MenuItem disabled={settings.sort === 'strategies'} data-target="strategies">
|
||||
Strategies
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
disabled={settings.sort === 'metrics'}
|
||||
data-target="metrics"
|
||||
>
|
||||
<MenuItem disabled={settings.sort === 'metrics'} data-target="metrics">
|
||||
Metrics
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
@ -170,12 +112,8 @@ export default class FeatureListComponent extends React.PureComponent {
|
||||
<Feature
|
||||
key={i}
|
||||
settings={settings}
|
||||
metricsLastHour={
|
||||
featureMetrics.lastHour[feature.name]
|
||||
}
|
||||
metricsLastMinute={
|
||||
featureMetrics.lastMinute[feature.name]
|
||||
}
|
||||
metricsLastHour={featureMetrics.lastHour[feature.name]}
|
||||
metricsLastMinute={featureMetrics.lastMinute[feature.name]}
|
||||
feature={feature}
|
||||
toggleFeature={toggleFeature}
|
||||
/>
|
||||
|
@ -1,8 +1,5 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
toggleFeature,
|
||||
fetchFeatureToggles,
|
||||
} from '../../store/feature-actions';
|
||||
import { toggleFeature, fetchFeatureToggles } from '../../store/feature-actions';
|
||||
import { fetchFeatureMetrics } from '../../store/feature-metrics-actions';
|
||||
import { updateSettingForGroup } from '../../store/settings/actions';
|
||||
|
||||
@ -33,9 +30,7 @@ const mapStateToProps = state => {
|
||||
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)
|
||||
);
|
||||
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) {
|
||||
@ -47,13 +42,9 @@ const mapStateToProps = state => {
|
||||
return 0;
|
||||
});
|
||||
} else if (settings.sort === 'strategies') {
|
||||
features = features.sort(
|
||||
(a, b) => (a.strategies.length > b.strategies.length ? -1 : 1)
|
||||
);
|
||||
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;
|
||||
const target = settings.showLastHour ? featureMetrics.lastHour : featureMetrics.lastMinute;
|
||||
|
||||
features = features.sort((a, b) => {
|
||||
if (!target[a.name]) {
|
||||
@ -83,8 +74,6 @@ const mapDispatchToProps = {
|
||||
updateSetting: updateSettingForGroup('feature'),
|
||||
};
|
||||
|
||||
const FeatureListContainer = connect(mapStateToProps, mapDispatchToProps)(
|
||||
FeatureListComponent
|
||||
);
|
||||
const FeatureListContainer = connect(mapStateToProps, mapDispatchToProps)(FeatureListComponent);
|
||||
|
||||
export default FeatureListContainer;
|
||||
|
@ -12,10 +12,7 @@ const StrategyChipItem = ({ strategy }) => (
|
||||
<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"
|
||||
>
|
||||
<Link to={`/strategies/view/${strategy.name}`} className="mdl-color-text--blue-grey">
|
||||
{strategy.name}
|
||||
</Link>
|
||||
</Chip>
|
||||
@ -25,9 +22,7 @@ const StrategyChipItem = ({ strategy }) => (
|
||||
const StrategiesList = ({ strategies }) => (
|
||||
<div style={{ verticalAlign: 'middle', paddingTop: '14px' }}>
|
||||
With {strategies.length > 1 ? 'strategies' : 'strategy'}{' '}
|
||||
{strategies.map((strategy, i) => (
|
||||
<StrategyChipItem key={i} strategy={strategy} />
|
||||
))}
|
||||
{strategies.map((strategy, i) => <StrategyChipItem key={i} strategy={strategy} />)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -59,10 +54,8 @@ export default class MetricComponent extends React.Component {
|
||||
seenApps = [],
|
||||
} = metrics;
|
||||
|
||||
const lastHourPercent =
|
||||
1 * calc(lastHour.yes, lastHour.yes + lastHour.no, 0);
|
||||
const lastMinutePercent =
|
||||
1 * calc(lastMinute.yes, lastMinute.yes + lastMinute.no, 0);
|
||||
const lastHourPercent = 1 * calc(lastHour.yes, lastHour.yes + lastHour.no, 0);
|
||||
const lastMinutePercent = 1 * calc(lastMinute.yes, lastMinute.yes + lastMinute.no, 0);
|
||||
|
||||
return (
|
||||
<div style={{ padding: '16px' }}>
|
||||
@ -75,9 +68,7 @@ export default class MetricComponent extends React.Component {
|
||||
animatePercentageText
|
||||
/>
|
||||
{lastMinute.isFallback ? (
|
||||
<p className="mdl-color-text--grey-500">
|
||||
No metrics available
|
||||
</p>
|
||||
<p className="mdl-color-text--grey-500">No metrics available</p>
|
||||
) : (
|
||||
<p>
|
||||
<strong>Last minute</strong>
|
||||
@ -86,14 +77,9 @@ export default class MetricComponent extends React.Component {
|
||||
)}
|
||||
</Cell>
|
||||
<Cell col={4} tablet={4} phone={12}>
|
||||
<Progress
|
||||
percentage={lastHourPercent}
|
||||
isFallback={lastHour.isFallback}
|
||||
/>
|
||||
<Progress percentage={lastHourPercent} isFallback={lastHour.isFallback} />
|
||||
{lastHour.isFallback ? (
|
||||
<p className="mdl-color-text--grey-500">
|
||||
No metrics available
|
||||
</p>
|
||||
<p className="mdl-color-text--grey-500">No metrics available</p>
|
||||
) : (
|
||||
<p>
|
||||
<strong>Last hour</strong>
|
||||
@ -115,20 +101,14 @@ export default class MetricComponent extends React.Component {
|
||||
/>
|
||||
<div>
|
||||
<small>
|
||||
<strong>
|
||||
Not used in an app in the last hour.
|
||||
</strong>
|
||||
This might be due to your client
|
||||
implementation is not reporting usage.
|
||||
<strong>Not used in an app in the last hour.</strong>
|
||||
This might be due to your client implementation is not reporting usage.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<AppsLinkList apps={seenApps} />
|
||||
<span>
|
||||
Created{' '}
|
||||
{formatFullDateTime(featureToggle.createdAt)}
|
||||
</span>
|
||||
<span>Created {formatFullDateTime(featureToggle.createdAt)}</span>
|
||||
</Cell>
|
||||
</Grid>
|
||||
<hr />
|
||||
|
@ -1,9 +1,6 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import {
|
||||
fetchFeatureMetrics,
|
||||
fetchSeenApps,
|
||||
} from '../../store/feature-metrics-actions';
|
||||
import { fetchFeatureMetrics, fetchSeenApps } from '../../store/feature-metrics-actions';
|
||||
|
||||
import MatricComponent from './metric-component';
|
||||
|
||||
@ -18,10 +15,7 @@ function getMetricsForToggle(state, toggleName) {
|
||||
}
|
||||
if (state.featureMetrics.hasIn(['lastHour', toggleName])) {
|
||||
result.lastHour = state.featureMetrics.getIn(['lastHour', toggleName]);
|
||||
result.lastMinute = state.featureMetrics.getIn([
|
||||
'lastMinute',
|
||||
toggleName,
|
||||
]);
|
||||
result.lastMinute = state.featureMetrics.getIn(['lastMinute', toggleName]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -41,9 +41,7 @@ class Progress extends Component {
|
||||
const TOTAL_ANIMATION_TIME = 5000;
|
||||
const diff = start > target ? -(start - target) : target - start;
|
||||
const perCycle = TOTAL_ANIMATION_TIME / diff;
|
||||
const cyclesCounter = Math.round(
|
||||
Math.abs(TOTAL_ANIMATION_TIME / perCycle)
|
||||
);
|
||||
const cyclesCounter = Math.round(Math.abs(TOTAL_ANIMATION_TIME / perCycle));
|
||||
const perCycleTime = Math.round(Math.abs(perCycle));
|
||||
|
||||
return {
|
||||
@ -95,9 +93,7 @@ class Progress extends Component {
|
||||
const diameter = Math.PI * 2 * radius;
|
||||
const progressStyle = {
|
||||
strokeDasharray: `${diameter}px ${diameter}px`,
|
||||
strokeDashoffset: `${(100 - this.state.percentage) /
|
||||
100 *
|
||||
diameter}px`,
|
||||
strokeDashoffset: `${(100 - this.state.percentage) / 100 * diameter}px`,
|
||||
};
|
||||
|
||||
return isFallback ? (
|
||||
@ -113,9 +109,7 @@ class Progress extends Component {
|
||||
) : (
|
||||
<svg viewBox="0 0 100 100">
|
||||
<path
|
||||
className={[styles.trail, 'mdl-color-text--grey-300'].join(
|
||||
' '
|
||||
)}
|
||||
className={[styles.trail, 'mdl-color-text--grey-300'].join(' ')}
|
||||
d={pathDescription}
|
||||
strokeWidth={strokeWidth}
|
||||
fillOpacity={0}
|
||||
|
@ -1,16 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Tabs,
|
||||
Tab,
|
||||
ProgressBar,
|
||||
Button,
|
||||
Card,
|
||||
CardTitle,
|
||||
CardText,
|
||||
CardActions,
|
||||
Switch,
|
||||
} from 'react-mdl';
|
||||
import { Tabs, Tab, ProgressBar, Button, Card, CardTitle, CardText, CardActions, Switch } from 'react-mdl';
|
||||
import { hashHistory, Link } from 'react-router';
|
||||
|
||||
import HistoryComponent from '../history/history-list-toggle-container';
|
||||
@ -90,9 +80,7 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
const activeTabId = TABS[this.props.activeTab]
|
||||
? TABS[this.props.activeTab]
|
||||
: TABS.view;
|
||||
const activeTabId = TABS[this.props.activeTab] ? TABS[this.props.activeTab] : TABS.view;
|
||||
const tabContent = this.getTabContent(activeTab);
|
||||
|
||||
const removeToggle = () => {
|
||||
@ -106,16 +94,8 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
shadow={0}
|
||||
className={commonStyles.fullwidth}
|
||||
style={{ overflow: 'visible' }}
|
||||
>
|
||||
<CardTitle
|
||||
style={{ paddingTop: '24px', wordBreak: 'break-all' }}
|
||||
>
|
||||
{featureToggle.name}
|
||||
</CardTitle>
|
||||
<Card shadow={0} className={commonStyles.fullwidth} style={{ overflow: 'visible' }}>
|
||||
<CardTitle style={{ paddingTop: '24px', wordBreak: 'break-all' }}>{featureToggle.name}</CardTitle>
|
||||
<CardText>{featureToggle.description}</CardText>
|
||||
<CardActions
|
||||
border
|
||||
@ -145,22 +125,9 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
||||
tabBarProps={{ style: { width: '100%' } }}
|
||||
className="mdl-color--grey-100"
|
||||
>
|
||||
<Tab
|
||||
onClick={() => this.goToTab('view', featureToggleName)}
|
||||
>
|
||||
Metrics
|
||||
</Tab>
|
||||
<Tab
|
||||
onClick={() => this.goToTab('edit', featureToggleName)}
|
||||
>
|
||||
Edit
|
||||
</Tab>
|
||||
<Tab
|
||||
onClick={() =>
|
||||
this.goToTab('history', featureToggleName)}
|
||||
>
|
||||
History
|
||||
</Tab>
|
||||
<Tab onClick={() => this.goToTab('view', featureToggleName)}>Metrics</Tab>
|
||||
<Tab onClick={() => this.goToTab('edit', featureToggleName)}>Edit</Tab>
|
||||
<Tab onClick={() => this.goToTab('history', featureToggleName)}>History</Tab>
|
||||
</Tabs>
|
||||
{tabContent}
|
||||
</Card>
|
||||
|
@ -1,19 +1,13 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import {
|
||||
fetchFeatureToggles,
|
||||
toggleFeature,
|
||||
removeFeatureToggle,
|
||||
} from '../../store/feature-actions';
|
||||
import { fetchFeatureToggles, toggleFeature, removeFeatureToggle } from '../../store/feature-actions';
|
||||
|
||||
import ViewToggleComponent from './view-component';
|
||||
|
||||
export default connect(
|
||||
(state, props) => ({
|
||||
features: state.features.toJS(),
|
||||
featureToggle: state.features
|
||||
.toJS()
|
||||
.find(toggle => toggle.name === props.featureToggleName),
|
||||
featureToggle: state.features.toJS().find(toggle => toggle.name === props.featureToggleName),
|
||||
activeTab: props.activeTab,
|
||||
}),
|
||||
{
|
||||
|
@ -9,8 +9,6 @@ const mapStateToProps = state => {
|
||||
};
|
||||
};
|
||||
|
||||
const HistoryListContainer = connect(mapStateToProps, { fetchHistory })(
|
||||
HistoryComponent
|
||||
);
|
||||
const HistoryListContainer = connect(mapStateToProps, { fetchHistory })(HistoryComponent);
|
||||
|
||||
export default HistoryListContainer;
|
||||
|
@ -84,18 +84,12 @@ class HistoryItem extends PureComponent {
|
||||
changes = entry.diffs.map(buildDiff);
|
||||
} else {
|
||||
// Just show the data if there is no diff yet.
|
||||
changes = (
|
||||
<div className={KLASSES.N}>
|
||||
{JSON.stringify(entry.data, null, 2)}
|
||||
</div>
|
||||
);
|
||||
changes = <div className={KLASSES.N}>{JSON.stringify(entry.data, null, 2)}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<pre style={{ overflowX: 'auto', overflowY: 'hidden' }}>
|
||||
<code className="smalltext man">
|
||||
{changes.length === 0 ? '(no changes)' : changes}
|
||||
</code>
|
||||
<code className="smalltext man">{changes.length === 0 ? '(no changes)' : changes}</code>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
|
@ -2,11 +2,7 @@ import React, { Component } from 'react';
|
||||
import HistoryItemDiff from './history-item-diff';
|
||||
import HistoryItemJson from './history-item-json';
|
||||
import { Table, TableHeader } from 'react-mdl';
|
||||
import {
|
||||
DataTableHeader,
|
||||
SwitchWithLabel,
|
||||
styles as commonStyles,
|
||||
} from '../common';
|
||||
import { DataTableHeader, SwitchWithLabel, styles as commonStyles } from '../common';
|
||||
import { formatFullDateTime } from '../common/util';
|
||||
|
||||
import styles from './history.scss';
|
||||
@ -26,11 +22,7 @@ class HistoryList extends Component {
|
||||
const truncateTableCell = v => (
|
||||
<span
|
||||
className={commonStyles.truncate}
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'middle',
|
||||
width: '100%',
|
||||
}}
|
||||
style={{ display: 'inline-block', verticalAlign: 'middle', width: '100%' }}
|
||||
>
|
||||
{v}
|
||||
</span>
|
||||
@ -39,9 +31,7 @@ class HistoryList extends Component {
|
||||
let entries;
|
||||
|
||||
if (showData) {
|
||||
entries = history.map(entry => (
|
||||
<HistoryItemJson key={`log${entry.id}`} entry={entry} />
|
||||
));
|
||||
entries = history.map(entry => <HistoryItemJson key={`log${entry.id}`} entry={entry} />);
|
||||
} else {
|
||||
entries = (
|
||||
<Table
|
||||
@ -55,33 +45,16 @@ class HistoryList extends Component {
|
||||
)
|
||||
)}
|
||||
className={commonStyles.fullwidth}
|
||||
style={{
|
||||
border: 0,
|
||||
tableLayout: 'fixed',
|
||||
minWidth: '840px',
|
||||
}}
|
||||
style={{ border: 0, tableLayout: 'fixed', minWidth: '840px' }}
|
||||
>
|
||||
<TableHeader
|
||||
name="type"
|
||||
cellFormatter={truncateTableCell}
|
||||
style={{ width: '136px' }}
|
||||
>
|
||||
<TableHeader name="type" cellFormatter={truncateTableCell} style={{ width: '136px' }}>
|
||||
Type
|
||||
</TableHeader>
|
||||
<TableHeader
|
||||
name="createdBy"
|
||||
cellFormatter={truncateTableCell}
|
||||
style={{ width: '115px' }}
|
||||
>
|
||||
<TableHeader name="createdBy" cellFormatter={truncateTableCell} style={{ width: '115px' }}>
|
||||
User
|
||||
</TableHeader>
|
||||
<TableHeader name="diff">Diff</TableHeader>
|
||||
<TableHeader
|
||||
numeric
|
||||
name="createdAt"
|
||||
cellFormatter={formatFullDateTime}
|
||||
style={{ width: '165px' }}
|
||||
>
|
||||
<TableHeader numeric name="createdAt" cellFormatter={formatFullDateTime} style={{ width: '165px' }}>
|
||||
Time
|
||||
</TableHeader>
|
||||
</Table>
|
||||
@ -93,10 +66,7 @@ class HistoryList extends Component {
|
||||
<DataTableHeader
|
||||
title={this.props.title}
|
||||
actions={
|
||||
<SwitchWithLabel
|
||||
checked={showData}
|
||||
onChange={this.toggleShowDiff.bind(this)}
|
||||
>
|
||||
<SwitchWithLabel checked={showData} onChange={this.toggleShowDiff.bind(this)}>
|
||||
Full events
|
||||
</SwitchWithLabel>
|
||||
}
|
||||
|
@ -52,44 +52,23 @@ export function createActions({ id, prepare = v => v }) {
|
||||
},
|
||||
|
||||
setValue(key, value) {
|
||||
dispatch(
|
||||
createSet({ id: getId(id, ownProps), key, value })
|
||||
);
|
||||
dispatch(createSet({ id: getId(id, ownProps), key, value }));
|
||||
},
|
||||
|
||||
pushToList(key, value) {
|
||||
dispatch(
|
||||
createPush({ id: getId(id, ownProps), key, value })
|
||||
);
|
||||
dispatch(createPush({ id: getId(id, ownProps), key, value }));
|
||||
},
|
||||
|
||||
removeFromList(key, index) {
|
||||
dispatch(
|
||||
createPop({ id: getId(id, ownProps), key, index })
|
||||
);
|
||||
dispatch(createPop({ id: getId(id, ownProps), key, index }));
|
||||
},
|
||||
|
||||
moveItem(key, index, toIndex) {
|
||||
dispatch(
|
||||
createMove({
|
||||
id: getId(id, ownProps),
|
||||
key,
|
||||
index,
|
||||
toIndex,
|
||||
})
|
||||
);
|
||||
dispatch(createMove({ id: getId(id, ownProps), key, index, toIndex }));
|
||||
},
|
||||
|
||||
updateInList(key, index, newValue, merge = false) {
|
||||
dispatch(
|
||||
createUp({
|
||||
id: getId(id, ownProps),
|
||||
key,
|
||||
index,
|
||||
newValue,
|
||||
merge,
|
||||
})
|
||||
);
|
||||
dispatch(createUp({ id: getId(id, ownProps), key, index, newValue, merge }));
|
||||
},
|
||||
|
||||
incValue(key) {
|
||||
|
@ -13,19 +13,12 @@ const prepare = (methods, dispatch) => {
|
||||
// clean
|
||||
const parameters = (input.parameters || [])
|
||||
.filter(name => !!name)
|
||||
.map(
|
||||
({
|
||||
name,
|
||||
type = 'string',
|
||||
description = '',
|
||||
required = false,
|
||||
}) => ({
|
||||
name,
|
||||
type,
|
||||
description,
|
||||
required,
|
||||
})
|
||||
);
|
||||
.map(({ name, type = 'string', description = '', required = false }) => ({
|
||||
name,
|
||||
type,
|
||||
description,
|
||||
required,
|
||||
}));
|
||||
|
||||
createStrategy({
|
||||
name: input.name,
|
||||
|
@ -1,15 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import {
|
||||
Textfield,
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Checkbox,
|
||||
Grid,
|
||||
Cell,
|
||||
} from 'react-mdl';
|
||||
import { Textfield, IconButton, Menu, MenuItem, Checkbox, Grid, Cell } from 'react-mdl';
|
||||
import { FormButtons } from '../common';
|
||||
|
||||
const trim = value => {
|
||||
@ -25,13 +17,7 @@ function gerArrayWithEntries(num) {
|
||||
}
|
||||
|
||||
const Parameter = ({ set, input = {}, index }) => (
|
||||
<div
|
||||
style={{
|
||||
background: '#f1f1f1',
|
||||
padding: '16px 20px',
|
||||
marginBottom: '20px',
|
||||
}}
|
||||
>
|
||||
<div style={{ background: '#f1f1f1', padding: '16px 20px', marginBottom: '20px' }}>
|
||||
<Textfield
|
||||
style={{ width: '50%' }}
|
||||
floatingLabel
|
||||
@ -46,8 +32,7 @@ const Parameter = ({ set, input = {}, index }) => (
|
||||
style={{
|
||||
borderRadius: '2px',
|
||||
cursor: 'pointer',
|
||||
boxShadow:
|
||||
'0 2px 2px 0 rgba(0,0,0,.04),0 3px 1px -2px rgba(0,0,0,.1),0 1px 5px 0 rgba(0,0,0,.12)',
|
||||
boxShadow: '0 2px 2px 0 rgba(0,0,0,.04),0 3px 1px -2px rgba(0,0,0,.1),0 1px 5px 0 rgba(0,0,0,.12)',
|
||||
marginLeft: '10px',
|
||||
border: '1px solid #f1f1f1',
|
||||
backgroundColor: 'white',
|
||||
@ -55,22 +40,13 @@ const Parameter = ({ set, input = {}, index }) => (
|
||||
}}
|
||||
>
|
||||
{input.type || 'string'}
|
||||
<IconButton
|
||||
name="arrow_drop_down"
|
||||
onClick={evt => evt.preventDefault()}
|
||||
/>
|
||||
<IconButton name="arrow_drop_down" onClick={evt => evt.preventDefault()} />
|
||||
</span>
|
||||
<Menu target={`${index}-type-menu`} align="right">
|
||||
<MenuItem onClick={() => set({ type: 'string' })}>
|
||||
string
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => set({ type: 'percentage' })}>
|
||||
percentage
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => set({ type: 'string' })}>string</MenuItem>
|
||||
<MenuItem onClick={() => set({ type: 'percentage' })}>percentage</MenuItem>
|
||||
<MenuItem onClick={() => set({ type: 'list' })}>list</MenuItem>
|
||||
<MenuItem onClick={() => set({ type: 'number' })}>
|
||||
number
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => set({ type: 'number' })}>number</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
<Textfield
|
||||
@ -95,8 +71,7 @@ const EditHeader = () => (
|
||||
<div>
|
||||
<h4 style={{ marginTop: '16px' }}>Edit strategy</h4>
|
||||
<p style={{ background: '#ffb7b7', padding: '16px 20px' }}>
|
||||
Be carefull! Changing a strategy definition might also require
|
||||
changes to the implementation in the clients.
|
||||
Be carefull! Changing a strategy definition might also require changes to the implementation in the clients.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@ -110,12 +85,7 @@ const CreateHeader = () => (
|
||||
const Parameters = ({ input = [], count = 0, updateInList }) => (
|
||||
<div>
|
||||
{gerArrayWithEntries(count).map((v, i) => (
|
||||
<Parameter
|
||||
key={i}
|
||||
set={v => updateInList('parameters', i, v, true)}
|
||||
index={i}
|
||||
input={input[i]}
|
||||
/>
|
||||
<Parameter key={i} set={v => updateInList('parameters', i, v, true)} index={i} input={input[i]} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
@ -139,24 +109,13 @@ class AddStrategy extends Component {
|
||||
if (this.props.initCallRequired === true) {
|
||||
this.props.init(this.props.input);
|
||||
if (this.props.input.parameters) {
|
||||
this.props.setValue(
|
||||
'_params',
|
||||
this.props.input.parameters.length
|
||||
);
|
||||
this.props.setValue('_params', this.props.input.parameters.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
input,
|
||||
setValue,
|
||||
updateInList,
|
||||
incValue,
|
||||
onCancel,
|
||||
editmode = false,
|
||||
onSubmit,
|
||||
} = this.props;
|
||||
const { input, setValue, updateInList, incValue, onCancel, editmode = false, onSubmit } = this.props;
|
||||
|
||||
return (
|
||||
<Grid className="mdl-color--white">
|
||||
@ -170,8 +129,7 @@ class AddStrategy extends Component {
|
||||
required
|
||||
disabled={editmode}
|
||||
pattern="^[0-9a-zA-Z\.\-]+$"
|
||||
onChange={({ target }) =>
|
||||
setValue('name', trim(target.value))}
|
||||
onChange={({ target }) => setValue('name', trim(target.value))}
|
||||
value={input.name}
|
||||
/>
|
||||
<br />
|
||||
@ -181,15 +139,10 @@ class AddStrategy extends Component {
|
||||
rows={1}
|
||||
label="Description"
|
||||
name="description"
|
||||
onChange={({ target }) =>
|
||||
setValue('description', target.value)}
|
||||
onChange={({ target }) => setValue('description', target.value)}
|
||||
value={input.description}
|
||||
/>
|
||||
<Parameters
|
||||
input={input.parameters}
|
||||
count={input._params}
|
||||
updateInList={updateInList}
|
||||
/>
|
||||
<Parameters input={input.parameters} count={input._params} updateInList={updateInList} />
|
||||
<IconButton
|
||||
raised
|
||||
name="add"
|
||||
@ -202,10 +155,7 @@ class AddStrategy extends Component {
|
||||
Add parameter
|
||||
<br />
|
||||
<hr />
|
||||
<FormButtons
|
||||
submitText={editmode ? 'Update' : 'Create'}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
<FormButtons submitText={editmode ? 'Update' : 'Create'} onCancel={onCancel} />
|
||||
</form>
|
||||
</Cell>
|
||||
</Grid>
|
||||
|
@ -28,19 +28,12 @@ const prepare = (methods, dispatch) => {
|
||||
// clean
|
||||
const parameters = (input.parameters || [])
|
||||
.filter(name => !!name)
|
||||
.map(
|
||||
({
|
||||
name,
|
||||
type = 'string',
|
||||
description = '',
|
||||
required = false,
|
||||
}) => ({
|
||||
name,
|
||||
type,
|
||||
description,
|
||||
required,
|
||||
})
|
||||
);
|
||||
.map(({ name, type = 'string', description = '', required = false }) => ({
|
||||
name,
|
||||
type,
|
||||
description,
|
||||
required,
|
||||
}));
|
||||
|
||||
updateStrategy({
|
||||
name: input.name,
|
||||
|
@ -2,14 +2,7 @@ import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import {
|
||||
List,
|
||||
ListItem,
|
||||
ListItemContent,
|
||||
IconButton,
|
||||
Grid,
|
||||
Cell,
|
||||
} from 'react-mdl';
|
||||
import { List, ListItem, ListItemContent, IconButton, Grid, Cell } from 'react-mdl';
|
||||
import { HeaderTitle } from '../common';
|
||||
|
||||
class StrategiesListComponent extends Component {
|
||||
@ -33,10 +26,7 @@ class StrategiesListComponent extends Component {
|
||||
<IconButton
|
||||
raised
|
||||
name="add"
|
||||
onClick={() =>
|
||||
this.context.router.push(
|
||||
'/strategies/create'
|
||||
)}
|
||||
onClick={() => this.context.router.push('/strategies/create')}
|
||||
title="Add new strategy"
|
||||
/>
|
||||
}
|
||||
@ -45,24 +35,15 @@ class StrategiesListComponent extends Component {
|
||||
{strategies.length > 0 ? (
|
||||
strategies.map((strategy, i) => (
|
||||
<ListItem key={i} twoLine>
|
||||
<ListItemContent
|
||||
icon="extension"
|
||||
subtitle={strategy.description}
|
||||
>
|
||||
<Link
|
||||
to={`/strategies/view/${strategy.name}`}
|
||||
>
|
||||
<ListItemContent icon="extension" subtitle={strategy.description}>
|
||||
<Link to={`/strategies/view/${strategy.name}`}>
|
||||
<strong>{strategy.name}</strong>
|
||||
</Link>
|
||||
</ListItemContent>
|
||||
{strategy.editable === false ? (
|
||||
''
|
||||
) : (
|
||||
<IconButton
|
||||
name="delete"
|
||||
onClick={() =>
|
||||
removeStrategy(strategy)}
|
||||
/>
|
||||
<IconButton name="delete" onClick={() => removeStrategy(strategy)} />
|
||||
)}
|
||||
</ListItem>
|
||||
))
|
||||
|
@ -20,8 +20,6 @@ const mapDispatchToProps = dispatch => ({
|
||||
fetchStrategies: () => fetchStrategies()(dispatch),
|
||||
});
|
||||
|
||||
const StrategiesListContainer = connect(mapStateToProps, mapDispatchToProps)(
|
||||
StrategiesListComponent
|
||||
);
|
||||
const StrategiesListContainer = connect(mapStateToProps, mapDispatchToProps)(StrategiesListComponent);
|
||||
|
||||
export default StrategiesListContainer;
|
||||
|
@ -13,15 +13,8 @@ class ShowStrategyComponent extends PureComponent {
|
||||
renderParameters(params) {
|
||||
if (params) {
|
||||
return params.map(({ name, type, description, required }, i) => (
|
||||
<ListItem
|
||||
twoLine
|
||||
key={`${name}-${i}`}
|
||||
title={required ? 'Required' : ''}
|
||||
>
|
||||
<ListItemContent
|
||||
avatar={required ? 'add' : ' '}
|
||||
subtitle={description}
|
||||
>
|
||||
<ListItem twoLine key={`${name}-${i}`} title={required ? 'Required' : ''}>
|
||||
<ListItemContent avatar={required ? 'add' : ' '} subtitle={description}>
|
||||
{name} <small>({type})</small>
|
||||
</ListItemContent>
|
||||
</ListItem>
|
||||
|
@ -54,9 +54,7 @@ export default class StrategyDetails extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const activeTabId = TABS[this.props.activeTab]
|
||||
? TABS[this.props.activeTab]
|
||||
: TABS.view;
|
||||
const activeTabId = TABS[this.props.activeTab] ? TABS[this.props.activeTab] : TABS.view;
|
||||
const strategy = this.props.strategy;
|
||||
if (!strategy) {
|
||||
return <ProgressBar indeterminate />;
|
||||
@ -67,17 +65,12 @@ export default class StrategyDetails extends Component {
|
||||
return (
|
||||
<Grid className="mdl-color--white">
|
||||
<Cell col={12}>
|
||||
<HeaderTitle
|
||||
title={strategy.name}
|
||||
subtitle={strategy.description}
|
||||
/>
|
||||
<HeaderTitle title={strategy.name} subtitle={strategy.description} />
|
||||
{strategy.editable === false ? (
|
||||
''
|
||||
) : (
|
||||
<Tabs activeTab={activeTabId} ripple>
|
||||
<Tab onClick={() => this.goToTab('view')}>
|
||||
Details
|
||||
</Tab>
|
||||
<Tab onClick={() => this.goToTab('view')}>Details</Tab>
|
||||
<Tab onClick={() => this.goToTab('edit')}>Edit</Tab>
|
||||
</Tabs>
|
||||
)}
|
||||
|
@ -5,17 +5,10 @@ import { fetchAll } from '../../store/application/actions';
|
||||
import { fetchFeatureToggles } from '../../store/feature-actions';
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
let strategy = state.strategies
|
||||
.get('list')
|
||||
.find(n => n.name === props.strategyName);
|
||||
const applications = state.applications
|
||||
.get('list')
|
||||
.filter(app => app.strategies.includes(props.strategyName));
|
||||
let strategy = state.strategies.get('list').find(n => n.name === props.strategyName);
|
||||
const applications = state.applications.get('list').filter(app => app.strategies.includes(props.strategyName));
|
||||
const toggles = state.features.filter(
|
||||
toggle =>
|
||||
toggle
|
||||
.get('strategies')
|
||||
.findIndex(s => s.name === props.strategyName) > -1
|
||||
toggle => toggle.get('strategies').findIndex(s => s.name === props.strategyName) > -1
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -15,11 +15,7 @@ export default class ShowUserComponent extends React.Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<a
|
||||
className="mdl-navigation__link"
|
||||
href="#edit-user"
|
||||
onClick={this.openEdit}
|
||||
>
|
||||
<a className="mdl-navigation__link" href="#edit-user" onClick={this.openEdit}>
|
||||
<Tooltip label={this.props.user.userName || 'Unknown'} large>
|
||||
<Icon name="account_circle" />
|
||||
</Tooltip>
|
||||
|
@ -39,25 +39,17 @@ class EditUserComponent extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
isOpen={this.props.user.showDialog}
|
||||
contentLabel="test"
|
||||
style={customStyles}
|
||||
>
|
||||
<Modal isOpen={this.props.user.showDialog} contentLabel="test" style={customStyles}>
|
||||
<h2>Action required</h2>
|
||||
<div>
|
||||
<p>
|
||||
You have to specify a username to use Unleash. This
|
||||
will allow us to track your changes.
|
||||
</p>
|
||||
<p>You have to specify a username to use Unleash. This will allow us to track your changes.</p>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<Textfield
|
||||
label="Username"
|
||||
name="username"
|
||||
required
|
||||
value={this.props.user.userName}
|
||||
onChange={e =>
|
||||
this.props.updateUserName(e.target.value)}
|
||||
onChange={e => this.props.updateUserName(e.target.value)}
|
||||
/>
|
||||
<br />
|
||||
<Button raised accent>
|
||||
|
@ -4,10 +4,7 @@ const URI = 'api/admin/features';
|
||||
|
||||
function validateToggle(featureToggle) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (
|
||||
!featureToggle.strategies ||
|
||||
featureToggle.strategies.length === 0
|
||||
) {
|
||||
if (!featureToggle.strategies || featureToggle.strategies.length === 0) {
|
||||
reject(new Error('You must add at least one activation strategy'));
|
||||
} else {
|
||||
resolve(featureToggle);
|
||||
|
@ -1,9 +1,7 @@
|
||||
const defaultErrorMessage = 'Unexptected exception when talking to unleash-api';
|
||||
|
||||
function extractJoiMsg(body) {
|
||||
return body.details.length > 0
|
||||
? body.details[0].message
|
||||
: defaultErrorMessage;
|
||||
return body.details.length > 0 ? body.details[0].message : defaultErrorMessage;
|
||||
}
|
||||
function extractLegacyMsg(body) {
|
||||
return body && body.length > 0 ? body[0].msg : defaultErrorMessage;
|
||||
@ -14,10 +12,7 @@ export function throwIfNotSuccess(response) {
|
||||
if (response.status > 399 && response.status < 404) {
|
||||
return new Promise((resolve, reject) => {
|
||||
response.json().then(body => {
|
||||
const errorMsg =
|
||||
body && body.isJoi
|
||||
? extractJoiMsg(body)
|
||||
: extractLegacyMsg(body);
|
||||
const errorMsg = body && body.isJoi ? extractJoiMsg(body) : extractLegacyMsg(body);
|
||||
let error = new Error(errorMsg);
|
||||
error.statusCode = response.status;
|
||||
reject(error);
|
||||
|
@ -4,13 +4,7 @@ import 'react-mdl/extra/material.js';
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import {
|
||||
applyRouterMiddleware,
|
||||
Router,
|
||||
Route,
|
||||
IndexRedirect,
|
||||
hashHistory,
|
||||
} from 'react-router';
|
||||
import { applyRouterMiddleware, Router, Route, IndexRedirect, hashHistory } from 'react-router';
|
||||
import { useScroll } from 'react-router-scroll';
|
||||
import { Provider } from 'react-redux';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
@ -33,60 +27,31 @@ import ApplicationView from './page/applications/view';
|
||||
|
||||
let composeEnhancers;
|
||||
|
||||
if (
|
||||
process.env.NODE_ENV !== 'production' &&
|
||||
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
|
||||
) {
|
||||
if (process.env.NODE_ENV !== 'production' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) {
|
||||
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;
|
||||
} else {
|
||||
composeEnhancers = compose;
|
||||
}
|
||||
|
||||
const unleashStore = createStore(
|
||||
store,
|
||||
composeEnhancers(applyMiddleware(thunkMiddleware))
|
||||
);
|
||||
const unleashStore = createStore(store, composeEnhancers(applyMiddleware(thunkMiddleware)));
|
||||
|
||||
// "pageTitle" and "link" attributes are for internal usage only
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={unleashStore}>
|
||||
<Router
|
||||
history={hashHistory}
|
||||
render={applyRouterMiddleware(useScroll())}
|
||||
>
|
||||
<Router history={hashHistory} render={applyRouterMiddleware(useScroll())}>
|
||||
<Route path="/" component={App}>
|
||||
<IndexRedirect to="/features" />
|
||||
|
||||
<Route pageTitle="Feature Toggles" link="/features">
|
||||
<Route
|
||||
pageTitle="Feature toggles"
|
||||
path="/features"
|
||||
component={Features}
|
||||
/>
|
||||
<Route
|
||||
pageTitle="New"
|
||||
path="/features/create"
|
||||
component={CreateFeatureToggle}
|
||||
/>
|
||||
<Route
|
||||
pageTitle=":name"
|
||||
path="/features/:activeTab/:name"
|
||||
component={ViewFeatureToggle}
|
||||
/>
|
||||
<Route pageTitle="Feature toggles" path="/features" component={Features} />
|
||||
<Route pageTitle="New" path="/features/create" component={CreateFeatureToggle} />
|
||||
<Route pageTitle=":name" path="/features/:activeTab/:name" component={ViewFeatureToggle} />
|
||||
</Route>
|
||||
|
||||
<Route pageTitle="Strategies" link="/strategies">
|
||||
<Route
|
||||
pageTitle="Strategies"
|
||||
path="/strategies"
|
||||
component={Strategies}
|
||||
/>
|
||||
<Route
|
||||
pageTitle="New"
|
||||
path="/strategies/create"
|
||||
component={CreateStrategies}
|
||||
/>
|
||||
<Route pageTitle="Strategies" path="/strategies" component={Strategies} />
|
||||
<Route pageTitle="New" path="/strategies/create" component={CreateStrategies} />
|
||||
<Route
|
||||
pageTitle=":strategyName"
|
||||
path="/strategies/:activeTab/:strategyName"
|
||||
@ -95,34 +60,14 @@ ReactDOM.render(
|
||||
</Route>
|
||||
|
||||
<Route pageTitle="Event History" link="/history">
|
||||
<Route
|
||||
pageTitle="Event history"
|
||||
path="/history"
|
||||
component={HistoryPage}
|
||||
/>
|
||||
<Route
|
||||
pageTitle=":toggleName"
|
||||
path="/history/:toggleName"
|
||||
component={HistoryTogglePage}
|
||||
/>
|
||||
<Route pageTitle="Event history" path="/history" component={HistoryPage} />
|
||||
<Route pageTitle=":toggleName" path="/history/:toggleName" component={HistoryTogglePage} />
|
||||
</Route>
|
||||
|
||||
<Route
|
||||
pageTitle="Archived Toggles"
|
||||
path="/archive"
|
||||
component={Archive}
|
||||
/>
|
||||
<Route pageTitle="Archived Toggles" path="/archive" component={Archive} />
|
||||
<Route pageTitle="Applications" link="/applications">
|
||||
<Route
|
||||
pageTitle="Applications"
|
||||
path="/applications"
|
||||
component={Applications}
|
||||
/>
|
||||
<Route
|
||||
pageTitle=":name"
|
||||
path="/applications/:name"
|
||||
component={ApplicationView}
|
||||
/>
|
||||
<Route pageTitle="Applications" path="/applications" component={Applications} />
|
||||
<Route pageTitle=":name" path="/applications/:name" component={ApplicationView} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Router>
|
||||
|
@ -2,9 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ApplicationEditComponent from '../../component/application/application-edit-container';
|
||||
|
||||
const render = ({ params }) => (
|
||||
<ApplicationEditComponent appName={params.name} />
|
||||
);
|
||||
const render = ({ params }) => <ApplicationEditComponent appName={params.name} />;
|
||||
|
||||
render.propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
|
@ -9,11 +9,6 @@ export default class Features extends PureComponent {
|
||||
|
||||
render() {
|
||||
const { params } = this.props;
|
||||
return (
|
||||
<ViewFeatureToggle
|
||||
featureToggleName={params.name}
|
||||
activeTab={params.activeTab}
|
||||
/>
|
||||
);
|
||||
return <ViewFeatureToggle featureToggleName={params.name} activeTab={params.activeTab} />;
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import HistoryListToggle from '../../component/history/history-list-toggle-container';
|
||||
|
||||
const render = ({ params }) => (
|
||||
<HistoryListToggle toggleName={params.toggleName} />
|
||||
);
|
||||
const render = ({ params }) => <HistoryListToggle toggleName={params.toggleName} />;
|
||||
|
||||
render.propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
|
@ -2,12 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ShowStrategy from '../../component/strategies/strategy-details-container';
|
||||
|
||||
const render = ({ params }) => (
|
||||
<ShowStrategy
|
||||
strategyName={params.strategyName}
|
||||
activeTab={params.activeTab}
|
||||
/>
|
||||
);
|
||||
const render = ({ params }) => <ShowStrategy strategyName={params.strategyName} activeTab={params.activeTab} />;
|
||||
|
||||
render.propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
|
@ -2,8 +2,7 @@ import api from '../../data/applications-api';
|
||||
|
||||
export const RECEIVE_ALL_APPLICATIONS = 'RECEIVE_ALL_APPLICATIONS';
|
||||
export const ERROR_RECEIVE_ALL_APPLICATIONS = 'ERROR_RECEIVE_ALL_APPLICATIONS';
|
||||
export const ERROR_UPDATING_APPLICATION_DATA =
|
||||
'ERROR_UPDATING_APPLICATION_DATA';
|
||||
export const ERROR_UPDATING_APPLICATION_DATA = 'ERROR_UPDATING_APPLICATION_DATA';
|
||||
|
||||
export const RECEIVE_APPLICATION = 'RECEIVE_APPLICATION';
|
||||
|
||||
@ -17,10 +16,7 @@ const recieveApplication = json => ({
|
||||
value: json,
|
||||
});
|
||||
|
||||
const errorReceiveApplications = (
|
||||
statusCode,
|
||||
type = ERROR_RECEIVE_ALL_APPLICATIONS
|
||||
) => ({
|
||||
const errorReceiveApplications = (statusCode, type = ERROR_RECEIVE_ALL_APPLICATIONS) => ({
|
||||
type,
|
||||
statusCode,
|
||||
});
|
||||
@ -37,14 +33,7 @@ export function storeApplicationMetaData(appName, key, value) {
|
||||
return dispatch =>
|
||||
api
|
||||
.storeApplicationMetaData(appName, key, value)
|
||||
.catch(error =>
|
||||
dispatch(
|
||||
errorReceiveApplications(
|
||||
error,
|
||||
ERROR_UPDATING_APPLICATION_DATA
|
||||
)
|
||||
)
|
||||
);
|
||||
.catch(error => dispatch(errorReceiveApplications(error, ERROR_UPDATING_APPLICATION_DATA)));
|
||||
}
|
||||
|
||||
export function fetchApplication(appName) {
|
||||
|
@ -8,10 +8,7 @@ function getInitState() {
|
||||
const store = (state = getInitState(), action) => {
|
||||
switch (action.type) {
|
||||
case RECEIVE_APPLICATION:
|
||||
return state.setIn(
|
||||
['apps', action.value.appName],
|
||||
new Map(action.value)
|
||||
);
|
||||
return state.setIn(['apps', action.value.appName], new Map(action.value));
|
||||
case RECEIVE_ALL_APPLICATIONS:
|
||||
return state.set('list', new List(action.value.applications));
|
||||
default:
|
||||
|
@ -8,9 +8,7 @@ function getInitState() {
|
||||
const archiveStore = (state = getInitState(), action) => {
|
||||
switch (action.type) {
|
||||
case REVIVE_TOGGLE:
|
||||
return state.update('list', list =>
|
||||
list.filter(item => item.name !== action.value)
|
||||
);
|
||||
return state.update('list', list => list.filter(item => item.name !== action.value));
|
||||
case RECEIVE_ARCHIVE:
|
||||
return state.set('list', new List(action.value));
|
||||
default:
|
||||
|
@ -7,11 +7,7 @@ import {
|
||||
ERROR_UPDATE_FEATURE_TOGGLE,
|
||||
} from './feature-actions';
|
||||
|
||||
import {
|
||||
ERROR_UPDATING_STRATEGY,
|
||||
ERROR_CREATING_STRATEGY,
|
||||
ERROR_RECEIVE_STRATEGIES,
|
||||
} from './strategy/actions';
|
||||
import { ERROR_UPDATING_STRATEGY, ERROR_CREATING_STRATEGY, ERROR_RECEIVE_STRATEGIES } from './strategy/actions';
|
||||
|
||||
const debug = require('debug')('unleash:error-store');
|
||||
|
||||
@ -40,9 +36,7 @@ const strategies = (state = getInitState(), action) => {
|
||||
case ERROR_RECEIVE_STRATEGIES:
|
||||
return addErrorIfNotAlreadyInList(state, action.error.message);
|
||||
case MUTE_ERROR:
|
||||
return state.update('list', list =>
|
||||
list.remove(list.indexOf(action.error))
|
||||
);
|
||||
return state.update('list', list => list.remove(list.indexOf(action.error)));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -85,9 +85,7 @@ export function requestUpdateFeatureToggle(featureToggle) {
|
||||
|
||||
return api
|
||||
.update(featureToggle)
|
||||
.then(() =>
|
||||
dispatch({ type: UPDATE_FEATURE_TOGGLE, featureToggle })
|
||||
)
|
||||
.then(() => dispatch({ type: UPDATE_FEATURE_TOGGLE, featureToggle }))
|
||||
.catch(dispatchAndThrow(dispatch, ERROR_UPDATE_FEATURE_TOGGLE));
|
||||
};
|
||||
}
|
||||
@ -98,9 +96,7 @@ export function removeFeatureToggle(featureToggleName) {
|
||||
|
||||
return api
|
||||
.remove(featureToggleName)
|
||||
.then(() =>
|
||||
dispatch({ type: REMOVE_FEATURE_TOGGLE, featureToggleName })
|
||||
)
|
||||
.then(() => dispatch({ type: REMOVE_FEATURE_TOGGLE, featureToggleName }))
|
||||
.catch(dispatchAndThrow(dispatch, ERROR_REMOVE_FEATURE_TOGGLE));
|
||||
};
|
||||
}
|
||||
|
@ -1,14 +1,8 @@
|
||||
import { Map as $Map, fromJS } from 'immutable';
|
||||
|
||||
import {
|
||||
RECEIVE_FEATURE_METRICS,
|
||||
RECEIVE_SEEN_APPS,
|
||||
} from './feature-metrics-actions';
|
||||
import { RECEIVE_FEATURE_METRICS, RECEIVE_SEEN_APPS } from './feature-metrics-actions';
|
||||
|
||||
const metrics = (
|
||||
state = fromJS({ lastHour: {}, lastMinute: {}, seenApps: {} }),
|
||||
action
|
||||
) => {
|
||||
const metrics = (state = fromJS({ lastHour: {}, lastMinute: {}, seenApps: {} }), action) => {
|
||||
switch (action.type) {
|
||||
case RECEIVE_SEEN_APPS:
|
||||
return state.set('seenApps', new $Map(action.value));
|
||||
|
@ -16,9 +16,7 @@ const features = (state = new List([]), action) => {
|
||||
return state.push(new $Map(action.featureToggle));
|
||||
case REMOVE_FEATURE_TOGGLE:
|
||||
debug(REMOVE_FEATURE_TOGGLE, action);
|
||||
return state.filter(
|
||||
toggle => toggle.get('name') !== action.featureToggleName
|
||||
);
|
||||
return state.filter(toggle => toggle.get('name') !== action.featureToggleName);
|
||||
case TOGGLE_FEATURE_TOGGLE:
|
||||
debug(TOGGLE_FEATURE_TOGGLE, action);
|
||||
return state.map(toggle => {
|
||||
|
@ -8,10 +8,7 @@ function getInitState() {
|
||||
const historyStore = (state = getInitState(), action) => {
|
||||
switch (action.type) {
|
||||
case RECEIVE_HISTORY_FOR_TOGGLE:
|
||||
return state.setIn(
|
||||
['toggles', action.value.toggleName],
|
||||
new List(action.value.events)
|
||||
);
|
||||
return state.setIn(['toggles', action.value.toggleName], new List(action.value.events));
|
||||
case RECEIVE_HISTORY:
|
||||
return state.set('list', new List(action.value));
|
||||
default:
|
||||
|
@ -9,41 +9,12 @@ export const actions = {
|
||||
MOVE: 'MOVE',
|
||||
};
|
||||
|
||||
export const createInit = ({ id, value }) => ({
|
||||
type: actions.INIT,
|
||||
id,
|
||||
value,
|
||||
});
|
||||
export const createInc = ({ id, key }) => ({
|
||||
type: actions.INCREMENT_VALUE,
|
||||
id,
|
||||
key,
|
||||
});
|
||||
export const createSet = ({ id, key, value }) => ({
|
||||
type: actions.SET_VALUE,
|
||||
id,
|
||||
key,
|
||||
value,
|
||||
});
|
||||
export const createPush = ({ id, key, value }) => ({
|
||||
type: actions.LIST_PUSH,
|
||||
id,
|
||||
key,
|
||||
value,
|
||||
});
|
||||
export const createPop = ({ id, key, index }) => ({
|
||||
type: actions.LIST_POP,
|
||||
id,
|
||||
key,
|
||||
index,
|
||||
});
|
||||
export const createMove = ({ id, key, index, toIndex }) => ({
|
||||
type: actions.MOVE,
|
||||
id,
|
||||
key,
|
||||
index,
|
||||
toIndex,
|
||||
});
|
||||
export const createInit = ({ id, value }) => ({ type: actions.INIT, id, value });
|
||||
export const createInc = ({ id, key }) => ({ type: actions.INCREMENT_VALUE, id, key });
|
||||
export const createSet = ({ id, key, value }) => ({ type: actions.SET_VALUE, id, key, value });
|
||||
export const createPush = ({ id, key, value }) => ({ type: actions.LIST_PUSH, id, key, value });
|
||||
export const createPop = ({ id, key, index }) => ({ type: actions.LIST_POP, id, key, index });
|
||||
export const createMove = ({ id, key, index, toIndex }) => ({ type: actions.MOVE, id, key, index, toIndex });
|
||||
export const createUp = ({ id, key, index, newValue, merge }) => ({
|
||||
type: actions.LIST_UP,
|
||||
id,
|
||||
|
@ -7,5 +7,4 @@ export const updateSetting = (group, field, value) => ({
|
||||
value,
|
||||
});
|
||||
|
||||
export const updateSettingForGroup = group => (field, value) =>
|
||||
updateSetting(group, field, value);
|
||||
export const updateSettingForGroup = group => (field, value) => updateSetting(group, field, value);
|
||||
|
@ -15,10 +15,7 @@ function getInitState() {
|
||||
}
|
||||
|
||||
function updateSetting(state, action) {
|
||||
const newState = state.updateIn(
|
||||
[action.group, action.field],
|
||||
() => action.value
|
||||
);
|
||||
const newState = state.updateIn([action.group, action.field], () => action.value);
|
||||
|
||||
localStorage.setItem(SETTINGS, JSON.stringify(newState.toJSON()));
|
||||
return newState;
|
||||
|
@ -1,10 +1,5 @@
|
||||
import { List, Map as $Map } from 'immutable';
|
||||
import {
|
||||
RECEIVE_STRATEGIES,
|
||||
REMOVE_STRATEGY,
|
||||
ADD_STRATEGY,
|
||||
UPDATE_STRATEGY,
|
||||
} from './actions';
|
||||
import { RECEIVE_STRATEGIES, REMOVE_STRATEGY, ADD_STRATEGY, UPDATE_STRATEGY } from './actions';
|
||||
|
||||
function getInitState() {
|
||||
return new $Map({ list: new List() });
|
||||
|
@ -20,9 +20,7 @@ function readCookie() {
|
||||
}
|
||||
|
||||
function writeCookie(userName) {
|
||||
document.cookie = `${COOKIE_NAME}=${encodeURIComponent(
|
||||
userName
|
||||
)}; expires=Thu, 18 Dec 2099 12:00:00 UTC`;
|
||||
document.cookie = `${COOKIE_NAME}=${encodeURIComponent(userName)}; expires=Thu, 18 Dec 2099 12:00:00 UTC`;
|
||||
}
|
||||
|
||||
function getInitState() {
|
||||
|
@ -54,17 +54,14 @@ module.exports = {
|
||||
sourceMap: true,
|
||||
modules: true,
|
||||
importLoaders: 1,
|
||||
localIdentName:
|
||||
'[name]__[local]___[hash:base64:5]',
|
||||
localIdentName: '[name]__[local]___[hash:base64:5]',
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
// data: '@import "theme/_config.scss";',
|
||||
includePaths: [
|
||||
path.resolve(__dirname, './src'),
|
||||
],
|
||||
includePaths: [path.resolve(__dirname, './src')],
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -72,10 +69,7 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
loader: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: 'css-loader',
|
||||
}),
|
||||
loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1253,7 +1253,15 @@ chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
|
||||
strip-ansi "^3.0.0"
|
||||
supports-color "^2.0.0"
|
||||
|
||||
chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0:
|
||||
chalk@^2.0.0, chalk@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.0.1.tgz#dbec49436d2ae15f536114e76d14656cdbc0f44d"
|
||||
dependencies:
|
||||
ansi-styles "^3.1.0"
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^4.0.0"
|
||||
|
||||
chalk@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e"
|
||||
dependencies:
|
||||
@ -2465,10 +2473,14 @@ fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2:
|
||||
mkdirp ">=0.5 0"
|
||||
rimraf "2"
|
||||
|
||||
function-bind@^1.0.2, function-bind@^1.1.0:
|
||||
function-bind@^1.0.2:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
|
||||
|
||||
function-bind@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
|
||||
functional-red-black-tree@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
||||
@ -3364,7 +3376,14 @@ js-tokens@^3.0.0:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
|
||||
|
||||
js-yaml@^3.7.0, js-yaml@^3.9.1:
|
||||
js-yaml@^3.7.0:
|
||||
version "3.9.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.9.0.tgz#4ffbbf25c2ac963b8299dc74da7e3740de1c18ce"
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
js-yaml@^3.9.1:
|
||||
version "3.9.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.9.1.tgz#08775cebdfdd359209f0d2acd383c8f86a6904a0"
|
||||
dependencies:
|
||||
|
Loading…
Reference in New Issue
Block a user