From 28cbf7b19eae1cdbbbc10958fb51c7fac3d7c54c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20G=C3=B3is?= Date: Mon, 8 Sep 2025 11:41:22 +0100 Subject: [PATCH] chore: add newStrategyModal flag --- .../FeatureStrategyMenu.tsx | 55 +++- .../FeatureStrategyMenuCards.tsx | 4 +- .../LegacyFeatureStrategyMenuCards.tsx | 294 ++++++++++++++++++ 3 files changed, 333 insertions(+), 20 deletions(-) create mode 100644 frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/LegacyFeatureStrategyMenuCards/LegacyFeatureStrategyMenuCards.tsx diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx index 7dc3234e9a..fce606af02 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx @@ -6,7 +6,7 @@ import PermissionButton, { } from 'component/common/PermissionButton/PermissionButton'; import { CREATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions'; import { Dialog, styled } from '@mui/material'; -import { FeatureStrategyMenuCards } from './FeatureStrategyMenuCards/FeatureStrategyMenuCards.tsx'; +import { LegacyFeatureStrategyMenuCards } from './LegacyFeatureStrategyMenuCards/LegacyFeatureStrategyMenuCards.tsx'; import { formatCreateStrategyPath } from '../FeatureStrategyCreate/FeatureStrategyCreate.tsx'; import MoreVert from '@mui/icons-material/MoreVert'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; @@ -20,6 +20,7 @@ import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; import { formatUnknownError } from 'utils/formatUnknownError'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { ReleasePlanReviewDialog } from '../../FeatureView/FeatureOverview/ReleasePlan/ReleasePlanReviewDialog.tsx'; +import { FeatureStrategyMenuCards } from './FeatureStrategyMenuCards/FeatureStrategyMenuCards.tsx'; interface IFeatureStrategyMenuProps { label: string; @@ -77,6 +78,7 @@ export const FeatureStrategyMenu = ({ const { isEnterprise } = useUiConfig(); const displayReleasePlanButton = isEnterprise(); const crProtected = isChangeRequestConfigured(environmentId); + const newStrategyModalEnabled = useUiFlag('newStrategyModal'); const onClose = () => { setIsStrategyMenuDialogOpen(false); @@ -222,22 +224,41 @@ export const FeatureStrategyMenu = ({ }, }} > - { - setSelectedTemplate(template); - addReleasePlan(template); - }} - onReviewReleasePlan={(template) => { - setSelectedTemplate(template); - setAddReleasePlanOpen(true); - onClose(); - }} - onClose={onClose} - /> + {newStrategyModalEnabled ? ( + { + setSelectedTemplate(template); + addReleasePlan(template); + }} + onReviewReleasePlan={(template) => { + setSelectedTemplate(template); + setAddReleasePlanOpen(true); + onClose(); + }} + onClose={onClose} + /> + ) : ( + { + setSelectedTemplate(template); + addReleasePlan(template); + }} + onReviewReleasePlan={(template) => { + setSelectedTemplate(template); + setAddReleasePlanOpen(true); + onClose(); + }} + onClose={onClose} + /> + )} {selectedTemplate && ( - - {onlyReleasePlans ? 'Select template' : 'Add configuration'} - + Add strategy void; + onReviewReleasePlan: (template: IReleasePlanTemplate) => void; + onClose: () => void; +} + +const GridContainer = styled(Box)(() => ({ + width: '100%', + display: 'flex', + flexDirection: 'column', +})); + +const ScrollableContent = styled(Box)(({ theme }) => ({ + width: '100%', + maxHeight: '70vh', + overflowY: 'auto', + padding: theme.spacing(4), + paddingTop: 0, + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(3), +})); + +const GridSection = styled(Box)(({ theme }) => ({ + display: 'grid', + gridTemplateColumns: 'repeat(2, 1fr)', + gap: theme.spacing(1.5), + width: '100%', +})); + +const CardWrapper = styled(Box)(() => ({ + width: '100%', + minWidth: 0, +})); + +const TitleRow = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + padding: theme.spacing(4, 4, 2, 4), +})); + +const TitleText = styled(Typography)(({ theme }) => ({ + fontSize: theme.typography.body1.fontSize, + fontWeight: theme.typography.fontWeightBold, + margin: 0, +})); + +const SectionTitle = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(0.5), + marginBottom: theme.spacing(1), + width: '100%', +})); + +const StyledIcon = styled('span')(({ theme }) => ({ + width: theme.spacing(3), + '& > svg': { + fill: theme.palette.primary.main, + width: theme.spacing(2.25), + height: theme.spacing(2.25), + }, + display: 'flex', + alignItems: 'center', +})); + +const EmptyStateContainer = styled(Box)(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + alignItems: 'flex-start', + justifyContent: 'flex-start', + backgroundColor: theme.palette.neutral.light, + borderRadius: theme.shape.borderRadiusMedium, + padding: theme.spacing(3), + width: 'auto', +})); + +const EmptyStateTitle = styled(Typography)(({ theme }) => ({ + fontSize: theme.typography.caption.fontSize, + fontWeight: theme.typography.fontWeightBold, + marginBottom: theme.spacing(1), + display: 'flex', + alignItems: 'center', +})); + +const EmptyStateDescription = styled(Typography)(({ theme }) => ({ + fontSize: theme.typography.caption.fontSize, + color: theme.palette.text.secondary, +})); + +const ClickableBoldText = styled(Link)(({ theme }) => ({ + fontWeight: theme.typography.fontWeightBold, + cursor: 'pointer', + '&:hover': { + textDecoration: 'underline', + }, +})); + +export const LegacyFeatureStrategyMenuCards = ({ + projectId, + featureId, + environmentId, + onlyReleasePlans, + onAddReleasePlan, + onReviewReleasePlan, + onClose, +}: IFeatureStrategyMenuCardsProps) => { + const { isEnterprise } = useUiConfig(); + + const { strategies } = useStrategies(); + const { templates } = useReleasePlanTemplates(); + const navigate = useNavigate(); + + const activeStrategies = strategies.filter( + (strategy) => !strategy.deprecated, + ); + + const standardStrategies = activeStrategies.filter( + (strategy) => !strategy.advanced && !strategy.editable, + ); + + const advancedAndCustomStrategies = activeStrategies.filter( + (strategy) => strategy.editable || strategy.advanced, + ); + + const defaultStrategy = { + name: 'flexibleRollout', + displayName: 'Default strategy', + description: + 'This is the default strategy defined for this environment in the project', + }; + + const renderReleasePlanTemplates = () => { + if (!isEnterprise()) { + return null; + } + + return ( + + + + Release templates + + + + {!templates.length ? ( + + + + + + Create your own release templates + + + Standardize your rollouts and save time by reusing + predefined strategies. Find release templates in the + side menu under{' '} + navigate('/release-templates')} + > + Configure > Release templates + + + + ) : ( + + {templates.map((template) => ( + + onAddReleasePlan(template)} + onPreviewClick={() => + onReviewReleasePlan(template) + } + /> + + ))} + + )} + + ); + }; + + return ( + + + + {onlyReleasePlans ? 'Select template' : 'Add configuration'} + + + + + + + {onlyReleasePlans ? ( + renderReleasePlanTemplates() + ) : ( + <> + + + + Standard strategies + + + + + + + + {standardStrategies.map((strategy) => ( + + + + ))} + + + {renderReleasePlanTemplates()} + {advancedAndCustomStrategies.length > 0 && ( + + + + Custom and advanced strategies + + + + + {advancedAndCustomStrategies.map( + (strategy) => ( + + + + ), + )} + + + )} + + )} + + + ); +};