diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenuCards/FeatureStrategyMenuCards.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenuCards/FeatureStrategyMenuCards.tsx
index 81535adc13..4f50059a1c 100644
--- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenuCards/FeatureStrategyMenuCards.tsx
+++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenuCards/FeatureStrategyMenuCards.tsx
@@ -1,72 +1,55 @@
-import { Link, styled, Typography, Box, IconButton } from '@mui/material';
+import { styled, Typography, Box, IconButton, Button } from '@mui/material';
import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
import { FeatureStrategyMenuCard } from '../FeatureStrategyMenuCard/FeatureStrategyMenuCard.tsx';
import { useReleasePlanTemplates } from 'hooks/api/getters/useReleasePlanTemplates/useReleasePlanTemplates';
import { FeatureReleasePlanCard } from '../FeatureReleasePlanCard/FeatureReleasePlanCard.tsx';
import type { IReleasePlanTemplate } from 'interfaces/releasePlans';
-import { useNavigate } from 'react-router-dom';
+import { Link as RouterLink } from 'react-router-dom';
import CloseIcon from '@mui/icons-material/Close';
import FactCheckOutlinedIcon from '@mui/icons-material/FactCheckOutlined';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig.ts';
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon.tsx';
+import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview.ts';
+import { useState } from 'react';
-interface IFeatureStrategyMenuCardsProps {
- projectId: string;
- featureId: string;
- environmentId: string;
- onlyReleasePlans: boolean;
- onAddReleasePlan: (template: IReleasePlanTemplate) => void;
- onReviewReleasePlan: (template: IReleasePlanTemplate) => void;
- onClose: () => void;
-}
+const RELEASE_TEMPLATE_DISPLAY_LIMIT = 5;
-const GridContainer = styled(Box)(() => ({
+const StyledContainer = styled(Box)(() => ({
width: '100%',
display: 'flex',
flexDirection: 'column',
}));
-const ScrollableContent = styled(Box)(({ theme }) => ({
+const StyledScrollableContent = styled(Box)(({ theme }) => ({
width: '100%',
- maxHeight: '70vh',
+ maxHeight: theme.spacing(62),
overflowY: 'auto',
padding: theme.spacing(4),
- paddingTop: 0,
+ paddingTop: theme.spacing(1),
display: 'flex',
flexDirection: 'column',
- gap: theme.spacing(3),
+ gap: theme.spacing(5),
}));
-const GridSection = styled(Box)(({ theme }) => ({
+const StyledGrid = styled(Box)(({ theme }) => ({
display: 'grid',
- gridTemplateColumns: 'repeat(2, 1fr)',
- gap: theme.spacing(1.5),
+ gridTemplateColumns: 'repeat(3, 1fr)',
+ gap: theme.spacing(2),
width: '100%',
}));
-const CardWrapper = styled(Box)(() => ({
- width: '100%',
- minWidth: 0,
-}));
-
-const TitleRow = styled(Box)(({ theme }) => ({
+const StyledHeader = 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 }) => ({
+const StyledSectionHeader = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(0.5),
- marginBottom: theme.spacing(1),
+ marginBottom: theme.spacing(0.5),
width: '100%',
}));
@@ -81,7 +64,7 @@ const StyledIcon = styled('span')(({ theme }) => ({
alignItems: 'center',
}));
-const EmptyStateContainer = styled(Box)(({ theme }) => ({
+const StyledNoTemplatesContainer = styled(Box)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
@@ -92,7 +75,7 @@ const EmptyStateContainer = styled(Box)(({ theme }) => ({
width: 'auto',
}));
-const EmptyStateTitle = styled(Typography)(({ theme }) => ({
+const StyledNoTemplatesTitle = styled(Typography)(({ theme }) => ({
fontSize: theme.typography.caption.fontSize,
fontWeight: theme.typography.fontWeightBold,
marginBottom: theme.spacing(1),
@@ -100,24 +83,39 @@ const EmptyStateTitle = styled(Typography)(({ theme }) => ({
alignItems: 'center',
}));
-const EmptyStateDescription = styled(Typography)(({ theme }) => ({
+const StyledNoTemplatesDescription = styled(Typography)(({ theme }) => ({
fontSize: theme.typography.caption.fontSize,
color: theme.palette.text.secondary,
}));
-const ClickableBoldText = styled(Link)(({ theme }) => ({
- fontWeight: theme.typography.fontWeightBold,
- cursor: 'pointer',
+const StyledLink = styled(RouterLink)({
+ textDecoration: 'none',
'&:hover': {
textDecoration: 'underline',
},
-}));
+});
+
+const StyledViewAllTemplates = styled(Box)({
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ height: '100%',
+});
+
+interface IFeatureStrategyMenuCardsProps {
+ projectId: string;
+ featureId: string;
+ environmentId: string;
+ onlyReleasePlans: boolean;
+ onAddReleasePlan: (template: IReleasePlanTemplate) => void;
+ onReviewReleasePlan: (template: IReleasePlanTemplate) => void;
+ onClose: () => void;
+}
export const FeatureStrategyMenuCards = ({
projectId,
featureId,
environmentId,
- onlyReleasePlans,
onAddReleasePlan,
onReviewReleasePlan,
onClose,
@@ -126,7 +124,9 @@ export const FeatureStrategyMenuCards = ({
const { strategies } = useStrategies();
const { templates } = useReleasePlanTemplates();
- const navigate = useNavigate();
+ const { project } = useProjectOverview(projectId);
+
+ const [seeAllReleaseTemplates, setSeeAllReleaseTemplates] = useState(false);
const activeStrategies = strategies.filter(
(strategy) => !strategy.deprecated,
@@ -136,14 +136,26 @@ export const FeatureStrategyMenuCards = ({
(strategy) => !strategy.advanced && !strategy.editable,
);
- const advancedAndCustomStrategies = activeStrategies.filter(
- (strategy) => strategy.editable || strategy.advanced,
+ const advancedStrategies = activeStrategies.filter(
+ (strategy) => strategy.advanced && !strategy.editable,
);
- const defaultStrategy = {
+ const customStrategies = activeStrategies.filter(
+ (strategy) => strategy.editable,
+ );
+
+ const projectDefaultStrategy = project?.environments?.find(
+ (env) => env.environment === environmentId,
+ )?.defaultStrategy || {
name: 'flexibleRollout',
+ title: '100% of all users',
+ };
+
+ const defaultStrategy = {
+ name: projectDefaultStrategy.name || 'flexibleRollout',
displayName: 'Default strategy',
description:
+ projectDefaultStrategy.title ||
'This is the default strategy defined for this environment in the project',
};
@@ -152,59 +164,71 @@ export const FeatureStrategyMenuCards = ({
return null;
}
+ const slicedTemplates = seeAllReleaseTemplates
+ ? templates
+ : templates.slice(0, RELEASE_TEMPLATE_DISPLAY_LIMIT);
+
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)
- }
- />
-
+
+ {slicedTemplates.map((template) => (
+ onAddReleasePlan(template)}
+ onPreviewClick={() =>
+ onReviewReleasePlan(template)
+ }
+ />
))}
-
+ {slicedTemplates.length < templates.length &&
+ templates.length >
+ RELEASE_TEMPLATE_DISPLAY_LIMIT && (
+
+
+
+ )}
+
)}
);
};
return (
-
-
- Add strategy
+
+
+ Add strategy
-
-
- {onlyReleasePlans ? (
- renderReleasePlanTemplates()
- ) : (
- <>
-
-
-
- Standard strategies
-
-
+
+
+
+
+
+ Project default
+
+
+ This is set per project, per
+ environment, and can be configured{' '}
+
+ here
+
+ >
+ }
+ size='16px'
+ />
+
+
+
+ Standard strategies
+
+
+
+
+
+ {standardStrategies.map((strategy) => (
+
+ ))}
+
+
+ {renderReleasePlanTemplates()}
+ {advancedStrategies.length > 0 && (
+
+
+
+ Advanced strategies
+
+
+
+ {advancedStrategies.map((strategy) => (
+
-
-
-
-
-
- {standardStrategies.map((strategy) => (
-
-
-
- ))}
-
-
- {renderReleasePlanTemplates()}
- {advancedAndCustomStrategies.length > 0 && (
-
-
-
- Custom and advanced strategies
-
-
-
-
- {advancedAndCustomStrategies.map(
- (strategy) => (
-
-
-
- ),
- )}
-
-
- )}
- >
+ ))}
+
+
)}
-
-
+ {customStrategies.length > 0 && (
+
+
+
+ Custom strategies
+
+
+
+ {customStrategies.map((strategy) => (
+
+ ))}
+
+
+ )}
+
+
);
};