From 6ff20cc7ffb2620eb175999469d856a63d45e395 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Fri, 18 Jul 2025 13:06:59 +0200 Subject: [PATCH] Propagate to use IConstraintWithId everywhere until editor stops complaining. This now passes checks to be "valid", but it doesn't do anything in and of itself. We still need to actually provide an id for these things to matter. I'm wondering whether it'd be better to make id mandatory on the regular constraints and add it on every incoming constraint. --- .../Changes/Change/EditChange.tsx | 4 ++-- .../ConstraintAccordionView.tsx | 4 ++-- .../EditableConstraintsList.tsx | 19 ++++------------ ...FeatureStrategyConstraintAccordionList.tsx | 22 ++++++++++--------- .../FeatureStrategyConstraints.tsx | 10 +++++---- .../RecentlyUsedConstraints.tsx | 6 ++--- .../useRecentlyUsedConstraints.ts | 8 +++---- .../FeatureStrategyForm.tsx | 6 ++--- .../FeatureStrategyType.tsx | 8 +++++-- .../StrategyTypes/NewStrategyVariants.tsx | 4 ++-- .../useChangeRequest/useChangeRequest.ts | 1 + frontend/src/interfaces/strategy.ts | 4 ++++ 12 files changed, 49 insertions(+), 47 deletions(-) diff --git a/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/EditChange.tsx b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/EditChange.tsx index bb60ca07b9..aa926db936 100644 --- a/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/EditChange.tsx +++ b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/EditChange.tsx @@ -4,7 +4,7 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { formatUnknownError } from 'utils/formatUnknownError'; 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 type { ISegment } from 'interfaces/segment'; import { useFormErrors } from 'hooks/useFormErrors'; @@ -63,7 +63,7 @@ export const EditChange = ({ const constraintsWithId = addIdSymbolToConstraints(change.payload); - const [strategy, setStrategy] = useState>({ + const [strategy, setStrategy] = useState>({ ...change.payload, constraints: constraintsWithId, }); diff --git a/frontend/src/component/common/NewConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx b/frontend/src/component/common/NewConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx index a8d90c81f1..ba7c9c944b 100644 --- a/frontend/src/component/common/NewConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx +++ b/frontend/src/component/common/NewConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx @@ -7,12 +7,12 @@ import { type Theme, styled, } from '@mui/material'; -import type { IConstraint } from 'interfaces/strategy'; import { ConstraintAccordionViewBody } from './ConstraintAccordionViewBody/ConstraintAccordionViewBody.tsx'; import { ConstraintAccordionViewHeader } from './ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx'; +import type { IConstraintWithId } from 'interfaces/strategy.ts'; interface IConstraintAccordionViewProps { - constraint: IConstraint; + constraint: IConstraintWithId; onUse?: () => void; sx?: SxProps; disabled?: boolean; diff --git a/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx b/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx index 0c37645762..b88f83e2e1 100644 --- a/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx +++ b/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx @@ -1,21 +1,20 @@ import type React from 'react'; -import { useEffect, useImperativeHandle } from 'react'; +import { useImperativeHandle } from 'react'; import { forwardRef } from 'react'; import { styled } from '@mui/material'; -import type { IConstraint } from 'interfaces/strategy'; +import type { IConstraint, IConstraintWithId } from 'interfaces/strategy'; import produce from 'immer'; import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext'; import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList'; import { EditableConstraint } from 'component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/EditableConstraint'; import { createEmptyConstraint } from '../../../../utils/createEmptyConstraint.ts'; import { constraintId } from 'constants/constraintId.ts'; -import { v4 as uuidv4 } from 'uuid'; export interface IEditableConstraintsListRef { addConstraint?: (contextName: string) => void; } export interface IEditableConstraintsListProps { - constraints: IConstraint[]; + constraints: IConstraintWithId[]; setConstraints: React.Dispatch>; } @@ -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) => { setConstraints( produce((draft) => { @@ -79,6 +67,7 @@ export const EditableConstraintsList = forwardRef< return ( + IN THE constraints list. mapping: {JSON.stringify(constraints)} {constraints.map((constraint, index) => ( >; + constraints: IConstraintWithId[]; + setConstraints?: React.Dispatch>; showCreateButton?: boolean; } @@ -57,7 +57,7 @@ interface IConstraintAccordionListItemState { const useConstraintAccordionList = ( setConstraints: - | React.Dispatch> + | React.Dispatch> | undefined, ref: React.RefObject, ) => { @@ -88,7 +88,7 @@ const useConstraintAccordionList = ( export const FeatureStrategyConstraintAccordionList = forwardRef< IConstraintAccordionListRef | undefined, IConstraintAccordionListProps ->(({ constraints, setConstraints, showCreateButton }, ref) => { +>(({ constraints, setConstraints }, ref) => { const { onAdd, context } = useConstraintAccordionList( setConstraints, ref as RefObject, @@ -101,8 +101,9 @@ export const FeatureStrategyConstraintAccordionList = forwardRef< return ( + constraints are {JSON.stringify(constraints)} @@ -128,13 +129,14 @@ export const FeatureStrategyConstraintAccordionList = forwardRef< } /> - {setConstraints ? ( + +
{}} constraints={constraints} /> - ) : null} +
({ marginTop: theme.spacing(2), @@ -156,7 +158,7 @@ export const FeatureStrategyConstraintAccordionList = forwardRef< variant='outlined' color='primary' data-testid='ADD_CONSTRAINT_BUTTON' - disabled={Boolean(limitReached)} + disabled={Boolean(limitReached || !onAdd)} > Add constraint diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/FeatureStrategyConstraints.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/FeatureStrategyConstraints.tsx index deb07a1301..6aecdb4959 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/FeatureStrategyConstraints.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/FeatureStrategyConstraints.tsx @@ -1,4 +1,4 @@ -import type { IConstraint, IFeatureStrategy } from 'interfaces/strategy'; +import type { IConstraintWithId, IEditableStrategy } from 'interfaces/strategy'; import type React from 'react'; import { useEffect } from 'react'; import { @@ -11,9 +11,9 @@ import { FeatureStrategyConstraintAccordionList } from './FeatureStrategyConstra interface IFeatureStrategyConstraintsProps { projectId: string; environmentId: string; - strategy: Partial; + strategy: Partial; setStrategy: React.Dispatch< - React.SetStateAction> + React.SetStateAction> >; } @@ -53,7 +53,9 @@ export const FeatureStrategyConstraints = ({ const constraints = strategy.constraints || []; - const setConstraints = (value: React.SetStateAction) => { + const setConstraints = ( + value: React.SetStateAction, + ) => { setStrategy((prev) => { return { ...prev, diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/RecentlyUsedConstraints/RecentlyUsedConstraints.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/RecentlyUsedConstraints/RecentlyUsedConstraints.tsx index f2bede7da8..3f26bbd9f6 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/RecentlyUsedConstraints/RecentlyUsedConstraints.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/RecentlyUsedConstraints/RecentlyUsedConstraints.tsx @@ -5,11 +5,11 @@ import { areConstraintsEqual, getConstraintKey, } from './useRecentlyUsedConstraints.ts'; -import type { IConstraint } from 'interfaces/strategy'; +import type { IConstraintWithId } from 'interfaces/strategy.ts'; type IRecentlyUsedConstraintsProps = { - setConstraints?: React.Dispatch>; - constraints?: IConstraint[]; + setConstraints?: React.Dispatch>; + constraints?: IConstraintWithId[]; }; const StyledContainer = styled('div')(({ theme }) => ({ diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/RecentlyUsedConstraints/useRecentlyUsedConstraints.ts b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/RecentlyUsedConstraints/useRecentlyUsedConstraints.ts index 2be72642cc..d4119528c1 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/RecentlyUsedConstraints/useRecentlyUsedConstraints.ts +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/RecentlyUsedConstraints/useRecentlyUsedConstraints.ts @@ -1,5 +1,5 @@ import { useLocalStorageState } from 'hooks/useLocalStorageState'; -import type { IConstraint } from 'interfaces/strategy'; +import type { IConstraint, IConstraintWithId } from 'interfaces/strategy'; const hashString = (str: string): number => { let hash = 0; @@ -41,14 +41,14 @@ export const areConstraintsEqual = ( }; export const useRecentlyUsedConstraints = ( - initialItems: IConstraint[] = [], + initialItems: IConstraintWithId[] = [], ) => { - const [items, setItems] = useLocalStorageState( + const [items, setItems] = useLocalStorageState( 'recently-used-constraints', initialItems, ); - const addItem = (newItem: IConstraint | IConstraint[]) => { + const addItem = (newItem: IConstraintWithId | IConstraintWithId[]) => { setItems((prevItems) => { const itemsToAdd = Array.isArray(newItem) ? newItem : [newItem]; diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm.tsx index 40a1dcf1e9..3ee08e0d86 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm.tsx @@ -13,7 +13,7 @@ import { Link, } from '@mui/material'; import type { - IFeatureStrategy, + IEditableStrategy, IFeatureStrategyParameters, IStrategyParameter, } from 'interfaces/strategy'; @@ -57,9 +57,9 @@ interface IFeatureStrategyFormProps { onCancel?: () => void; loading: boolean; isChangeRequest: boolean; - strategy: Partial; + strategy: Partial; setStrategy: React.Dispatch< - React.SetStateAction> + React.SetStateAction> >; segments: ISegment[]; setSegments: React.Dispatch>; diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyType/FeatureStrategyType.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyType/FeatureStrategyType.tsx index cadc095128..244f275aac 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyType/FeatureStrategyType.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyType/FeatureStrategyType.tsx @@ -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 FlexibleStrategy from 'component/feature/StrategyTypes/FlexibleStrategy/FlexibleStrategy'; import GeneralStrategy from 'component/feature/StrategyTypes/GeneralStrategy/GeneralStrategy'; @@ -12,7 +16,7 @@ interface IFeatureStrategyTypeProps { strategy: Partial; strategyDefinition: IStrategy; setStrategy: React.Dispatch< - React.SetStateAction> + React.SetStateAction> >; validateParameter: (name: string, value: string) => boolean; errors: IFormErrors; diff --git a/frontend/src/component/feature/StrategyTypes/NewStrategyVariants.tsx b/frontend/src/component/feature/StrategyTypes/NewStrategyVariants.tsx index db742d1d47..2bbf872b88 100644 --- a/frontend/src/component/feature/StrategyTypes/NewStrategyVariants.tsx +++ b/frontend/src/component/feature/StrategyTypes/NewStrategyVariants.tsx @@ -8,7 +8,7 @@ import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvi import { v4 as uuidv4 } from 'uuid'; import { WeightType } from '../../../constants/variantTypes.ts'; 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 { HelpIcon } from 'component/common/HelpIcon/HelpIcon'; import { StrategyVariantsUpgradeAlert } from 'component/common/StrategyVariantsUpgradeAlert/StrategyVariantsUpgradeAlert'; @@ -30,7 +30,7 @@ const StyledHelpIconBox = styled(Box)(({ theme }) => ({ export const NewStrategyVariants: FC<{ setStrategy: React.Dispatch< - React.SetStateAction> + React.SetStateAction> >; strategy: Partial; projectId: string; diff --git a/frontend/src/hooks/api/getters/useChangeRequest/useChangeRequest.ts b/frontend/src/hooks/api/getters/useChangeRequest/useChangeRequest.ts index 98e5f217ea..5ac0e3e2b9 100644 --- a/frontend/src/hooks/api/getters/useChangeRequest/useChangeRequest.ts +++ b/frontend/src/hooks/api/getters/useChangeRequest/useChangeRequest.ts @@ -3,6 +3,7 @@ import { formatApiPath } from 'utils/formatPath'; import handleErrorResponses from '../httpErrorResponseHandler.js'; import type { ChangeRequestType } from 'component/changeRequest/changeRequest.types'; +// we get constraints here export const useChangeRequest = (projectId: string, id: string) => { const { data, error, mutate } = useSWR( formatApiPath(`api/admin/projects/${projectId}/change-requests/${id}`), diff --git a/frontend/src/interfaces/strategy.ts b/frontend/src/interfaces/strategy.ts index 0f46621f79..4c9bba805b 100644 --- a/frontend/src/interfaces/strategy.ts +++ b/frontend/src/interfaces/strategy.ts @@ -68,6 +68,10 @@ export interface IConstraint { [constraintId]?: string; } +export interface IEditableStrategy extends IFeatureStrategy { + constraints: IConstraintWithId[]; +} + export interface IConstraintWithId extends IConstraint { [constraintId]: string; }