mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-19 17:52:45 +02:00
* refactor: add param helper hooks * refactor: remove first add strategy link * refactor: add more types to useStrategies * refactor: port strategy utils to TS * refactor: replace rollout strategy icon * refactor: use a named export for useFeature * refactor: add more types to useFeature * refactor: adjust code box styles * refactor: add missing PermissionButton variant prop * refactor: add missing button icon label * refactor: move common feature components * refactor: fix StrategyConstraints error prop types * refactor: fix GeneralStrategy prop types * feat: add new feature strategy create/edit pages * refactor: remove feature strategies page * refactor: fix types in GeneralStrategy * refactor: use ConstraintAccordion on the new pages * refactor: use ConditionallyRender for remove button * refactor: rename FeatureStrategyForm component * refactor: use the Edit icon for feature strategies * refactor: fix initial edit mode for new constraints * refactor: add FeatureStrategyMenu to closed accordions * refactor: allow editing multiple constraints * refactor: show single-valued constraint value * refactor: increase feature overview strategy width * refactor: add remove button to feature overview strategies * refactor: move createEmptyConstraint to own file * refactor: disable submit button for invalid constraints * refactor: fix nested paragraphs on the metrics page * refactor: move create/edit feature strategy to modal * refactor: always open new constraints in edit mode * refactor: use a PermissionButton for the save button * refactor: remvoe unsaved constraints on cancel * refactor: clarify useConstraintsValidation logic * refactor: remove unused strategy descriptions * refactor: restore Rollout icon * refactor: remove sidebar modal slide animation * refactor: avoid constraint accordion toggle on edit/delete * refactor: truncate long strategy names * refactor: find the correct remove button
128 lines
4.2 KiB
TypeScript
128 lines
4.2 KiB
TypeScript
import { IConstraint, IFeatureStrategy } from 'interfaces/strategy';
|
|
import Constraint from '../../../common/Constraint/Constraint';
|
|
import Dialogue from '../../../common/Dialogue/Dialogue';
|
|
import React, { useState } from 'react';
|
|
import StrategyConstraints from '../../StrategyConstraints/StrategyConstraints';
|
|
import { List, ListItem } from '@material-ui/core';
|
|
import produce from 'immer';
|
|
import {
|
|
CREATE_FEATURE_STRATEGY,
|
|
UPDATE_FEATURE_STRATEGY,
|
|
} from '../../../providers/AccessProvider/permissions';
|
|
import PermissionButton from '../../../common/PermissionButton/PermissionButton';
|
|
|
|
interface IFeatureStrategyConstraintsProps {
|
|
projectId: string;
|
|
environmentId: string;
|
|
strategy: Partial<IFeatureStrategy>;
|
|
setStrategy: React.Dispatch<
|
|
React.SetStateAction<Partial<IFeatureStrategy>>
|
|
>;
|
|
}
|
|
|
|
export const FeatureStrategyConstraints = ({
|
|
projectId,
|
|
environmentId,
|
|
strategy,
|
|
setStrategy,
|
|
}: IFeatureStrategyConstraintsProps) => {
|
|
const [showConstraintsDialog, setShowConstraintsDialog] = useState(false);
|
|
|
|
const [constraintErrors, setConstraintErrors] = useState<
|
|
Record<string, string>
|
|
>({});
|
|
|
|
const updateConstraints = (constraints: IConstraint[]) => {
|
|
setStrategy(prev => ({ ...prev, constraints }));
|
|
};
|
|
|
|
const removeConstraint = (index: number) => {
|
|
setStrategy(
|
|
produce(draft => {
|
|
draft.constraints?.splice(index, 1);
|
|
})
|
|
);
|
|
};
|
|
|
|
const onConstraintsDialogSave = () => {
|
|
const errors = findConstraintErrors(strategy.constraints);
|
|
if (Object.keys(errors).length > 0) {
|
|
setConstraintErrors(errors);
|
|
} else {
|
|
setShowConstraintsDialog(false);
|
|
}
|
|
};
|
|
|
|
const onConstraintsDialogClose = () => {
|
|
setStrategy(
|
|
produce(draft => {
|
|
draft.constraints = removeEmptyConstraints(draft.constraints);
|
|
})
|
|
);
|
|
setShowConstraintsDialog(false);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<List disablePadding dense>
|
|
{strategy.constraints?.map((constraint, index) => (
|
|
<ListItem key={index} disableGutters dense>
|
|
<Constraint
|
|
constraint={constraint}
|
|
editCallback={() => setShowConstraintsDialog(true)}
|
|
deleteCallback={removeConstraint.bind(null, index)}
|
|
/>
|
|
</ListItem>
|
|
))}
|
|
</List>
|
|
<Dialogue
|
|
title="Define constraints"
|
|
open={showConstraintsDialog}
|
|
onClick={onConstraintsDialogSave}
|
|
primaryButtonText="Update constraints"
|
|
secondaryButtonText="Cancel"
|
|
onClose={onConstraintsDialogClose}
|
|
fullWidth
|
|
maxWidth="md"
|
|
>
|
|
<StrategyConstraints
|
|
updateConstraints={updateConstraints}
|
|
constraints={strategy.constraints ?? []}
|
|
constraintError={constraintErrors}
|
|
setConstraintError={setConstraintErrors}
|
|
/>
|
|
</Dialogue>
|
|
<PermissionButton
|
|
onClick={() => setShowConstraintsDialog(true)}
|
|
variant="text"
|
|
permission={[UPDATE_FEATURE_STRATEGY, CREATE_FEATURE_STRATEGY]}
|
|
environmentId={environmentId}
|
|
projectId={projectId}
|
|
>
|
|
Add constraints
|
|
</PermissionButton>
|
|
</>
|
|
);
|
|
};
|
|
|
|
const findConstraintErrors = (
|
|
constraints: IConstraint[] = []
|
|
): Record<string, string> => {
|
|
const entries = constraints
|
|
.filter(isEmptyConstraint)
|
|
.map((constraint, index) => `${constraint.contextName}-${index}`)
|
|
.map(id => [id, 'You need to specify at least one value']);
|
|
|
|
return Object.fromEntries(entries);
|
|
};
|
|
|
|
const removeEmptyConstraints = (
|
|
constraints: IConstraint[] = []
|
|
): IConstraint[] => {
|
|
return constraints.filter(constraint => !isEmptyConstraint(constraint));
|
|
};
|
|
|
|
const isEmptyConstraint = (constraint: IConstraint): boolean => {
|
|
return !constraint.values || constraint.values.length === 0;
|
|
};
|