From d998f4c67a828dd8f4cabe429fb86b999c24375f Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Wed, 9 Nov 2022 09:29:33 +0100 Subject: [PATCH] feat: suggest strategy from template (#2340) --- .../AddStrategyMessage.tsx | 21 +++ .../CopyStrategiesMessage.tsx | 21 +-- .../CopyStrategyMessage.tsx | 9 +- .../AddFromTemplateCard.tsx | 130 ++++++++++++++++++ .../FeatureStrategyEmpty.tsx | 82 +++++------ .../PresetCard/PresetCard.tsx | 64 --------- .../src/hooks/useChangeRequestAddStrategy.ts | 13 +- frontend/src/interfaces/strategy.ts | 1 + 8 files changed, 207 insertions(+), 134 deletions(-) create mode 100644 frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/AddStrategyMessage.tsx create mode 100644 frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/AddFromTemplateCard/AddFromTemplateCard.tsx delete mode 100644 frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/PresetCard/PresetCard.tsx diff --git a/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/AddStrategyMessage.tsx b/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/AddStrategyMessage.tsx new file mode 100644 index 0000000000..93aaafaa79 --- /dev/null +++ b/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/AddStrategyMessage.tsx @@ -0,0 +1,21 @@ +import { VFC } from 'react'; +import { Typography } from '@mui/material'; +import { formatStrategyName } from 'utils/strategyNames'; +import { IFeatureStrategyPayload } from 'interfaces/strategy'; + +interface IAddStrategyMessageProps { + payload?: IFeatureStrategyPayload; + environment?: string; +} + +export const AddStrategyMessage: VFC = ({ + payload, + environment, +}) => ( + <> + Add + + {formatStrategyName(payload?.name || '')} strategy + to {environment} + +); diff --git a/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/CopyStrategiesMessage.tsx b/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/CopyStrategiesMessage.tsx index f99b9fddb7..0df9859178 100644 --- a/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/CopyStrategiesMessage.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/CopyStrategiesMessage.tsx @@ -1,7 +1,13 @@ +import { VFC } from 'react'; import { styled, Typography } from '@mui/material'; -import { formatStrategyName } from '../../../../utils/strategyNames'; -import { IFeatureStrategy } from '../../../../interfaces/strategy'; -import { CopyStrategyMsg } from './CopyStrategyMessage'; +import { formatStrategyName } from 'utils/strategyNames'; +import { IFeatureStrategy, IFeatureStrategyPayload } from 'interfaces/strategy'; + +interface ICopyStrategiesMessageProps { + payload?: IFeatureStrategyPayload[]; + fromEnvironment?: string; + environment?: string; +} const MsgContainer = styled('div')(({ theme }) => ({ '&>*:nth-child(n)': { @@ -9,20 +15,19 @@ const MsgContainer = styled('div')(({ theme }) => ({ }, })); -export const CopyStrategiesMessage = ({ +export const CopyStrategiesMessage: VFC = ({ payload, fromEnvironment, environment, -}: CopyStrategyMsg) => ( +}) => ( Copy: - {(payload as IFeatureStrategy[])?.map(strategy => ( + {payload?.map(strategy => ( - {formatStrategyName((strategy as IFeatureStrategy)?.name)}{' '} - strategy{' '} + {formatStrategyName(strategy?.name || '')} strategy{' '} {' '} ))} diff --git a/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/CopyStrategyMessage.tsx b/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/CopyStrategyMessage.tsx index d7bfb3ea7e..c86759b5ab 100644 --- a/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/CopyStrategyMessage.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/CopyStrategyMessage.tsx @@ -1,9 +1,9 @@ import { Typography } from '@mui/material'; -import { formatStrategyName } from '../../../../utils/strategyNames'; -import { IFeatureStrategy } from '../../../../interfaces/strategy'; +import { formatStrategyName } from 'utils/strategyNames'; +import { IFeatureStrategyPayload } from 'interfaces/strategy'; export interface CopyStrategyMsg { - payload: IFeatureStrategy | IFeatureStrategy[]; + payload?: IFeatureStrategyPayload; fromEnvironment?: string; environment?: string; } @@ -15,8 +15,7 @@ export const CopyStrategyMessage = ({ }: CopyStrategyMsg) => ( - Copy {formatStrategyName((payload as IFeatureStrategy)?.name)}{' '} - strategy{' '} + Copy {formatStrategyName(payload?.name || '')} strategy{' '} {' '} from {fromEnvironment} to {environment} diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/AddFromTemplateCard/AddFromTemplateCard.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/AddFromTemplateCard/AddFromTemplateCard.tsx new file mode 100644 index 0000000000..ecbd489a2c --- /dev/null +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/AddFromTemplateCard/AddFromTemplateCard.tsx @@ -0,0 +1,130 @@ +import { ElementType, FC } from 'react'; +import { Card, CardContent, Typography, styled, Box } from '@mui/material'; +import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog'; +import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi'; +import useToast from 'hooks/useToast'; +import { AddStrategyMessage } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/AddStrategyMessage'; +import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; +import { useChangeRequestAddStrategy } from 'hooks/useChangeRequestAddStrategy'; +import { formatUnknownError } from 'utils/formatUnknownError'; +import PermissionButton from 'component/common/PermissionButton/PermissionButton'; +import { CREATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions'; +import { IFeatureStrategyPayload } from 'interfaces/strategy'; + +interface IAddFromTemplateCardProps { + title: string; + featureId: string; + projectId: string; + environmentId: string; + strategy: IFeatureStrategyPayload; + Icon: ElementType; + onAfterAddStrategy: () => void; +} + +const StyledCard = styled(Card)(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + borderRadius: theme.shape.borderRadiusMedium, +})); + +export const AddFromTemplateCard: FC = ({ + title, + children, + featureId, + projectId, + environmentId, + strategy, + Icon, + onAfterAddStrategy, +}) => { + const { addStrategyToFeature } = useFeatureStrategyApi(); + const { setToastApiError } = useToast(); + + const isChangeRequestEnabled = useChangeRequestsEnabled(environmentId); + + const { + changeRequestDialogDetails, + onChangeRequestAddStrategy, + onChangeRequestAddStrategyConfirm, + onChangeRequestAddStrategyClose, + } = useChangeRequestAddStrategy(projectId, featureId, 'addStrategy'); + + const onStrategyAdd = async () => { + try { + if (isChangeRequestEnabled) { + onChangeRequestAddStrategy(environmentId, strategy); + } else { + await addStrategyToFeature( + projectId, + featureId, + environmentId, + strategy + ); + onAfterAddStrategy(); + } + } catch (error) { + setToastApiError(formatUnknownError(error)); + } + }; + + return ( + <> + + + + {title} + + + {children} + + + + + Use template + + + + + + } + /> + + ); +}; diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty.tsx index 81993c2a0b..72ca972566 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty.tsx @@ -4,18 +4,18 @@ import { SectionSeparator } from 'component/feature/FeatureView/FeatureOverview/ import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi'; import useToast from 'hooks/useToast'; import { useFeature } from 'hooks/api/getters/useFeature/useFeature'; -import { FeatureStrategyMenu } from '../FeatureStrategyMenu/FeatureStrategyMenu'; -import { PresetCard } from './PresetCard/PresetCard'; import { useStyles } from './FeatureStrategyEmpty.styles'; import { formatUnknownError } from 'utils/formatUnknownError'; import { useFeatureImmutable } from 'hooks/api/getters/useFeature/useFeatureImmutable'; -import { getFeatureStrategyIcon } from 'utils/strategyNames'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { CopyButton } from './CopyButton/CopyButton'; -import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig'; -import { useChangeRequestAddStrategy } from '../../../../hooks/useChangeRequestAddStrategy'; -import { ChangeRequestDialogue } from '../../../changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog'; -import { CopyStrategiesMessage } from '../../../changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/CopyStrategiesMessage'; +import { useChangeRequestAddStrategy } from 'hooks/useChangeRequestAddStrategy'; +import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog'; +import { CopyStrategiesMessage } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/CopyStrategiesMessage'; +import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; +import { getFeatureStrategyIcon } from 'utils/strategyNames'; +import { AddFromTemplateCard } from './AddFromTemplateCard/AddFromTemplateCard'; +import { FeatureStrategyMenu } from '../FeatureStrategyMenu/FeatureStrategyMenu'; interface IFeatureStrategyEmptyProps { projectId: string; @@ -43,9 +43,7 @@ export const FeatureStrategyEmpty = ({ environment.strategies && environment.strategies.length > 0 ); - - const { uiConfig } = useUiConfig(); - const changeRequestsEnabled = uiConfig?.flags?.changeRequests; + const isChangeRequestEnabled = useChangeRequestsEnabled(environmentId); const { changeRequestDialogDetails, @@ -73,7 +71,7 @@ export const FeatureStrategyEmpty = ({ environment => environment.name === fromEnvironmentName )?.strategies || []; - if (changeRequestsEnabled) { + if (isChangeRequestEnabled) { await onChangeRequestAddStrategies( environmentId, strategies, @@ -105,36 +103,6 @@ export const FeatureStrategyEmpty = ({ } }; - const onAddSimpleStrategy = async () => { - try { - await addStrategyToFeature(projectId, featureId, environmentId, { - name: 'default', - parameters: {}, - constraints: [], - }); - onAfterAddStrategy(); - } catch (error) { - setToastApiError(formatUnknownError(error)); - } - }; - - const onAddGradualRolloutStrategy = async () => { - try { - await addStrategyToFeature(projectId, featureId, environmentId, { - name: 'flexibleRollout', - parameters: { - rollout: '50', - stickiness: 'default', - groupId: feature.name, - }, - constraints: [], - }); - onAfterAddStrategy(); - } catch (error) { - setToastApiError(formatUnknownError(error)); - } - }; - const canCopyFromOtherEnvironment = otherAvailableEnvironments && otherAvailableEnvironments.length > 0; @@ -208,25 +176,41 @@ export const FeatureStrategyEmpty = ({ gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' }, }} > - The standard strategy is strictly on/off for your entire userbase. - - + Roll out to a percentage of your userbase. - + diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/PresetCard/PresetCard.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/PresetCard/PresetCard.tsx deleted file mode 100644 index 75ffa587a3..0000000000 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/PresetCard/PresetCard.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { ElementType, FC } from 'react'; -import { Card, CardContent, Typography, styled, Box } from '@mui/material'; -import PermissionButton from 'component/common/PermissionButton/PermissionButton'; -import { CREATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions'; - -interface IPresetCardProps { - title: string; - projectId: string; - environmentId: string; - onClick: () => void; - Icon: ElementType; -} - -const StyledCard = styled(Card)(({ theme }) => ({ - display: 'flex', - flexDirection: 'column', - borderRadius: theme.shape.borderRadiusMedium, -})); - -export const PresetCard: FC = ({ - title, - children, - Icon, - projectId, - environmentId, - onClick, -}) => ( - - - - {title} - - - {children} - - - - - Use template - - - - -); diff --git a/frontend/src/hooks/useChangeRequestAddStrategy.ts b/frontend/src/hooks/useChangeRequestAddStrategy.ts index 09eac6f486..ad92c3175c 100644 --- a/frontend/src/hooks/useChangeRequestAddStrategy.ts +++ b/frontend/src/hooks/useChangeRequestAddStrategy.ts @@ -1,10 +1,7 @@ import { useCallback, useState } from 'react'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; -import { - IFeatureStrategy, - IFeatureStrategyPayload, -} from '../interfaces/strategy'; +import { IFeatureStrategyPayload } from '../interfaces/strategy'; import { useChangeRequestApi } from './api/actions/useChangeRequestApi/useChangeRequestApi'; import { useChangeRequestOpen } from './api/getters/useChangeRequestOpen/useChangeRequestOpen'; @@ -24,8 +21,8 @@ export const useChangeRequestAddStrategy = ( const [changeRequestDialogDetails, setChangeRequestDialogDetails] = useState<{ - strategy?: IFeatureStrategy; - strategies?: IFeatureStrategy[]; + strategy?: IFeatureStrategyPayload; + strategies?: IFeatureStrategyPayload[]; featureName?: string; environment?: string; fromEnvironment?: string; @@ -35,7 +32,7 @@ export const useChangeRequestAddStrategy = ( const onChangeRequestAddStrategy = useCallback( ( environment: string, - strategy: IFeatureStrategy, + strategy: IFeatureStrategyPayload, fromEnvironment?: string ) => { setChangeRequestDialogDetails({ @@ -52,7 +49,7 @@ export const useChangeRequestAddStrategy = ( const onChangeRequestAddStrategies = useCallback( ( environment: string, - strategies: IFeatureStrategy[], + strategies: IFeatureStrategyPayload[], fromEnvironment: string ) => { setChangeRequestDialogDetails({ diff --git a/frontend/src/interfaces/strategy.ts b/frontend/src/interfaces/strategy.ts index 23c820b9fa..cd36b82393 100644 --- a/frontend/src/interfaces/strategy.ts +++ b/frontend/src/interfaces/strategy.ts @@ -16,6 +16,7 @@ export interface IFeatureStrategyParameters { } export interface IFeatureStrategyPayload { + id?: string; name?: string; constraints: IConstraint[]; parameters: IFeatureStrategyParameters;