mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-04 13:48:56 +02:00
chore: add feature configuration based on addConfiguration flag (#10420)
https://linear.app/unleash/issue/2-3729/single-add-configuration-button-based-on-flag Shows a single "Add configuration" button based on whether the new `addConfiguration` flag is enabled. This button then shows our "Add configuration" modal which allows you to choose how to proceed in terms of your feature flag configuration. Also updates this modal to better match the latest sketches. Includes scouting. ### Single "Add configuration" button <img width="738" height="121" alt="image" src="https://github.com/user-attachments/assets/9cce7fba-5e0c-42e0-a3d1-8ccc34f730bb" /> ### Modal <img width="983" height="663" alt="image" src="https://github.com/user-attachments/assets/b59abad2-f1cd-4b62-bf2e-9c3b24cbb60e" />
This commit is contained in:
parent
0a9d6437c5
commit
15449e83d3
@ -27,7 +27,10 @@ const CardContent = styled('div')(({ theme }) => ({
|
||||
|
||||
const HoverButtonsContainer = styled('div')(({ theme }) => ({
|
||||
position: 'absolute',
|
||||
right: theme.spacing(2),
|
||||
background: theme.palette.background.paper,
|
||||
padding: theme.spacing(1),
|
||||
top: theme.spacing(1),
|
||||
right: theme.spacing(1),
|
||||
display: 'flex',
|
||||
gap: theme.spacing(1),
|
||||
opacity: 0,
|
||||
|
@ -75,9 +75,10 @@ export const FeatureStrategyMenu = ({
|
||||
usePendingChangeRequests(projectId);
|
||||
const { refetch } = useReleasePlans(projectId, featureId, environmentId);
|
||||
const { addReleasePlanToFeature } = useReleasePlansApi();
|
||||
const { isOss } = useUiConfig();
|
||||
const { isEnterprise } = useUiConfig();
|
||||
const addConfigurationEnabled = useUiFlag('addConfiguration');
|
||||
const releasePlansEnabled = useUiFlag('releasePlans');
|
||||
const displayReleasePlanButton = !isOss() && releasePlansEnabled;
|
||||
const displayReleasePlanButton = isEnterprise() && releasePlansEnabled;
|
||||
const crProtected =
|
||||
releasePlansEnabled && isChangeRequestConfigured(environmentId);
|
||||
|
||||
@ -161,56 +162,81 @@ export const FeatureStrategyMenu = ({
|
||||
|
||||
return (
|
||||
<StyledStrategyMenu onClick={(event) => event.stopPropagation()}>
|
||||
{displayReleasePlanButton ? (
|
||||
{addConfigurationEnabled ? (
|
||||
<PermissionButton
|
||||
data-testid='ADD_TEMPLATE_BUTTON'
|
||||
data-testid='ADD_STRATEGY_BUTTON'
|
||||
permission={CREATE_FEATURE_STRATEGY}
|
||||
projectId={projectId}
|
||||
environmentId={environmentId}
|
||||
onClick={openReleasePlans}
|
||||
onClick={openMoreStrategies}
|
||||
aria-labelledby={dialogId}
|
||||
variant='outlined'
|
||||
variant={variant}
|
||||
sx={{ minWidth: matchWidth ? '282px' : 'auto' }}
|
||||
disabled={Boolean(disableReason)}
|
||||
tooltipProps={{
|
||||
title: disableReason ? disableReason : undefined,
|
||||
}}
|
||||
>
|
||||
Use template
|
||||
Add configuration
|
||||
</PermissionButton>
|
||||
) : null}
|
||||
) : (
|
||||
<>
|
||||
{displayReleasePlanButton ? (
|
||||
<PermissionButton
|
||||
data-testid='ADD_TEMPLATE_BUTTON'
|
||||
permission={CREATE_FEATURE_STRATEGY}
|
||||
projectId={projectId}
|
||||
environmentId={environmentId}
|
||||
onClick={openReleasePlans}
|
||||
aria-labelledby={dialogId}
|
||||
variant='outlined'
|
||||
sx={{ minWidth: matchWidth ? '282px' : 'auto' }}
|
||||
disabled={Boolean(disableReason)}
|
||||
tooltipProps={{
|
||||
title: disableReason
|
||||
? disableReason
|
||||
: undefined,
|
||||
}}
|
||||
>
|
||||
Use template
|
||||
</PermissionButton>
|
||||
) : null}
|
||||
|
||||
<PermissionButton
|
||||
data-testid='ADD_STRATEGY_BUTTON'
|
||||
permission={CREATE_FEATURE_STRATEGY}
|
||||
projectId={projectId}
|
||||
environmentId={environmentId}
|
||||
onClick={openDefaultStrategyCreationModal}
|
||||
aria-labelledby={dialogId}
|
||||
variant={variant}
|
||||
sx={{ minWidth: matchWidth ? '282px' : 'auto' }}
|
||||
disabled={Boolean(disableReason)}
|
||||
tooltipProps={{
|
||||
title: disableReason ? disableReason : undefined,
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</PermissionButton>
|
||||
<PermissionButton
|
||||
data-testid='ADD_STRATEGY_BUTTON'
|
||||
permission={CREATE_FEATURE_STRATEGY}
|
||||
projectId={projectId}
|
||||
environmentId={environmentId}
|
||||
onClick={openDefaultStrategyCreationModal}
|
||||
aria-labelledby={dialogId}
|
||||
variant={variant}
|
||||
sx={{ minWidth: matchWidth ? '282px' : 'auto' }}
|
||||
disabled={Boolean(disableReason)}
|
||||
tooltipProps={{
|
||||
title: disableReason ? disableReason : undefined,
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</PermissionButton>
|
||||
|
||||
<StyledAdditionalMenuButton
|
||||
permission={CREATE_FEATURE_STRATEGY}
|
||||
projectId={projectId}
|
||||
environmentId={environmentId}
|
||||
onClick={openMoreStrategies}
|
||||
variant='outlined'
|
||||
hideLockIcon
|
||||
disabled={Boolean(disableReason)}
|
||||
tooltipProps={{
|
||||
title: disableReason ? disableReason : 'More strategies',
|
||||
}}
|
||||
>
|
||||
<MoreVert />
|
||||
</StyledAdditionalMenuButton>
|
||||
<StyledAdditionalMenuButton
|
||||
permission={CREATE_FEATURE_STRATEGY}
|
||||
projectId={projectId}
|
||||
environmentId={environmentId}
|
||||
onClick={openMoreStrategies}
|
||||
variant='outlined'
|
||||
hideLockIcon
|
||||
disabled={Boolean(disableReason)}
|
||||
tooltipProps={{
|
||||
title: disableReason
|
||||
? disableReason
|
||||
: 'More strategies',
|
||||
}}
|
||||
>
|
||||
<MoreVert />
|
||||
</StyledAdditionalMenuButton>
|
||||
</>
|
||||
)}
|
||||
<Dialog
|
||||
open={isStrategyMenuDialogOpen}
|
||||
onClose={onClose}
|
||||
|
@ -1,11 +1,4 @@
|
||||
import {
|
||||
Link,
|
||||
styled,
|
||||
Typography,
|
||||
Box,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
} from '@mui/material';
|
||||
import { Link, styled, Typography, Box, IconButton } 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';
|
||||
@ -13,10 +6,10 @@ import { FeatureReleasePlanCard } from '../FeatureReleasePlanCard/FeatureRelease
|
||||
import type { IReleasePlanTemplate } from 'interfaces/releasePlans';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
||||
import FactCheckOutlinedIcon from '@mui/icons-material/FactCheckOutlined';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig.ts';
|
||||
import { useUiFlag } from 'hooks/useUiFlag.ts';
|
||||
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon.tsx';
|
||||
|
||||
interface IFeatureStrategyMenuCardsProps {
|
||||
projectId: string;
|
||||
@ -78,11 +71,6 @@ const SectionTitle = styled(Box)(({ theme }) => ({
|
||||
width: '100%',
|
||||
}));
|
||||
|
||||
const StyledInfoIcon = styled(InfoOutlinedIcon)(({ theme }) => ({
|
||||
fontSize: theme.typography.body2.fontSize,
|
||||
color: theme.palette.text.secondary,
|
||||
}));
|
||||
|
||||
const StyledIcon = styled('span')(({ theme }) => ({
|
||||
width: theme.spacing(3),
|
||||
'& > svg': {
|
||||
@ -162,55 +150,51 @@ export const FeatureStrategyMenuCards = ({
|
||||
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
|
||||
Release templates
|
||||
</Typography>
|
||||
<Tooltip
|
||||
title='Use a predefined template to roll out features to users'
|
||||
arrow
|
||||
>
|
||||
<StyledInfoIcon />
|
||||
</Tooltip>
|
||||
<HelpIcon
|
||||
tooltip='Use a predefined template to roll out features to users'
|
||||
size='16px'
|
||||
/>
|
||||
</SectionTitle>
|
||||
<GridSection>
|
||||
{templates.map((template) => (
|
||||
<CardWrapper key={template.id}>
|
||||
<FeatureReleasePlanCard
|
||||
template={template}
|
||||
onClick={() => onAddReleasePlan(template)}
|
||||
onPreviewClick={() =>
|
||||
onReviewReleasePlan(template)
|
||||
}
|
||||
/>
|
||||
</CardWrapper>
|
||||
))}
|
||||
</GridSection>
|
||||
{!templates.length ? (
|
||||
<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>
|
||||
) : (
|
||||
<GridSection>
|
||||
{templates.map((template) => (
|
||||
<CardWrapper key={template.id}>
|
||||
<FeatureReleasePlanCard
|
||||
template={template}
|
||||
onClick={() => onAddReleasePlan(template)}
|
||||
onPreviewClick={() =>
|
||||
onReviewReleasePlan(template)
|
||||
}
|
||||
/>
|
||||
</CardWrapper>
|
||||
))}
|
||||
</GridSection>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@ -219,7 +203,7 @@ export const FeatureStrategyMenuCards = ({
|
||||
<GridContainer>
|
||||
<TitleRow>
|
||||
<TitleText variant='h2'>
|
||||
{onlyReleasePlans ? 'Select template' : 'Select strategy'}
|
||||
{onlyReleasePlans ? 'Select template' : 'Add configuration'}
|
||||
</TitleText>
|
||||
<IconButton
|
||||
size='small'
|
||||
@ -238,14 +222,12 @@ export const FeatureStrategyMenuCards = ({
|
||||
<Box>
|
||||
<SectionTitle>
|
||||
<Typography color='inherit' variant='body2'>
|
||||
Pre-defined strategy types
|
||||
Standard strategies
|
||||
</Typography>
|
||||
<Tooltip
|
||||
title='Select a starting setup, and customize the strategy to your need with targeting and variants'
|
||||
arrow
|
||||
>
|
||||
<StyledInfoIcon />
|
||||
</Tooltip>
|
||||
<HelpIcon
|
||||
tooltip='Select a starting setup, then customize your strategy with targeting and variants'
|
||||
size='16px'
|
||||
/>
|
||||
</SectionTitle>
|
||||
<GridSection>
|
||||
<CardWrapper key={defaultStrategy.name}>
|
||||
@ -278,12 +260,10 @@ export const FeatureStrategyMenuCards = ({
|
||||
<Typography color='inherit' variant='body2'>
|
||||
Custom strategies
|
||||
</Typography>
|
||||
<Tooltip
|
||||
title='Custom strategies you have defined in Unleash'
|
||||
arrow
|
||||
>
|
||||
<StyledInfoIcon />
|
||||
</Tooltip>
|
||||
<HelpIcon
|
||||
tooltip='Custom strategies you have defined in Unleash'
|
||||
size='16px'
|
||||
/>
|
||||
</SectionTitle>
|
||||
<GridSection>
|
||||
{customStrategies.map((strategy) => (
|
||||
|
@ -2,6 +2,8 @@ import type { FC } from 'react';
|
||||
import { styled, Link } from '@mui/material';
|
||||
import type { Link as RouterLink } from 'react-router-dom';
|
||||
import { RELEASE_TEMPLATE_FEEDBACK } from 'constants/links';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
|
||||
const StyledLink = styled(Link<typeof RouterLink | 'a'>)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
@ -13,14 +15,23 @@ const StyledLink = styled(Link<typeof RouterLink | 'a'>)(({ theme }) => ({
|
||||
marginRight: 'auto',
|
||||
}));
|
||||
|
||||
export const ReleaseTemplatesFeedback: FC = () => (
|
||||
<StyledLink
|
||||
component='a'
|
||||
href={RELEASE_TEMPLATE_FEEDBACK}
|
||||
underline='hover'
|
||||
rel='noopener noreferrer'
|
||||
target='_blank'
|
||||
>
|
||||
Give feedback to release templates
|
||||
</StyledLink>
|
||||
);
|
||||
export const ReleaseTemplatesFeedback: FC = () => {
|
||||
const { isEnterprise } = useUiConfig();
|
||||
const releaseTemplatesEnabled = useUiFlag('releasePlans');
|
||||
|
||||
if (!isEnterprise() || !releaseTemplatesEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledLink
|
||||
component='a'
|
||||
href={RELEASE_TEMPLATE_FEEDBACK}
|
||||
underline='hover'
|
||||
rel='noopener noreferrer'
|
||||
target='_blank'
|
||||
>
|
||||
Give feedback to release templates
|
||||
</StyledLink>
|
||||
);
|
||||
};
|
||||
|
@ -95,6 +95,7 @@ export type UiFlags = {
|
||||
timestampsInChangeRequestTimeline?: boolean;
|
||||
reportUnknownFlags?: boolean;
|
||||
lifecycleGraphs?: boolean;
|
||||
addConfiguration?: boolean;
|
||||
};
|
||||
|
||||
export interface IVersionInfo {
|
||||
|
@ -65,7 +65,8 @@ export type IFlagKey =
|
||||
| 'paygInstanceStatsEvents'
|
||||
| 'timestampsInChangeRequestTimeline'
|
||||
| 'lifecycleGraphs'
|
||||
| 'githubAuth';
|
||||
| 'githubAuth'
|
||||
| 'addConfiguration';
|
||||
|
||||
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
||||
|
||||
@ -305,6 +306,10 @@ const flags: IFlags = {
|
||||
process.env.UNLEASH_EXPERIMENTAL_GITHUB_AUTH,
|
||||
false,
|
||||
),
|
||||
addConfiguration: parseEnvVarBoolean(
|
||||
process.env.UNLEASH_EXPERIMENTAL_ADD_CONFIGURATION,
|
||||
false,
|
||||
),
|
||||
};
|
||||
|
||||
export const defaultExperimentalOptions: IExperimentalOptions = {
|
||||
|
@ -61,6 +61,7 @@ process.nextTick(async () => {
|
||||
paygTrialEvents: true,
|
||||
timestampsInChangeRequestTimeline: true,
|
||||
lifecycleGraphs: true,
|
||||
addConfiguration: true,
|
||||
},
|
||||
},
|
||||
authentication: {
|
||||
|
Loading…
Reference in New Issue
Block a user