diff --git a/frontend/src/component/demo/demo-topics.tsx b/frontend/src/component/demo/demo-topics.tsx index 3fed931dfa..095edcf167 100644 --- a/frontend/src/component/demo/demo-topics.tsx +++ b/frontend/src/component/demo/demo-topics.tsx @@ -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: ( - - Select the Standard strategy - type. - + Select the default strategy. ), placement: 'right', optional: true, @@ -480,6 +477,15 @@ export const TOPICS: ITutorialTopic[] = [ ), }, + { + target: `a[href="${basePath}/projects/${PROJECT}/features/demoApp.step4/strategies/create?environmentId=${ENVIRONMENT}&strategyName=flexibleRollout&defaultStrategy=true"]`, + content: ( + Select the default strategy. + ), + placement: 'right', + optional: true, + backCloseModal: true, + }, { target: 'button[data-testid="STRATEGY_TARGETING_TAB"]', content: ( diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureReleasePlanCard/FeatureReleasePlanCard.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureReleasePlanCard/FeatureReleasePlanCard.tsx new file mode 100644 index 0000000000..ab2e169a64 --- /dev/null +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureReleasePlanCard/FeatureReleasePlanCard.tsx @@ -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 ( + + + + +
+ + + {releasePlanTemplate.description} + +
+
+ ); +}; diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx index 4d17eb5682..e456fa09a1 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx @@ -10,6 +10,7 @@ import { FeatureStrategyMenuCards } from './FeatureStrategyMenuCards/FeatureStra import { formatCreateStrategyPath } from '../FeatureStrategyCreate/FeatureStrategyCreate'; import MoreVert from '@mui/icons-material/MoreVert'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; +import { useUiFlag } from 'hooks/useUiFlag'; interface IFeatureStrategyMenuProps { label: string; @@ -51,6 +52,7 @@ export const FeatureStrategyMenu = ({ const { trackEvent } = usePlausibleTracker(); const isPopoverOpen = Boolean(anchor); const popoverId = isPopoverOpen ? 'FeatureStrategyMenuPopover' : undefined; + const flagOverviewRedesignEnabled = useUiFlag('flagOverviewRedesign'); const onClose = () => { setAnchor(undefined); @@ -77,6 +79,48 @@ export const FeatureStrategyMenu = ({ true, ); + if (flagOverviewRedesignEnabled) { + return ( + event.stopPropagation()}> + + {label} + + ({ + paddingBottom: theme.spacing(1), + }), + }} + > + + + + ); + } + return ( event.stopPropagation()}> { const { strategies } = useStrategies(); + const { templates } = useReleasePlanTemplates(); const preDefinedStrategies = strategies.filter( (strategy) => !strategy.deprecated && !strategy.editable, @@ -39,7 +42,7 @@ export const FeatureStrategyMenuCards = ({ <> - {environmentId} environment default strategy + Default strategy for {environmentId} environment + 0} + show={ + <> + + Release templates + + {templates.map((template) => ( + + + + ))} + + } + /> Predefined strategy types diff --git a/frontend/src/hooks/api/getters/useReleasePlanTemplates/useReleasePlanTemplates.ts b/frontend/src/hooks/api/getters/useReleasePlanTemplates/useReleasePlanTemplates.ts index 3e08a42783..485639b372 100644 --- a/frontend/src/hooks/api/getters/useReleasePlanTemplates/useReleasePlanTemplates.ts +++ b/frontend/src/hooks/api/getters/useReleasePlanTemplates/useReleasePlanTemplates.ts @@ -1,6 +1,5 @@ -import { useContext, useMemo } from 'react'; +import { useMemo } from 'react'; import useUiConfig from '../useUiConfig/useUiConfig'; -import AccessContext from 'contexts/AccessContext'; import { formatApiPath } from 'utils/formatPath'; import handleErrorResponses from '../httpErrorResponseHandler'; import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR'; @@ -12,12 +11,11 @@ const ENDPOINT = 'api/admin/release-plan-templates'; const DEFAULT_DATA: IReleasePlanTemplate[] = []; export const useReleasePlanTemplates = () => { - const { isAdmin } = useContext(AccessContext); const { isEnterprise } = useUiConfig(); - const signalsEnabled = useUiFlag('releasePlans'); + const releasePlansEnabled = useUiFlag('releasePlans'); const { data, error, mutate } = useConditionalSWR( - isEnterprise() && isAdmin && signalsEnabled, + isEnterprise() && releasePlansEnabled, DEFAULT_DATA, formatApiPath(ENDPOINT), fetcher, diff --git a/frontend/src/hooks/usePlausibleTracker.ts b/frontend/src/hooks/usePlausibleTracker.ts index 9f3ec64be6..95859a1142 100644 --- a/frontend/src/hooks/usePlausibleTracker.ts +++ b/frontend/src/hooks/usePlausibleTracker.ts @@ -73,7 +73,8 @@ export type CustomEvents = | 'order-environments' | 'unleash-ai-chat' | 'project-navigation' - | 'productivity-report'; + | 'productivity-report' + | 'release-plans'; export const usePlausibleTracker = () => { const plausible = useContext(PlausibleContext); diff --git a/frontend/src/utils/strategyNames.tsx b/frontend/src/utils/strategyNames.tsx index 9e96217634..2ae68f7f1a 100644 --- a/frontend/src/utils/strategyNames.tsx +++ b/frontend/src/utils/strategyNames.tsx @@ -6,6 +6,7 @@ import LanguageIcon from '@mui/icons-material/Language'; import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew'; import CodeIcon from '@mui/icons-material/Code'; import { ReactComponent as RolloutIcon } from 'assets/icons/rollout.svg'; +import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered'; export const formatStrategyName = (strategyName: string): string => { return formattedStrategyNames[strategyName] ?? strategyName; @@ -31,6 +32,8 @@ export const getFeatureStrategyIcon = (strategyName: string) => { return PeopleIcon; case 'applicationHostname': return LocationOnIcon; + case 'releasePlanTemplate': + return FormatListNumberedIcon; default: return CodeIcon; }