mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-24 17:51:14 +02:00
Merge 6ff20cc7ff
into 3bb317ad6d
This commit is contained in:
commit
0def4cb3b7
@ -4,7 +4,7 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
|||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import type { IFeatureStrategy } from 'interfaces/strategy';
|
import type { IEditableStrategy, IFeatureStrategy } from 'interfaces/strategy';
|
||||||
import { UPDATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
|
import { UPDATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
|
||||||
import type { ISegment } from 'interfaces/segment';
|
import type { ISegment } from 'interfaces/segment';
|
||||||
import { useFormErrors } from 'hooks/useFormErrors';
|
import { useFormErrors } from 'hooks/useFormErrors';
|
||||||
@ -63,7 +63,7 @@ export const EditChange = ({
|
|||||||
|
|
||||||
const constraintsWithId = addIdSymbolToConstraints(change.payload);
|
const constraintsWithId = addIdSymbolToConstraints(change.payload);
|
||||||
|
|
||||||
const [strategy, setStrategy] = useState<Partial<IFeatureStrategy>>({
|
const [strategy, setStrategy] = useState<Partial<IEditableStrategy>>({
|
||||||
...change.payload,
|
...change.payload,
|
||||||
constraints: constraintsWithId,
|
constraints: constraintsWithId,
|
||||||
});
|
});
|
||||||
|
@ -7,12 +7,12 @@ import {
|
|||||||
type Theme,
|
type Theme,
|
||||||
styled,
|
styled,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import type { IConstraint } from 'interfaces/strategy';
|
|
||||||
import { ConstraintAccordionViewBody } from './ConstraintAccordionViewBody/ConstraintAccordionViewBody.tsx';
|
import { ConstraintAccordionViewBody } from './ConstraintAccordionViewBody/ConstraintAccordionViewBody.tsx';
|
||||||
import { ConstraintAccordionViewHeader } from './ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx';
|
import { ConstraintAccordionViewHeader } from './ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx';
|
||||||
|
import type { IConstraintWithId } from 'interfaces/strategy.ts';
|
||||||
|
|
||||||
interface IConstraintAccordionViewProps {
|
interface IConstraintAccordionViewProps {
|
||||||
constraint: IConstraint;
|
constraint: IConstraintWithId;
|
||||||
onUse?: () => void;
|
onUse?: () => void;
|
||||||
sx?: SxProps<Theme>;
|
sx?: SxProps<Theme>;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { useEffect, useImperativeHandle } from 'react';
|
import { useImperativeHandle } from 'react';
|
||||||
import { forwardRef } from 'react';
|
import { forwardRef } from 'react';
|
||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
import type { IConstraint } from 'interfaces/strategy';
|
import type { IConstraint, IConstraintWithId } from 'interfaces/strategy';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
|
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
|
||||||
import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList';
|
import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList';
|
||||||
import { EditableConstraint } from 'component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/EditableConstraint';
|
import { EditableConstraint } from 'component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/EditableConstraint';
|
||||||
import { createEmptyConstraint } from '../../../../utils/createEmptyConstraint.ts';
|
import { createEmptyConstraint } from '../../../../utils/createEmptyConstraint.ts';
|
||||||
import { constraintId } from 'constants/constraintId.ts';
|
import { constraintId } from 'constants/constraintId.ts';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
export interface IEditableConstraintsListRef {
|
export interface IEditableConstraintsListRef {
|
||||||
addConstraint?: (contextName: string) => void;
|
addConstraint?: (contextName: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEditableConstraintsListProps {
|
export interface IEditableConstraintsListProps {
|
||||||
constraints: IConstraint[];
|
constraints: IConstraintWithId[];
|
||||||
setConstraints: React.Dispatch<React.SetStateAction<IConstraint[]>>;
|
setConstraints: React.Dispatch<React.SetStateAction<IConstraint[]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,17 +39,6 @@ export const EditableConstraintsList = forwardRef<
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!constraints.every((constraint) => constraintId in constraint)) {
|
|
||||||
setConstraints(
|
|
||||||
constraints.map((constraint) => ({
|
|
||||||
[constraintId]: uuidv4(),
|
|
||||||
...constraint,
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [constraints, setConstraints]);
|
|
||||||
|
|
||||||
const onDelete = (index: number) => {
|
const onDelete = (index: number) => {
|
||||||
setConstraints(
|
setConstraints(
|
||||||
produce((draft) => {
|
produce((draft) => {
|
||||||
@ -79,6 +67,7 @@ export const EditableConstraintsList = forwardRef<
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
|
IN THE constraints list. mapping: {JSON.stringify(constraints)}
|
||||||
<ConstraintsList>
|
<ConstraintsList>
|
||||||
{constraints.map((constraint, index) => (
|
{constraints.map((constraint, index) => (
|
||||||
<EditableConstraint
|
<EditableConstraint
|
||||||
|
@ -2,7 +2,7 @@ import type React from 'react';
|
|||||||
import { forwardRef, useImperativeHandle, type RefObject } from 'react';
|
import { forwardRef, useImperativeHandle, type RefObject } from 'react';
|
||||||
import { Box, Button, styled, Typography } from '@mui/material';
|
import { Box, Button, styled, Typography } from '@mui/material';
|
||||||
import Add from '@mui/icons-material/Add';
|
import Add from '@mui/icons-material/Add';
|
||||||
import type { IConstraint } from 'interfaces/strategy';
|
import type { IConstraint, IConstraintWithId } from 'interfaces/strategy';
|
||||||
|
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
||||||
@ -15,8 +15,8 @@ import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashCon
|
|||||||
import { createEmptyConstraint } from 'utils/createEmptyConstraint.ts';
|
import { createEmptyConstraint } from 'utils/createEmptyConstraint.ts';
|
||||||
|
|
||||||
interface IConstraintAccordionListProps {
|
interface IConstraintAccordionListProps {
|
||||||
constraints: IConstraint[];
|
constraints: IConstraintWithId[];
|
||||||
setConstraints?: React.Dispatch<React.SetStateAction<IConstraint[]>>;
|
setConstraints?: React.Dispatch<React.SetStateAction<IConstraintWithId[]>>;
|
||||||
showCreateButton?: boolean;
|
showCreateButton?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ interface IConstraintAccordionListItemState {
|
|||||||
|
|
||||||
const useConstraintAccordionList = (
|
const useConstraintAccordionList = (
|
||||||
setConstraints:
|
setConstraints:
|
||||||
| React.Dispatch<React.SetStateAction<IConstraint[]>>
|
| React.Dispatch<React.SetStateAction<IConstraintWithId[]>>
|
||||||
| undefined,
|
| undefined,
|
||||||
ref: React.RefObject<IConstraintAccordionListRef>,
|
ref: React.RefObject<IConstraintAccordionListRef>,
|
||||||
) => {
|
) => {
|
||||||
@ -88,7 +88,7 @@ const useConstraintAccordionList = (
|
|||||||
export const FeatureStrategyConstraintAccordionList = forwardRef<
|
export const FeatureStrategyConstraintAccordionList = forwardRef<
|
||||||
IConstraintAccordionListRef | undefined,
|
IConstraintAccordionListRef | undefined,
|
||||||
IConstraintAccordionListProps
|
IConstraintAccordionListProps
|
||||||
>(({ constraints, setConstraints, showCreateButton }, ref) => {
|
>(({ constraints, setConstraints }, ref) => {
|
||||||
const { onAdd, context } = useConstraintAccordionList(
|
const { onAdd, context } = useConstraintAccordionList(
|
||||||
setConstraints,
|
setConstraints,
|
||||||
ref as RefObject<IConstraintAccordionListRef>,
|
ref as RefObject<IConstraintAccordionListRef>,
|
||||||
@ -101,8 +101,9 @@ export const FeatureStrategyConstraintAccordionList = forwardRef<
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
|
constraints are {JSON.stringify(constraints)}
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={Boolean(showCreateButton && onAdd)}
|
condition={Boolean(true)}
|
||||||
show={
|
show={
|
||||||
<div>
|
<div>
|
||||||
<StyledHelpIconBox>
|
<StyledHelpIconBox>
|
||||||
@ -128,13 +129,14 @@ export const FeatureStrategyConstraintAccordionList = forwardRef<
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</StyledHelpIconBox>
|
</StyledHelpIconBox>
|
||||||
{setConstraints ? (
|
|
||||||
|
<fieldset disabled>
|
||||||
<EditableConstraintsList
|
<EditableConstraintsList
|
||||||
ref={ref}
|
ref={ref}
|
||||||
setConstraints={setConstraints}
|
setConstraints={() => {}}
|
||||||
constraints={constraints}
|
constraints={constraints}
|
||||||
/>
|
/>
|
||||||
) : null}
|
</fieldset>
|
||||||
<Box
|
<Box
|
||||||
sx={(theme) => ({
|
sx={(theme) => ({
|
||||||
marginTop: theme.spacing(2),
|
marginTop: theme.spacing(2),
|
||||||
@ -156,7 +158,7 @@ export const FeatureStrategyConstraintAccordionList = forwardRef<
|
|||||||
variant='outlined'
|
variant='outlined'
|
||||||
color='primary'
|
color='primary'
|
||||||
data-testid='ADD_CONSTRAINT_BUTTON'
|
data-testid='ADD_CONSTRAINT_BUTTON'
|
||||||
disabled={Boolean(limitReached)}
|
disabled={Boolean(limitReached || !onAdd)}
|
||||||
>
|
>
|
||||||
Add constraint
|
Add constraint
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { IConstraint, IFeatureStrategy } from 'interfaces/strategy';
|
import type { IConstraintWithId, IEditableStrategy } from 'interfaces/strategy';
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
@ -11,9 +11,9 @@ import { FeatureStrategyConstraintAccordionList } from './FeatureStrategyConstra
|
|||||||
interface IFeatureStrategyConstraintsProps {
|
interface IFeatureStrategyConstraintsProps {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
environmentId: string;
|
environmentId: string;
|
||||||
strategy: Partial<IFeatureStrategy>;
|
strategy: Partial<IEditableStrategy>;
|
||||||
setStrategy: React.Dispatch<
|
setStrategy: React.Dispatch<
|
||||||
React.SetStateAction<Partial<IFeatureStrategy>>
|
React.SetStateAction<Partial<IEditableStrategy>>
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +53,9 @@ export const FeatureStrategyConstraints = ({
|
|||||||
|
|
||||||
const constraints = strategy.constraints || [];
|
const constraints = strategy.constraints || [];
|
||||||
|
|
||||||
const setConstraints = (value: React.SetStateAction<IConstraint[]>) => {
|
const setConstraints = (
|
||||||
|
value: React.SetStateAction<IConstraintWithId[]>,
|
||||||
|
) => {
|
||||||
setStrategy((prev) => {
|
setStrategy((prev) => {
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
|
@ -5,11 +5,11 @@ import {
|
|||||||
areConstraintsEqual,
|
areConstraintsEqual,
|
||||||
getConstraintKey,
|
getConstraintKey,
|
||||||
} from './useRecentlyUsedConstraints.ts';
|
} from './useRecentlyUsedConstraints.ts';
|
||||||
import type { IConstraint } from 'interfaces/strategy';
|
import type { IConstraintWithId } from 'interfaces/strategy.ts';
|
||||||
|
|
||||||
type IRecentlyUsedConstraintsProps = {
|
type IRecentlyUsedConstraintsProps = {
|
||||||
setConstraints?: React.Dispatch<React.SetStateAction<IConstraint[]>>;
|
setConstraints?: React.Dispatch<React.SetStateAction<IConstraintWithId[]>>;
|
||||||
constraints?: IConstraint[];
|
constraints?: IConstraintWithId[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledContainer = styled('div')(({ theme }) => ({
|
const StyledContainer = styled('div')(({ theme }) => ({
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useLocalStorageState } from 'hooks/useLocalStorageState';
|
import { useLocalStorageState } from 'hooks/useLocalStorageState';
|
||||||
import type { IConstraint } from 'interfaces/strategy';
|
import type { IConstraint, IConstraintWithId } from 'interfaces/strategy';
|
||||||
|
|
||||||
const hashString = (str: string): number => {
|
const hashString = (str: string): number => {
|
||||||
let hash = 0;
|
let hash = 0;
|
||||||
@ -41,14 +41,14 @@ export const areConstraintsEqual = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const useRecentlyUsedConstraints = (
|
export const useRecentlyUsedConstraints = (
|
||||||
initialItems: IConstraint[] = [],
|
initialItems: IConstraintWithId[] = [],
|
||||||
) => {
|
) => {
|
||||||
const [items, setItems] = useLocalStorageState<IConstraint[]>(
|
const [items, setItems] = useLocalStorageState<IConstraintWithId[]>(
|
||||||
'recently-used-constraints',
|
'recently-used-constraints',
|
||||||
initialItems,
|
initialItems,
|
||||||
);
|
);
|
||||||
|
|
||||||
const addItem = (newItem: IConstraint | IConstraint[]) => {
|
const addItem = (newItem: IConstraintWithId | IConstraintWithId[]) => {
|
||||||
setItems((prevItems) => {
|
setItems((prevItems) => {
|
||||||
const itemsToAdd = Array.isArray(newItem) ? newItem : [newItem];
|
const itemsToAdd = Array.isArray(newItem) ? newItem : [newItem];
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
Link,
|
Link,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import type {
|
import type {
|
||||||
IFeatureStrategy,
|
IEditableStrategy,
|
||||||
IFeatureStrategyParameters,
|
IFeatureStrategyParameters,
|
||||||
IStrategyParameter,
|
IStrategyParameter,
|
||||||
} from 'interfaces/strategy';
|
} from 'interfaces/strategy';
|
||||||
@ -57,9 +57,9 @@ interface IFeatureStrategyFormProps {
|
|||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
isChangeRequest: boolean;
|
isChangeRequest: boolean;
|
||||||
strategy: Partial<IFeatureStrategy>;
|
strategy: Partial<IEditableStrategy>;
|
||||||
setStrategy: React.Dispatch<
|
setStrategy: React.Dispatch<
|
||||||
React.SetStateAction<Partial<IFeatureStrategy>>
|
React.SetStateAction<Partial<IEditableStrategy>>
|
||||||
>;
|
>;
|
||||||
segments: ISegment[];
|
segments: ISegment[];
|
||||||
setSegments: React.Dispatch<React.SetStateAction<ISegment[]>>;
|
setSegments: React.Dispatch<React.SetStateAction<ISegment[]>>;
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import type { IFeatureStrategy, IStrategy } from 'interfaces/strategy';
|
import type {
|
||||||
|
IEditableStrategy,
|
||||||
|
IFeatureStrategy,
|
||||||
|
IStrategy,
|
||||||
|
} from 'interfaces/strategy';
|
||||||
import DefaultStrategy from 'component/feature/StrategyTypes/DefaultStrategy/DefaultStrategy';
|
import DefaultStrategy from 'component/feature/StrategyTypes/DefaultStrategy/DefaultStrategy';
|
||||||
import FlexibleStrategy from 'component/feature/StrategyTypes/FlexibleStrategy/FlexibleStrategy';
|
import FlexibleStrategy from 'component/feature/StrategyTypes/FlexibleStrategy/FlexibleStrategy';
|
||||||
import GeneralStrategy from 'component/feature/StrategyTypes/GeneralStrategy/GeneralStrategy';
|
import GeneralStrategy from 'component/feature/StrategyTypes/GeneralStrategy/GeneralStrategy';
|
||||||
@ -12,7 +16,7 @@ interface IFeatureStrategyTypeProps {
|
|||||||
strategy: Partial<IFeatureStrategy>;
|
strategy: Partial<IFeatureStrategy>;
|
||||||
strategyDefinition: IStrategy;
|
strategyDefinition: IStrategy;
|
||||||
setStrategy: React.Dispatch<
|
setStrategy: React.Dispatch<
|
||||||
React.SetStateAction<Partial<IFeatureStrategy>>
|
React.SetStateAction<Partial<IEditableStrategy>>
|
||||||
>;
|
>;
|
||||||
validateParameter: (name: string, value: string) => boolean;
|
validateParameter: (name: string, value: string) => boolean;
|
||||||
errors: IFormErrors;
|
errors: IFormErrors;
|
||||||
|
@ -8,7 +8,7 @@ import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvi
|
|||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { WeightType } from '../../../constants/variantTypes.ts';
|
import { WeightType } from '../../../constants/variantTypes.ts';
|
||||||
import { Box, styled, Typography, useTheme, Alert } from '@mui/material';
|
import { Box, styled, Typography, useTheme, Alert } from '@mui/material';
|
||||||
import type { IFeatureStrategy } from 'interfaces/strategy';
|
import type { IEditableStrategy, IFeatureStrategy } from 'interfaces/strategy';
|
||||||
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
|
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
|
||||||
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
||||||
import { StrategyVariantsUpgradeAlert } from 'component/common/StrategyVariantsUpgradeAlert/StrategyVariantsUpgradeAlert';
|
import { StrategyVariantsUpgradeAlert } from 'component/common/StrategyVariantsUpgradeAlert/StrategyVariantsUpgradeAlert';
|
||||||
@ -30,7 +30,7 @@ const StyledHelpIconBox = styled(Box)(({ theme }) => ({
|
|||||||
|
|
||||||
export const NewStrategyVariants: FC<{
|
export const NewStrategyVariants: FC<{
|
||||||
setStrategy: React.Dispatch<
|
setStrategy: React.Dispatch<
|
||||||
React.SetStateAction<Partial<IFeatureStrategy>>
|
React.SetStateAction<Partial<IEditableStrategy>>
|
||||||
>;
|
>;
|
||||||
strategy: Partial<IFeatureStrategy>;
|
strategy: Partial<IFeatureStrategy>;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
@ -3,6 +3,7 @@ import { formatApiPath } from 'utils/formatPath';
|
|||||||
import handleErrorResponses from '../httpErrorResponseHandler.js';
|
import handleErrorResponses from '../httpErrorResponseHandler.js';
|
||||||
import type { ChangeRequestType } from 'component/changeRequest/changeRequest.types';
|
import type { ChangeRequestType } from 'component/changeRequest/changeRequest.types';
|
||||||
|
|
||||||
|
// we get constraints here
|
||||||
export const useChangeRequest = (projectId: string, id: string) => {
|
export const useChangeRequest = (projectId: string, id: string) => {
|
||||||
const { data, error, mutate } = useSWR<ChangeRequestType>(
|
const { data, error, mutate } = useSWR<ChangeRequestType>(
|
||||||
formatApiPath(`api/admin/projects/${projectId}/change-requests/${id}`),
|
formatApiPath(`api/admin/projects/${projectId}/change-requests/${id}`),
|
||||||
|
@ -69,6 +69,10 @@ export interface IConstraint {
|
|||||||
[constraintId]?: string;
|
[constraintId]?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IEditableStrategy extends IFeatureStrategy {
|
||||||
|
constraints: IConstraintWithId[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface IConstraintWithId extends IConstraint {
|
export interface IConstraintWithId extends IConstraint {
|
||||||
[constraintId]: string;
|
[constraintId]: string;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user