diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx index 57368f83d0..007ad90b27 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx @@ -1,14 +1,15 @@ import type React from 'react'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import PermissionButton, { type IPermissionButtonProps, } from 'component/common/PermissionButton/PermissionButton'; import { CREATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions'; -import { Dialog, styled } from '@mui/material'; +import { Box, Dialog, IconButton, styled, Typography } from '@mui/material'; import { LegacyFeatureStrategyMenuCards } from './LegacyFeatureStrategyMenuCards/LegacyFeatureStrategyMenuCards.tsx'; import { formatCreateStrategyPath } from '../FeatureStrategyCreate/FeatureStrategyCreate.tsx'; import MoreVert from '@mui/icons-material/MoreVert'; +import CloseIcon from '@mui/icons-material/Close'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import type { IReleasePlanTemplate } from 'interfaces/releasePlans'; import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi'; @@ -20,7 +21,7 @@ import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; import { formatUnknownError } from 'utils/formatUnknownError'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { LegacyReleasePlanReviewDialog } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/LegacyReleasePlanReviewDialog.tsx'; -import { ReleasePlanReviewDialog } from '../../FeatureView/FeatureOverview/ReleasePlan/ReleasePlanReviewDialog.tsx'; +import { ReleasePlanPreview } from '../../FeatureView/FeatureOverview/ReleasePlan/ReleasePlanPreview.tsx'; import { FeatureStrategyMenuCards, type StrategyFilterValue, @@ -34,7 +35,6 @@ interface IFeatureStrategyMenuProps { environmentId: string; variant?: IPermissionButtonProps['variant']; matchWidth?: boolean; - size?: IPermissionButtonProps['size']; disableReason?: string; } @@ -52,13 +52,19 @@ const StyledAdditionalMenuButton = styled(PermissionButton)(({ theme }) => ({ paddingBlock: 0, })); +const StyledHeader = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + padding: theme.spacing(4, 4, 2, 4), +})); + export const FeatureStrategyMenu = ({ label, projectId, featureId, environmentId, variant, - size, matchWidth, disableReason, }: IFeatureStrategyMenuProps) => { @@ -71,6 +77,7 @@ export const FeatureStrategyMenu = ({ const [selectedTemplate, setSelectedTemplate] = useState(); const [addReleasePlanOpen, setAddReleasePlanOpen] = useState(false); + const [releasePlanPreview, setReleasePlanPreview] = useState(false); const dialogId = isStrategyMenuDialogOpen ? 'FeatureStrategyMenuDialog' : undefined; @@ -90,6 +97,11 @@ export const FeatureStrategyMenu = ({ setIsStrategyMenuDialogOpen(false); }; + useEffect(() => { + if (!isStrategyMenuDialogOpen) return; + setReleasePlanPreview(false); + }, [isStrategyMenuDialogOpen]); + const openDefaultStrategyCreationModal = (event: React.SyntheticEvent) => { trackEvent('strategy-add', { props: { @@ -249,27 +261,54 @@ export const FeatureStrategyMenu = ({ sx: { borderRadius: '12px', height: newStrategyModalEnabled ? '100%' : 'auto', + width: '100%', }, }} > {newStrategyModalEnabled ? ( - { - setSelectedTemplate(template); - addReleasePlan(template); - }} - onReviewReleasePlan={(template) => { - setSelectedTemplate(template); - setAddReleasePlanOpen(true); - onClose(); - }} - onClose={onClose} - /> + <> + + Add strategy + + + + + {releasePlanPreview && selectedTemplate ? ( + setReleasePlanPreview(false)} + onConfirm={() => { + addReleasePlan(selectedTemplate); + }} + /> + ) : ( + { + setSelectedTemplate(template); + addReleasePlan(template); + }} + onReviewReleasePlan={(template) => { + setSelectedTemplate(template); + setReleasePlanPreview(true); + }} + onClose={onClose} + /> + )} + ) : ( {selectedTemplate && ( - <> - {newStrategyModalEnabled ? ( - { - setAddReleasePlanOpen(open); - if (!open) { - setIsStrategyMenuDialogOpen(true); - } - }} - onConfirm={() => { - addReleasePlan(selectedTemplate); - }} - template={selectedTemplate} - projectId={projectId} - featureName={featureId} - environment={environmentId} - crProtected={crProtected} - /> - ) : ( - { - setAddReleasePlanOpen(open); - if (!open) { - setIsStrategyMenuDialogOpen(true); - } - }} - onConfirm={() => { - addReleasePlan(selectedTemplate); - }} - template={selectedTemplate} - projectId={projectId} - featureName={featureId} - environment={environmentId} - crProtected={crProtected} - /> - )} - + { + setAddReleasePlanOpen(open); + if (!open) { + setIsStrategyMenuDialogOpen(true); + } + }} + onConfirm={() => { + addReleasePlan(selectedTemplate); + }} + template={selectedTemplate} + projectId={projectId} + featureName={featureId} + environment={environmentId} + crProtected={crProtected} + /> )} ); diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenuCards/FeatureStrategyMenuCards.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenuCards/FeatureStrategyMenuCards.tsx index 2847355dc7..6dbcd1ca23 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenuCards/FeatureStrategyMenuCards.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenuCards/FeatureStrategyMenuCards.tsx @@ -1,16 +1,12 @@ -import { styled, Typography, Box, IconButton } from '@mui/material'; +import { styled, Box } from '@mui/material'; import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies'; import { FeatureStrategyMenuCard } from '../FeatureStrategyMenuCard/FeatureStrategyMenuCard.tsx'; import type { IReleasePlanTemplate } from 'interfaces/releasePlans'; import { Link as RouterLink, useNavigate } from 'react-router-dom'; -import CloseIcon from '@mui/icons-material/Close'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig.ts'; import { HelpIcon } from 'component/common/HelpIcon/HelpIcon.tsx'; import { type Dispatch, type SetStateAction, useContext, useMemo } from 'react'; -import { - FeatureStrategyMenuCardsSection, - StyledStrategyModalSectionHeader, -} from './FeatureStrategyMenuCardsSection.tsx'; +import { FeatureStrategyMenuCardsSection } from './FeatureStrategyMenuCardsSection.tsx'; import { FeatureStrategyMenuCardsReleaseTemplates } from './FeatureStrategyMenuCardsReleaseTemplates.tsx'; import { QuickFilters } from 'component/common/QuickFilters/QuickFilters.tsx'; import { @@ -41,13 +37,12 @@ export type StrategyFilterValue = (typeof FILTERS)[number]['value']; const CUSTOM_STRATEGY_DISPLAY_LIMIT = 5; const StyledContainer = styled(Box)(() => ({ - width: '100%', display: 'flex', flexDirection: 'column', })); const StyledScrollableContent = styled(Box)(({ theme }) => ({ - width: theme.breakpoints.values.md, + width: '100%', height: '100%', overflowY: 'auto', padding: theme.spacing(4), @@ -57,13 +52,6 @@ const StyledScrollableContent = styled(Box)(({ theme }) => ({ gap: theme.spacing(5), })); -const StyledHeader = styled(Box)(({ theme }) => ({ - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - padding: theme.spacing(4, 4, 2, 4), -})); - const StyledFiltersContainer = styled(Box)(({ theme }) => ({ display: 'flex', padding: theme.spacing(0, 4, 3, 4), @@ -214,17 +202,6 @@ export const FeatureStrategyMenuCards = ({ return ( - - Add strategy - - - - - {(shouldRender('default') || shouldRender('standard')) && ( - - - {shouldRender('default') && ( - - - Project default - - - - )} - {shouldRender('standard') && ( - - - Standard strategies - - - )} - - - {shouldRender('default') && ( - + Project default + - )} - {shouldRender('standard') && ( - <>{standardStrategies.map(renderStrategy)} - )} - - + + } + > + + + )} + {shouldRender('standard') && ( + + {standardStrategies.map(renderStrategy)} + )} {shouldRender('releaseTemplates') && ( ({ const StyledStrategyModalSectionGrid = styled(Box)(({ theme }) => ({ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', + [theme.breakpoints.down('md')]: { + gridTemplateColumns: 'repeat(2, 1fr)', + }, + [theme.breakpoints.down('sm')]: { + gridTemplateColumns: 'repeat(1, 1fr)', + }, gap: theme.spacing(2), width: '100%', })); @@ -28,21 +34,22 @@ const StyledViewMoreButton = styled(Button)(({ theme }) => ({ })); interface IFeatureStrategyMenuCardsSectionProps { - title?: string; + title?: ReactNode; limit?: number; viewMore?: () => void; viewMoreLabel?: string; - children: ReactNode[]; + children: ReactNode; } export const FeatureStrategyMenuCardsSection = ({ title, limit, viewMore, - viewMoreLabel = 'View more', + viewMoreLabel = 'View more strategies', children, }: IFeatureStrategyMenuCardsSectionProps) => { - const limitedChildren = limit ? children.slice(0, limit) : children; + const allChildren = Array.isArray(children) ? children : [children]; + const limitedChildren = limit ? allChildren.slice(0, limit) : allChildren; return ( @@ -53,7 +60,7 @@ export const FeatureStrategyMenuCardsSection = ({ )} {limitedChildren} - {viewMore && limitedChildren.length < children.length && ( + {viewMore && limitedChildren.length < allChildren.length && ( ) : ( ({ - '& .MuiDialog-paper': { - borderRadius: theme.shape.borderRadiusLarge, - height: '100%', - }, -})); const StyledScrollableContent = styled(Box)(({ theme }) => ({ width: theme.breakpoints.values.md, @@ -31,13 +21,6 @@ const StyledScrollableContent = styled(Box)(({ theme }) => ({ flexDirection: 'column', })); -const StyledHeader = styled(Box)(({ theme }) => ({ - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - padding: theme.spacing(4, 4, 2, 4), -})); - const StyledSubHeader = styled(Box)(({ theme }) => ({ padding: theme.spacing(0, 3, 3, 3), })); @@ -50,29 +33,27 @@ const StyledDialogActions = styled(DialogActions)(({ theme }) => ({ padding: theme.spacing(2, 4, 4), })); -interface IReleasePlanAddDialogProps { - open: boolean; - setOpen: React.Dispatch>; - onConfirm: () => void; +interface IReleasePlanPreviewProps { template: IReleasePlanTemplate; projectId: string; featureName: string; environment: string; crProtected?: boolean; + onConfirm: () => void; + onBack: () => void; } -export const ReleasePlanReviewDialog = ({ - open, - setOpen, - onConfirm, +export const ReleasePlanPreview = ({ template, projectId, featureName, environment, crProtected, -}: IReleasePlanAddDialogProps) => { + onConfirm, + onBack, +}: IReleasePlanPreviewProps) => { const { feature } = useFeature(projectId, featureName); - const { releasePlans } = useReleasePlans( + const { releasePlans, loading } = useReleasePlans( projectId, featureName, environment, @@ -91,23 +72,12 @@ export const ReleasePlanReviewDialog = ({ environment, ); - const handleClose = () => setOpen(false); + if (loading) return null; return ( - - - Add strategy - - - - + <> - @@ -146,6 +116,6 @@ export const ReleasePlanReviewDialog = ({ {crProtected ? 'Add suggestion to draft' : 'Apply template'} - + ); };