1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-19 17:52:45 +02:00
unleash.unleash/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/FeatureStrategyConstraints.tsx
olav baae88c7a5 feat: add new feature strategy create/edit pages (#739)
* 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
2022-03-09 14:59:24 +01:00

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;
};