mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-31 13:47:02 +02:00
fix: add strategy release templates visibility for non Enterprise (#10401)
https://linear.app/unleash/issue/2-3711/add-strategy-modal-shows-release-templates-section-for-non-enterprise Fixes a bug for non-Enterprise where release templates were mentioned (and even linked to) even though they were not available for these plans. When following the link the result was a page that did not render. Also slightly refactors and improves this component. <img width="870" height="496" alt="image" src="https://github.com/user-attachments/assets/47499e21-73fc-4ddf-8eed-6146be31b074" />
This commit is contained in:
parent
0e015d6686
commit
45e5b217aa
@ -8,7 +8,6 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
|
import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
|
||||||
import { FeatureStrategyMenuCard } from '../FeatureStrategyMenuCard/FeatureStrategyMenuCard.tsx';
|
import { FeatureStrategyMenuCard } from '../FeatureStrategyMenuCard/FeatureStrategyMenuCard.tsx';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
||||||
import { useReleasePlanTemplates } from 'hooks/api/getters/useReleasePlanTemplates/useReleasePlanTemplates';
|
import { useReleasePlanTemplates } from 'hooks/api/getters/useReleasePlanTemplates/useReleasePlanTemplates';
|
||||||
import { FeatureReleasePlanCard } from '../FeatureReleasePlanCard/FeatureReleasePlanCard.tsx';
|
import { FeatureReleasePlanCard } from '../FeatureReleasePlanCard/FeatureReleasePlanCard.tsx';
|
||||||
import type { IReleasePlanTemplate } from 'interfaces/releasePlans';
|
import type { IReleasePlanTemplate } from 'interfaces/releasePlans';
|
||||||
@ -16,6 +15,8 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
||||||
import FactCheckOutlinedIcon from '@mui/icons-material/FactCheckOutlined';
|
import FactCheckOutlinedIcon from '@mui/icons-material/FactCheckOutlined';
|
||||||
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig.ts';
|
||||||
|
import { useUiFlag } from 'hooks/useUiFlag.ts';
|
||||||
|
|
||||||
interface IFeatureStrategyMenuCardsProps {
|
interface IFeatureStrategyMenuCardsProps {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
@ -27,17 +28,6 @@ interface IFeatureStrategyMenuCardsProps {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StyledTypography = styled(Typography)(({ theme }) => ({
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
padding: theme.spacing(1, 4),
|
|
||||||
width: '100%',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledLink = styled(Link)(({ theme }) => ({
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
cursor: 'pointer',
|
|
||||||
})) as typeof Link;
|
|
||||||
|
|
||||||
const GridContainer = styled(Box)(() => ({
|
const GridContainer = styled(Box)(() => ({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -48,15 +38,17 @@ const ScrollableContent = styled(Box)(({ theme }) => ({
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
maxHeight: '70vh',
|
maxHeight: '70vh',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
padding: theme.spacing(1, 0, 1, 0),
|
padding: theme.spacing(4),
|
||||||
|
paddingTop: 0,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: theme.spacing(3),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const GridSection = styled(Box)(({ theme }) => ({
|
const GridSection = styled(Box)(({ theme }) => ({
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridTemplateColumns: 'repeat(2, 1fr)',
|
gridTemplateColumns: 'repeat(2, 1fr)',
|
||||||
gap: theme.spacing(1.5),
|
gap: theme.spacing(1.5),
|
||||||
padding: theme.spacing(0, 4),
|
|
||||||
marginBottom: theme.spacing(3),
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -82,7 +74,7 @@ const SectionTitle = styled(Box)(({ theme }) => ({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: theme.spacing(0.5),
|
gap: theme.spacing(0.5),
|
||||||
padding: theme.spacing(0, 4, 1, 4),
|
marginBottom: theme.spacing(1),
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -110,7 +102,6 @@ const EmptyStateContainer = styled(Box)(({ theme }) => ({
|
|||||||
backgroundColor: theme.palette.neutral.light,
|
backgroundColor: theme.palette.neutral.light,
|
||||||
borderRadius: theme.shape.borderRadiusMedium,
|
borderRadius: theme.shape.borderRadiusMedium,
|
||||||
padding: theme.spacing(3),
|
padding: theme.spacing(3),
|
||||||
margin: theme.spacing(0, 4),
|
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -144,10 +135,12 @@ export const FeatureStrategyMenuCards = ({
|
|||||||
onReviewReleasePlan,
|
onReviewReleasePlan,
|
||||||
onClose,
|
onClose,
|
||||||
}: IFeatureStrategyMenuCardsProps) => {
|
}: IFeatureStrategyMenuCardsProps) => {
|
||||||
|
const { isEnterprise } = useUiConfig();
|
||||||
|
const releasePlansEnabled = useUiFlag('releasePlans');
|
||||||
|
|
||||||
const { strategies } = useStrategies();
|
const { strategies } = useStrategies();
|
||||||
const { templates } = useReleasePlanTemplates();
|
const { templates } = useReleasePlanTemplates();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const allStrategies = !onlyReleasePlans;
|
|
||||||
|
|
||||||
const preDefinedStrategies = strategies.filter(
|
const preDefinedStrategies = strategies.filter(
|
||||||
(strategy) => !strategy.deprecated && !strategy.editable,
|
(strategy) => !strategy.deprecated && !strategy.editable,
|
||||||
@ -163,6 +156,65 @@ export const FeatureStrategyMenuCards = ({
|
|||||||
description:
|
description:
|
||||||
'This is the default strategy defined for this environment in the project',
|
'This is the default strategy defined for this environment in the project',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderReleasePlanTemplates = () => {
|
||||||
|
if (!isEnterprise() || !releasePlansEnabled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!templates.length) {
|
||||||
|
return (
|
||||||
|
<EmptyStateContainer>
|
||||||
|
<EmptyStateTitle>
|
||||||
|
<StyledIcon>
|
||||||
|
<FactCheckOutlinedIcon />
|
||||||
|
</StyledIcon>
|
||||||
|
Create your own release templates
|
||||||
|
</EmptyStateTitle>
|
||||||
|
<EmptyStateDescription>
|
||||||
|
Standardize your rollouts and save time by reusing
|
||||||
|
predefined strategies. Find release templates in the
|
||||||
|
side menu under{' '}
|
||||||
|
<ClickableBoldText
|
||||||
|
onClick={() => navigate('/release-templates')}
|
||||||
|
>
|
||||||
|
Configure > Release templates
|
||||||
|
</ClickableBoldText>
|
||||||
|
</EmptyStateDescription>
|
||||||
|
</EmptyStateContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<SectionTitle>
|
||||||
|
<Typography color='inherit' variant='body2'>
|
||||||
|
Apply a release template
|
||||||
|
</Typography>
|
||||||
|
<Tooltip
|
||||||
|
title='Use a predefined template to roll out features to users'
|
||||||
|
arrow
|
||||||
|
>
|
||||||
|
<StyledInfoIcon />
|
||||||
|
</Tooltip>
|
||||||
|
</SectionTitle>
|
||||||
|
<GridSection>
|
||||||
|
{templates.map((template) => (
|
||||||
|
<CardWrapper key={template.id}>
|
||||||
|
<FeatureReleasePlanCard
|
||||||
|
template={template}
|
||||||
|
onClick={() => onAddReleasePlan(template)}
|
||||||
|
onPreviewClick={() =>
|
||||||
|
onReviewReleasePlan(template)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</CardWrapper>
|
||||||
|
))}
|
||||||
|
</GridSection>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridContainer>
|
<GridContainer>
|
||||||
<TitleRow>
|
<TitleRow>
|
||||||
@ -179,167 +231,77 @@ export const FeatureStrategyMenuCards = ({
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</TitleRow>
|
</TitleRow>
|
||||||
<ScrollableContent>
|
<ScrollableContent>
|
||||||
{allStrategies ? (
|
{onlyReleasePlans ? (
|
||||||
|
renderReleasePlanTemplates()
|
||||||
|
) : (
|
||||||
<>
|
<>
|
||||||
<SectionTitle>
|
<Box>
|
||||||
<Typography color='inherit' variant='body2'>
|
|
||||||
Pre-defined strategy types
|
|
||||||
</Typography>
|
|
||||||
<Tooltip
|
|
||||||
title='Select a starting setup, and customize the strategy to your need with targeting and variants'
|
|
||||||
arrow
|
|
||||||
>
|
|
||||||
<StyledInfoIcon />
|
|
||||||
</Tooltip>
|
|
||||||
</SectionTitle>
|
|
||||||
<GridSection>
|
|
||||||
<CardWrapper key={defaultStrategy.name}>
|
|
||||||
<FeatureStrategyMenuCard
|
|
||||||
projectId={projectId}
|
|
||||||
featureId={featureId}
|
|
||||||
environmentId={environmentId}
|
|
||||||
strategy={defaultStrategy}
|
|
||||||
defaultStrategy={true}
|
|
||||||
onClose={onClose}
|
|
||||||
/>
|
|
||||||
</CardWrapper>
|
|
||||||
{preDefinedStrategies.map((strategy) => (
|
|
||||||
<CardWrapper key={strategy.name}>
|
|
||||||
<FeatureStrategyMenuCard
|
|
||||||
projectId={projectId}
|
|
||||||
featureId={featureId}
|
|
||||||
environmentId={environmentId}
|
|
||||||
strategy={strategy}
|
|
||||||
onClose={onClose}
|
|
||||||
/>
|
|
||||||
</CardWrapper>
|
|
||||||
))}
|
|
||||||
</GridSection>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={templates.length > 0}
|
|
||||||
show={
|
|
||||||
<>
|
|
||||||
<SectionTitle>
|
<SectionTitle>
|
||||||
<Typography color='inherit' variant='body2'>
|
<Typography color='inherit' variant='body2'>
|
||||||
Apply a release template
|
Pre-defined strategy types
|
||||||
</Typography>
|
</Typography>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title='Use one of the pre-defined templates defined in your company for rolling out features to users'
|
title='Select a starting setup, and customize the strategy to your need with targeting and variants'
|
||||||
arrow
|
arrow
|
||||||
>
|
>
|
||||||
<StyledInfoIcon />
|
<StyledInfoIcon />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</SectionTitle>
|
</SectionTitle>
|
||||||
<GridSection>
|
<GridSection>
|
||||||
{templates.map((template) => (
|
<CardWrapper key={defaultStrategy.name}>
|
||||||
<CardWrapper key={template.id}>
|
<FeatureStrategyMenuCard
|
||||||
<FeatureReleasePlanCard
|
projectId={projectId}
|
||||||
template={template}
|
featureId={featureId}
|
||||||
onClick={() =>
|
environmentId={environmentId}
|
||||||
onAddReleasePlan(template)
|
strategy={defaultStrategy}
|
||||||
}
|
defaultStrategy
|
||||||
onPreviewClick={() =>
|
onClose={onClose}
|
||||||
onReviewReleasePlan(template)
|
/>
|
||||||
}
|
</CardWrapper>
|
||||||
|
{preDefinedStrategies.map((strategy) => (
|
||||||
|
<CardWrapper key={strategy.name}>
|
||||||
|
<FeatureStrategyMenuCard
|
||||||
|
projectId={projectId}
|
||||||
|
featureId={featureId}
|
||||||
|
environmentId={environmentId}
|
||||||
|
strategy={strategy}
|
||||||
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
</CardWrapper>
|
</CardWrapper>
|
||||||
))}
|
))}
|
||||||
</GridSection>
|
</GridSection>
|
||||||
</>
|
</Box>
|
||||||
}
|
{renderReleasePlanTemplates()}
|
||||||
elseShow={
|
{customStrategies.length > 0 && (
|
||||||
<EmptyStateContainer>
|
<Box>
|
||||||
<EmptyStateTitle>
|
<SectionTitle>
|
||||||
<StyledIcon>
|
<Typography color='inherit' variant='body2'>
|
||||||
<FactCheckOutlinedIcon />
|
Custom strategies
|
||||||
</StyledIcon>
|
</Typography>
|
||||||
Create your own templates
|
<Tooltip
|
||||||
</EmptyStateTitle>
|
title='Custom strategies you have defined in Unleash'
|
||||||
<EmptyStateDescription>
|
arrow
|
||||||
Standardize how you do rollouts and make it more
|
|
||||||
efficient without having to set up the same
|
|
||||||
stategies from time to time. You find it in the
|
|
||||||
sidemenu under{' '}
|
|
||||||
<ClickableBoldText
|
|
||||||
onClick={() =>
|
|
||||||
navigate('/release-templates')
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Configure > Release templates
|
|
||||||
</ClickableBoldText>
|
|
||||||
</EmptyStateDescription>
|
|
||||||
</EmptyStateContainer>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={templates.length === 0 && onlyReleasePlans}
|
|
||||||
show={
|
|
||||||
<>
|
|
||||||
<StyledTypography
|
|
||||||
color='textSecondary'
|
|
||||||
sx={{
|
|
||||||
padding: (theme) =>
|
|
||||||
theme.spacing(1, 2, 0, 2),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p>No templates created.</p>
|
|
||||||
<p>
|
|
||||||
Go to
|
|
||||||
<StyledLink
|
|
||||||
onClick={() =>
|
|
||||||
navigate('/release-templates')
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
Release templates
|
<StyledInfoIcon />
|
||||||
</StyledLink>
|
</Tooltip>
|
||||||
to get started
|
</SectionTitle>
|
||||||
</p>
|
<GridSection>
|
||||||
</StyledTypography>
|
{customStrategies.map((strategy) => (
|
||||||
</>
|
<CardWrapper key={strategy.name}>
|
||||||
}
|
<FeatureStrategyMenuCard
|
||||||
/>
|
projectId={projectId}
|
||||||
{allStrategies ? (
|
featureId={featureId}
|
||||||
<>
|
environmentId={environmentId}
|
||||||
<ConditionallyRender
|
strategy={strategy}
|
||||||
condition={customStrategies.length > 0}
|
onClose={onClose}
|
||||||
show={
|
/>
|
||||||
<>
|
</CardWrapper>
|
||||||
<SectionTitle>
|
))}
|
||||||
<Typography
|
</GridSection>
|
||||||
color='inherit'
|
</Box>
|
||||||
variant='body2'
|
)}
|
||||||
>
|
|
||||||
Custom strategies
|
|
||||||
</Typography>
|
|
||||||
<Tooltip
|
|
||||||
title='Custom strategies you have defined in Unleash'
|
|
||||||
arrow
|
|
||||||
>
|
|
||||||
<StyledInfoIcon />
|
|
||||||
</Tooltip>
|
|
||||||
</SectionTitle>
|
|
||||||
<GridSection>
|
|
||||||
{customStrategies.map((strategy) => (
|
|
||||||
<CardWrapper key={strategy.name}>
|
|
||||||
<FeatureStrategyMenuCard
|
|
||||||
projectId={projectId}
|
|
||||||
featureId={featureId}
|
|
||||||
environmentId={
|
|
||||||
environmentId
|
|
||||||
}
|
|
||||||
strategy={strategy}
|
|
||||||
onClose={onClose}
|
|
||||||
/>
|
|
||||||
</CardWrapper>
|
|
||||||
))}
|
|
||||||
</GridSection>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
) : null}
|
)}
|
||||||
</ScrollableContent>
|
</ScrollableContent>
|
||||||
</GridContainer>
|
</GridContainer>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user