mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feature: Add support for permission system in unleash frontend
This commit is contained in:
		
							parent
							
								
									6de8c297fd
								
							
						
					
					
						commit
						1eb8fc0464
					
				@ -22,6 +22,8 @@ import {
 | 
				
			|||||||
} from 'react-mdl';
 | 
					} from 'react-mdl';
 | 
				
			||||||
import { IconLink, shorten, styles as commonStyles } from '../common';
 | 
					import { IconLink, shorten, styles as commonStyles } from '../common';
 | 
				
			||||||
import { formatFullDateTimeWithLocale } from '../common/util';
 | 
					import { formatFullDateTimeWithLocale } from '../common/util';
 | 
				
			||||||
 | 
					import { CREATE_FEATURE, CREATE_STRATEGY, UPDATE_APPLICATION } from '../../permissions';
 | 
				
			||||||
 | 
					import PermissionComponent from '../common/permission-container';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StatefulTextfield extends Component {
 | 
					class StatefulTextfield extends Component {
 | 
				
			||||||
    static propTypes = {
 | 
					    static propTypes = {
 | 
				
			||||||
@ -91,11 +93,26 @@ class ClientApplications extends PureComponent {
 | 
				
			|||||||
                            {seenToggles.map(
 | 
					                            {seenToggles.map(
 | 
				
			||||||
                                ({ name, description, enabled, notFound }, i) =>
 | 
					                                ({ name, description, enabled, notFound }, i) =>
 | 
				
			||||||
                                    notFound ? (
 | 
					                                    notFound ? (
 | 
				
			||||||
                                        <ListItem twoLine key={i}>
 | 
					                                        <PermissionComponent
 | 
				
			||||||
                                            <ListItemContent icon={'report'} subtitle={'Missing, want to create?'}>
 | 
					                                            permission={CREATE_FEATURE}
 | 
				
			||||||
                                                <Link to={`/features/create?name=${name}`}>{name}</Link>
 | 
					                                            component={
 | 
				
			||||||
                                            </ListItemContent>
 | 
					                                                <ListItem twoLine key={i}>
 | 
				
			||||||
                                        </ListItem>
 | 
					                                                    <ListItemContent
 | 
				
			||||||
 | 
					                                                        icon={'report'}
 | 
				
			||||||
 | 
					                                                        subtitle={'Missing, want to create?'}
 | 
				
			||||||
 | 
					                                                    >
 | 
				
			||||||
 | 
					                                                        <Link to={`/features/create?name=${name}`}>{name}</Link>
 | 
				
			||||||
 | 
					                                                    </ListItemContent>
 | 
				
			||||||
 | 
					                                                </ListItem>
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                            otherwise={
 | 
				
			||||||
 | 
					                                                <ListItem twoLine key={i}>
 | 
				
			||||||
 | 
					                                                    <ListItemContent icon={'report'} subtitle={'Missing'}>
 | 
				
			||||||
 | 
					                                                        {name}
 | 
				
			||||||
 | 
					                                                    </ListItemContent>
 | 
				
			||||||
 | 
					                                                </ListItem>
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                        />
 | 
				
			||||||
                                    ) : (
 | 
					                                    ) : (
 | 
				
			||||||
                                        <ListItem twoLine key={i}>
 | 
					                                        <ListItem twoLine key={i}>
 | 
				
			||||||
                                            <ListItemContent
 | 
					                                            <ListItemContent
 | 
				
			||||||
@ -120,11 +137,26 @@ class ClientApplications extends PureComponent {
 | 
				
			|||||||
                            {strategies.map(
 | 
					                            {strategies.map(
 | 
				
			||||||
                                ({ name, description, notFound }, i) =>
 | 
					                                ({ name, description, notFound }, i) =>
 | 
				
			||||||
                                    notFound ? (
 | 
					                                    notFound ? (
 | 
				
			||||||
                                        <ListItem twoLine key={`${name}-${i}`}>
 | 
					                                        <PermissionComponent
 | 
				
			||||||
                                            <ListItemContent icon={'report'} subtitle={'Missing, want to create?'}>
 | 
					                                            permission={CREATE_STRATEGY}
 | 
				
			||||||
                                                <Link to={`/strategies/create?name=${name}`}>{name}</Link>
 | 
					                                            component={
 | 
				
			||||||
                                            </ListItemContent>
 | 
					                                                <ListItem twoLine key={`${name}-${i}`}>
 | 
				
			||||||
                                        </ListItem>
 | 
					                                                    <ListItemContent
 | 
				
			||||||
 | 
					                                                        icon={'report'}
 | 
				
			||||||
 | 
					                                                        subtitle={'Missing, want to create?'}
 | 
				
			||||||
 | 
					                                                    >
 | 
				
			||||||
 | 
					                                                        <Link to={`/strategies/create?name=${name}`}>{name}</Link>
 | 
				
			||||||
 | 
					                                                    </ListItemContent>
 | 
				
			||||||
 | 
					                                                </ListItem>
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                            otherwise={
 | 
				
			||||||
 | 
					                                                <ListItem twoLine key={`${name}-${i}`}>
 | 
				
			||||||
 | 
					                                                    <ListItemContent icon={'report'} subtitle={'Missing'}>
 | 
				
			||||||
 | 
					                                                        {name}
 | 
				
			||||||
 | 
					                                                    </ListItemContent>
 | 
				
			||||||
 | 
					                                                </ListItem>
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                        />
 | 
				
			||||||
                                    ) : (
 | 
					                                    ) : (
 | 
				
			||||||
                                        <ListItem twoLine key={`${name}-${i}`}>
 | 
					                                        <ListItem twoLine key={`${name}-${i}`}>
 | 
				
			||||||
                                            <ListItemContent icon={'extension'} subtitle={shorten(description, 60)}>
 | 
					                                            <ListItemContent icon={'extension'} subtitle={shorten(description, 60)}>
 | 
				
			||||||
@ -203,16 +235,21 @@ class ClientApplications extends PureComponent {
 | 
				
			|||||||
                    </CardMenu>
 | 
					                    </CardMenu>
 | 
				
			||||||
                )}
 | 
					                )}
 | 
				
			||||||
                <hr />
 | 
					                <hr />
 | 
				
			||||||
                <Tabs
 | 
					                <PermissionComponent
 | 
				
			||||||
                    activeTab={this.state.activeTab}
 | 
					                    permission={UPDATE_APPLICATION}
 | 
				
			||||||
                    onChange={tabId => this.setState({ activeTab: tabId })}
 | 
					                    component={
 | 
				
			||||||
                    ripple
 | 
					                        <Tabs
 | 
				
			||||||
                    tabBarProps={{ style: { width: '100%' } }}
 | 
					                            activeTab={this.state.activeTab}
 | 
				
			||||||
                    className="mdl-color--grey-100"
 | 
					                            onChange={tabId => this.setState({ activeTab: tabId })}
 | 
				
			||||||
                >
 | 
					                            ripple
 | 
				
			||||||
                    <Tab>Details</Tab>
 | 
					                            tabBarProps={{ style: { width: '100%' } }}
 | 
				
			||||||
                    <Tab>Edit</Tab>
 | 
					                            className="mdl-color--grey-100"
 | 
				
			||||||
                </Tabs>
 | 
					                        >
 | 
				
			||||||
 | 
					                            <Tab>Details</Tab>
 | 
				
			||||||
 | 
					                            <Tab>Edit</Tab>
 | 
				
			||||||
 | 
					                        </Tabs>
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                {content}
 | 
					                {content}
 | 
				
			||||||
            </Card>
 | 
					            </Card>
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										47
									
								
								frontend/src/component/common/permission-component.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								frontend/src/component/common/permission-component.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					import React, { Component } from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import { ADMIN } from '../../permissions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PermissionComponent extends Component {
 | 
				
			||||||
 | 
					    static propTypes = {
 | 
				
			||||||
 | 
					        user: PropTypes.object,
 | 
				
			||||||
 | 
					        component: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
 | 
				
			||||||
 | 
					        others: PropTypes.object,
 | 
				
			||||||
 | 
					        denied: PropTypes.object,
 | 
				
			||||||
 | 
					        granted: PropTypes.object,
 | 
				
			||||||
 | 
					        otherwise: PropTypes.node,
 | 
				
			||||||
 | 
					        permission: PropTypes.string,
 | 
				
			||||||
 | 
					        children: PropTypes.node,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render() {
 | 
				
			||||||
 | 
					        const { user, otherwise, component: Component, permission, granted, denied, children, ...others } = this.props;
 | 
				
			||||||
 | 
					        let grantedComponent = Component;
 | 
				
			||||||
 | 
					        let deniedCompoinent = otherwise || '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (granted || denied) {
 | 
				
			||||||
 | 
					            grantedComponent = (
 | 
				
			||||||
 | 
					                <Component {...others} {...granted || {}}>
 | 
				
			||||||
 | 
					                    {children}
 | 
				
			||||||
 | 
					                </Component>
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            deniedCompoinent = (
 | 
				
			||||||
 | 
					                <Component {...others} {...denied || {}}>
 | 
				
			||||||
 | 
					                    {children}
 | 
				
			||||||
 | 
					                </Component>
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!user) return deniedCompoinent;
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					            !user.permissions ||
 | 
				
			||||||
 | 
					            user.permissions.indexOf(ADMIN) !== -1 ||
 | 
				
			||||||
 | 
					            user.permissions.indexOf(permission) !== -1
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            return grantedComponent;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return deniedCompoinent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default PermissionComponent;
 | 
				
			||||||
							
								
								
									
										8
									
								
								frontend/src/component/common/permission-container.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								frontend/src/component/common/permission-container.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
 | 
					import PermissionComponent from './permission-component';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mapStateToProps = state => ({ user: state.user.get('profile') });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Container = connect(mapStateToProps)(PermissionComponent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Container;
 | 
				
			||||||
@ -22,7 +22,7 @@ exports[`renders correctly with one feature 1`] = `
 | 
				
			|||||||
  >
 | 
					  >
 | 
				
			||||||
    <react-mdl-Switch
 | 
					    <react-mdl-Switch
 | 
				
			||||||
      checked={false}
 | 
					      checked={false}
 | 
				
			||||||
      disabled={false}
 | 
					      disabled={true}
 | 
				
			||||||
      onChange={[Function]}
 | 
					      onChange={[Function]}
 | 
				
			||||||
      title="Toggle Another"
 | 
					      title="Toggle Another"
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
 | 
				
			|||||||
@ -14,6 +14,7 @@ const Feature = ({
 | 
				
			|||||||
    metricsLastHour = { yes: 0, no: 0, isFallback: true },
 | 
					    metricsLastHour = { yes: 0, no: 0, isFallback: true },
 | 
				
			||||||
    metricsLastMinute = { yes: 0, no: 0, isFallback: true },
 | 
					    metricsLastMinute = { yes: 0, no: 0, isFallback: true },
 | 
				
			||||||
    revive,
 | 
					    revive,
 | 
				
			||||||
 | 
					    updateable,
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
    const { name, description, enabled, strategies } = feature;
 | 
					    const { name, description, enabled, strategies } = feature;
 | 
				
			||||||
    const { showLastHour = false } = settings;
 | 
					    const { showLastHour = false } = settings;
 | 
				
			||||||
@ -42,7 +43,7 @@ const Feature = ({
 | 
				
			|||||||
            </span>
 | 
					            </span>
 | 
				
			||||||
            <span className={styles.listItemToggle}>
 | 
					            <span className={styles.listItemToggle}>
 | 
				
			||||||
                <Switch
 | 
					                <Switch
 | 
				
			||||||
                    disabled={toggleFeature === undefined}
 | 
					                    disabled={!updateable || toggleFeature === undefined}
 | 
				
			||||||
                    title={`Toggle ${name}`}
 | 
					                    title={`Toggle ${name}`}
 | 
				
			||||||
                    key="left-actions"
 | 
					                    key="left-actions"
 | 
				
			||||||
                    onChange={() => toggleFeature(name)}
 | 
					                    onChange={() => toggleFeature(name)}
 | 
				
			||||||
@ -59,7 +60,7 @@ const Feature = ({
 | 
				
			|||||||
                {strategyChips}
 | 
					                {strategyChips}
 | 
				
			||||||
                {summaryChip}
 | 
					                {summaryChip}
 | 
				
			||||||
            </span>
 | 
					            </span>
 | 
				
			||||||
            {revive ? (
 | 
					            {updateable && revive ? (
 | 
				
			||||||
                <ListItemAction onClick={() => revive(feature.name)}>
 | 
					                <ListItemAction onClick={() => revive(feature.name)}>
 | 
				
			||||||
                    <Icon name="undo" />
 | 
					                    <Icon name="undo" />
 | 
				
			||||||
                </ListItemAction>
 | 
					                </ListItemAction>
 | 
				
			||||||
@ -77,6 +78,7 @@ Feature.propTypes = {
 | 
				
			|||||||
    metricsLastHour: PropTypes.object,
 | 
					    metricsLastHour: PropTypes.object,
 | 
				
			||||||
    metricsLastMinute: PropTypes.object,
 | 
					    metricsLastMinute: PropTypes.object,
 | 
				
			||||||
    revive: PropTypes.func,
 | 
					    revive: PropTypes.func,
 | 
				
			||||||
 | 
					    updateable: PropTypes.bool,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Feature;
 | 
					export default Feature;
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,8 @@ import { Link } from 'react-router-dom';
 | 
				
			|||||||
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';
 | 
					import styles from './feature.scss';
 | 
				
			||||||
 | 
					import { CREATE_FEATURE } from '../../permissions';
 | 
				
			||||||
 | 
					import PermissionComponent from '../common/permission-container';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class FeatureListComponent extends React.Component {
 | 
					export default class FeatureListComponent extends React.Component {
 | 
				
			||||||
    static propTypes = {
 | 
					    static propTypes = {
 | 
				
			||||||
@ -62,11 +64,16 @@ export default class FeatureListComponent extends React.Component {
 | 
				
			|||||||
                        label="Search"
 | 
					                        label="Search"
 | 
				
			||||||
                        style={{ width: '100%' }}
 | 
					                        style={{ width: '100%' }}
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
                    <Link to="/features/create" className={styles.toolbarButton}>
 | 
					                    <PermissionComponent
 | 
				
			||||||
                        <FABButton accent title="Create feature toggle">
 | 
					                        permission={CREATE_FEATURE}
 | 
				
			||||||
                            <Icon name="add" />
 | 
					                        component={
 | 
				
			||||||
                        </FABButton>
 | 
					                            <Link to="/features/create" className={styles.toolbarButton}>
 | 
				
			||||||
                    </Link>
 | 
					                                <FABButton accent title="Create feature toggle">
 | 
				
			||||||
 | 
					                                    <Icon name="add" />
 | 
				
			||||||
 | 
					                                </FABButton>
 | 
				
			||||||
 | 
					                            </Link>
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <Card shadow={0} className={commonStyles.fullwidth} style={{ overflow: 'visible' }}>
 | 
					                <Card shadow={0} className={commonStyles.fullwidth} style={{ overflow: 'visible' }}>
 | 
				
			||||||
                    <CardActions>
 | 
					                    <CardActions>
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,8 @@ import MetricComponent from './metric-container';
 | 
				
			|||||||
import EditFeatureToggle from './form/form-update-feature-container';
 | 
					import EditFeatureToggle from './form/form-update-feature-container';
 | 
				
			||||||
import ViewFeatureToggle from './form/form-view-feature-container';
 | 
					import ViewFeatureToggle from './form/form-view-feature-container';
 | 
				
			||||||
import { styles as commonStyles } from '../common';
 | 
					import { styles as commonStyles } from '../common';
 | 
				
			||||||
 | 
					import { CREATE_FEATURE, DELETE_FEATURE, UPDATE_FEATURE } from '../../permissions';
 | 
				
			||||||
 | 
					import PermissionComponent from '../common/permission-container';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TABS = {
 | 
					const TABS = {
 | 
				
			||||||
    strategies: 0,
 | 
					    strategies: 0,
 | 
				
			||||||
@ -54,7 +56,17 @@ export default class ViewFeatureToggleComponent extends React.Component {
 | 
				
			|||||||
        } else if (TABS[activeTab] === TABS.strategies) {
 | 
					        } else if (TABS[activeTab] === TABS.strategies) {
 | 
				
			||||||
            if (this.isFeatureView) {
 | 
					            if (this.isFeatureView) {
 | 
				
			||||||
                return (
 | 
					                return (
 | 
				
			||||||
                    <EditFeatureToggle featureToggle={featureToggle} features={features} history={this.props.history} />
 | 
					                    <PermissionComponent
 | 
				
			||||||
 | 
					                        permission={UPDATE_FEATURE}
 | 
				
			||||||
 | 
					                        component={
 | 
				
			||||||
 | 
					                            <EditFeatureToggle
 | 
				
			||||||
 | 
					                                featureToggle={featureToggle}
 | 
				
			||||||
 | 
					                                features={features}
 | 
				
			||||||
 | 
					                                history={this.props.history}
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        otherwise={<ViewFeatureToggle featureToggle={featureToggle} />}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return <ViewFeatureToggle featureToggle={featureToggle} />;
 | 
					            return <ViewFeatureToggle featureToggle={featureToggle} />;
 | 
				
			||||||
@ -87,14 +99,20 @@ export default class ViewFeatureToggleComponent extends React.Component {
 | 
				
			|||||||
            return (
 | 
					            return (
 | 
				
			||||||
                <span>
 | 
					                <span>
 | 
				
			||||||
                    Could not find the toggle{' '}
 | 
					                    Could not find the toggle{' '}
 | 
				
			||||||
                    <Link
 | 
					                    <PermissionComponent
 | 
				
			||||||
                        to={{
 | 
					                        permission={CREATE_FEATURE}
 | 
				
			||||||
                            pathname: '/features/create',
 | 
					                        component={
 | 
				
			||||||
                            query: { name: featureToggleName },
 | 
					                            <Link
 | 
				
			||||||
                        }}
 | 
					                                to={{
 | 
				
			||||||
                    >
 | 
					                                    pathname: '/features/create',
 | 
				
			||||||
                        {featureToggleName}
 | 
					                                    query: { name: featureToggleName },
 | 
				
			||||||
                    </Link>
 | 
					                                }}
 | 
				
			||||||
 | 
					                            >
 | 
				
			||||||
 | 
					                                {featureToggleName}
 | 
				
			||||||
 | 
					                            </Link>
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        otherwise={featureToggleName}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
                </span>
 | 
					                </span>
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -115,8 +133,8 @@ export default class ViewFeatureToggleComponent extends React.Component {
 | 
				
			|||||||
            revive(featureToggle.name);
 | 
					            revive(featureToggle.name);
 | 
				
			||||||
            this.props.history.push('/features');
 | 
					            this.props.history.push('/features');
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        const updateFeatureToggle = () => {
 | 
					        const updateFeatureToggle = e => {
 | 
				
			||||||
            let feature = { ...featureToggle };
 | 
					            let feature = { ...featureToggle, description: e.target.value };
 | 
				
			||||||
            if (Array.isArray(feature.strategies)) {
 | 
					            if (Array.isArray(feature.strategies)) {
 | 
				
			||||||
                feature.strategies.forEach(s => {
 | 
					                feature.strategies.forEach(s => {
 | 
				
			||||||
                    delete s.id;
 | 
					                    delete s.id;
 | 
				
			||||||
@ -135,15 +153,22 @@ export default class ViewFeatureToggleComponent extends React.Component {
 | 
				
			|||||||
                <CardTitle style={{ paddingTop: '24px', wordBreak: 'break-all' }}>{featureToggle.name}</CardTitle>
 | 
					                <CardTitle style={{ paddingTop: '24px', wordBreak: 'break-all' }}>{featureToggle.name}</CardTitle>
 | 
				
			||||||
                <CardText>
 | 
					                <CardText>
 | 
				
			||||||
                    {this.isFeatureView ? (
 | 
					                    {this.isFeatureView ? (
 | 
				
			||||||
                        <Textfield
 | 
					                        <PermissionComponent
 | 
				
			||||||
 | 
					                            permission={UPDATE_FEATURE}
 | 
				
			||||||
 | 
					                            component={Textfield}
 | 
				
			||||||
 | 
					                            granted={{
 | 
				
			||||||
 | 
					                                onChange: v => setValue('description', v),
 | 
				
			||||||
 | 
					                                onBlur: updateFeatureToggle,
 | 
				
			||||||
 | 
					                            }}
 | 
				
			||||||
 | 
					                            denied={{
 | 
				
			||||||
 | 
					                                disabled: true,
 | 
				
			||||||
 | 
					                            }}
 | 
				
			||||||
                            floatingLabel
 | 
					                            floatingLabel
 | 
				
			||||||
                            style={{ width: '100%' }}
 | 
					                            style={{ width: '100%' }}
 | 
				
			||||||
                            rows={1}
 | 
					                            rows={1}
 | 
				
			||||||
                            label="Description"
 | 
					                            label="Description"
 | 
				
			||||||
                            required
 | 
					                            required
 | 
				
			||||||
                            value={featureToggle.description}
 | 
					                            value={featureToggle.description}
 | 
				
			||||||
                            onChange={v => setValue('description', v)}
 | 
					 | 
				
			||||||
                            onBlur={updateFeatureToggle}
 | 
					 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    ) : (
 | 
					                    ) : (
 | 
				
			||||||
                        <Textfield
 | 
					                        <Textfield
 | 
				
			||||||
@ -167,24 +192,41 @@ export default class ViewFeatureToggleComponent extends React.Component {
 | 
				
			|||||||
                    }}
 | 
					                    }}
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                    <span style={{ paddingRight: '24px' }}>
 | 
					                    <span style={{ paddingRight: '24px' }}>
 | 
				
			||||||
                        <Switch
 | 
					                        <PermissionComponent
 | 
				
			||||||
                            disabled={!this.isFeatureView}
 | 
					                            permission={UPDATE_FEATURE}
 | 
				
			||||||
 | 
					                            component={Switch}
 | 
				
			||||||
 | 
					                            granted={{
 | 
				
			||||||
 | 
					                                disabled: !this.isFeatureView,
 | 
				
			||||||
 | 
					                            }}
 | 
				
			||||||
 | 
					                            denied={{
 | 
				
			||||||
 | 
					                                disabled: true,
 | 
				
			||||||
 | 
					                            }}
 | 
				
			||||||
                            ripple
 | 
					                            ripple
 | 
				
			||||||
                            checked={featureToggle.enabled}
 | 
					                            checked={featureToggle.enabled}
 | 
				
			||||||
                            onChange={() => toggleFeature(featureToggle.name)}
 | 
					                            onChange={() => toggleFeature(featureToggle.name)}
 | 
				
			||||||
                        >
 | 
					                        >
 | 
				
			||||||
                            {featureToggle.enabled ? 'Enabled' : 'Disabled'}
 | 
					                            {featureToggle.enabled ? 'Enabled' : 'Disabled'}
 | 
				
			||||||
                        </Switch>
 | 
					                        </PermissionComponent>
 | 
				
			||||||
                    </span>
 | 
					                    </span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    {this.isFeatureView ? (
 | 
					                    {this.isFeatureView ? (
 | 
				
			||||||
                        <Button onClick={removeToggle} style={{ flexShrink: 0 }}>
 | 
					                        <PermissionComponent
 | 
				
			||||||
                            Archive
 | 
					                            permission={DELETE_FEATURE}
 | 
				
			||||||
                        </Button>
 | 
					                            component={
 | 
				
			||||||
 | 
					                                <Button onClick={removeToggle} style={{ flexShrink: 0 }}>
 | 
				
			||||||
 | 
					                                    Archive
 | 
				
			||||||
 | 
					                                </Button>
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
                    ) : (
 | 
					                    ) : (
 | 
				
			||||||
                        <Button onClick={reviveToggle} style={{ flexShrink: 0 }}>
 | 
					                        <PermissionComponent
 | 
				
			||||||
                            Revive
 | 
					                            permission={UPDATE_FEATURE}
 | 
				
			||||||
                        </Button>
 | 
					                            component={
 | 
				
			||||||
 | 
					                                <Button onClick={reviveToggle} style={{ flexShrink: 0 }}>
 | 
				
			||||||
 | 
					                                    Revive
 | 
				
			||||||
 | 
					                                </Button>
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
                    )}
 | 
					                    )}
 | 
				
			||||||
                </CardActions>
 | 
					                </CardActions>
 | 
				
			||||||
                <hr />
 | 
					                <hr />
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,8 @@ import { Link } from 'react-router-dom';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { List, ListItem, ListItemContent, IconButton, Grid, Cell } from 'react-mdl';
 | 
					import { List, ListItem, ListItemContent, IconButton, Grid, Cell } from 'react-mdl';
 | 
				
			||||||
import { HeaderTitle } from '../common';
 | 
					import { HeaderTitle } from '../common';
 | 
				
			||||||
 | 
					import { CREATE_STRATEGY, DELETE_STRATEGY } from '../../permissions';
 | 
				
			||||||
 | 
					import PermissionComponent from '../common/permission-container';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StrategiesListComponent extends Component {
 | 
					class StrategiesListComponent extends Component {
 | 
				
			||||||
    static propTypes = {
 | 
					    static propTypes = {
 | 
				
			||||||
@ -26,11 +28,16 @@ class StrategiesListComponent extends Component {
 | 
				
			|||||||
                    <HeaderTitle
 | 
					                    <HeaderTitle
 | 
				
			||||||
                        title="Strategies"
 | 
					                        title="Strategies"
 | 
				
			||||||
                        actions={
 | 
					                        actions={
 | 
				
			||||||
                            <IconButton
 | 
					                            <PermissionComponent
 | 
				
			||||||
                                raised
 | 
					                                permission={CREATE_STRATEGY}
 | 
				
			||||||
                                name="add"
 | 
					                                component={
 | 
				
			||||||
                                onClick={() => this.props.history.push('/strategies/create')}
 | 
					                                    <IconButton
 | 
				
			||||||
                                title="Add new strategy"
 | 
					                                        raised
 | 
				
			||||||
 | 
					                                        name="add"
 | 
				
			||||||
 | 
					                                        onClick={() => this.props.history.push('/strategies/create')}
 | 
				
			||||||
 | 
					                                        title="Add new strategy"
 | 
				
			||||||
 | 
					                                    />
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
@ -46,7 +53,12 @@ class StrategiesListComponent extends Component {
 | 
				
			|||||||
                                    {strategy.editable === false ? (
 | 
					                                    {strategy.editable === false ? (
 | 
				
			||||||
                                        ''
 | 
					                                        ''
 | 
				
			||||||
                                    ) : (
 | 
					                                    ) : (
 | 
				
			||||||
                                        <IconButton name="delete" onClick={() => removeStrategy(strategy)} />
 | 
					                                        <PermissionComponent
 | 
				
			||||||
 | 
					                                            permission={DELETE_STRATEGY}
 | 
				
			||||||
 | 
					                                            component={
 | 
				
			||||||
 | 
					                                                <IconButton name="delete" onClick={() => removeStrategy(strategy)} />
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                        />
 | 
				
			||||||
                                    )}
 | 
					                                    )}
 | 
				
			||||||
                                </ListItem>
 | 
					                                </ListItem>
 | 
				
			||||||
                            ))
 | 
					                            ))
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,8 @@ import { Tabs, Tab, ProgressBar, Grid, Cell } from 'react-mdl';
 | 
				
			|||||||
import ShowStrategy from './show-strategy-component';
 | 
					import ShowStrategy from './show-strategy-component';
 | 
				
			||||||
import EditStrategy from './edit-container';
 | 
					import EditStrategy from './edit-container';
 | 
				
			||||||
import { HeaderTitle } from '../common';
 | 
					import { HeaderTitle } from '../common';
 | 
				
			||||||
 | 
					import { UPDATE_STRATEGY } from '../../permissions';
 | 
				
			||||||
 | 
					import PermissionComponent from '../common/permission-container';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TABS = {
 | 
					const TABS = {
 | 
				
			||||||
    view: 0,
 | 
					    view: 0,
 | 
				
			||||||
@ -69,10 +71,15 @@ export default class StrategyDetails extends Component {
 | 
				
			|||||||
                    {strategy.editable === false ? (
 | 
					                    {strategy.editable === false ? (
 | 
				
			||||||
                        ''
 | 
					                        ''
 | 
				
			||||||
                    ) : (
 | 
					                    ) : (
 | 
				
			||||||
                        <Tabs activeTab={activeTabId} ripple>
 | 
					                        <PermissionComponent
 | 
				
			||||||
                            <Tab onClick={() => this.goToTab('view')}>Details</Tab>
 | 
					                            permission={UPDATE_STRATEGY}
 | 
				
			||||||
                            <Tab onClick={() => this.goToTab('edit')}>Edit</Tab>
 | 
					                            component={
 | 
				
			||||||
                        </Tabs>
 | 
					                                <Tabs activeTab={activeTabId} ripple>
 | 
				
			||||||
 | 
					                                    <Tab onClick={() => this.goToTab('view')}>Details</Tab>
 | 
				
			||||||
 | 
					                                    <Tab onClick={() => this.goToTab('edit')}>Edit</Tab>
 | 
				
			||||||
 | 
					                                </Tabs>
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
                    )}
 | 
					                    )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <section>
 | 
					                    <section>
 | 
				
			||||||
 | 
				
			|||||||
@ -23,12 +23,25 @@ export class AuthenticationError extends Error {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class ForbiddenError extends Error {
 | 
				
			||||||
 | 
					    constructor(statusCode, body) {
 | 
				
			||||||
 | 
					        super('You cannot perform this action');
 | 
				
			||||||
 | 
					        this.name = 'ForbiddenError';
 | 
				
			||||||
 | 
					        this.statusCode = statusCode;
 | 
				
			||||||
 | 
					        this.body = body;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function throwIfNotSuccess(response) {
 | 
					export function throwIfNotSuccess(response) {
 | 
				
			||||||
    if (!response.ok) {
 | 
					    if (!response.ok) {
 | 
				
			||||||
        if (response.status === 401) {
 | 
					        if (response.status === 401) {
 | 
				
			||||||
            return new Promise((resolve, reject) => {
 | 
					            return new Promise((resolve, reject) => {
 | 
				
			||||||
                response.json().then(body => reject(new AuthenticationError(response.status, body)));
 | 
					                response.json().then(body => reject(new AuthenticationError(response.status, body)));
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					        } else if (response.status === 403) {
 | 
				
			||||||
 | 
					            return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					                response.json().then(body => reject(new ForbiddenError(response.status, body)));
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        } else if (response.status > 399 && response.status < 404) {
 | 
					        } else if (response.status > 399 && response.status < 404) {
 | 
				
			||||||
            return new Promise((resolve, reject) => {
 | 
					            return new Promise((resolve, reject) => {
 | 
				
			||||||
                response.json().then(body => {
 | 
					                response.json().then(body => {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										8
									
								
								frontend/src/permissions.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								frontend/src/permissions.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					export const ADMIN = 'ADMIN';
 | 
				
			||||||
 | 
					export const CREATE_FEATURE = 'CREATE_FEATURE';
 | 
				
			||||||
 | 
					export const UPDATE_FEATURE = 'UPDATE_FEATURE';
 | 
				
			||||||
 | 
					export const DELETE_FEATURE = 'DELETE_FEATURE';
 | 
				
			||||||
 | 
					export const CREATE_STRATEGY = 'CREATE_STRATEGY';
 | 
				
			||||||
 | 
					export const UPDATE_STRATEGY = 'UPDATE_STRATEGY';
 | 
				
			||||||
 | 
					export const DELETE_STRATEGY = 'DELETE_STRATEGY';
 | 
				
			||||||
 | 
					export const UPDATE_APPLICATION = 'UPDATE_APPLICATION';
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user