1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-12 01:17:04 +02:00

Frontend - Suggest change copy strategy (#2312)

* Suggest change copy strategy

* Fix merge conflicts

* Copy strategies from other environment added to draft

* Copy strategies from other environment added to draft

* Copy strategies from other environment added to draft

* Copy strategies from other environment added to draft

* fmt

* PR comments

* PR comments

* PR comments

* PR comments

* Fix: Conditionally hide Change Requests tab
This commit is contained in:
andreas-unleash 2022-11-04 11:33:07 +02:00 committed by GitHub
parent a267f13a7d
commit c1e0bd83b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 500 additions and 133 deletions

View File

@ -7,7 +7,20 @@ import { ToggleStatusChange } from '../ChangeRequestOverview/ChangeRequestFeatur
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
import { formatUnknownError } from 'utils/formatUnknownError';
import useToast from 'hooks/useToast';
import type { IChangeRequest } from '../changeRequest.types';
import type {
IChangeRequest,
IChangeRequestAddStrategy,
} from '../changeRequest.types';
import {
StrategyAddedChange,
StrategyDeletedChange,
StrategyEditedChange,
} from '../ChangeRequestOverview/ChangeRequestFeatureToggleChange/StrategyChange';
import {
formatStrategyName,
GetFeatureStrategyIcon,
} from '../../../utils/strategyNames';
import { IChangeRequestEnabled } from '../changeRequest.types';
interface IChangeRequestProps {
changeRequest: IChangeRequest;
@ -54,21 +67,29 @@ export const ChangeRequest: VFC<IChangeRequestProps> = ({
condition={change.action === 'updateEnabled'}
show={
<ToggleStatusChange
// @ts-expect-error TODO: fix types
enabled={change?.payload?.enabled}
onDiscard={onDiscard(change.id)}
enabled={
(change as IChangeRequestEnabled)
?.payload?.enabled
}
onDiscard={onDiscard(change.id!)}
/>
}
/>
{/* <ConditionallyRender
<ConditionallyRender
condition={change.action === 'addStrategy'}
show={
<StrategyAddedChange>
<GetFeatureStrategyIcon
strategyName={change.payload.name}
strategyName={
(
change as IChangeRequestAddStrategy
)?.payload.name!
}
/>
{formatStrategyName(
change.payload.name
(
change as IChangeRequestAddStrategy
)?.payload.name!
)}
</StrategyAddedChange>
}
@ -80,7 +101,7 @@ export const ChangeRequest: VFC<IChangeRequestProps> = ({
<ConditionallyRender
condition={change.action === 'updateStrategy'}
show={<StrategyEditedChange />}
/> */}
/>
</Box>
))}
</ChangeRequestFeatureToggleChange>

View File

@ -6,38 +6,39 @@ interface IChangeRequestDialogueProps {
isOpen: boolean;
onConfirm: () => void;
onClose: () => void;
featureName?: string;
environment?: string;
enabled?: boolean;
showBanner?: boolean;
messageComponent: JSX.Element;
}
export const ChangeRequestDialogue: FC<IChangeRequestDialogueProps> = ({
isOpen,
onConfirm,
onClose,
enabled,
featureName,
showBanner,
environment,
messageComponent,
}) => (
<Dialogue
open={isOpen}
primaryButtonText="Add to draft"
primaryButtonText="Add suggestion to draft"
secondaryButtonText="Cancel"
onClick={onConfirm}
onClose={onClose}
title="Request changes"
fullWidth
>
<Alert severity="info" sx={{ mb: 2 }}>
Change requests is enabled for {environment}. Your changes needs to
be approved before they will be live. All the changes you do now
will be added into a draft that you can submit for review.
</Alert>
{showBanner && (
<Alert severity="info" sx={{ mb: 2 }}>
Change requests feature is enabled for {environment}. Your
changes needs to be approved before they will be live. All the
changes you do now will be added into a draft that you can
submit for review.
</Alert>
)}
<Typography variant="body2" color="text.secondary">
Change requests:
</Typography>
<Typography>
<strong>{enabled ? 'Disable' : 'Enable'}</strong> feature toggle{' '}
<strong>{featureName}</strong> in <strong>{environment}</strong>
Your suggestion:
</Typography>
{messageComponent}
</Dialogue>
);

View File

@ -0,0 +1,33 @@
import { styled, Typography } from '@mui/material';
import { formatStrategyName } from '../../../../utils/strategyNames';
import { IFeatureStrategy } from '../../../../interfaces/strategy';
import { CopyStrategyMsg } from './CopyStrategyMessage';
const MsgContainer = styled('div')(({ theme }) => ({
'&>*:nth-child(n)': {
margin: theme.spacing(1, 0),
},
}));
export const CopyStrategiesMessage = ({
payload,
fromEnvironment,
environment,
}: CopyStrategyMsg) => (
<MsgContainer>
<Typography>
<strong>Copy: </strong>
</Typography>
{(payload as IFeatureStrategy[])?.map(strategy => (
<Typography>
<strong>
{formatStrategyName((strategy as IFeatureStrategy)?.name)}{' '}
strategy{' '}
</strong>{' '}
</Typography>
))}
<Typography>
from {fromEnvironment} to {environment}
</Typography>
</MsgContainer>
);

View File

@ -0,0 +1,23 @@
import { Typography } from '@mui/material';
import { formatStrategyName } from '../../../../utils/strategyNames';
import { IFeatureStrategy } from '../../../../interfaces/strategy';
export interface CopyStrategyMsg {
payload: IFeatureStrategy | IFeatureStrategy[];
fromEnvironment?: string;
environment?: string;
}
export const CopyStrategyMessage = ({
payload,
fromEnvironment,
environment,
}: CopyStrategyMsg) => (
<Typography>
<strong>
Copy {formatStrategyName((payload as IFeatureStrategy)?.name)}{' '}
strategy{' '}
</strong>{' '}
from {fromEnvironment} to {environment}
</Typography>
);

View File

@ -0,0 +1,18 @@
import { Typography } from '@mui/material';
interface UpdateEnabledMsg {
enabled: boolean;
featureName: string;
environment: string;
}
export const UpdateEnabledMessage = ({
enabled,
featureName,
environment,
}: UpdateEnabledMsg) => (
<Typography>
<strong>{enabled ? 'Disable' : 'Enable'}</strong> feature toggle{' '}
<strong>{featureName}</strong> in <strong>{environment}</strong>
</Typography>
);

View File

@ -1,3 +1,31 @@
import { IFeatureStrategy } from '../../interfaces/strategy';
import { IUser } from '../../interfaces/user';
export interface IChangeRequest {
id: number;
state: ChangeRequestState;
project: string;
environment: string;
createdBy: Pick<IUser, 'id' | 'username' | 'imageUrl'>;
createdAt: Date;
features: IChangeRequestFeature[];
}
export interface IChangeRequestFeature {
name: string;
conflict?: string;
changes: IChangeRequestEvent[];
}
export interface IChangeRequestBase {
id?: number;
action: ChangeRequestAction;
payload: ChangeRequestPayload;
conflict?: string;
createdBy?: Pick<IUser, 'id' | 'username' | 'imageUrl'>;
createdAt?: Date;
}
export type ChangeRequestState =
| 'Draft'
| 'Approved'
@ -5,32 +33,53 @@ export type ChangeRequestState =
| 'Applied'
| 'Cancelled';
export interface IChangeRequest {
id: number;
environment: string;
state: ChangeRequestState;
project: string;
createdBy: ICreatedBy;
createdAt: string;
features: IChangeRequestFeatures[];
type ChangeRequestPayload =
| ChangeRequestEnabled
| ChangeRequestAddStrategy
| ChangeRequestEditStrategy
| ChangeRequestDeleteStrategy;
export interface IChangeRequestAddStrategy extends IChangeRequestBase {
action: 'addStrategy';
payload: ChangeRequestAddStrategy;
}
interface ICreatedBy {
id: number;
username: string;
imageUrl: string;
export interface IChangeRequestDeleteStrategy extends IChangeRequestBase {
action: 'deleteStrategy';
payload: ChangeRequestDeleteStrategy;
}
interface IChangeRequestFeatures {
name: string;
changes: IChangeRequestFeatureChanges[];
export interface IChangeRequestUpdateStrategy extends IChangeRequestBase {
action: 'updateStrategy';
payload: ChangeRequestEditStrategy;
}
interface IChangeRequestFeatureChanges {
id: number;
action: string;
payload: unknown;
createdAt: string;
createdBy: ICreatedBy;
warning?: string;
export interface IChangeRequestEnabled extends IChangeRequestBase {
action: 'updateEnabled';
payload: ChangeRequestEnabled;
}
export type IChangeRequestEvent =
| IChangeRequestAddStrategy
| IChangeRequestDeleteStrategy
| IChangeRequestUpdateStrategy
| IChangeRequestEnabled;
type ChangeRequestEnabled = { enabled: boolean };
type ChangeRequestAddStrategy = Pick<
IFeatureStrategy,
'parameters' | 'constraints'
> & { name: string };
type ChangeRequestEditStrategy = ChangeRequestAddStrategy & { id: string };
type ChangeRequestDeleteStrategy = {
id: string;
};
export type ChangeRequestAction =
| 'updateEnabled'
| 'addStrategy'
| 'updateStrategy'
| 'deleteStrategy';

View File

@ -12,8 +12,10 @@ import { useFeatureImmutable } from 'hooks/api/getters/useFeature/useFeatureImmu
import { getFeatureStrategyIcon } from 'utils/strategyNames';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { CopyButton } from './CopyButton/CopyButton';
import { useSegments } from '../../../../hooks/api/getters/useSegments/useSegments';
import { IFeatureStrategyPayload } from '../../../../interfaces/strategy';
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';
interface IFeatureStrategyEmptyProps {
projectId: string;
@ -42,6 +44,16 @@ export const FeatureStrategyEmpty = ({
environment.strategies.length > 0
);
const { uiConfig } = useUiConfig();
const changeRequestsEnabled = uiConfig?.flags?.changeRequests;
const {
changeRequestDialogDetails,
onChangeRequestAddStrategies,
onChangeRequestAddStrategiesConfirm,
onChangeRequestAddStrategyClose,
} = useChangeRequestAddStrategy(projectId, featureId, 'addStrategy');
const onAfterAddStrategy = (multiple = false) => {
refetchFeature();
refetchFeatureImmutable();
@ -61,6 +73,15 @@ export const FeatureStrategyEmpty = ({
environment => environment.name === fromEnvironmentName
)?.strategies || [];
if (changeRequestsEnabled) {
await onChangeRequestAddStrategies(
environmentId,
strategies,
fromEnvironmentName
);
return;
}
try {
await Promise.all(
strategies.map(strategy => {
@ -118,77 +139,96 @@ export const FeatureStrategyEmpty = ({
otherAvailableEnvironments && otherAvailableEnvironments.length > 0;
return (
<div className={styles.container}>
<div className={styles.title}>
You have not defined any strategies yet.
<>
<ChangeRequestDialogue
isOpen={changeRequestDialogDetails.isOpen}
onClose={onChangeRequestAddStrategyClose}
environment={changeRequestDialogDetails?.environment}
onConfirm={onChangeRequestAddStrategiesConfirm}
messageComponent={
<CopyStrategiesMessage
fromEnvironment={
changeRequestDialogDetails.fromEnvironment!
}
payload={changeRequestDialogDetails.strategies!}
/>
}
/>
<div className={styles.container}>
<div className={styles.title}>
You have not defined any strategies yet.
</div>
<p className={styles.description}>
Strategies added in this environment will only be executed
if the SDK is using an{' '}
<Link to="/admin/api">API key configured</Link> for this
environment.
</p>
<Box
sx={{
w: '100%',
display: 'flex',
flexWrap: 'wrap',
gap: 2,
alignItems: 'center',
justifyContent: 'center',
}}
>
<FeatureStrategyMenu
label="Add your first strategy"
projectId={projectId}
featureId={featureId}
environmentId={environmentId}
matchWidth={canCopyFromOtherEnvironment}
/>
<ConditionallyRender
condition={canCopyFromOtherEnvironment}
show={
<CopyButton
environmentId={environmentId}
environments={otherAvailableEnvironments.map(
environment => environment.name
)}
onClick={onCopyStrategies}
/>
}
/>
</Box>
<Box sx={{ width: '100%', mt: 3 }}>
<SectionSeparator>
Or use a strategy template
</SectionSeparator>
</Box>
<Box
sx={{
display: 'grid',
width: '100%',
gap: 2,
gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' },
}}
>
<PresetCard
title="Standard strategy"
Icon={getFeatureStrategyIcon('default')}
onClick={onAddSimpleStrategy}
projectId={projectId}
environmentId={environmentId}
>
The standard strategy is strictly on/off for your entire
userbase.
</PresetCard>
<PresetCard
title="Gradual rollout"
Icon={getFeatureStrategyIcon('flexibleRollout')}
onClick={onAddGradualRolloutStrategy}
projectId={projectId}
environmentId={environmentId}
>
Roll out to a percentage of your userbase.
</PresetCard>
</Box>
</div>
<p className={styles.description}>
Strategies added in this environment will only be executed if
the SDK is using an{' '}
<Link to="/admin/api">API key configured</Link> for this
environment.
</p>
<Box
sx={{
w: '100%',
display: 'flex',
flexWrap: 'wrap',
gap: 2,
alignItems: 'center',
justifyContent: 'center',
}}
>
<FeatureStrategyMenu
label="Add your first strategy"
projectId={projectId}
featureId={featureId}
environmentId={environmentId}
matchWidth={canCopyFromOtherEnvironment}
/>
<ConditionallyRender
condition={canCopyFromOtherEnvironment}
show={
<CopyButton
environmentId={environmentId}
environments={otherAvailableEnvironments.map(
environment => environment.name
)}
onClick={onCopyStrategies}
/>
}
/>
</Box>
<Box sx={{ width: '100%', mt: 3 }}>
<SectionSeparator>Or use a strategy template</SectionSeparator>
</Box>
<Box
sx={{
display: 'grid',
width: '100%',
gap: 2,
gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' },
}}
>
<PresetCard
title="Standard strategy"
Icon={getFeatureStrategyIcon('default')}
onClick={onAddSimpleStrategy}
projectId={projectId}
environmentId={environmentId}
>
The standard strategy is strictly on/off for your entire
userbase.
</PresetCard>
<PresetCard
title="Gradual rollout"
Icon={getFeatureStrategyIcon('flexibleRollout')}
onClick={onAddGradualRolloutStrategy}
projectId={projectId}
environmentId={environmentId}
>
Roll out to a percentage of your userbase.
</PresetCard>
</Box>
</div>
</>
);
};

View File

@ -10,9 +10,9 @@ import React from 'react';
import { formatUnknownError } from 'utils/formatUnknownError';
import { useStyles } from './FeatureOverviewEnvSwitch.styles';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { useChangeRequestToggle } from 'hooks/useChangeRequestToggle';
import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog';
import { UpdateEnabledMessage } from '../../../../../changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/UpdateEnabledMessage';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
interface IFeatureOverviewEnvSwitchProps {
@ -123,9 +123,15 @@ const FeatureOverviewEnvSwitch = ({
<ChangeRequestDialogue
isOpen={changeRequestDialogDetails.isOpen}
onClose={onChangeRequestToggleClose}
featureName={featureId}
environment={changeRequestDialogDetails?.environment}
onConfirm={onChangeRequestToggleConfirm}
messageComponent={
<UpdateEnabledMessage
enabled={changeRequestDialogDetails?.enabled!}
featureName={changeRequestDialogDetails?.featureName!}
environment={changeRequestDialogDetails.environment!}
/>
}
/>
</div>
);

View File

@ -8,7 +8,7 @@ import {
Tooltip,
} from '@mui/material';
import { AddToPhotos as CopyIcon, Lock } from '@mui/icons-material';
import { IFeatureStrategy, IFeatureStrategyPayload } from 'interfaces/strategy';
import { IFeatureStrategy } from 'interfaces/strategy';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { IFeatureEnvironment } from 'interfaces/featureToggle';
import AccessContext from 'contexts/AccessContext';
@ -19,20 +19,24 @@ import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFe
import useToast from 'hooks/useToast';
import { useFeatureImmutable } from 'hooks/api/getters/useFeature/useFeatureImmutable';
import { formatUnknownError } from 'utils/formatUnknownError';
import { useSegments } from '../../../../../../../../../../hooks/api/getters/useSegments/useSegments';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { useChangeRequestAddStrategy } from 'hooks/useChangeRequestAddStrategy';
import { ChangeRequestDialogue } from '../../../../../../../../../changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog';
import { CopyStrategyMessage } from '../../../../../../../../../changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/CopyStrategyMessage';
interface ICopyStrategyIconMenuProps {
environmentId: string;
environments: IFeatureEnvironment['name'][];
strategy: IFeatureStrategy;
}
export const CopyStrategyIconMenu: VFC<ICopyStrategyIconMenuProps> = ({
environmentId,
environments,
strategy,
}) => {
const projectId = useRequiredPathParam('projectId');
const featureId = useRequiredPathParam('featureId');
const { segments } = useSegments(strategy.id);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
@ -47,19 +51,40 @@ export const CopyStrategyIconMenu: VFC<ICopyStrategyIconMenuProps> = ({
setAnchorEl(null);
};
const { hasAccess } = useContext(AccessContext);
const onClick = async (environmentId: string) => {
const { uiConfig } = useUiConfig();
const changeRequestsEnabled = uiConfig?.flags?.changeRequests;
const {
changeRequestDialogDetails,
onChangeRequestAddStrategyClose,
onChangeRequestAddStrategy,
onChangeRequestAddStrategyConfirm,
} = useChangeRequestAddStrategy(projectId, featureId, 'addStrategy');
const onCopyStrategy = async (environment: string) => {
const { id, ...strategyCopy } = {
...strategy,
environment: environmentId,
environment,
copyOf: strategy.id,
};
if (changeRequestsEnabled) {
await onChangeRequestAddStrategy(
environment,
{
id,
...strategyCopy,
},
environmentId
);
return;
}
try {
await addStrategyToFeature(
projectId,
featureId,
environmentId,
strategyCopy
strategy
);
refetchFeature();
refetchFeatureImmutable();
@ -80,6 +105,20 @@ export const CopyStrategyIconMenu: VFC<ICopyStrategyIconMenuProps> = ({
return (
<div>
<ChangeRequestDialogue
isOpen={changeRequestDialogDetails.isOpen}
onClose={onChangeRequestAddStrategyClose}
environment={changeRequestDialogDetails?.environment}
onConfirm={onChangeRequestAddStrategyConfirm}
messageComponent={
<CopyStrategyMessage
fromEnvironment={
changeRequestDialogDetails.fromEnvironment!
}
payload={changeRequestDialogDetails.strategy!}
/>
}
/>
<Tooltip
title={`Copy to another environment${
enabled ? '' : ' (Access denied)'
@ -128,7 +167,7 @@ export const CopyStrategyIconMenu: VFC<ICopyStrategyIconMenuProps> = ({
>
<div>
<MenuItem
onClick={() => onClick(environment)}
onClick={() => onCopyStrategy(environment)}
disabled={!access}
>
<ConditionallyRender

View File

@ -54,6 +54,7 @@ export const StrategyItem: VFC<IStrategyItemProps> = ({
)}
show={() => (
<CopyStrategyIconMenu
environmentId={environmentId}
environments={otherEnvironments as string[]}
strategy={strategy}
/>

View File

@ -38,6 +38,8 @@ import { useMediaQuery } from '@mui/material';
import { Search } from 'component/common/Search/Search';
import { useChangeRequestToggle } from 'hooks/useChangeRequestToggle';
import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog';
import { CopyStrategyMessage } from '../../../changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/CopyStrategyMessage';
import { UpdateEnabledMessage } from '../../../changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/UpdateEnabledMessage';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
interface IProjectFeatureTogglesProps {
@ -524,9 +526,15 @@ export const ProjectFeatureToggles = ({
<ChangeRequestDialogue
isOpen={changeRequestDialogDetails.isOpen}
onClose={onChangeRequestToggleClose}
featureName={changeRequestDialogDetails?.featureName}
environment={changeRequestDialogDetails?.environment}
onConfirm={onChangeRequestToggleConfirm}
messageComponent={
<UpdateEnabledMessage
featureName={changeRequestDialogDetails.featureName!}
enabled={changeRequestDialogDetails.enabled!}
environment={changeRequestDialogDetails?.environment!}
/>
}
/>
</PageContent>
);

View File

@ -1,6 +1,6 @@
import useAPI from '../useApi/useApi';
interface IChangeRequestsSchema {
export interface IChangeRequestsSchema {
feature: string;
action:
| 'updateEnabled'

View File

@ -2,10 +2,7 @@ import useSWR from 'swr';
import { useMemo } from 'react';
import { formatApiPath } from 'utils/formatPath';
import handleErrorResponses from '../httpErrorResponseHandler';
import {
ChangeRequestState,
IChangeRequest,
} from 'component/changeRequest/changeRequest.types';
import { IChangeRequest } from 'component/changeRequest/changeRequest.types';
const fetcher = (path: string) => {
return fetch(path)

View File

@ -0,0 +1,131 @@
import { useCallback, useState } from 'react';
import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError';
import {
IFeatureStrategy,
IFeatureStrategyPayload,
} from '../interfaces/strategy';
import { useChangeRequestApi } from './api/actions/useChangeRequestApi/useChangeRequestApi';
import { useChangeRequestOpen } from './api/getters/useChangeRequestOpen/useChangeRequestOpen';
export type ChangeRequestStrategyAction =
| 'addStrategy'
| 'updateStrategy'
| 'deleteStrategy';
export const useChangeRequestAddStrategy = (
project: string,
featureName: string,
action: ChangeRequestStrategyAction
) => {
const { setToastData, setToastApiError } = useToast();
const { addChangeRequest } = useChangeRequestApi();
const { refetch } = useChangeRequestOpen(project);
const [changeRequestDialogDetails, setChangeRequestDialogDetails] =
useState<{
strategy?: IFeatureStrategy;
strategies?: IFeatureStrategy[];
featureName?: string;
environment?: string;
fromEnvironment?: string;
isOpen: boolean;
}>({ isOpen: false });
const onChangeRequestAddStrategy = useCallback(
(
environment: string,
strategy: IFeatureStrategy,
fromEnvironment?: string
) => {
setChangeRequestDialogDetails({
featureName,
environment,
fromEnvironment,
strategy,
isOpen: true,
});
},
[]
);
const onChangeRequestAddStrategies = useCallback(
(
environment: string,
strategies: IFeatureStrategy[],
fromEnvironment: string
) => {
setChangeRequestDialogDetails({
featureName,
environment,
fromEnvironment,
strategies,
isOpen: true,
});
},
[]
);
const onChangeRequestAddStrategyClose = useCallback(() => {
setChangeRequestDialogDetails({ isOpen: false });
}, []);
const onChangeRequestAddStrategyConfirm = useCallback(async () => {
try {
await addChangeRequest(
project,
changeRequestDialogDetails.environment!,
{
feature: changeRequestDialogDetails.featureName!,
action: action,
payload: changeRequestDialogDetails.strategy!,
}
);
refetch();
setChangeRequestDialogDetails({ isOpen: false });
setToastData({
type: 'success',
title: 'Changes added to the draft!',
});
} catch (error) {
setToastApiError(formatUnknownError(error));
setChangeRequestDialogDetails({ isOpen: false });
}
}, [addChangeRequest]);
const onChangeRequestAddStrategiesConfirm = useCallback(async () => {
try {
await Promise.all(
changeRequestDialogDetails.strategies!.map(strategy => {
return addChangeRequest(
project,
changeRequestDialogDetails.environment!,
{
feature: changeRequestDialogDetails.featureName!,
action: action,
payload: strategy,
}
);
})
);
refetch();
setChangeRequestDialogDetails({ isOpen: false });
setToastData({
type: 'success',
title: 'Changes added to the draft!',
});
} catch (error) {
setToastApiError(formatUnknownError(error));
setChangeRequestDialogDetails({ isOpen: false });
}
}, [addChangeRequest]);
return {
onChangeRequestAddStrategy,
onChangeRequestAddStrategies,
onChangeRequestAddStrategyClose,
onChangeRequestAddStrategyConfirm,
onChangeRequestAddStrategiesConfirm,
changeRequestDialogDetails,
};
};