mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Feat/new strategy variants tab (#5649)
This PR sets up the variants tab for the new strategy configuration. Also some minor adjustments to the new form: * Change where padding is controlled to allow us to have more granular control over how the buttons width and border should look and have the tabs have full-width borders across the form * Move the buttons to be absolutely positioned at the bottom of the page * Move where we display banners to avoid clutter <img width="1284" alt="Skjermbilde 2023-12-14 kl 21 17 53" src="https://github.com/Unleash/unleash/assets/16081982/45e6a364-e4aa-47ac-b420-f3be9b39a15e">
This commit is contained in:
		
							parent
							
								
									848415c5ca
								
							
						
					
					
						commit
						cbd6aa1324
					
				@ -1,15 +1,6 @@
 | 
				
			|||||||
import React, { useState } from 'react';
 | 
					import React, { useState } from 'react';
 | 
				
			||||||
import { useNavigate } from 'react-router-dom';
 | 
					import { useNavigate } from 'react-router-dom';
 | 
				
			||||||
import {
 | 
					import { Alert, Button, styled, Tabs, Tab, Box, Divider } from '@mui/material';
 | 
				
			||||||
    Alert,
 | 
					 | 
				
			||||||
    Button,
 | 
					 | 
				
			||||||
    styled,
 | 
					 | 
				
			||||||
    Tabs,
 | 
					 | 
				
			||||||
    Tab,
 | 
					 | 
				
			||||||
    Typography,
 | 
					 | 
				
			||||||
    Divider,
 | 
					 | 
				
			||||||
    Box,
 | 
					 | 
				
			||||||
} from '@mui/material';
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    IFeatureStrategy,
 | 
					    IFeatureStrategy,
 | 
				
			||||||
    IFeatureStrategyParameters,
 | 
					    IFeatureStrategyParameters,
 | 
				
			||||||
@ -42,6 +33,7 @@ import { FeatureStrategyTitle } from './FeatureStrategyTitle/FeatureStrategyTitl
 | 
				
			|||||||
import { FeatureStrategyEnabledDisabled } from './FeatureStrategyEnabledDisabled/FeatureStrategyEnabledDisabled';
 | 
					import { FeatureStrategyEnabledDisabled } from './FeatureStrategyEnabledDisabled/FeatureStrategyEnabledDisabled';
 | 
				
			||||||
import { StrategyVariants } from 'component/feature/StrategyTypes/StrategyVariants';
 | 
					import { StrategyVariants } from 'component/feature/StrategyTypes/StrategyVariants';
 | 
				
			||||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
 | 
					import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
 | 
				
			||||||
 | 
					import { formatStrategyName } from 'utils/strategyNames';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IFeatureStrategyFormProps {
 | 
					interface IFeatureStrategyFormProps {
 | 
				
			||||||
    feature: IFeatureToggle;
 | 
					    feature: IFeatureToggle;
 | 
				
			||||||
@ -77,8 +69,15 @@ const StyledDividerContent = styled(Box)(({ theme }) => ({
 | 
				
			|||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledForm = styled('form')(({ theme }) => ({
 | 
					const StyledForm = styled('form')(({ theme }) => ({
 | 
				
			||||||
    display: 'grid',
 | 
					    position: 'relative',
 | 
				
			||||||
 | 
					    display: 'flex',
 | 
				
			||||||
 | 
					    flexDirection: 'column',
 | 
				
			||||||
    gap: theme.spacing(2),
 | 
					    gap: theme.spacing(2),
 | 
				
			||||||
 | 
					    padding: theme.spacing(6),
 | 
				
			||||||
 | 
					    paddingBottom: theme.spacing(12),
 | 
				
			||||||
 | 
					    paddingTop: theme.spacing(4),
 | 
				
			||||||
 | 
					    overflow: 'auto',
 | 
				
			||||||
 | 
					    height: '100%',
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledHr = styled('hr')(({ theme }) => ({
 | 
					const StyledHr = styled('hr')(({ theme }) => ({
 | 
				
			||||||
@ -89,11 +88,31 @@ const StyledHr = styled('hr')(({ theme }) => ({
 | 
				
			|||||||
    background: theme.palette.background.elevation2,
 | 
					    background: theme.palette.background.elevation2,
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledButtons = styled('div')(({ theme }) => ({
 | 
					const StyledTitle = styled('h1')(({ theme }) => ({
 | 
				
			||||||
 | 
					    fontWeight: 'normal',
 | 
				
			||||||
    display: 'flex',
 | 
					    display: 'flex',
 | 
				
			||||||
 | 
					    alignItems: 'center',
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StyledButtons = styled('div')(({ theme }) => ({
 | 
				
			||||||
 | 
					    bottom: 0,
 | 
				
			||||||
 | 
					    right: 0,
 | 
				
			||||||
 | 
					    left: 0,
 | 
				
			||||||
 | 
					    position: 'absolute',
 | 
				
			||||||
 | 
					    display: 'flex',
 | 
				
			||||||
 | 
					    padding: theme.spacing(3),
 | 
				
			||||||
 | 
					    paddingRight: theme.spacing(6),
 | 
				
			||||||
 | 
					    paddingLeft: theme.spacing(6),
 | 
				
			||||||
 | 
					    backgroundColor: theme.palette.common.white,
 | 
				
			||||||
    justifyContent: 'end',
 | 
					    justifyContent: 'end',
 | 
				
			||||||
    gap: theme.spacing(2),
 | 
					    borderTop: `1px solid ${theme.palette.divider}`,
 | 
				
			||||||
    paddingBottom: theme.spacing(10),
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StyledTabs = styled(Tabs)(({ theme }) => ({
 | 
				
			||||||
 | 
					    borderTop: `1px solid ${theme.palette.divider}`,
 | 
				
			||||||
 | 
					    borderBottom: `1px solid ${theme.palette.divider}`,
 | 
				
			||||||
 | 
					    paddingLeft: theme.spacing(6),
 | 
				
			||||||
 | 
					    paddingRight: theme.spacing(6),
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledBox = styled(Box)(({ theme }) => ({
 | 
					const StyledBox = styled(Box)(({ theme }) => ({
 | 
				
			||||||
@ -111,6 +130,15 @@ const StyledTargetingHeader = styled('div')(({ theme }) => ({
 | 
				
			|||||||
    marginTop: theme.spacing(1.5),
 | 
					    marginTop: theme.spacing(1.5),
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StyledHeaderBox = styled(Box)(({ theme }) => ({
 | 
				
			||||||
 | 
					    display: 'flex',
 | 
				
			||||||
 | 
					    alignItems: 'center',
 | 
				
			||||||
 | 
					    paddingLeft: theme.spacing(6),
 | 
				
			||||||
 | 
					    paddingRight: theme.spacing(6),
 | 
				
			||||||
 | 
					    paddingTop: theme.spacing(2),
 | 
				
			||||||
 | 
					    paddingBottom: theme.spacing(2),
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const NewFeatureStrategyForm = ({
 | 
					export const NewFeatureStrategyForm = ({
 | 
				
			||||||
    projectId,
 | 
					    projectId,
 | 
				
			||||||
    feature,
 | 
					    feature,
 | 
				
			||||||
@ -225,173 +253,186 @@ export const NewFeatureStrategyForm = ({
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <StyledForm onSubmit={onSubmitWithValidation}>
 | 
					        <>
 | 
				
			||||||
            <Tabs value={tab} onChange={handleChange}>
 | 
					            <StyledHeaderBox>
 | 
				
			||||||
 | 
					                <StyledTitle>
 | 
				
			||||||
 | 
					                    {formatStrategyName(strategy.name || '')}
 | 
				
			||||||
 | 
					                </StyledTitle>
 | 
				
			||||||
 | 
					            </StyledHeaderBox>
 | 
				
			||||||
 | 
					            <StyledTabs value={tab} onChange={handleChange}>
 | 
				
			||||||
                <Tab label='General' />
 | 
					                <Tab label='General' />
 | 
				
			||||||
                <Tab label='Targeting' />
 | 
					                <Tab label='Targeting' />
 | 
				
			||||||
                <Tab label='Variants' />
 | 
					                <Tab label='Variants' />
 | 
				
			||||||
            </Tabs>
 | 
					            </StyledTabs>
 | 
				
			||||||
            <ConditionallyRender
 | 
					            <StyledForm onSubmit={onSubmitWithValidation}>
 | 
				
			||||||
                condition={tab === 0}
 | 
					                <ConditionallyRender
 | 
				
			||||||
                show={
 | 
					                    condition={tab === 0}
 | 
				
			||||||
                    <>
 | 
					                    show={
 | 
				
			||||||
                        <ConditionallyRender
 | 
					                        <>
 | 
				
			||||||
                            condition={hasChangeRequestInReviewForEnvironment}
 | 
					                            <FeatureStrategyTitle
 | 
				
			||||||
                            show={alert}
 | 
					                                title={strategy.title || ''}
 | 
				
			||||||
                            elseShow={
 | 
					                                setTitle={(title) => {
 | 
				
			||||||
 | 
					                                    setStrategy((prev) => ({
 | 
				
			||||||
 | 
					                                        ...prev,
 | 
				
			||||||
 | 
					                                        title,
 | 
				
			||||||
 | 
					                                    }));
 | 
				
			||||||
 | 
					                                }}
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					                            <FeatureStrategyEnabledDisabled
 | 
				
			||||||
 | 
					                                enabled={!strategy?.disabled}
 | 
				
			||||||
 | 
					                                onToggleEnabled={() =>
 | 
				
			||||||
 | 
					                                    setStrategy((strategyState) => ({
 | 
				
			||||||
 | 
					                                        ...strategyState,
 | 
				
			||||||
 | 
					                                        disabled: !strategyState.disabled,
 | 
				
			||||||
 | 
					                                    }))
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					                            <FeatureStrategyType
 | 
				
			||||||
 | 
					                                strategy={strategy}
 | 
				
			||||||
 | 
					                                strategyDefinition={strategyDefinition}
 | 
				
			||||||
 | 
					                                setStrategy={setStrategy}
 | 
				
			||||||
 | 
					                                validateParameter={validateParameter}
 | 
				
			||||||
 | 
					                                errors={errors}
 | 
				
			||||||
 | 
					                                hasAccess={access}
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            <ConditionallyRender
 | 
				
			||||||
 | 
					                                condition={
 | 
				
			||||||
 | 
					                                    hasChangeRequestInReviewForEnvironment
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                show={alert}
 | 
				
			||||||
 | 
					                                elseShow={
 | 
				
			||||||
 | 
					                                    <ConditionallyRender
 | 
				
			||||||
 | 
					                                        condition={Boolean(isChangeRequest)}
 | 
				
			||||||
 | 
					                                        show={
 | 
				
			||||||
 | 
					                                            <FeatureStrategyChangeRequestAlert
 | 
				
			||||||
 | 
					                                                environment={environmentId}
 | 
				
			||||||
 | 
					                                            />
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    />
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            <FeatureStrategyEnabled
 | 
				
			||||||
 | 
					                                projectId={feature.project}
 | 
				
			||||||
 | 
					                                featureId={feature.name}
 | 
				
			||||||
 | 
					                                environmentId={environmentId}
 | 
				
			||||||
 | 
					                            >
 | 
				
			||||||
                                <ConditionallyRender
 | 
					                                <ConditionallyRender
 | 
				
			||||||
                                    condition={Boolean(isChangeRequest)}
 | 
					                                    condition={Boolean(isChangeRequest)}
 | 
				
			||||||
                                    show={
 | 
					                                    show={
 | 
				
			||||||
                                        <FeatureStrategyChangeRequestAlert
 | 
					                                        <Alert severity='success'>
 | 
				
			||||||
                                            environment={environmentId}
 | 
					                                            This feature toggle is currently
 | 
				
			||||||
                                        />
 | 
					                                            enabled in the{' '}
 | 
				
			||||||
 | 
					                                            <strong>{environmentId}</strong>{' '}
 | 
				
			||||||
 | 
					                                            environment. Any changes made here
 | 
				
			||||||
 | 
					                                            will be available to users as soon
 | 
				
			||||||
 | 
					                                            as these changes are approved and
 | 
				
			||||||
 | 
					                                            applied.
 | 
				
			||||||
 | 
					                                        </Alert>
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
 | 
					                                    elseShow={
 | 
				
			||||||
 | 
					                                        <Alert severity='success'>
 | 
				
			||||||
 | 
					                                            This feature toggle is currently
 | 
				
			||||||
 | 
					                                            enabled in the{' '}
 | 
				
			||||||
 | 
					                                            <strong>{environmentId}</strong>{' '}
 | 
				
			||||||
 | 
					                                            environment. Any changes made here
 | 
				
			||||||
 | 
					                                            will be available to users as soon
 | 
				
			||||||
 | 
					                                            as you hit <strong>save</strong>.
 | 
				
			||||||
 | 
					                                        </Alert>
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                />
 | 
				
			||||||
 | 
					                            </FeatureStrategyEnabled>
 | 
				
			||||||
 | 
					                        </>
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <ConditionallyRender
 | 
				
			||||||
 | 
					                    condition={tab === 1}
 | 
				
			||||||
 | 
					                    show={
 | 
				
			||||||
 | 
					                        <>
 | 
				
			||||||
 | 
					                            <StyledTargetingHeader>
 | 
				
			||||||
 | 
					                                Segmentation and constraints allow you to set
 | 
				
			||||||
 | 
					                                filters on your strategies, so that they will
 | 
				
			||||||
 | 
					                                only be evaluated for users and applications
 | 
				
			||||||
 | 
					                                that match the specified preconditions.
 | 
				
			||||||
 | 
					                            </StyledTargetingHeader>
 | 
				
			||||||
 | 
					                            <FeatureStrategySegment
 | 
				
			||||||
 | 
					                                segments={segments}
 | 
				
			||||||
 | 
					                                setSegments={setSegments}
 | 
				
			||||||
 | 
					                                projectId={projectId}
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            <StyledBox>
 | 
				
			||||||
 | 
					                                <StyledDivider />
 | 
				
			||||||
 | 
					                                <StyledDividerContent>AND</StyledDividerContent>
 | 
				
			||||||
 | 
					                            </StyledBox>
 | 
				
			||||||
 | 
					                            <FeatureStrategyConstraints
 | 
				
			||||||
 | 
					                                projectId={feature.project}
 | 
				
			||||||
 | 
					                                environmentId={environmentId}
 | 
				
			||||||
 | 
					                                strategy={strategy}
 | 
				
			||||||
 | 
					                                setStrategy={setStrategy}
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					                        </>
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <ConditionallyRender
 | 
				
			||||||
 | 
					                    condition={tab === 2}
 | 
				
			||||||
 | 
					                    show={
 | 
				
			||||||
 | 
					                        <ConditionallyRender
 | 
				
			||||||
 | 
					                            condition={
 | 
				
			||||||
 | 
					                                strategy.parameters != null &&
 | 
				
			||||||
 | 
					                                'stickiness' in strategy.parameters
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            show={
 | 
				
			||||||
 | 
					                                <StrategyVariants
 | 
				
			||||||
 | 
					                                    strategy={strategy}
 | 
				
			||||||
 | 
					                                    setStrategy={setStrategy}
 | 
				
			||||||
 | 
					                                    environment={environmentId}
 | 
				
			||||||
 | 
					                                    projectId={projectId}
 | 
				
			||||||
                                />
 | 
					                                />
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                        <FeatureStrategyEnabled
 | 
					 | 
				
			||||||
                            projectId={feature.project}
 | 
					 | 
				
			||||||
                            featureId={feature.name}
 | 
					 | 
				
			||||||
                            environmentId={environmentId}
 | 
					 | 
				
			||||||
                        >
 | 
					 | 
				
			||||||
                            <ConditionallyRender
 | 
					 | 
				
			||||||
                                condition={Boolean(isChangeRequest)}
 | 
					 | 
				
			||||||
                                show={
 | 
					 | 
				
			||||||
                                    <Alert severity='success'>
 | 
					 | 
				
			||||||
                                        This feature toggle is currently enabled
 | 
					 | 
				
			||||||
                                        in the <strong>{environmentId}</strong>{' '}
 | 
					 | 
				
			||||||
                                        environment. Any changes made here will
 | 
					 | 
				
			||||||
                                        be available to users as soon as these
 | 
					 | 
				
			||||||
                                        changes are approved and applied.
 | 
					 | 
				
			||||||
                                    </Alert>
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                                elseShow={
 | 
					 | 
				
			||||||
                                    <Alert severity='success'>
 | 
					 | 
				
			||||||
                                        This feature toggle is currently enabled
 | 
					 | 
				
			||||||
                                        in the <strong>{environmentId}</strong>{' '}
 | 
					 | 
				
			||||||
                                        environment. Any changes made here will
 | 
					 | 
				
			||||||
                                        be available to users as soon as you hit{' '}
 | 
					 | 
				
			||||||
                                        <strong>save</strong>.
 | 
					 | 
				
			||||||
                                    </Alert>
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            />
 | 
					 | 
				
			||||||
                        </FeatureStrategyEnabled>
 | 
					 | 
				
			||||||
                        <StyledHr />
 | 
					 | 
				
			||||||
                        <FeatureStrategyTitle
 | 
					 | 
				
			||||||
                            title={strategy.title || ''}
 | 
					 | 
				
			||||||
                            setTitle={(title) => {
 | 
					 | 
				
			||||||
                                setStrategy((prev) => ({
 | 
					 | 
				
			||||||
                                    ...prev,
 | 
					 | 
				
			||||||
                                    title,
 | 
					 | 
				
			||||||
                                }));
 | 
					 | 
				
			||||||
                            }}
 | 
					 | 
				
			||||||
                        />
 | 
					 | 
				
			||||||
                        <FeatureStrategyEnabledDisabled
 | 
					 | 
				
			||||||
                            enabled={!strategy?.disabled}
 | 
					 | 
				
			||||||
                            onToggleEnabled={() =>
 | 
					 | 
				
			||||||
                                setStrategy((strategyState) => ({
 | 
					 | 
				
			||||||
                                    ...strategyState,
 | 
					 | 
				
			||||||
                                    disabled: !strategyState.disabled,
 | 
					 | 
				
			||||||
                                }))
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        />
 | 
					 | 
				
			||||||
                        <FeatureStrategyType
 | 
					 | 
				
			||||||
                            strategy={strategy}
 | 
					 | 
				
			||||||
                            strategyDefinition={strategyDefinition}
 | 
					 | 
				
			||||||
                            setStrategy={setStrategy}
 | 
					 | 
				
			||||||
                            validateParameter={validateParameter}
 | 
					 | 
				
			||||||
                            errors={errors}
 | 
					 | 
				
			||||||
                            hasAccess={access}
 | 
					 | 
				
			||||||
                        />
 | 
					 | 
				
			||||||
                    </>
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <ConditionallyRender
 | 
					 | 
				
			||||||
                condition={tab === 1}
 | 
					 | 
				
			||||||
                show={
 | 
					 | 
				
			||||||
                    <>
 | 
					 | 
				
			||||||
                        <StyledTargetingHeader>
 | 
					 | 
				
			||||||
                            Segmentation and constraints allow you to set
 | 
					 | 
				
			||||||
                            filters on your strategies, so that they will only
 | 
					 | 
				
			||||||
                            be evaluated for users and applications that match
 | 
					 | 
				
			||||||
                            the specified preconditions.
 | 
					 | 
				
			||||||
                        </StyledTargetingHeader>
 | 
					 | 
				
			||||||
                        <FeatureStrategySegment
 | 
					 | 
				
			||||||
                            segments={segments}
 | 
					 | 
				
			||||||
                            setSegments={setSegments}
 | 
					 | 
				
			||||||
                            projectId={projectId}
 | 
					 | 
				
			||||||
                        />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        <StyledBox>
 | 
					 | 
				
			||||||
                            <StyledDivider />
 | 
					 | 
				
			||||||
                            <StyledDividerContent>AND</StyledDividerContent>
 | 
					 | 
				
			||||||
                        </StyledBox>
 | 
					 | 
				
			||||||
                        <FeatureStrategyConstraints
 | 
					 | 
				
			||||||
                            projectId={feature.project}
 | 
					 | 
				
			||||||
                            environmentId={environmentId}
 | 
					 | 
				
			||||||
                            strategy={strategy}
 | 
					 | 
				
			||||||
                            setStrategy={setStrategy}
 | 
					 | 
				
			||||||
                        />
 | 
					 | 
				
			||||||
                    </>
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <ConditionallyRender
 | 
					 | 
				
			||||||
                condition={tab === 2}
 | 
					 | 
				
			||||||
                show={
 | 
					 | 
				
			||||||
                    <ConditionallyRender
 | 
					 | 
				
			||||||
                        condition={
 | 
					 | 
				
			||||||
                            strategy.parameters != null &&
 | 
					 | 
				
			||||||
                            'stickiness' in strategy.parameters
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        show={
 | 
					 | 
				
			||||||
                            <StrategyVariants
 | 
					 | 
				
			||||||
                                strategy={strategy}
 | 
					 | 
				
			||||||
                                setStrategy={setStrategy}
 | 
					 | 
				
			||||||
                                environment={environmentId}
 | 
					 | 
				
			||||||
                                projectId={projectId}
 | 
					 | 
				
			||||||
                            />
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <StyledButtons>
 | 
					 | 
				
			||||||
                <PermissionButton
 | 
					 | 
				
			||||||
                    permission={permission}
 | 
					 | 
				
			||||||
                    projectId={feature.project}
 | 
					 | 
				
			||||||
                    environmentId={environmentId}
 | 
					 | 
				
			||||||
                    variant='contained'
 | 
					 | 
				
			||||||
                    color='primary'
 | 
					 | 
				
			||||||
                    type='submit'
 | 
					 | 
				
			||||||
                    disabled={
 | 
					 | 
				
			||||||
                        loading ||
 | 
					 | 
				
			||||||
                        !hasValidConstraints ||
 | 
					 | 
				
			||||||
                        errors.hasFormErrors()
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    data-testid={STRATEGY_FORM_SUBMIT_ID}
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    {isChangeRequest
 | 
					 | 
				
			||||||
                        ? changeRequestButtonText
 | 
					 | 
				
			||||||
                        : 'Save strategy'}
 | 
					 | 
				
			||||||
                </PermissionButton>
 | 
					 | 
				
			||||||
                <Button
 | 
					 | 
				
			||||||
                    type='button'
 | 
					 | 
				
			||||||
                    color='primary'
 | 
					 | 
				
			||||||
                    onClick={onCancel ? onCancel : onDefaultCancel}
 | 
					 | 
				
			||||||
                    disabled={loading}
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    Cancel
 | 
					 | 
				
			||||||
                </Button>
 | 
					 | 
				
			||||||
                <FeatureStrategyProdGuard
 | 
					 | 
				
			||||||
                    open={showProdGuard}
 | 
					 | 
				
			||||||
                    onClose={() => setShowProdGuard(false)}
 | 
					 | 
				
			||||||
                    onClick={onSubmit}
 | 
					 | 
				
			||||||
                    loading={loading}
 | 
					 | 
				
			||||||
                    label='Save strategy'
 | 
					 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
            </StyledButtons>
 | 
					
 | 
				
			||||||
        </StyledForm>
 | 
					                <StyledButtons>
 | 
				
			||||||
 | 
					                    <PermissionButton
 | 
				
			||||||
 | 
					                        permission={permission}
 | 
				
			||||||
 | 
					                        projectId={feature.project}
 | 
				
			||||||
 | 
					                        environmentId={environmentId}
 | 
				
			||||||
 | 
					                        variant='contained'
 | 
				
			||||||
 | 
					                        color='primary'
 | 
				
			||||||
 | 
					                        type='submit'
 | 
				
			||||||
 | 
					                        disabled={
 | 
				
			||||||
 | 
					                            loading ||
 | 
				
			||||||
 | 
					                            !hasValidConstraints ||
 | 
				
			||||||
 | 
					                            errors.hasFormErrors()
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        data-testid={STRATEGY_FORM_SUBMIT_ID}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                        {isChangeRequest
 | 
				
			||||||
 | 
					                            ? changeRequestButtonText
 | 
				
			||||||
 | 
					                            : 'Save strategy'}
 | 
				
			||||||
 | 
					                    </PermissionButton>
 | 
				
			||||||
 | 
					                    <Button
 | 
				
			||||||
 | 
					                        type='button'
 | 
				
			||||||
 | 
					                        color='primary'
 | 
				
			||||||
 | 
					                        onClick={onCancel ? onCancel : onDefaultCancel}
 | 
				
			||||||
 | 
					                        disabled={loading}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                        Cancel
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                    <FeatureStrategyProdGuard
 | 
				
			||||||
 | 
					                        open={showProdGuard}
 | 
				
			||||||
 | 
					                        onClose={() => setShowProdGuard(false)}
 | 
				
			||||||
 | 
					                        onClick={onSubmit}
 | 
				
			||||||
 | 
					                        loading={loading}
 | 
				
			||||||
 | 
					                        label='Save strategy'
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                </StyledButtons>
 | 
				
			||||||
 | 
					            </StyledForm>
 | 
				
			||||||
 | 
					        </>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -180,10 +180,10 @@ export const NewFeatureStrategyCreate = () => {
 | 
				
			|||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <FormTemplate
 | 
					        <FormTemplate
 | 
				
			||||||
            modal
 | 
					            modal
 | 
				
			||||||
            title={formatStrategyName(strategyName)}
 | 
					 | 
				
			||||||
            description={featureStrategyHelp}
 | 
					            description={featureStrategyHelp}
 | 
				
			||||||
            documentationLink={featureStrategyDocsLink}
 | 
					            documentationLink={featureStrategyDocsLink}
 | 
				
			||||||
            documentationLinkLabel={featureStrategyDocsLinkLabel}
 | 
					            documentationLinkLabel={featureStrategyDocsLinkLabel}
 | 
				
			||||||
 | 
					            disablePadding
 | 
				
			||||||
            formatApiCode={() =>
 | 
					            formatApiCode={() =>
 | 
				
			||||||
                formatAddStrategyApiCode(
 | 
					                formatAddStrategyApiCode(
 | 
				
			||||||
                    projectId,
 | 
					                    projectId,
 | 
				
			||||||
 | 
				
			|||||||
@ -200,7 +200,7 @@ export const NewFeatureStrategyEdit = () => {
 | 
				
			|||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <FormTemplate
 | 
					        <FormTemplate
 | 
				
			||||||
            modal
 | 
					            modal
 | 
				
			||||||
            title={formatStrategyName(strategy.name ?? '')}
 | 
					            disablePadding
 | 
				
			||||||
            description={featureStrategyHelp}
 | 
					            description={featureStrategyHelp}
 | 
				
			||||||
            documentationLink={featureStrategyDocsLink}
 | 
					            documentationLink={featureStrategyDocsLink}
 | 
				
			||||||
            documentationLinkLabel={featureStrategyDocsLinkLabel}
 | 
					            documentationLinkLabel={featureStrategyDocsLinkLabel}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,18 +6,32 @@ import PermissionButton from '../../common/PermissionButton/PermissionButton';
 | 
				
			|||||||
import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvider/permissions';
 | 
					import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvider/permissions';
 | 
				
			||||||
import { v4 as uuidv4 } from 'uuid';
 | 
					import { v4 as uuidv4 } from 'uuid';
 | 
				
			||||||
import { WeightType } from '../../../constants/variantTypes';
 | 
					import { WeightType } from '../../../constants/variantTypes';
 | 
				
			||||||
import { Link, styled, Typography, useTheme } from '@mui/material';
 | 
					import { Box, Link, styled, Typography, useTheme } from '@mui/material';
 | 
				
			||||||
import { IFeatureStrategy } from 'interfaces/strategy';
 | 
					import { IFeatureStrategy } from 'interfaces/strategy';
 | 
				
			||||||
import SplitPreviewSlider from './SplitPreviewSlider/SplitPreviewSlider';
 | 
					import SplitPreviewSlider from './SplitPreviewSlider/SplitPreviewSlider';
 | 
				
			||||||
import { HelpIcon } from '../../common/HelpIcon/HelpIcon';
 | 
					import { HelpIcon } from '../../common/HelpIcon/HelpIcon';
 | 
				
			||||||
import { StrategyVariantsUpgradeAlert } from '../../common/StrategyVariantsUpgradeAlert/StrategyVariantsUpgradeAlert';
 | 
					import { StrategyVariantsUpgradeAlert } from '../../common/StrategyVariantsUpgradeAlert/StrategyVariantsUpgradeAlert';
 | 
				
			||||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
 | 
					import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
 | 
				
			||||||
 | 
					import { useUiFlag } from 'hooks/useUiFlag';
 | 
				
			||||||
 | 
					import { Add } from '@mui/icons-material';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledVariantForms = styled('div')({
 | 
					const StyledVariantForms = styled('div')({
 | 
				
			||||||
    display: 'flex',
 | 
					    display: 'flex',
 | 
				
			||||||
    flexDirection: 'column',
 | 
					    flexDirection: 'column',
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StyledHelpIconBox = styled(Box)(({ theme }) => ({
 | 
				
			||||||
 | 
					    display: 'flex',
 | 
				
			||||||
 | 
					    alignItems: 'center',
 | 
				
			||||||
 | 
					    marginTop: theme.spacing(1),
 | 
				
			||||||
 | 
					    marginBottom: theme.spacing(1),
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StyledVariantsHeader = styled('div')(({ theme }) => ({
 | 
				
			||||||
 | 
					    color: theme.palette.text.secondary,
 | 
				
			||||||
 | 
					    marginTop: theme.spacing(1.5),
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const StrategyVariants: FC<{
 | 
					export const StrategyVariants: FC<{
 | 
				
			||||||
    setStrategy: React.Dispatch<
 | 
					    setStrategy: React.Dispatch<
 | 
				
			||||||
        React.SetStateAction<Partial<IFeatureStrategy>>
 | 
					        React.SetStateAction<Partial<IFeatureStrategy>>
 | 
				
			||||||
@ -29,6 +43,8 @@ export const StrategyVariants: FC<{
 | 
				
			|||||||
    const { trackEvent } = usePlausibleTracker();
 | 
					    const { trackEvent } = usePlausibleTracker();
 | 
				
			||||||
    const [variantsEdit, setVariantsEdit] = useState<IFeatureVariantEdit[]>([]);
 | 
					    const [variantsEdit, setVariantsEdit] = useState<IFeatureVariantEdit[]>([]);
 | 
				
			||||||
    const theme = useTheme();
 | 
					    const theme = useTheme();
 | 
				
			||||||
 | 
					    const newStrategyConfiguration = useUiFlag('newStrategyConfiguration');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const stickiness =
 | 
					    const stickiness =
 | 
				
			||||||
        strategy?.parameters && 'stickiness' in strategy?.parameters
 | 
					        strategy?.parameters && 'stickiness' in strategy?.parameters
 | 
				
			||||||
            ? String(strategy.parameters.stickiness)
 | 
					            ? String(strategy.parameters.stickiness)
 | 
				
			||||||
@ -91,6 +107,86 @@ export const StrategyVariants: FC<{
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (newStrategyConfiguration) {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            <>
 | 
				
			||||||
 | 
					                <StyledVariantsHeader>
 | 
				
			||||||
 | 
					                    Variants enhance a feature flag by providing a version of
 | 
				
			||||||
 | 
					                    the feature to be enabled
 | 
				
			||||||
 | 
					                </StyledVariantsHeader>
 | 
				
			||||||
 | 
					                <StyledHelpIconBox>
 | 
				
			||||||
 | 
					                    <Typography>Variants</Typography>
 | 
				
			||||||
 | 
					                    <HelpIcon
 | 
				
			||||||
 | 
					                        htmlTooltip
 | 
				
			||||||
 | 
					                        tooltip={
 | 
				
			||||||
 | 
					                            <Box>
 | 
				
			||||||
 | 
					                                <Typography variant='body2'>
 | 
				
			||||||
 | 
					                                    Variants in feature toggling allow you to
 | 
				
			||||||
 | 
					                                    serve different versions of a feature to
 | 
				
			||||||
 | 
					                                    different users. This can be used for A/B
 | 
				
			||||||
 | 
					                                    testing, gradual rollouts, and canary
 | 
				
			||||||
 | 
					                                    releases. Variants provide a way to control
 | 
				
			||||||
 | 
					                                    the user experience at a granular level,
 | 
				
			||||||
 | 
					                                    enabling you to test and optimize different
 | 
				
			||||||
 | 
					                                    aspects of your features. Read more about
 | 
				
			||||||
 | 
					                                    variants{' '}
 | 
				
			||||||
 | 
					                                    <a
 | 
				
			||||||
 | 
					                                        href='https://docs.getunleash.io/reference/strategy-variants'
 | 
				
			||||||
 | 
					                                        target='_blank'
 | 
				
			||||||
 | 
					                                        rel='noopener noreferrer'
 | 
				
			||||||
 | 
					                                    >
 | 
				
			||||||
 | 
					                                        here
 | 
				
			||||||
 | 
					                                    </a>
 | 
				
			||||||
 | 
					                                </Typography>
 | 
				
			||||||
 | 
					                            </Box>
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                </StyledHelpIconBox>
 | 
				
			||||||
 | 
					                <StyledVariantForms>
 | 
				
			||||||
 | 
					                    <StrategyVariantsUpgradeAlert />
 | 
				
			||||||
 | 
					                    {variantsEdit.map((variant, i) => (
 | 
				
			||||||
 | 
					                        <VariantForm
 | 
				
			||||||
 | 
					                            disableOverrides={true}
 | 
				
			||||||
 | 
					                            key={variant.id}
 | 
				
			||||||
 | 
					                            variant={variant}
 | 
				
			||||||
 | 
					                            variants={variantsEdit}
 | 
				
			||||||
 | 
					                            updateVariant={(updatedVariant) =>
 | 
				
			||||||
 | 
					                                updateVariant(updatedVariant, variant.id)
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            removeVariant={() =>
 | 
				
			||||||
 | 
					                                setVariantsEdit((variantsEdit) =>
 | 
				
			||||||
 | 
					                                    updateWeightEdit(
 | 
				
			||||||
 | 
					                                        variantsEdit.filter(
 | 
				
			||||||
 | 
					                                            (v) => v.id !== variant.id,
 | 
				
			||||||
 | 
					                                        ),
 | 
				
			||||||
 | 
					                                        1000,
 | 
				
			||||||
 | 
					                                    ),
 | 
				
			||||||
 | 
					                                )
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            decorationColor={
 | 
				
			||||||
 | 
					                                theme.palette.variants[
 | 
				
			||||||
 | 
					                                    i % theme.palette.variants.length
 | 
				
			||||||
 | 
					                                ]
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                    ))}
 | 
				
			||||||
 | 
					                </StyledVariantForms>
 | 
				
			||||||
 | 
					                <PermissionButton
 | 
				
			||||||
 | 
					                    onClick={addVariant}
 | 
				
			||||||
 | 
					                    variant='outlined'
 | 
				
			||||||
 | 
					                    permission={UPDATE_FEATURE_ENVIRONMENT_VARIANTS}
 | 
				
			||||||
 | 
					                    projectId={projectId}
 | 
				
			||||||
 | 
					                    environmentId={environment}
 | 
				
			||||||
 | 
					                    data-testid='ADD_STRATEGY_VARIANT_BUTTON'
 | 
				
			||||||
 | 
					                    startIcon={<Add />}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    Add variant
 | 
				
			||||||
 | 
					                </PermissionButton>
 | 
				
			||||||
 | 
					                <SplitPreviewSlider variants={variantsEdit} />
 | 
				
			||||||
 | 
					            </>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <>
 | 
					        <>
 | 
				
			||||||
            <Typography
 | 
					            <Typography
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user