mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-23 00:22:19 +01:00
chore: list release templates in strategy popover (#8703)
https://linear.app/unleash/issue/2-2817/expand-the-strategy-dropdown-with-release-plan-templates Does what it says on the tin, lists release plan templates in the strategy popover. I think we should improve this popover soon, at least behind the `flagOverviewRedesign` flag, but not in this PR. 
This commit is contained in:
parent
da805f2036
commit
044e61454b
@ -140,12 +140,9 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
target: `a[href="${basePath}/projects/${PROJECT}/features/demoApp.step2/strategies/create?environmentId=${ENVIRONMENT}&strategyName=default&defaultStrategy=false"]`,
|
target: `a[href="${basePath}/projects/${PROJECT}/features/demoApp.step2/strategies/create?environmentId=${ENVIRONMENT}&strategyName=flexibleRollout&defaultStrategy=true"]`,
|
||||||
content: (
|
content: (
|
||||||
<Description>
|
<Description>Select the default strategy.</Description>
|
||||||
Select the <Badge as='span'>Standard</Badge> strategy
|
|
||||||
type.
|
|
||||||
</Description>
|
|
||||||
),
|
),
|
||||||
placement: 'right',
|
placement: 'right',
|
||||||
optional: true,
|
optional: true,
|
||||||
@ -480,6 +477,15 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
</Description>
|
</Description>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
target: `a[href="${basePath}/projects/${PROJECT}/features/demoApp.step4/strategies/create?environmentId=${ENVIRONMENT}&strategyName=flexibleRollout&defaultStrategy=true"]`,
|
||||||
|
content: (
|
||||||
|
<Description>Select the default strategy.</Description>
|
||||||
|
),
|
||||||
|
placement: 'right',
|
||||||
|
optional: true,
|
||||||
|
backCloseModal: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
target: 'button[data-testid="STRATEGY_TARGETING_TAB"]',
|
target: 'button[data-testid="STRATEGY_TARGETING_TAB"]',
|
||||||
content: (
|
content: (
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
import { getFeatureStrategyIcon } from 'utils/strategyNames';
|
||||||
|
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
|
||||||
|
import { Link, styled } from '@mui/material';
|
||||||
|
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||||
|
import type { IReleasePlanTemplate } from 'interfaces/releasePlans';
|
||||||
|
|
||||||
|
const StyledIcon = styled('div')(({ theme }) => ({
|
||||||
|
width: theme.spacing(4),
|
||||||
|
height: 'auto',
|
||||||
|
'& > svg': {
|
||||||
|
fill: theme.palette.primary.main,
|
||||||
|
},
|
||||||
|
'& > div': {
|
||||||
|
height: theme.spacing(2),
|
||||||
|
marginLeft: '-.75rem',
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledDescription = styled('div')(({ theme }) => ({
|
||||||
|
fontSize: theme.fontSizes.smallBody,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledName = styled(StringTruncator)(({ theme }) => ({
|
||||||
|
fontWeight: theme.fontWeight.bold,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledCard = styled(Link)(({ theme }) => ({
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: '3rem 1fr',
|
||||||
|
width: '20rem',
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
color: 'inherit',
|
||||||
|
textDecoration: 'inherit',
|
||||||
|
lineHeight: 1.25,
|
||||||
|
borderWidth: '1px',
|
||||||
|
borderStyle: 'solid',
|
||||||
|
borderColor: theme.palette.divider,
|
||||||
|
borderRadius: theme.spacing(1),
|
||||||
|
'&:hover, &:focus': {
|
||||||
|
borderColor: theme.palette.primary.main,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface IFeatureReleasePlanCardProps {
|
||||||
|
projectId: string;
|
||||||
|
featureId: string;
|
||||||
|
environmentId: string;
|
||||||
|
releasePlanTemplate: IReleasePlanTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FeatureReleasePlanCard = ({
|
||||||
|
projectId,
|
||||||
|
featureId,
|
||||||
|
environmentId,
|
||||||
|
releasePlanTemplate,
|
||||||
|
}: IFeatureReleasePlanCardProps) => {
|
||||||
|
const Icon = getFeatureStrategyIcon('releasePlanTemplate');
|
||||||
|
const { trackEvent } = usePlausibleTracker();
|
||||||
|
|
||||||
|
const addReleasePlan = () => {
|
||||||
|
trackEvent('release-plans', {
|
||||||
|
props: {
|
||||||
|
eventType: 'add',
|
||||||
|
name: releasePlanTemplate.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log('TODO: call and implement addReleasePlan');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledCard onClick={addReleasePlan}>
|
||||||
|
<StyledIcon>
|
||||||
|
<Icon />
|
||||||
|
</StyledIcon>
|
||||||
|
<div>
|
||||||
|
<StyledName
|
||||||
|
text={releasePlanTemplate.name}
|
||||||
|
maxWidth='200'
|
||||||
|
maxLength={25}
|
||||||
|
/>
|
||||||
|
<StyledDescription>
|
||||||
|
{releasePlanTemplate.description}
|
||||||
|
</StyledDescription>
|
||||||
|
</div>
|
||||||
|
</StyledCard>
|
||||||
|
);
|
||||||
|
};
|
@ -10,6 +10,7 @@ import { FeatureStrategyMenuCards } from './FeatureStrategyMenuCards/FeatureStra
|
|||||||
import { formatCreateStrategyPath } from '../FeatureStrategyCreate/FeatureStrategyCreate';
|
import { formatCreateStrategyPath } from '../FeatureStrategyCreate/FeatureStrategyCreate';
|
||||||
import MoreVert from '@mui/icons-material/MoreVert';
|
import MoreVert from '@mui/icons-material/MoreVert';
|
||||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||||
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
|
|
||||||
interface IFeatureStrategyMenuProps {
|
interface IFeatureStrategyMenuProps {
|
||||||
label: string;
|
label: string;
|
||||||
@ -51,6 +52,7 @@ export const FeatureStrategyMenu = ({
|
|||||||
const { trackEvent } = usePlausibleTracker();
|
const { trackEvent } = usePlausibleTracker();
|
||||||
const isPopoverOpen = Boolean(anchor);
|
const isPopoverOpen = Boolean(anchor);
|
||||||
const popoverId = isPopoverOpen ? 'FeatureStrategyMenuPopover' : undefined;
|
const popoverId = isPopoverOpen ? 'FeatureStrategyMenuPopover' : undefined;
|
||||||
|
const flagOverviewRedesignEnabled = useUiFlag('flagOverviewRedesign');
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
setAnchor(undefined);
|
setAnchor(undefined);
|
||||||
@ -77,6 +79,48 @@ export const FeatureStrategyMenu = ({
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (flagOverviewRedesignEnabled) {
|
||||||
|
return (
|
||||||
|
<StyledStrategyMenu onClick={(event) => event.stopPropagation()}>
|
||||||
|
<PermissionButton
|
||||||
|
data-testid='ADD_STRATEGY_BUTTON'
|
||||||
|
permission={CREATE_FEATURE_STRATEGY}
|
||||||
|
projectId={projectId}
|
||||||
|
environmentId={environmentId}
|
||||||
|
onClick={openMoreStrategies}
|
||||||
|
aria-labelledby={popoverId}
|
||||||
|
variant={variant}
|
||||||
|
size={size}
|
||||||
|
sx={{ minWidth: matchWidth ? '282px' : 'auto' }}
|
||||||
|
disabled={Boolean(disableReason)}
|
||||||
|
tooltipProps={{
|
||||||
|
title: disableReason ? disableReason : undefined,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</PermissionButton>
|
||||||
|
<Popover
|
||||||
|
id={popoverId}
|
||||||
|
open={isPopoverOpen}
|
||||||
|
anchorEl={anchor}
|
||||||
|
onClose={onClose}
|
||||||
|
onClick={onClose}
|
||||||
|
PaperProps={{
|
||||||
|
sx: (theme) => ({
|
||||||
|
paddingBottom: theme.spacing(1),
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FeatureStrategyMenuCards
|
||||||
|
projectId={projectId}
|
||||||
|
featureId={featureId}
|
||||||
|
environmentId={environmentId}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
</StyledStrategyMenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledStrategyMenu onClick={(event) => event.stopPropagation()}>
|
<StyledStrategyMenu onClick={(event) => event.stopPropagation()}>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
|
@ -2,6 +2,8 @@ import { List, ListItem, styled, Typography } from '@mui/material';
|
|||||||
import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
|
import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
|
||||||
import { FeatureStrategyMenuCard } from '../FeatureStrategyMenuCard/FeatureStrategyMenuCard';
|
import { FeatureStrategyMenuCard } from '../FeatureStrategyMenuCard/FeatureStrategyMenuCard';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
import { useReleasePlanTemplates } from 'hooks/api/getters/useReleasePlanTemplates/useReleasePlanTemplates';
|
||||||
|
import { FeatureReleasePlanCard } from '../FeatureReleasePlanCard/FeatureReleasePlanCard';
|
||||||
|
|
||||||
interface IFeatureStrategyMenuCardsProps {
|
interface IFeatureStrategyMenuCardsProps {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
@ -20,6 +22,7 @@ export const FeatureStrategyMenuCards = ({
|
|||||||
environmentId,
|
environmentId,
|
||||||
}: IFeatureStrategyMenuCardsProps) => {
|
}: IFeatureStrategyMenuCardsProps) => {
|
||||||
const { strategies } = useStrategies();
|
const { strategies } = useStrategies();
|
||||||
|
const { templates } = useReleasePlanTemplates();
|
||||||
|
|
||||||
const preDefinedStrategies = strategies.filter(
|
const preDefinedStrategies = strategies.filter(
|
||||||
(strategy) => !strategy.deprecated && !strategy.editable,
|
(strategy) => !strategy.deprecated && !strategy.editable,
|
||||||
@ -39,7 +42,7 @@ export const FeatureStrategyMenuCards = ({
|
|||||||
<List dense>
|
<List dense>
|
||||||
<>
|
<>
|
||||||
<StyledTypography color='textSecondary'>
|
<StyledTypography color='textSecondary'>
|
||||||
{environmentId} environment default strategy
|
Default strategy for {environmentId} environment
|
||||||
</StyledTypography>
|
</StyledTypography>
|
||||||
<ListItem key={defaultStrategy.name}>
|
<ListItem key={defaultStrategy.name}>
|
||||||
<FeatureStrategyMenuCard
|
<FeatureStrategyMenuCard
|
||||||
@ -51,6 +54,26 @@ export const FeatureStrategyMenuCards = ({
|
|||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</>
|
</>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={templates.length > 0}
|
||||||
|
show={
|
||||||
|
<>
|
||||||
|
<StyledTypography color='textSecondary'>
|
||||||
|
Release templates
|
||||||
|
</StyledTypography>
|
||||||
|
{templates.map((template) => (
|
||||||
|
<ListItem key={template.id}>
|
||||||
|
<FeatureReleasePlanCard
|
||||||
|
projectId={projectId}
|
||||||
|
featureId={featureId}
|
||||||
|
environmentId={environmentId}
|
||||||
|
releasePlanTemplate={template}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<StyledTypography color='textSecondary'>
|
<StyledTypography color='textSecondary'>
|
||||||
Predefined strategy types
|
Predefined strategy types
|
||||||
</StyledTypography>
|
</StyledTypography>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { useContext, useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import useUiConfig from '../useUiConfig/useUiConfig';
|
import useUiConfig from '../useUiConfig/useUiConfig';
|
||||||
import AccessContext from 'contexts/AccessContext';
|
|
||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
|
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
|
||||||
@ -12,12 +11,11 @@ const ENDPOINT = 'api/admin/release-plan-templates';
|
|||||||
const DEFAULT_DATA: IReleasePlanTemplate[] = [];
|
const DEFAULT_DATA: IReleasePlanTemplate[] = [];
|
||||||
|
|
||||||
export const useReleasePlanTemplates = () => {
|
export const useReleasePlanTemplates = () => {
|
||||||
const { isAdmin } = useContext(AccessContext);
|
|
||||||
const { isEnterprise } = useUiConfig();
|
const { isEnterprise } = useUiConfig();
|
||||||
const signalsEnabled = useUiFlag('releasePlans');
|
const releasePlansEnabled = useUiFlag('releasePlans');
|
||||||
|
|
||||||
const { data, error, mutate } = useConditionalSWR<IReleasePlanTemplate[]>(
|
const { data, error, mutate } = useConditionalSWR<IReleasePlanTemplate[]>(
|
||||||
isEnterprise() && isAdmin && signalsEnabled,
|
isEnterprise() && releasePlansEnabled,
|
||||||
DEFAULT_DATA,
|
DEFAULT_DATA,
|
||||||
formatApiPath(ENDPOINT),
|
formatApiPath(ENDPOINT),
|
||||||
fetcher,
|
fetcher,
|
||||||
|
@ -73,7 +73,8 @@ export type CustomEvents =
|
|||||||
| 'order-environments'
|
| 'order-environments'
|
||||||
| 'unleash-ai-chat'
|
| 'unleash-ai-chat'
|
||||||
| 'project-navigation'
|
| 'project-navigation'
|
||||||
| 'productivity-report';
|
| 'productivity-report'
|
||||||
|
| 'release-plans';
|
||||||
|
|
||||||
export const usePlausibleTracker = () => {
|
export const usePlausibleTracker = () => {
|
||||||
const plausible = useContext(PlausibleContext);
|
const plausible = useContext(PlausibleContext);
|
||||||
|
@ -6,6 +6,7 @@ import LanguageIcon from '@mui/icons-material/Language';
|
|||||||
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
||||||
import CodeIcon from '@mui/icons-material/Code';
|
import CodeIcon from '@mui/icons-material/Code';
|
||||||
import { ReactComponent as RolloutIcon } from 'assets/icons/rollout.svg';
|
import { ReactComponent as RolloutIcon } from 'assets/icons/rollout.svg';
|
||||||
|
import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered';
|
||||||
|
|
||||||
export const formatStrategyName = (strategyName: string): string => {
|
export const formatStrategyName = (strategyName: string): string => {
|
||||||
return formattedStrategyNames[strategyName] ?? strategyName;
|
return formattedStrategyNames[strategyName] ?? strategyName;
|
||||||
@ -31,6 +32,8 @@ export const getFeatureStrategyIcon = (strategyName: string) => {
|
|||||||
return PeopleIcon;
|
return PeopleIcon;
|
||||||
case 'applicationHostname':
|
case 'applicationHostname':
|
||||||
return LocationOnIcon;
|
return LocationOnIcon;
|
||||||
|
case 'releasePlanTemplate':
|
||||||
|
return FormatListNumberedIcon;
|
||||||
default:
|
default:
|
||||||
return CodeIcon;
|
return CodeIcon;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user