mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Fix/bugfixes (#279)
* fix: add try catch to copy * fix: show constraints on default strategy * fix: require name to submit context field * fix: require name and project id to be set in order to create a project * fix: change documentation icon * fix: only validate unique names on create * Update src/component/context/form-context-component.jsx Co-authored-by: Christopher Kolstad <chriswk@getunleash.ai> Co-authored-by: Christopher Kolstad <chriswk@getunleash.ai>
This commit is contained in:
		
							parent
							
								
									0340573199
								
							
						
					
					
						commit
						f8e34d53ff
					
				@ -3,5 +3,9 @@ import { makeStyles } from '@material-ui/styles';
 | 
				
			|||||||
export const useStyles = makeStyles({
 | 
					export const useStyles = makeStyles({
 | 
				
			||||||
    listItem: {
 | 
					    listItem: {
 | 
				
			||||||
        padding: 0,
 | 
					        padding: 0,
 | 
				
			||||||
 | 
					        ['& a']: {
 | 
				
			||||||
 | 
					            textDecoration: 'none',
 | 
				
			||||||
 | 
					            color: 'inherit',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,20 @@
 | 
				
			|||||||
import React, { Component } from 'react';
 | 
					import { Component } from 'react';
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
import { Button, Chip, TextField, Switch, Icon, Typography } from '@material-ui/core';
 | 
					import {
 | 
				
			||||||
 | 
					    Button,
 | 
				
			||||||
 | 
					    Chip,
 | 
				
			||||||
 | 
					    TextField,
 | 
				
			||||||
 | 
					    Switch,
 | 
				
			||||||
 | 
					    Icon,
 | 
				
			||||||
 | 
					    Typography,
 | 
				
			||||||
 | 
					} from '@material-ui/core';
 | 
				
			||||||
import styles from './Context.module.scss';
 | 
					import styles from './Context.module.scss';
 | 
				
			||||||
import classnames from 'classnames';
 | 
					import classnames from 'classnames';
 | 
				
			||||||
import { FormButtons, styles as commonStyles } from '../common';
 | 
					import { FormButtons, styles as commonStyles } from '../common';
 | 
				
			||||||
import { trim } from '../common/util';
 | 
					import { trim } from '../common/util';
 | 
				
			||||||
import PageContent from '../common/PageContent/PageContent';
 | 
					import PageContent from '../common/PageContent/PageContent';
 | 
				
			||||||
 | 
					import ConditionallyRender from '../common/ConditionallyRender';
 | 
				
			||||||
 | 
					import { Alert } from '@material-ui/lab';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const sortIgnoreCase = (a, b) => {
 | 
					const sortIgnoreCase = (a, b) => {
 | 
				
			||||||
    a = a.toLowerCase();
 | 
					    a = a.toLowerCase();
 | 
				
			||||||
@ -23,9 +32,26 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
            errors: {},
 | 
					            errors: {},
 | 
				
			||||||
            currentLegalValue: '',
 | 
					            currentLegalValue: '',
 | 
				
			||||||
            dirty: false,
 | 
					            dirty: false,
 | 
				
			||||||
 | 
					            focusedLegalValue: false,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    handleKeydown = e => {
 | 
				
			||||||
 | 
					        if (e.key === 'Enter' && this.state.focusedLegalValue) {
 | 
				
			||||||
 | 
					            this.addLegalValue(e);
 | 
				
			||||||
 | 
					        } else if (e.key === 'Enter') {
 | 
				
			||||||
 | 
					            this.onSubmit(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    componentDidMount() {
 | 
				
			||||||
 | 
					        window.addEventListener('keydown', this.handleKeydown);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    componentWillUnmount() {
 | 
				
			||||||
 | 
					        window.removeEventListener('keydown', this.handleKeydown);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static getDerivedStateFromProps(props, state) {
 | 
					    static getDerivedStateFromProps(props, state) {
 | 
				
			||||||
        if (state.contextField.initial && !props.contextField.initial) {
 | 
					        if (state.contextField.initial && !props.contextField.initial) {
 | 
				
			||||||
            return { contextField: props.contextField };
 | 
					            return { contextField: props.contextField };
 | 
				
			||||||
@ -42,15 +68,19 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    validateContextName = async name => {
 | 
					    validateContextName = async name => {
 | 
				
			||||||
        const { errors } = this.state;
 | 
					        const { errors } = this.state;
 | 
				
			||||||
        const { validateName } = this.props;
 | 
					        const { validateName, editMode } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (editMode) return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            await validateName(name);
 | 
					            await validateName(name);
 | 
				
			||||||
            errors.name = undefined;
 | 
					            errors.name = undefined;
 | 
				
			||||||
        } catch (err) {
 | 
					        } catch (err) {
 | 
				
			||||||
            errors.name = err.message;
 | 
					            errors.name = err.message;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.setState({ errors });
 | 
					        this.setState({ errors });
 | 
				
			||||||
 | 
					        if (errors.name) return false;
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    onCancel = evt => {
 | 
					    onCancel = evt => {
 | 
				
			||||||
@ -58,10 +88,23 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
        this.props.history.push('/context');
 | 
					        this.props.history.push('/context');
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    onSubmit = evt => {
 | 
					    onSubmit = async evt => {
 | 
				
			||||||
        evt.preventDefault();
 | 
					        evt.preventDefault();
 | 
				
			||||||
        const { contextField } = this.state;
 | 
					        const { contextField } = this.state;
 | 
				
			||||||
        this.props.submit(contextField).then(() => this.props.history.push('/context'));
 | 
					
 | 
				
			||||||
 | 
					        const valid = await this.validateContextName(contextField.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (valid) {
 | 
				
			||||||
 | 
					            this.props
 | 
				
			||||||
 | 
					                .submit(contextField)
 | 
				
			||||||
 | 
					                .then(() => this.props.history.push('/context'))
 | 
				
			||||||
 | 
					                .catch(e =>
 | 
				
			||||||
 | 
					                    this.setState(prev => ({
 | 
				
			||||||
 | 
					                        ...prev,
 | 
				
			||||||
 | 
					                        errors: { api: e.toString() },
 | 
				
			||||||
 | 
					                    }))
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    updateCurrentLegalValue = evt => {
 | 
					    updateCurrentLegalValue = evt => {
 | 
				
			||||||
@ -82,7 +125,9 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const legalValues = contextField.legalValues.concat(trim(currentLegalValue));
 | 
					        const legalValues = contextField.legalValues.concat(
 | 
				
			||||||
 | 
					            trim(currentLegalValue)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
        contextField.legalValues = legalValues.sort(sortIgnoreCase);
 | 
					        contextField.legalValues = legalValues.sort(sortIgnoreCase);
 | 
				
			||||||
        this.setState({
 | 
					        this.setState({
 | 
				
			||||||
            contextField,
 | 
					            contextField,
 | 
				
			||||||
@ -93,7 +138,9 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    removeLegalValue = index => {
 | 
					    removeLegalValue = index => {
 | 
				
			||||||
        const { contextField } = this.state;
 | 
					        const { contextField } = this.state;
 | 
				
			||||||
        const legalValues = contextField.legalValues.filter((_, i) => i !== index);
 | 
					        const legalValues = contextField.legalValues.filter(
 | 
				
			||||||
 | 
					            (_, i) => i !== index
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
        contextField.legalValues = legalValues;
 | 
					        contextField.legalValues = legalValues;
 | 
				
			||||||
        this.setState({ contextField });
 | 
					        this.setState({ contextField });
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@ -115,11 +162,20 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <PageContent headerContent="Create context field">
 | 
					            <PageContent headerContent="Create context field">
 | 
				
			||||||
                <div className={styles.supporting}>
 | 
					                <div className={styles.supporting}>
 | 
				
			||||||
                    Context fields are a basic building block used in Unleash to control roll-out. They can be used
 | 
					                    Context fields are a basic building block used in Unleash to
 | 
				
			||||||
                    together with strategy constraints as part of the activation strategy evaluation.
 | 
					                    control roll-out. They can be used together with strategy
 | 
				
			||||||
 | 
					                    constraints as part of the activation strategy evaluation.
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <form onSubmit={this.onSubmit}>
 | 
					                <form onSubmit={this.onSubmit}>
 | 
				
			||||||
                    <section className={styles.formContainer}>
 | 
					                    <section className={styles.formContainer}>
 | 
				
			||||||
 | 
					                        <ConditionallyRender
 | 
				
			||||||
 | 
					                            condition={errors.api}
 | 
				
			||||||
 | 
					                            show={
 | 
				
			||||||
 | 
					                                <Alert severity="error">
 | 
				
			||||||
 | 
					                                    {this.state.errors.api}
 | 
				
			||||||
 | 
					                                </Alert>
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
                        <TextField
 | 
					                        <TextField
 | 
				
			||||||
                            className={commonStyles.fullwidth}
 | 
					                            className={commonStyles.fullwidth}
 | 
				
			||||||
                            label="Name"
 | 
					                            label="Name"
 | 
				
			||||||
@ -130,8 +186,12 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
                            disabled={editMode}
 | 
					                            disabled={editMode}
 | 
				
			||||||
                            variant="outlined"
 | 
					                            variant="outlined"
 | 
				
			||||||
                            size="small"
 | 
					                            size="small"
 | 
				
			||||||
                            onBlur={v => this.validateContextName(v.target.value)}
 | 
					                            onBlur={v =>
 | 
				
			||||||
                            onChange={v => this.setValue('name', trim(v.target.value))}
 | 
					                                this.validateContextName(v.target.value)
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            onChange={v =>
 | 
				
			||||||
 | 
					                                this.setValue('name', trim(v.target.value))
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                        <TextField
 | 
					                        <TextField
 | 
				
			||||||
                            className={commonStyles.fullwidth}
 | 
					                            className={commonStyles.fullwidth}
 | 
				
			||||||
@ -142,7 +202,9 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
                            variant="outlined"
 | 
					                            variant="outlined"
 | 
				
			||||||
                            size="small"
 | 
					                            size="small"
 | 
				
			||||||
                            defaultValue={contextField.description}
 | 
					                            defaultValue={contextField.description}
 | 
				
			||||||
                            onChange={v => this.setValue('description', v.target.value)}
 | 
					                            onChange={v =>
 | 
				
			||||||
 | 
					                                this.setValue('description', v.target.value)
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                        <br />
 | 
					                        <br />
 | 
				
			||||||
                        <br />
 | 
					                        <br />
 | 
				
			||||||
@ -150,8 +212,10 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
                    <section className={styles.inset}>
 | 
					                    <section className={styles.inset}>
 | 
				
			||||||
                        <h6 className={styles.h6}>Legal values</h6>
 | 
					                        <h6 className={styles.h6}>Legal values</h6>
 | 
				
			||||||
                        <p className={styles.alpha}>
 | 
					                        <p className={styles.alpha}>
 | 
				
			||||||
                            By defining the legal values the Unleash Admin UI will validate the user input. A concrete
 | 
					                            By defining the legal values the Unleash Admin UI
 | 
				
			||||||
                            example would be that we know all values for our “environment” (local, development, stage,
 | 
					                            will validate the user input. A concrete example
 | 
				
			||||||
 | 
					                            would be that we know all values for our
 | 
				
			||||||
 | 
					                            “environment” (local, development, stage,
 | 
				
			||||||
                            production).
 | 
					                            production).
 | 
				
			||||||
                        </p>
 | 
					                        </p>
 | 
				
			||||||
                        <div>
 | 
					                        <div>
 | 
				
			||||||
@ -159,6 +223,18 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
                                label="Value"
 | 
					                                label="Value"
 | 
				
			||||||
                                name="value"
 | 
					                                name="value"
 | 
				
			||||||
                                className={styles.valueField}
 | 
					                                className={styles.valueField}
 | 
				
			||||||
 | 
					                                onFocus={() =>
 | 
				
			||||||
 | 
					                                    this.setState(prev => ({
 | 
				
			||||||
 | 
					                                        ...prev,
 | 
				
			||||||
 | 
					                                        focusedLegalValue: true,
 | 
				
			||||||
 | 
					                                    }))
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                onBlur={() =>
 | 
				
			||||||
 | 
					                                    this.setState(prev => ({
 | 
				
			||||||
 | 
					                                        ...prev,
 | 
				
			||||||
 | 
					                                        focusedLegalValue: false,
 | 
				
			||||||
 | 
					                                    }))
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
                                value={this.state.currentLegalValue}
 | 
					                                value={this.state.currentLegalValue}
 | 
				
			||||||
                                error={!!errors.currentLegalValue}
 | 
					                                error={!!errors.currentLegalValue}
 | 
				
			||||||
                                helperText={errors.currentLegalValue}
 | 
					                                helperText={errors.currentLegalValue}
 | 
				
			||||||
@ -176,15 +252,28 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
                                Add
 | 
					                                Add
 | 
				
			||||||
                            </Button>
 | 
					                            </Button>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                        <div>{contextField.legalValues.map(this.renderLegalValue)}</div>
 | 
					                        <div>
 | 
				
			||||||
 | 
					                            {contextField.legalValues.map(
 | 
				
			||||||
 | 
					                                this.renderLegalValue
 | 
				
			||||||
 | 
					                            )}
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
                    </section>
 | 
					                    </section>
 | 
				
			||||||
                    <br />
 | 
					                    <br />
 | 
				
			||||||
                    <section>
 | 
					                    <section>
 | 
				
			||||||
                        <Typography variant="subtitle1">Custom stickiness (beta)</Typography>
 | 
					                        <Typography variant="subtitle1">
 | 
				
			||||||
                        <p className={classnames(styles.alpha, styles.formContainer)}>
 | 
					                            Custom stickiness (beta)
 | 
				
			||||||
                            By enabling stickiness on this context field you can use it together with the
 | 
					                        </Typography>
 | 
				
			||||||
                            flexible-rollout strategy. This will guarantee a consistent behavior for specific values of
 | 
					                        <p
 | 
				
			||||||
                            this context field. PS! Not all client SDK's support this feature yet!{' '}
 | 
					                            className={classnames(
 | 
				
			||||||
 | 
					                                styles.alpha,
 | 
				
			||||||
 | 
					                                styles.formContainer
 | 
				
			||||||
 | 
					                            )}
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
 | 
					                            By enabling stickiness on this context field you can
 | 
				
			||||||
 | 
					                            use it together with the flexible-rollout strategy.
 | 
				
			||||||
 | 
					                            This will guarantee a consistent behavior for
 | 
				
			||||||
 | 
					                            specific values of this context field. PS! Not all
 | 
				
			||||||
 | 
					                            client SDK's support this feature yet!{' '}
 | 
				
			||||||
                            <a
 | 
					                            <a
 | 
				
			||||||
                                href="https://unleash.github.io/docs/activation_strategy#flexiblerollout"
 | 
					                                href="https://unleash.github.io/docs/activation_strategy#flexiblerollout"
 | 
				
			||||||
                                target="_blank"
 | 
					                                target="_blank"
 | 
				
			||||||
@ -193,14 +282,24 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
                                Read more
 | 
					                                Read more
 | 
				
			||||||
                            </a>
 | 
					                            </a>
 | 
				
			||||||
                        </p>
 | 
					                        </p>
 | 
				
			||||||
 | 
					                        {console.log(contextField.stickiness)}
 | 
				
			||||||
                        <Switch
 | 
					                        <Switch
 | 
				
			||||||
                            label="Allow stickiness"
 | 
					                            label="Allow stickiness"
 | 
				
			||||||
 | 
					                            checked={contextField.stickiness}
 | 
				
			||||||
                            value={contextField.stickiness}
 | 
					                            value={contextField.stickiness}
 | 
				
			||||||
                            onChange={() => this.setValue('stickiness', !contextField.stickiness)}
 | 
					                            onChange={() =>
 | 
				
			||||||
 | 
					                                this.setValue(
 | 
				
			||||||
 | 
					                                    'stickiness',
 | 
				
			||||||
 | 
					                                    !contextField.stickiness
 | 
				
			||||||
 | 
					                                )
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    </section>
 | 
					                    </section>
 | 
				
			||||||
                    <div className={styles.formButtons}>
 | 
					                    <div className={styles.formButtons}>
 | 
				
			||||||
                        <FormButtons submitText={submitText} onCancel={this.onCancel} />
 | 
					                        <FormButtons
 | 
				
			||||||
 | 
					                            submitText={submitText}
 | 
				
			||||||
 | 
					                            onCancel={this.onCancel}
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </form>
 | 
					                </form>
 | 
				
			||||||
            </PageContent>
 | 
					            </PageContent>
 | 
				
			||||||
 | 
				
			|||||||
@ -12,22 +12,45 @@ const StrategyCardContent = ({ strategy, strategyDefinition }) => {
 | 
				
			|||||||
    const resolveContent = () => {
 | 
					    const resolveContent = () => {
 | 
				
			||||||
        switch (strategy.name) {
 | 
					        switch (strategy.name) {
 | 
				
			||||||
            case 'default':
 | 
					            case 'default':
 | 
				
			||||||
                return <StrategyCardContentDefault />;
 | 
					                return <StrategyCardContentDefault strategy={strategy} />;
 | 
				
			||||||
            case 'flexibleRollout':
 | 
					            case 'flexibleRollout':
 | 
				
			||||||
                return <StrategyCardContentFlexible strategy={strategy} />;
 | 
					                return <StrategyCardContentFlexible strategy={strategy} />;
 | 
				
			||||||
            case 'userWithId':
 | 
					            case 'userWithId':
 | 
				
			||||||
                return <StrategyCardContentList parameter={'userIds'} valuesName={'userIds'} strategy={strategy} />;
 | 
					                return (
 | 
				
			||||||
 | 
					                    <StrategyCardContentList
 | 
				
			||||||
 | 
					                        parameter={'userIds'}
 | 
				
			||||||
 | 
					                        valuesName={'userIds'}
 | 
				
			||||||
 | 
					                        strategy={strategy}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
            case 'gradualRolloutRandom':
 | 
					            case 'gradualRolloutRandom':
 | 
				
			||||||
                return <StrategyCardContentGradRandom strategy={strategy} />;
 | 
					                return <StrategyCardContentGradRandom strategy={strategy} />;
 | 
				
			||||||
            case 'remoteAddress':
 | 
					            case 'remoteAddress':
 | 
				
			||||||
                return <StrategyCardContentList parameter={'IPs'} valuesName={'IPs'} strategy={strategy} />;
 | 
					                return (
 | 
				
			||||||
 | 
					                    <StrategyCardContentList
 | 
				
			||||||
 | 
					                        parameter={'IPs'}
 | 
				
			||||||
 | 
					                        valuesName={'IPs'}
 | 
				
			||||||
 | 
					                        strategy={strategy}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
            case 'applicationHostname':
 | 
					            case 'applicationHostname':
 | 
				
			||||||
                return <StrategyCardContentList parameter={'hostNames'} valuesName={'hostnames'} strategy={strategy} />;
 | 
					                return (
 | 
				
			||||||
 | 
					                    <StrategyCardContentList
 | 
				
			||||||
 | 
					                        parameter={'hostNames'}
 | 
				
			||||||
 | 
					                        valuesName={'hostnames'}
 | 
				
			||||||
 | 
					                        strategy={strategy}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
            case 'gradualRolloutUserId':
 | 
					            case 'gradualRolloutUserId':
 | 
				
			||||||
            case 'gradualRolloutSessionId':
 | 
					            case 'gradualRolloutSessionId':
 | 
				
			||||||
                return <StrategyCardContentRollout strategy={strategy} />;
 | 
					                return <StrategyCardContentRollout strategy={strategy} />;
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                return <StrategyCardContentCustom strategy={strategy} strategyDefinition={strategyDefinition} />;
 | 
					                return (
 | 
				
			||||||
 | 
					                    <StrategyCardContentCustom
 | 
				
			||||||
 | 
					                        strategy={strategy}
 | 
				
			||||||
 | 
					                        strategyDefinition={strategyDefinition}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,14 +3,29 @@ import React from 'react';
 | 
				
			|||||||
import { Typography } from '@material-ui/core';
 | 
					import { Typography } from '@material-ui/core';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { useCommonStyles } from '../../../../../../common.styles';
 | 
					import { useCommonStyles } from '../../../../../../common.styles';
 | 
				
			||||||
 | 
					import ConditionallyRender from '../../../../../common/ConditionallyRender';
 | 
				
			||||||
 | 
					import StrategyCardConstraints from '../common/StrategyCardConstraints/StrategyCardConstraints';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StrategyCardContentDefault = () => {
 | 
					const StrategyCardContentDefault = ({ strategy }) => {
 | 
				
			||||||
    const commonStyles = useCommonStyles();
 | 
					    const commonStyles = useCommonStyles();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { constraints } = strategy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Typography className={commonStyles.textCenter}>
 | 
					        <>
 | 
				
			||||||
            The default strategy is either on or off for all users.
 | 
					            <Typography className={commonStyles.textCenter}>
 | 
				
			||||||
        </Typography>
 | 
					                The default strategy is either on or off for all users.
 | 
				
			||||||
 | 
					            </Typography>
 | 
				
			||||||
 | 
					            <ConditionallyRender
 | 
				
			||||||
 | 
					                condition={constraints && constraints.length > 0}
 | 
				
			||||||
 | 
					                show={
 | 
				
			||||||
 | 
					                    <>
 | 
				
			||||||
 | 
					                        <div className={commonStyles.divider} />
 | 
				
			||||||
 | 
					                        <StrategyCardConstraints constraints={constraints} />
 | 
				
			||||||
 | 
					                    </>
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        </>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -15,8 +15,7 @@ import MenuIcon from '@material-ui/icons/Menu';
 | 
				
			|||||||
import Breadcrumb from '../breadcrumb';
 | 
					import Breadcrumb from '../breadcrumb';
 | 
				
			||||||
import UserProfile from '../../user/UserProfile';
 | 
					import UserProfile from '../../user/UserProfile';
 | 
				
			||||||
import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender';
 | 
					import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender';
 | 
				
			||||||
import HelpIcon from '@material-ui/icons/Help';
 | 
					import MenuBookIcon from '@material-ui/icons/MenuBook';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import { useStyles } from './styles';
 | 
					import { useStyles } from './styles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Header = ({ uiConfig, init }) => {
 | 
					const Header = ({ uiConfig, init }) => {
 | 
				
			||||||
@ -64,7 +63,7 @@ const Header = ({ uiConfig, init }) => {
 | 
				
			|||||||
                                rel="noopener noreferrer"
 | 
					                                rel="noopener noreferrer"
 | 
				
			||||||
                                className={styles.docsLink}
 | 
					                                className={styles.docsLink}
 | 
				
			||||||
                            >
 | 
					                            >
 | 
				
			||||||
                                <HelpIcon className={styles.docsIcon} />
 | 
					                                <MenuBookIcon className={styles.docsIcon} />
 | 
				
			||||||
                            </a>
 | 
					                            </a>
 | 
				
			||||||
                        </Tooltip>
 | 
					                        </Tooltip>
 | 
				
			||||||
                        <UserProfile />
 | 
					                        <UserProfile />
 | 
				
			||||||
 | 
				
			|||||||
@ -3,5 +3,9 @@ import { makeStyles } from '@material-ui/styles';
 | 
				
			|||||||
export const useStyles = makeStyles({
 | 
					export const useStyles = makeStyles({
 | 
				
			||||||
    listItem: {
 | 
					    listItem: {
 | 
				
			||||||
        padding: 0,
 | 
					        padding: 0,
 | 
				
			||||||
 | 
					        ['& a']: {
 | 
				
			||||||
 | 
					            textDecoration: 'none',
 | 
				
			||||||
 | 
					            color: 'inherit',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -40,7 +40,9 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    validateId = async id => {
 | 
					    validateId = async id => {
 | 
				
			||||||
        const { errors } = this.state;
 | 
					        const { errors } = this.state;
 | 
				
			||||||
        const { validateId } = this.props;
 | 
					        const { validateId, editMode } = this.props;
 | 
				
			||||||
 | 
					        if (editMode) return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            await validateId(id);
 | 
					            await validateId(id);
 | 
				
			||||||
            errors.id = undefined;
 | 
					            errors.id = undefined;
 | 
				
			||||||
@ -49,6 +51,26 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.setState({ errors });
 | 
					        this.setState({ errors });
 | 
				
			||||||
 | 
					        if (errors.id) return false;
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    validateName = () => {
 | 
				
			||||||
 | 
					        const { project } = this.state;
 | 
				
			||||||
 | 
					        if (project.name.length === 0) {
 | 
				
			||||||
 | 
					            this.setState(prev => ({
 | 
				
			||||||
 | 
					                errors: { ...prev.errors, name: 'Name can not be empty.' },
 | 
				
			||||||
 | 
					            }));
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    validate = async id => {
 | 
				
			||||||
 | 
					        const validId = await this.validateId(id);
 | 
				
			||||||
 | 
					        const validName = this.validateName();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return validId && validName;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    onCancel = evt => {
 | 
					    onCancel = evt => {
 | 
				
			||||||
@ -59,8 +81,13 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
    onSubmit = async evt => {
 | 
					    onSubmit = async evt => {
 | 
				
			||||||
        evt.preventDefault();
 | 
					        evt.preventDefault();
 | 
				
			||||||
        const { project } = this.state;
 | 
					        const { project } = this.state;
 | 
				
			||||||
        await this.props.submit(project);
 | 
					
 | 
				
			||||||
        this.props.history.push('/projects');
 | 
					        const valid = await this.validate(project.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (valid) {
 | 
				
			||||||
 | 
					            await this.props.submit(project);
 | 
				
			||||||
 | 
					            this.props.history.push('/projects');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
@ -71,12 +98,19 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <PageContent headerContent={`${submitText} Project`}>
 | 
					            <PageContent headerContent={`${submitText} Project`}>
 | 
				
			||||||
                <Typography variant="subtitle1" style={{ marginBottom: '0.5rem' }}>
 | 
					                <Typography
 | 
				
			||||||
                    Projects allows you to group feature toggles together in the management UI.
 | 
					                    variant="subtitle1"
 | 
				
			||||||
 | 
					                    style={{ marginBottom: '0.5rem' }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    Projects allows you to group feature toggles together in the
 | 
				
			||||||
 | 
					                    management UI.
 | 
				
			||||||
                </Typography>
 | 
					                </Typography>
 | 
				
			||||||
                <form
 | 
					                <form
 | 
				
			||||||
                    onSubmit={this.onSubmit}
 | 
					                    onSubmit={this.onSubmit}
 | 
				
			||||||
                    className={classnames(commonStyles.contentSpacing, styles.formContainer)}
 | 
					                    className={classnames(
 | 
				
			||||||
 | 
					                        commonStyles.contentSpacing,
 | 
				
			||||||
 | 
					                        styles.formContainer
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                    <TextField
 | 
					                    <TextField
 | 
				
			||||||
                        label="Project Id"
 | 
					                        label="Project Id"
 | 
				
			||||||
@ -89,7 +123,9 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
                        variant="outlined"
 | 
					                        variant="outlined"
 | 
				
			||||||
                        size="small"
 | 
					                        size="small"
 | 
				
			||||||
                        onBlur={v => this.validateId(v.target.value)}
 | 
					                        onBlur={v => this.validateId(v.target.value)}
 | 
				
			||||||
                        onChange={v => this.setValue('id', trim(v.target.value))}
 | 
					                        onChange={v =>
 | 
				
			||||||
 | 
					                            this.setValue('id', trim(v.target.value))
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
                    <br />
 | 
					                    <br />
 | 
				
			||||||
                    <TextField
 | 
					                    <TextField
 | 
				
			||||||
@ -114,14 +150,21 @@ class AddContextComponent extends Component {
 | 
				
			|||||||
                        size="small"
 | 
					                        size="small"
 | 
				
			||||||
                        multiline
 | 
					                        multiline
 | 
				
			||||||
                        value={project.description}
 | 
					                        value={project.description}
 | 
				
			||||||
                        onChange={v => this.setValue('description', v.target.value)}
 | 
					                        onChange={v =>
 | 
				
			||||||
 | 
					                            this.setValue('description', v.target.value)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                    <ConditionallyRender
 | 
				
			||||||
 | 
					                        condition={hasAccess(CREATE_PROJECT)}
 | 
				
			||||||
 | 
					                        show={
 | 
				
			||||||
 | 
					                            <div className={styles.formButtons}>
 | 
				
			||||||
 | 
					                                <FormButtons
 | 
				
			||||||
 | 
					                                    submitText={submitText}
 | 
				
			||||||
 | 
					                                    onCancel={this.onCancel}
 | 
				
			||||||
 | 
					                                />
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
                    <ConditionallyRender condition={hasAccess(CREATE_PROJECT)} show={
 | 
					 | 
				
			||||||
                        <div className={styles.formButtons}>
 | 
					 | 
				
			||||||
                            <FormButtons submitText={submitText} onCancel={this.onCancel} />
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                    } />
 | 
					 | 
				
			||||||
                    
 | 
					 | 
				
			||||||
                </form>
 | 
					                </form>
 | 
				
			||||||
            </PageContent>
 | 
					            </PageContent>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
				
			|||||||
@ -22,24 +22,31 @@ const UserInviteLink = ({ inviteLink }: IInviteLinkProps) => {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const handleCopy = () => {
 | 
					    const handleCopy = () => {
 | 
				
			||||||
        return navigator.clipboard
 | 
					        try {
 | 
				
			||||||
            .writeText(inviteLink)
 | 
					            return navigator.clipboard
 | 
				
			||||||
            .then(() => {
 | 
					                .writeText(inviteLink)
 | 
				
			||||||
                setSnackbar({
 | 
					                .then(() => {
 | 
				
			||||||
                    show: true,
 | 
					                    setSnackbar({
 | 
				
			||||||
                    type: 'success',
 | 
					                        show: true,
 | 
				
			||||||
                    text: 'Successfully copied invite link.',
 | 
					                        type: 'success',
 | 
				
			||||||
 | 
					                        text: 'Successfully copied invite link.',
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .catch(() => {
 | 
				
			||||||
 | 
					                    setError();
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            })
 | 
					        } catch (e) {
 | 
				
			||||||
            .catch(() => {
 | 
					            setError();
 | 
				
			||||||
                setSnackbar({
 | 
					        }
 | 
				
			||||||
                    show: true,
 | 
					 | 
				
			||||||
                    type: 'error',
 | 
					 | 
				
			||||||
                    text: 'Could not copy invite link.',
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const setError = () =>
 | 
				
			||||||
 | 
					        setSnackbar({
 | 
				
			||||||
 | 
					            show: true,
 | 
				
			||||||
 | 
					            type: 'error',
 | 
				
			||||||
 | 
					            text: 'Could not copy invite link.',
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <div
 | 
					        <div
 | 
				
			||||||
            style={{
 | 
					            style={{
 | 
				
			||||||
 | 
				
			|||||||
@ -39,7 +39,10 @@ export function createContextField(context) {
 | 
				
			|||||||
        api
 | 
					        api
 | 
				
			||||||
            .create(context)
 | 
					            .create(context)
 | 
				
			||||||
            .then(() => dispatch(addContextField(context)))
 | 
					            .then(() => dispatch(addContextField(context)))
 | 
				
			||||||
            .catch(dispatchError(dispatch, ERROR_ADD_CONTEXT_FIELD));
 | 
					            .catch(e => {
 | 
				
			||||||
 | 
					                dispatchError(dispatch, ERROR_ADD_CONTEXT_FIELD);
 | 
				
			||||||
 | 
					                throw e;
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function updateContextField(context) {
 | 
					export function updateContextField(context) {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user