mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
feat: strategy variant UI spike (#4246)
This commit is contained in:
parent
e8ea79c967
commit
99d63cff33
@ -242,6 +242,7 @@ export const createStrategyPayload = (
|
||||
title: strategy.title,
|
||||
constraints: strategy.constraints ?? [],
|
||||
parameters: strategy.parameters ?? {},
|
||||
variants: strategy.variants ?? [],
|
||||
segments: segments.map(segment => segment.id),
|
||||
disabled: strategy.disabled ?? false,
|
||||
});
|
||||
|
@ -31,6 +31,7 @@ import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequ
|
||||
import { useHasProjectEnvironmentAccess } from 'hooks/useHasAccess';
|
||||
import { FeatureStrategyTitle } from './FeatureStrategyTitle/FeatureStrategyTitle';
|
||||
import { FeatureStrategyEnabledDisabled } from './FeatureStrategyEnabledDisabled/FeatureStrategyEnabledDisabled';
|
||||
import { StrategyVariants } from 'component/feature/StrategyTypes/StrategyVariants';
|
||||
|
||||
interface IFeatureStrategyFormProps {
|
||||
feature: IFeatureToggle;
|
||||
@ -246,6 +247,16 @@ export const FeatureStrategyForm = ({
|
||||
hasAccess={access}
|
||||
/>
|
||||
<StyledHr />
|
||||
<ConditionallyRender
|
||||
condition={Boolean(uiConfig?.flags?.strategyVariant)}
|
||||
show={
|
||||
<StrategyVariants
|
||||
strategy={strategy}
|
||||
setStrategy={setStrategy}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<StyledHr />
|
||||
<FeatureStrategyEnabledDisabled
|
||||
enabled={!strategy?.disabled}
|
||||
onToggleEnabled={() =>
|
||||
|
@ -390,7 +390,6 @@ export const EnvironmentVariantsModal = ({
|
||||
)
|
||||
)
|
||||
}
|
||||
projectId={projectId}
|
||||
apiPayload={apiPayload}
|
||||
/>
|
||||
))}
|
||||
|
@ -150,7 +150,6 @@ interface IVariantFormProps {
|
||||
variants: IFeatureVariantEdit[];
|
||||
updateVariant: (updatedVariant: IFeatureVariantEdit) => void;
|
||||
removeVariant: (variantId: string) => void;
|
||||
projectId: string;
|
||||
apiPayload: {
|
||||
patch: Operation[];
|
||||
error?: string;
|
||||
|
@ -17,8 +17,6 @@ import Loader from '../../../common/Loader/Loader';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { useLocation } from 'react-router';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
|
||||
interface IFlexibleStrategyProps {
|
||||
parameters: IFeatureStrategyParameters;
|
||||
@ -35,7 +33,6 @@ const FlexibleStrategy = ({
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const { defaultStickiness, loading } = useDefaultProjectSettings(projectId);
|
||||
const { pathname } = useLocation();
|
||||
const { uiConfig } = useUiConfig();
|
||||
|
||||
const isDefaultStrategyEdit = pathname.includes('default-strategy');
|
||||
const onUpdate = (field: string) => (newValue: string) => {
|
||||
@ -126,34 +123,6 @@ const FlexibleStrategy = ({
|
||||
onChange={e => onUpdate('groupId')(e.target.value)}
|
||||
data-testid={FLEXIBLE_STRATEGY_GROUP_ID}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(uiConfig?.flags?.strategyVariant)}
|
||||
show={
|
||||
<>
|
||||
<br />
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
style={{
|
||||
marginBottom: '1rem',
|
||||
display: 'flex',
|
||||
gap: '1ch',
|
||||
}}
|
||||
component="h2"
|
||||
>
|
||||
Variant
|
||||
</Typography>
|
||||
<Input
|
||||
label="variant"
|
||||
id="variant-input"
|
||||
value={parseParameterString(parameters.variant)}
|
||||
disabled={!editable}
|
||||
onChange={e =>
|
||||
onUpdate('variant')(e.target.value)
|
||||
}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -0,0 +1,125 @@
|
||||
import { VariantForm } from '../FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/VariantForm/VariantForm';
|
||||
import { updateWeightEdit } from '../../common/util';
|
||||
import React, { FC, useEffect, useState } from 'react';
|
||||
import { IFeatureVariantEdit } from '../FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/EnvironmentVariantsModal';
|
||||
import PermissionButton from '../../common/PermissionButton/PermissionButton';
|
||||
import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvider/permissions';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { WeightType } from '../../../constants/variantTypes';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { useDefaultProjectSettings } from 'hooks/useDefaultProjectSettings';
|
||||
import { styled } from '@mui/material';
|
||||
import { useRequiredQueryParam } from 'hooks/useRequiredQueryParam';
|
||||
import { IFeatureStrategy } from 'interfaces/strategy';
|
||||
|
||||
const StyledVariantForms = styled('div')({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
});
|
||||
|
||||
export const StrategyVariants: FC<{
|
||||
setStrategy: React.Dispatch<
|
||||
React.SetStateAction<Partial<IFeatureStrategy>>
|
||||
>;
|
||||
strategy: Partial<IFeatureStrategy>;
|
||||
}> = ({ strategy, setStrategy }) => {
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const environment = useRequiredQueryParam('environmentId');
|
||||
const [variantsEdit, setVariantsEdit] = useState<IFeatureVariantEdit[]>([]);
|
||||
const [newVariant, setNewVariant] = useState<string>();
|
||||
const { defaultStickiness, loading } = useDefaultProjectSettings(projectId);
|
||||
|
||||
useEffect(() => {
|
||||
setVariantsEdit(
|
||||
(strategy.variants || []).map(variant => ({
|
||||
...variant,
|
||||
new: false,
|
||||
isValid: true,
|
||||
id: uuidv4(),
|
||||
overrides: [],
|
||||
}))
|
||||
);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setStrategy(prev => ({
|
||||
...prev,
|
||||
variants: variantsEdit.map(variant => ({
|
||||
name: variant.name,
|
||||
weight: variant.weight,
|
||||
stickiness: variant.stickiness,
|
||||
payload: variant.payload,
|
||||
weightType: variant.weightType,
|
||||
})),
|
||||
}));
|
||||
}, [JSON.stringify(variantsEdit)]);
|
||||
|
||||
const updateVariant = (updatedVariant: IFeatureVariantEdit, id: string) => {
|
||||
setVariantsEdit(prevVariants =>
|
||||
updateWeightEdit(
|
||||
prevVariants.map(prevVariant =>
|
||||
prevVariant.id === id ? updatedVariant : prevVariant
|
||||
),
|
||||
1000
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const addVariant = () => {
|
||||
const id = uuidv4();
|
||||
setVariantsEdit(variantsEdit => [
|
||||
...variantsEdit,
|
||||
{
|
||||
name: '',
|
||||
weightType: WeightType.VARIABLE,
|
||||
weight: 0,
|
||||
overrides: [],
|
||||
stickiness:
|
||||
variantsEdit?.length > 0
|
||||
? variantsEdit[0].stickiness
|
||||
: defaultStickiness,
|
||||
new: true,
|
||||
isValid: false,
|
||||
id,
|
||||
},
|
||||
]);
|
||||
setNewVariant(id);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledVariantForms>
|
||||
{variantsEdit.map(variant => (
|
||||
<VariantForm
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
apiPayload={{ patch: [] }}
|
||||
/>
|
||||
))}
|
||||
</StyledVariantForms>
|
||||
<PermissionButton
|
||||
onClick={addVariant}
|
||||
variant="outlined"
|
||||
permission={UPDATE_FEATURE_ENVIRONMENT_VARIANTS}
|
||||
projectId={projectId}
|
||||
environmentId={environment}
|
||||
>
|
||||
Add variant
|
||||
</PermissionButton>
|
||||
</>
|
||||
);
|
||||
};
|
@ -57,6 +57,14 @@ export interface IFeatureVariant {
|
||||
payload?: IPayload;
|
||||
}
|
||||
|
||||
export interface IFeatureStrategyVariant {
|
||||
name: string;
|
||||
stickiness: string;
|
||||
weight: number;
|
||||
weightType: string;
|
||||
payload?: IPayload;
|
||||
}
|
||||
|
||||
export interface IOverride {
|
||||
contextName: string;
|
||||
values: string[];
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Operator } from 'constants/operators';
|
||||
import { IFeatureStrategyVariant } from './featureToggle';
|
||||
|
||||
export interface IFeatureStrategy {
|
||||
id: string;
|
||||
@ -7,6 +8,7 @@ export interface IFeatureStrategy {
|
||||
title?: string;
|
||||
constraints: IConstraint[];
|
||||
parameters: IFeatureStrategyParameters;
|
||||
variants?: IFeatureStrategyVariant[];
|
||||
featureName?: string;
|
||||
projectId?: string;
|
||||
environment?: string;
|
||||
@ -24,6 +26,7 @@ export interface IFeatureStrategyPayload {
|
||||
title?: string;
|
||||
constraints: IConstraint[];
|
||||
parameters: IFeatureStrategyParameters;
|
||||
variants?: IFeatureStrategyVariant[];
|
||||
segments?: number[];
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user