From e25fb9f7c08dda06c8b7710a14b4da0297239197 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Wed, 26 Feb 2025 16:24:50 +0100 Subject: [PATCH] chore(1-3389): new env strategy containers (#9361) Updates the strategy list based on the new designs and moves the current versions of the touched components into `Legacy...` files (the vast majority of changes are that and updating imports). The relevant changes to the components are listed in their original files. Flag on: ![image](https://github.com/user-attachments/assets/cd49c283-6044-46d4-bcef-182cb6a1de4e) Flag off: ![image](https://github.com/user-attachments/assets/7ef92b6d-31e5-4218-90b2-dedd5e6cc6de) ## Next steps There's two items to review for improving these current comments (also noted inline): - Whether to aria-hide the "or" separator or not (I need to read up a bit and think whether it makes sense to show that or not) - Changing the list of strategies into an actual ordered list (`ol`). That'd reflect the semantics better. Next would be checking the other places we use strategy lists and then updating those too. In doing so, I might find that some things need to be updated, but I'll handle those when I get there. There's also handling release plans. --- .../ConstraintAccordionList.tsx | 2 +- .../NewConstraintAccordionList.tsx | 2 +- .../LegacyStrategyItemContainer.tsx | 205 ++++++++++ .../StrategyItemContainer.test.tsx | 2 +- .../StrategyItemContainer.tsx | 47 +-- .../LegacyStrategySeparator.tsx | 52 +++ .../StrategySeparator/StrategySeparator.tsx | 42 ++- .../EnvironmentAccordionBody.tsx | 21 +- .../LegacyEnvironmentAccordionBody.tsx | 354 ++++++++++++++++++ .../LegacyStrategyDraggableItem.tsx | 150 ++++++++ .../StrategyDraggableItem.tsx | 8 +- .../StrategyItem/LegacyStrategyItem.tsx | 181 +++++++++ .../StrategyExecution/StrategyExecution.tsx | 2 +- .../StrategyItem/StrategyItem.tsx | 6 +- .../FeatureOverviewEnvironment.tsx | 16 +- .../LegacyFeatureOverviewEnvironment.tsx | 2 +- .../FeatureOverviewSegment.tsx | 2 +- .../ReleasePlanMilestone.tsx | 2 +- .../ConstraintExecution.tsx | 2 +- .../ConstraintExecutionWithoutResults.tsx | 2 +- .../CustomStrategyParams.tsx | 2 +- .../DisabledStrategyExecution.tsx | 2 +- .../SegmentExecution/SegmentExecution.tsx | 2 +- .../SegmentExecutionWithoutResult.tsx | 2 +- .../StrategyExecution/StrategyExecution.tsx | 2 +- .../playgroundResultStrategyLists.tsx | 2 +- .../MilestoneStrategyDraggableItem.tsx | 2 +- 27 files changed, 1009 insertions(+), 105 deletions(-) create mode 100644 frontend/src/component/common/StrategyItemContainer/LegacyStrategyItemContainer.tsx create mode 100644 frontend/src/component/common/StrategySeparator/LegacyStrategySeparator.tsx create mode 100644 frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/LegacyEnvironmentAccordionBody.tsx create mode 100644 frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/LegacyStrategyDraggableItem.tsx create mode 100644 frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/LegacyStrategyItem.tsx diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx index e09fd45a62..57942de315 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx @@ -15,7 +15,7 @@ import { type IUseWeakMap, useWeakMap } from 'hooks/useWeakMap'; import { objectId } from 'utils/objectId'; import { createEmptyConstraint } from 'component/common/ConstraintAccordion/ConstraintAccordionList/createEmptyConstraint'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; export interface IConstraintAccordionListProps { constraints: IConstraint[]; diff --git a/frontend/src/component/common/NewConstraintAccordion/NewConstraintAccordionList/NewConstraintAccordionList.tsx b/frontend/src/component/common/NewConstraintAccordion/NewConstraintAccordionList/NewConstraintAccordionList.tsx index f3dec38b25..b348e9af38 100644 --- a/frontend/src/component/common/NewConstraintAccordion/NewConstraintAccordionList/NewConstraintAccordionList.tsx +++ b/frontend/src/component/common/NewConstraintAccordion/NewConstraintAccordionList/NewConstraintAccordionList.tsx @@ -11,7 +11,7 @@ import { createEmptyConstraint, } from 'component/common/ConstraintAccordion/ConstraintAccordionList/createEmptyConstraint'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; import { NewConstraintAccordion } from 'component/common/NewConstraintAccordion/NewConstraintAccordion'; export interface IConstraintAccordionListProps { diff --git a/frontend/src/component/common/StrategyItemContainer/LegacyStrategyItemContainer.tsx b/frontend/src/component/common/StrategyItemContainer/LegacyStrategyItemContainer.tsx new file mode 100644 index 0000000000..731b8b01d5 --- /dev/null +++ b/frontend/src/component/common/StrategyItemContainer/LegacyStrategyItemContainer.tsx @@ -0,0 +1,205 @@ +// deprecated; remove with the `flagOverviewRedesign` flag +import type React from 'react'; +import type { DragEventHandler, FC, ReactNode } from 'react'; +import DragIndicator from '@mui/icons-material/DragIndicator'; +import { Box, IconButton, styled } from '@mui/material'; +import type { IFeatureStrategy } from 'interfaces/strategy'; +import { + formatStrategyName, + getFeatureStrategyIcon, +} from 'utils/strategyNames'; +import StringTruncator from 'component/common/StringTruncator/StringTruncator'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import type { PlaygroundStrategySchema } from 'openapi'; +import { Badge } from '../Badge/Badge'; +import { Link } from 'react-router-dom'; + +interface IStrategyItemContainerProps { + strategy: IFeatureStrategy | PlaygroundStrategySchema; + onDragStart?: DragEventHandler; + onDragEnd?: DragEventHandler; + actions?: ReactNode; + orderNumber?: number; + className?: string; + style?: React.CSSProperties; + description?: string; + children?: React.ReactNode; +} + +const DragIcon = styled(IconButton)({ + padding: 0, + cursor: 'inherit', + transition: 'color 0.2s ease-in-out', +}); + +const StyledIndexLabel = styled('div')(({ theme }) => ({ + fontSize: theme.typography.fontSize, + color: theme.palette.text.secondary, + position: 'absolute', + display: 'none', + right: 'calc(100% + 6px)', + top: theme.spacing(2.5), + [theme.breakpoints.up('md')]: { + display: 'block', + }, +})); +const StyledDescription = styled('div')(({ theme }) => ({ + fontSize: theme.typography.fontSize, + fontWeight: 'normal', + color: theme.palette.text.secondary, + display: 'none', + top: theme.spacing(2.5), + [theme.breakpoints.up('md')]: { + display: 'block', + }, +})); +const StyledCustomTitle = styled('div')(({ theme }) => ({ + fontWeight: 'normal', + display: 'none', + [theme.breakpoints.up('md')]: { + display: 'block', + }, +})); +const StyledHeaderContainer = styled('div')({ + flexDirection: 'column', + justifyContent: 'center', + verticalAlign: 'middle', +}); + +const StyledContainer = styled(Box, { + shouldForwardProp: (prop) => prop !== 'disabled', +})<{ disabled?: boolean }>(({ theme, disabled }) => ({ + borderRadius: theme.shape.borderRadiusMedium, + border: `1px solid ${theme.palette.divider}`, + '& + &': { + marginTop: theme.spacing(2), + }, + background: disabled + ? theme.palette.envAccordion.disabled + : theme.palette.background.paper, +})); + +const StyledHeader = styled('div', { + shouldForwardProp: (prop) => prop !== 'draggable' && prop !== 'disabled', +})<{ draggable: boolean; disabled: boolean }>( + ({ theme, draggable, disabled }) => ({ + padding: theme.spacing(0.5, 2), + display: 'flex', + gap: theme.spacing(1), + alignItems: 'center', + borderBottom: `1px solid ${theme.palette.divider}`, + fontWeight: theme.typography.fontWeightMedium, + paddingLeft: draggable ? theme.spacing(1) : theme.spacing(2), + color: disabled + ? theme.palette.text.secondary + : theme.palette.text.primary, + }), +); + +export const StrategyItemContainer: FC = ({ + strategy, + onDragStart, + onDragEnd, + actions, + children, + orderNumber, + style = {}, + description, +}) => { + const Icon = getFeatureStrategyIcon(strategy.name); + + const StrategyHeaderLink: React.FC<{ children?: React.ReactNode }> = + 'links' in strategy + ? ({ children }) => {children} + : ({ children }) => <> {children} ; + + return ( + + {orderNumber}} + /> + + + ( + + + + )} + /> + theme.palette.action.disabled, + }} + /> + + + + + {formatStrategyName( + String(strategy.title), + )} + + } + /> + + + {description} + + } + /> + + + ( + <> + Disabled + + )} + /> + theme.spacing(6), + alignItems: 'center', + }} + > + {actions} + + + {children} + + + ); +}; diff --git a/frontend/src/component/common/StrategyItemContainer/StrategyItemContainer.test.tsx b/frontend/src/component/common/StrategyItemContainer/StrategyItemContainer.test.tsx index c83f2cccac..9ec65ea4be 100644 --- a/frontend/src/component/common/StrategyItemContainer/StrategyItemContainer.test.tsx +++ b/frontend/src/component/common/StrategyItemContainer/StrategyItemContainer.test.tsx @@ -1,6 +1,6 @@ import { screen } from '@testing-library/react'; import { render } from 'utils/testRenderer'; -import { StrategyItemContainer } from './StrategyItemContainer'; +import { StrategyItemContainer } from './LegacyStrategyItemContainer'; import type { IFeatureStrategy } from 'interfaces/strategy'; test('should render strategy name, custom title and description', async () => { diff --git a/frontend/src/component/common/StrategyItemContainer/StrategyItemContainer.tsx b/frontend/src/component/common/StrategyItemContainer/StrategyItemContainer.tsx index c074ef318b..79bdc5d04d 100644 --- a/frontend/src/component/common/StrategyItemContainer/StrategyItemContainer.tsx +++ b/frontend/src/component/common/StrategyItemContainer/StrategyItemContainer.tsx @@ -31,17 +31,6 @@ const DragIcon = styled(IconButton)({ transition: 'color 0.2s ease-in-out', }); -const StyledIndexLabel = styled('div')(({ theme }) => ({ - fontSize: theme.typography.fontSize, - color: theme.palette.text.secondary, - position: 'absolute', - display: 'none', - right: 'calc(100% + 6px)', - top: theme.spacing(2.5), - [theme.breakpoints.up('md')]: { - display: 'block', - }, -})); const StyledDescription = styled('div')(({ theme }) => ({ fontSize: theme.typography.fontSize, fontWeight: 'normal', @@ -65,20 +54,13 @@ const StyledHeaderContainer = styled('div')({ verticalAlign: 'middle', }); -const StyledContainer = styled(Box, { +const NewStyledContainer = styled(Box, { shouldForwardProp: (prop) => prop !== 'disabled', -})<{ disabled?: boolean }>(({ theme, disabled }) => ({ - borderRadius: theme.shape.borderRadiusMedium, - border: `1px solid ${theme.palette.divider}`, - '& + &': { - marginTop: theme.spacing(2), - }, - background: disabled - ? theme.palette.envAccordion.disabled - : theme.palette.background.paper, -})); +})({ + background: 'inherit', +}); -const StyledHeader = styled('div', { +const NewStyledHeader = styled('div', { shouldForwardProp: (prop) => prop !== 'draggable' && prop !== 'disabled', })<{ draggable: boolean; disabled: boolean }>( ({ theme, draggable, disabled }) => ({ @@ -86,7 +68,6 @@ const StyledHeader = styled('div', { display: 'flex', gap: theme.spacing(1), alignItems: 'center', - borderBottom: `1px solid ${theme.palette.divider}`, fontWeight: theme.typography.fontWeightMedium, paddingLeft: draggable ? theme.spacing(1) : theme.spacing(2), color: disabled @@ -101,7 +82,6 @@ export const StrategyItemContainer: FC = ({ onDragEnd, actions, children, - orderNumber, style = {}, description, }) => { @@ -114,15 +94,8 @@ export const StrategyItemContainer: FC = ({ return ( - {orderNumber}} - /> - - + @@ -196,9 +169,9 @@ export const StrategyItemContainer: FC = ({ > {actions} - - {children} - + + {children} + ); }; diff --git a/frontend/src/component/common/StrategySeparator/LegacyStrategySeparator.tsx b/frontend/src/component/common/StrategySeparator/LegacyStrategySeparator.tsx new file mode 100644 index 0000000000..9a7502a3c9 --- /dev/null +++ b/frontend/src/component/common/StrategySeparator/LegacyStrategySeparator.tsx @@ -0,0 +1,52 @@ +// deprecated; remove with the `flagOverviewRedesign` flag +import { Box, styled, useTheme } from '@mui/material'; +import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender'; + +interface IStrategySeparatorProps { + text: 'AND' | 'OR'; +} + +const StyledContent = styled('div')(({ theme }) => ({ + padding: theme.spacing(0.75, 1), + color: theme.palette.text.primary, + fontSize: theme.fontSizes.smallerBody, + backgroundColor: theme.palette.background.elevation2, + borderRadius: theme.shape.borderRadius, + position: 'absolute', + zIndex: theme.zIndex.fab, + top: '50%', + left: theme.spacing(2), + transform: 'translateY(-50%)', + lineHeight: 1, +})); + +const StyledCenteredContent = styled(StyledContent)(({ theme }) => ({ + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + backgroundColor: theme.palette.seen.primary, + borderRadius: theme.shape.borderRadiusLarge, + padding: theme.spacing(0.75, 1.5), +})); + +export const StrategySeparator = ({ text }: IStrategySeparatorProps) => { + const theme = useTheme(); + + return ( + + {text}} + elseShow={() => ( + {text} + )} + /> + + ); +}; diff --git a/frontend/src/component/common/StrategySeparator/StrategySeparator.tsx b/frontend/src/component/common/StrategySeparator/StrategySeparator.tsx index 441aafcbfb..6a7ccfeeb0 100644 --- a/frontend/src/component/common/StrategySeparator/StrategySeparator.tsx +++ b/frontend/src/component/common/StrategySeparator/StrategySeparator.tsx @@ -1,51 +1,57 @@ import { Box, styled, useTheme } from '@mui/material'; -import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender'; interface IStrategySeparatorProps { text: 'AND' | 'OR'; } -const StyledContent = styled('div')(({ theme }) => ({ +const StyledAnd = styled('div')(({ theme }) => ({ padding: theme.spacing(0.75, 1), color: theme.palette.text.primary, fontSize: theme.fontSizes.smallerBody, backgroundColor: theme.palette.background.elevation2, - borderRadius: theme.shape.borderRadius, position: 'absolute', zIndex: theme.zIndex.fab, top: '50%', left: theme.spacing(2), transform: 'translateY(-50%)', lineHeight: 1, + borderRadius: theme.shape.borderRadiusLarge, })); -const StyledCenteredContent = styled(StyledContent)(({ theme }) => ({ +const StyledOr = styled(StyledAnd)(({ theme }) => ({ + fontWeight: 'bold', + backgroundColor: theme.palette.background.alternative, + color: theme.palette.primary.contrastText, + left: theme.spacing(4), +})); + +const StyledSeparator = styled('hr')(({ theme }) => ({ + border: 0, + borderTop: `1px solid ${theme.palette.divider}`, + margin: 0, + position: 'absolute', top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - backgroundColor: theme.palette.seen.primary, - borderRadius: theme.shape.borderRadiusLarge, - padding: theme.spacing(0.75, 1.5), + width: '100%', })); export const StrategySeparator = ({ text }: IStrategySeparatorProps) => { const theme = useTheme(); - return ( - {text}} - elseShow={() => ( - {text} - )} - /> + {text === 'AND' ? ( + {text} + ) : ( + <> + + {text} + + )} ); }; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx index 56689fa101..2869c1723e 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx @@ -9,7 +9,7 @@ import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFe import { formatUnknownError } from 'utils/formatUnknownError'; import useToast from 'hooks/useToast'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { StrategyDraggableItem } from './StrategyDraggableItem/StrategyDraggableItem'; +import { StrategyDraggableItem } from './StrategyDraggableItem/LegacyStrategyDraggableItem'; import type { IFeatureEnvironment } from 'interfaces/featureToggle'; import { FeatureStrategyEmpty } from 'component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; @@ -25,6 +25,7 @@ import { useReleasePlans } from 'hooks/api/getters/useReleasePlans/useReleasePla import { ReleasePlan } from '../../../ReleasePlan/ReleasePlan'; import { Badge } from 'component/common/Badge/Badge'; import { SectionSeparator } from '../SectionSeparator/SectionSeparator'; +import { StrategyDraggableItem as NewStrategyDraggableItem } from './StrategyDraggableItem/StrategyDraggableItem'; interface IEnvironmentAccordionBodyProps { isDisabled: boolean; @@ -59,7 +60,7 @@ const AdditionalStrategiesDiv = styled('div')(({ theme }) => ({ marginBottom: theme.spacing(2), })); -const EnvironmentAccordionBody = ({ +export const EnvironmentAccordionBody = ({ featureEnvironment, isDisabled, otherEnvironments, @@ -223,18 +224,6 @@ const EnvironmentAccordionBody = ({ return ( - 0 || strategies.length > 0) && - isDisabled - } - show={() => ( - - This environment is disabled, which means that none - of your strategies are executing. - - )} - /> 0 || strategies.length > 0} show={ @@ -270,7 +259,7 @@ const EnvironmentAccordionBody = ({ show={ <> {strategies.map((strategy, index) => ( - ); }; - -export default EnvironmentAccordionBody; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/LegacyEnvironmentAccordionBody.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/LegacyEnvironmentAccordionBody.tsx new file mode 100644 index 0000000000..237eb0c5bf --- /dev/null +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/LegacyEnvironmentAccordionBody.tsx @@ -0,0 +1,354 @@ +// deprecated; remove with the `flagOverviewRedesign` flag +import { + type DragEventHandler, + type RefObject, + useEffect, + useState, +} from 'react'; +import { Alert, Pagination, styled } from '@mui/material'; +import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi'; +import { formatUnknownError } from 'utils/formatUnknownError'; +import useToast from 'hooks/useToast'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { StrategyDraggableItem } from './StrategyDraggableItem/LegacyStrategyDraggableItem'; +import type { IFeatureEnvironment } from 'interfaces/featureToggle'; +import { FeatureStrategyEmpty } from 'component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty'; +import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; +import { useFeature } from 'hooks/api/getters/useFeature/useFeature'; +import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi'; +import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; +import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests'; +import usePagination from 'hooks/usePagination'; +import type { IFeatureStrategy } from 'interfaces/strategy'; +import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; +import { useUiFlag } from 'hooks/useUiFlag'; +import { useReleasePlans } from 'hooks/api/getters/useReleasePlans/useReleasePlans'; +import { ReleasePlan } from '../../../ReleasePlan/ReleasePlan'; +import { Badge } from 'component/common/Badge/Badge'; +import { SectionSeparator } from '../SectionSeparator/SectionSeparator'; + +interface IEnvironmentAccordionBodyProps { + isDisabled: boolean; + featureEnvironment?: IFeatureEnvironment; + otherEnvironments?: IFeatureEnvironment['name'][]; +} + +const StyledAccordionBody = styled('div')(({ theme }) => ({ + width: '100%', + position: 'relative', + paddingBottom: theme.spacing(2), +})); + +const StyledAccordionBodyInnerContainer = styled('div')(({ theme }) => ({ + [theme.breakpoints.down(400)]: { + padding: theme.spacing(1), + }, +})); + +const StyledBadge = styled(Badge)(({ theme }) => ({ + backgroundColor: theme.palette.primary.light, + border: 'none', + padding: theme.spacing(0.75, 1.5), + borderRadius: theme.shape.borderRadiusLarge, + color: theme.palette.common.white, +})); + +const AdditionalStrategiesDiv = styled('div')(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + marginBottom: theme.spacing(2), +})); + +const EnvironmentAccordionBody = ({ + featureEnvironment, + isDisabled, + otherEnvironments, +}: IEnvironmentAccordionBodyProps) => { + const projectId = useRequiredPathParam('projectId'); + const featureId = useRequiredPathParam('featureId'); + const { setStrategiesSortOrder } = useFeatureStrategyApi(); + const { addChange } = useChangeRequestApi(); + const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId); + const { refetch: refetchChangeRequests } = + usePendingChangeRequests(projectId); + const { setToastData, setToastApiError } = useToast(); + const { refetchFeature } = useFeature(projectId, featureId); + const manyStrategiesPagination = useUiFlag('manyStrategiesPagination'); + const [strategies, setStrategies] = useState( + featureEnvironment?.strategies || [], + ); + const { releasePlans } = useReleasePlans( + projectId, + featureId, + featureEnvironment?.name, + ); + const { trackEvent } = usePlausibleTracker(); + + const [dragItem, setDragItem] = useState<{ + id: string; + index: number; + height: number; + } | null>(null); + useEffect(() => { + // Use state to enable drag and drop, but switch to API output when it arrives + setStrategies(featureEnvironment?.strategies || []); + }, [featureEnvironment?.strategies]); + + useEffect(() => { + if (strategies.length > 50) { + trackEvent('many-strategies'); + } + }, []); + + if (!featureEnvironment) { + return null; + } + + const pageSize = 20; + const { page, pages, setPageIndex, pageIndex } = + usePagination(strategies, pageSize); + + const onReorder = async (payload: { id: string; sortOrder: number }[]) => { + try { + await setStrategiesSortOrder( + projectId, + featureId, + featureEnvironment.name, + payload, + ); + refetchFeature(); + setToastData({ + text: 'Order of strategies updated', + type: 'success', + }); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); + } + }; + + const onChangeRequestReorder = async ( + payload: { id: string; sortOrder: number }[], + ) => { + await addChange(projectId, featureEnvironment.name, { + action: 'reorderStrategy', + feature: featureId, + payload, + }); + + setToastData({ + text: 'Strategy execution order added to draft', + type: 'success', + }); + refetchChangeRequests(); + }; + + const onStrategyReorder = async ( + payload: { id: string; sortOrder: number }[], + ) => { + try { + if (isChangeRequestConfigured(featureEnvironment.name)) { + await onChangeRequestReorder(payload); + } else { + await onReorder(payload); + } + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); + } + }; + + const onDragStartRef = + ( + ref: RefObject, + index: number, + ): DragEventHandler => + (event) => { + setDragItem({ + id: strategies[index].id, + index, + height: ref.current?.offsetHeight || 0, + }); + + if (ref?.current) { + event.dataTransfer.effectAllowed = 'move'; + event.dataTransfer.setData('text/html', ref.current.outerHTML); + event.dataTransfer.setDragImage(ref.current, 20, 20); + } + }; + + const onDragOver = + (targetId: string) => + ( + ref: RefObject, + targetIndex: number, + ): DragEventHandler => + (event) => { + if (dragItem === null || ref.current === null) return; + if (dragItem.index === targetIndex || targetId === dragItem.id) + return; + + const { top, bottom } = ref.current.getBoundingClientRect(); + const overTargetTop = event.clientY - top < dragItem.height; + const overTargetBottom = bottom - event.clientY < dragItem.height; + const draggingUp = dragItem.index > targetIndex; + + // prevent oscillating by only reordering if there is sufficient space + if ( + (overTargetTop && draggingUp) || + (overTargetBottom && !draggingUp) + ) { + const newStrategies = [...strategies]; + const movedStrategy = newStrategies.splice( + dragItem.index, + 1, + )[0]; + newStrategies.splice(targetIndex, 0, movedStrategy); + setStrategies(newStrategies); + setDragItem({ + ...dragItem, + index: targetIndex, + }); + } + }; + + const onDragEnd = () => { + setDragItem(null); + onStrategyReorder( + strategies.map((strategy, sortOrder) => ({ + id: strategy.id, + sortOrder, + })), + ); + }; + + return ( + + + 0 || strategies.length > 0) && + isDisabled + } + show={() => ( + + This environment is disabled, which means that none + of your strategies are executing. + + )} + /> + 0 || strategies.length > 0} + show={ + <> + {releasePlans.map((plan) => ( + + ))} + 0 && + strategies.length > 0 + } + show={ + <> + + OR + + + Additional strategies + + + } + /> + + {strategies.map((strategy, index) => ( + + ))} + + } + elseShow={ + <> + + We noticed you're using a high + number of activation strategies. To + ensure a more targeted approach, + consider leveraging constraints or + segments. + +
+ {page.map((strategy, index) => ( + {}) as any + } + onDragOver={(() => {}) as any} + onDragEnd={(() => {}) as any} + /> + ))} +
+ + setPageIndex(page - 1) + } + /> + + } + /> + + } + elseShow={ + + } + /> +
+
+ ); +}; + +export default EnvironmentAccordionBody; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/LegacyStrategyDraggableItem.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/LegacyStrategyDraggableItem.tsx new file mode 100644 index 0000000000..db47857047 --- /dev/null +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/LegacyStrategyDraggableItem.tsx @@ -0,0 +1,150 @@ +// deprecated; remove with the `flagOverviewRedesign` flag +import { type DragEventHandler, type RefObject, useRef } from 'react'; +import { Box, useMediaQuery, useTheme } from '@mui/material'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; +import type { IFeatureEnvironment } from 'interfaces/featureToggle'; +import type { IFeatureStrategy } from 'interfaces/strategy'; +import { StrategyItem } from './StrategyItem/LegacyStrategyItem'; +import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; +import { + useStrategyChangesFromRequest, + type UseStrategyChangeFromRequestResult, +} from './StrategyItem/useStrategyChangesFromRequest'; +import { ChangesScheduledBadge } from 'component/changeRequest/ModifiedInChangeRequestStatusBadge/ChangesScheduledBadge'; +import type { IFeatureChange } from 'component/changeRequest/changeRequest.types'; +import { Badge } from 'component/common/Badge/Badge'; +import { + type ScheduledChangeRequestViewModel, + useScheduledChangeRequestsWithStrategy, +} from 'hooks/api/getters/useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy'; + +interface IStrategyDraggableItemProps { + strategy: IFeatureStrategy; + environmentName: string; + index: number; + otherEnvironments?: IFeatureEnvironment['name'][]; + isDragging?: boolean; + onDragStartRef: ( + ref: RefObject, + index: number, + ) => DragEventHandler; + onDragOver: ( + ref: RefObject, + index: number, + ) => DragEventHandler; + onDragEnd: () => void; +} + +export const StrategyDraggableItem = ({ + strategy, + index, + environmentName, + otherEnvironments, + isDragging, + onDragStartRef, + onDragOver, + onDragEnd, +}: IStrategyDraggableItemProps) => { + const projectId = useRequiredPathParam('projectId'); + const featureId = useRequiredPathParam('featureId'); + const ref = useRef(null); + const strategyChangesFromRequest = useStrategyChangesFromRequest( + projectId, + featureId, + environmentName, + strategy.id, + ); + + const { changeRequests: scheduledChangesUsingStrategy } = + useScheduledChangeRequestsWithStrategy(projectId, strategy.id); + + return ( + + 0} + show={} + /> + + + + ); +}; + +const ChangeRequestStatusBadge = ({ + change, +}: { + change: IFeatureChange | undefined; +}) => { + const theme = useTheme(); + const isSmallScreen = useMediaQuery(theme.breakpoints.down('sm')); + + if (isSmallScreen) { + return null; + } + + return ( + + Modified in draft} + /> + Deleted in draft} + /> + + ); +}; + +const renderHeaderChildren = ( + changes?: UseStrategyChangeFromRequestResult, + scheduledChanges?: ScheduledChangeRequestViewModel[], +): JSX.Element[] => { + const badges: JSX.Element[] = []; + if (changes?.length === 0 && scheduledChanges?.length === 0) { + return []; + } + + const draftChange = changes?.find( + ({ isScheduledChange }) => !isScheduledChange, + ); + + if (draftChange) { + badges.push( + , + ); + } + + if (scheduledChanges && scheduledChanges.length > 0) { + badges.push( + scheduledChange.id, + )} + />, + ); + } + + return badges; +}; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem.tsx index 94ced968b1..0bceaf04a6 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem.tsx @@ -1,10 +1,8 @@ import { type DragEventHandler, type RefObject, useRef } from 'react'; import { Box, useMediaQuery, useTheme } from '@mui/material'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; import type { IFeatureEnvironment } from 'interfaces/featureToggle'; import type { IFeatureStrategy } from 'interfaces/strategy'; -import { StrategyItem } from './StrategyItem/StrategyItem'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useStrategyChangesFromRequest, @@ -17,6 +15,8 @@ import { type ScheduledChangeRequestViewModel, useScheduledChangeRequestsWithStrategy, } from 'hooks/api/getters/useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy'; +import { StrategySeparator as NewStrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategyItem as NewStrategyItem } from './StrategyItem/StrategyItem'; interface IStrategyDraggableItemProps { strategy: IFeatureStrategy; @@ -67,10 +67,10 @@ export const StrategyDraggableItem = ({ > 0} - show={} + show={} /> - ; + onDragEnd?: DragEventHandler; + otherEnvironments?: IFeatureEnvironment['name'][]; + orderNumber?: number; + headerChildren?: JSX.Element[] | JSX.Element; +} + +export const StrategyItem: FC = ({ + environmentId, + strategy, + onDragStart, + onDragEnd, + otherEnvironments, + orderNumber, + headerChildren, +}) => { + const projectId = useRequiredPathParam('projectId'); + const featureId = useRequiredPathParam('featureId'); + + const editStrategyPath = formatEditStrategyPath( + projectId, + featureId, + environmentId, + strategy.id, + ); + + return ( + + {headerChildren} + 0, + )} + show={() => ( + + )} + /> + + + + + + } + > + + + {strategy.variants && + strategy.variants.length > 0 && + (strategy.disabled ? ( + + + + ) : ( + + ))} + + ); +}; + +export const NewStrategyItem: FC = ({ + environmentId, + strategy, + onDragStart, + onDragEnd, + otherEnvironments, + orderNumber, + headerChildren, +}) => { + const projectId = useRequiredPathParam('projectId'); + const featureId = useRequiredPathParam('featureId'); + + const editStrategyPath = formatEditStrategyPath( + projectId, + featureId, + environmentId, + strategy.id, + ); + + return ( + + {headerChildren} + 0, + )} + show={() => ( + + )} + /> + + + + + + } + > + + + {strategy.variants && + strategy.variants.length > 0 && + (strategy.disabled ? ( + + + + ) : ( + + ))} + + ); +}; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.tsx index 41574fea6f..6fc2c3951b 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.tsx @@ -2,7 +2,7 @@ import { type FC, Fragment, useMemo } from 'react'; import { Alert, Box, Chip, Link, styled } from '@mui/material'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import PercentageCircle from 'component/common/PercentageCircle/PercentageCircle'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; import { ConstraintItem } from './ConstraintItem/ConstraintItem'; import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies'; import { useSegments } from 'hooks/api/getters/useSegments/useSegments'; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx index 2e7cd133ec..af927059df 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx @@ -10,10 +10,10 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { StrategyExecution } from './StrategyExecution/StrategyExecution'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { CopyStrategyIconMenu } from './CopyStrategyIconMenu/CopyStrategyIconMenu'; -import { StrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer'; import MenuStrategyRemove from './MenuStrategyRemove/MenuStrategyRemove'; import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider'; import { Box } from '@mui/material'; +import { StrategyItemContainer as NewStrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer'; interface IStrategyItemProps { environmentId: string; strategy: IFeatureStrategy; @@ -44,7 +44,7 @@ export const StrategyItem: FC = ({ ); return ( - = ({ ) : ( ))} - + ); }; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.tsx index 60687a70c5..6b60046c1b 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.tsx @@ -3,7 +3,6 @@ import type { IFeatureEnvironment, IFeatureEnvironmentMetrics, } from 'interfaces/featureToggle'; -import EnvironmentAccordionBody from './EnvironmentAccordionBody/EnvironmentAccordionBody'; import { FeatureStrategyMenu } from 'component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu'; import { FEATURE_ENVIRONMENT_ACCORDION } from 'utils/testIds'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; @@ -14,6 +13,7 @@ import FeatureOverviewEnvironmentMetrics from './EnvironmentHeader/FeatureOvervi import { FeatureOverviewEnvironmentToggle } from './EnvironmentHeader/FeatureOverviewEnvironmentToggle/FeatureOverviewEnvironmentToggle'; import { useState } from 'react'; import type { IReleasePlan } from 'interfaces/releasePlans'; +import { EnvironmentAccordionBody as NewEnvironmentAccordionBody } from './EnvironmentAccordionBody/EnvironmentAccordionBody'; const StyledFeatureOverviewEnvironment = styled('div')(({ theme }) => ({ borderRadius: theme.shape.borderRadiusLarge, @@ -32,15 +32,12 @@ const StyledAccordion = styled(Accordion)(({ theme }) => ({ }, })); -const StyledAccordionDetails = styled(AccordionDetails)(({ theme }) => ({ +const NewStyledAccordionDetails = styled(AccordionDetails)(({ theme }) => ({ padding: 0, - background: theme.palette.envAccordion.expanded, + background: theme.palette.background.elevation1, borderBottomLeftRadius: theme.shape.borderRadiusLarge, borderBottomRightRadius: theme.shape.borderRadiusLarge, boxShadow: theme.boxShadows.accordionFooter, - [theme.breakpoints.down('md')]: { - padding: theme.spacing(2, 1), - }, })); const StyledAccordionFooter = styled('footer')(({ theme }) => ({ @@ -55,7 +52,6 @@ const StyledAccordionFooter = styled('footer')(({ theme }) => ({ const StyledEnvironmentAccordionContainer = styled('div')(({ theme }) => ({ width: '100%', position: 'relative', - padding: theme.spacing(3, 3, 1), })); type FeatureOverviewEnvironmentProps = { @@ -112,9 +108,9 @@ export const FeatureOverviewEnvironment = ({ collapsed={!hasActivations} /> - + - ) : null} - + ); diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/LegacyFeatureOverviewEnvironment.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/LegacyFeatureOverviewEnvironment.tsx index 6fcf87eb84..9a471551a9 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/LegacyFeatureOverviewEnvironment.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/LegacyFeatureOverviewEnvironment.tsx @@ -13,7 +13,7 @@ import { getFeatureMetrics } from 'utils/getFeatureMetrics'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import EnvironmentIcon from 'component/common/EnvironmentIcon/EnvironmentIcon'; import StringTruncator from 'component/common/StringTruncator/StringTruncator'; -import EnvironmentAccordionBody from './EnvironmentAccordionBody/EnvironmentAccordionBody'; +import EnvironmentAccordionBody from './EnvironmentAccordionBody/LegacyEnvironmentAccordionBody'; import { EnvironmentFooter } from './EnvironmentFooter/EnvironmentFooter'; import FeatureOverviewEnvironmentMetrics from './EnvironmentHeader/FeatureOverviewEnvironmentMetrics/LegacyFeatureOverviewEnvironmentMetrics'; import { FeatureStrategyMenu } from 'component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu'; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment.tsx index 9921f1fd05..2a0bc6dc31 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment.tsx @@ -1,6 +1,6 @@ import { Fragment } from 'react'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; import { SegmentItem } from '../../../../common/SegmentItem/SegmentItem'; import type { ISegment } from 'interfaces/segment'; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone.tsx index b091eed72f..4706bfb903 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone.tsx @@ -8,7 +8,7 @@ import { import type { IReleasePlanMilestone } from 'interfaces/releasePlans'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ReleasePlanMilestoneStrategy } from './ReleasePlanMilestoneStrategy'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; import { ReleasePlanMilestoneStatus, type MilestoneStatus, diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/ConstraintExecution/ConstraintExecution.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/ConstraintExecution/ConstraintExecution.tsx index 1cd27bac0f..b1f1499d36 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/ConstraintExecution/ConstraintExecution.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/ConstraintExecution/ConstraintExecution.tsx @@ -5,7 +5,7 @@ import type { } from 'openapi'; import { objectId } from 'utils/objectId'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; import { styled } from '@mui/material'; import { ConstraintAccordionView } from 'component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView'; import { ConstraintError } from './ConstraintError/ConstraintError'; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/ConstraintExecution/ConstraintExecutionWithoutResults.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/ConstraintExecution/ConstraintExecutionWithoutResults.tsx index e53ac10b14..eeca727e52 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/ConstraintExecution/ConstraintExecutionWithoutResults.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/ConstraintExecution/ConstraintExecutionWithoutResults.tsx @@ -2,7 +2,7 @@ import { Fragment, type VFC } from 'react'; import type { PlaygroundConstraintSchema } from 'openapi'; import { objectId } from 'utils/objectId'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; import { styled } from '@mui/material'; import { ConstraintAccordionView } from 'component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView'; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/CustomStrategyParams/CustomStrategyParams.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/CustomStrategyParams/CustomStrategyParams.tsx index 9478a9e017..8974e374bf 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/CustomStrategyParams/CustomStrategyParams.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/CustomStrategyParams/CustomStrategyParams.tsx @@ -5,7 +5,7 @@ import { parseParameterStrings, } from 'utils/parseParameter'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies'; import { CustomParameterItem } from './CustomParameterItem/CustomParameterItem'; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/DisabledStrategyExecution.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/DisabledStrategyExecution.tsx index c6ebe493d6..188e701988 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/DisabledStrategyExecution.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/DisabledStrategyExecution.tsx @@ -1,6 +1,6 @@ import { Fragment, type VFC } from 'react'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; import { styled } from '@mui/material'; import type { PlaygroundRequestSchema, diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/SegmentExecution/SegmentExecution.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/SegmentExecution/SegmentExecution.tsx index c8d4d2575f..4ce4465eee 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/SegmentExecution/SegmentExecution.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/SegmentExecution/SegmentExecution.tsx @@ -2,7 +2,7 @@ import { Fragment, type VFC } from 'react'; import type { PlaygroundSegmentSchema, PlaygroundRequestSchema } from 'openapi'; import { ConstraintExecution } from '../ConstraintExecution/ConstraintExecution'; import CancelOutlined from '@mui/icons-material/CancelOutlined'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; import { styled, Typography } from '@mui/material'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { SegmentItem } from 'component/common/SegmentItem/SegmentItem'; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/SegmentExecution/SegmentExecutionWithoutResult.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/SegmentExecution/SegmentExecutionWithoutResult.tsx index f6a5fe491c..3efb6d9a1d 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/SegmentExecution/SegmentExecutionWithoutResult.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/SegmentExecution/SegmentExecutionWithoutResult.tsx @@ -1,6 +1,6 @@ import { Fragment, type VFC } from 'react'; import type { PlaygroundSegmentSchema } from 'openapi'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { SegmentItem } from 'component/common/SegmentItem/SegmentItem'; import { ConstraintExecutionWithoutResults } from '../ConstraintExecution/ConstraintExecutionWithoutResults'; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/StrategyExecution.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/StrategyExecution.tsx index 67143ea1d2..0cf90d70b1 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/StrategyExecution.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/StrategyExecution.tsx @@ -1,6 +1,6 @@ import { Fragment, type VFC } from 'react'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; import { styled } from '@mui/material'; import type { PlaygroundRequestSchema, diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/playgroundResultStrategyLists.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/playgroundResultStrategyLists.tsx index 5a8abce32b..82d6faa32e 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/playgroundResultStrategyLists.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/playgroundResultStrategyLists.tsx @@ -7,7 +7,7 @@ import type { } from 'openapi'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { FeatureStrategyItem } from './StrategyItem/FeatureStrategyItem'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; const StyledAlertWrapper = styled('div')(({ theme }) => ({ display: 'flex', diff --git a/frontend/src/component/releases/ReleasePlanTemplate/TemplateForm/MilestoneList/MilestoneCard/MilestoneStrategyDraggableItem.tsx b/frontend/src/component/releases/ReleasePlanTemplate/TemplateForm/MilestoneList/MilestoneCard/MilestoneStrategyDraggableItem.tsx index f507b691bc..be01b37939 100644 --- a/frontend/src/component/releases/ReleasePlanTemplate/TemplateForm/MilestoneList/MilestoneCard/MilestoneStrategyDraggableItem.tsx +++ b/frontend/src/component/releases/ReleasePlanTemplate/TemplateForm/MilestoneList/MilestoneCard/MilestoneStrategyDraggableItem.tsx @@ -3,7 +3,7 @@ import { type DragEventHandler, type RefObject, useRef } from 'react'; import { Box, IconButton } from '@mui/material'; import Edit from '@mui/icons-material/Edit'; import Delete from '@mui/icons-material/DeleteOutlined'; -import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator'; import { MilestoneStrategyItem } from './MilestoneStrategyItem'; interface IMilestoneStrategyDraggableItemProps {