mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: suggest remove strategy UI (#2321)
* feat: suggest remove strategy UI * add delete strategy to draft * refactor: extract hooks * refactor: consistent fn usage
This commit is contained in:
		
							parent
							
								
									4937b23e16
								
							
						
					
					
						commit
						147408045b
					
				| @ -1,11 +1,11 @@ | |||||||
| import React, { useState } from 'react'; | import React, { FC, useState } from 'react'; | ||||||
| import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi'; | import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi'; | ||||||
| import { formatUnknownError } from 'utils/formatUnknownError'; | import { formatUnknownError } from 'utils/formatUnknownError'; | ||||||
| import { useNavigate } from 'react-router-dom'; | import { useNavigate } from 'react-router-dom'; | ||||||
| import useToast from 'hooks/useToast'; | import useToast from 'hooks/useToast'; | ||||||
| import { formatFeaturePath } from '../FeatureStrategyEdit/FeatureStrategyEdit'; | import { formatFeaturePath } from '../FeatureStrategyEdit/FeatureStrategyEdit'; | ||||||
| import { Dialogue } from 'component/common/Dialogue/Dialogue'; | import { Dialogue } from 'component/common/Dialogue/Dialogue'; | ||||||
| import { Alert } from '@mui/material'; | import { Alert, styled, Typography } from '@mui/material'; | ||||||
| import PermissionButton from 'component/common/PermissionButton/PermissionButton'; | import PermissionButton from 'component/common/PermissionButton/PermissionButton'; | ||||||
| import { DELETE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions'; | import { DELETE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions'; | ||||||
| import { useFeature } from 'hooks/api/getters/useFeature/useFeature'; | import { useFeature } from 'hooks/api/getters/useFeature/useFeature'; | ||||||
| @ -13,6 +13,8 @@ import { STRATEGY_FORM_REMOVE_ID } from 'utils/testIds'; | |||||||
| import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | ||||||
| import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; | import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; | ||||||
| import { Delete } from '@mui/icons-material'; | import { Delete } from '@mui/icons-material'; | ||||||
|  | import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig'; | ||||||
|  | import { useChangeRequestApi } from '../../../../hooks/api/actions/useChangeRequestApi/useChangeRequestApi'; | ||||||
| 
 | 
 | ||||||
| interface IFeatureStrategyRemoveProps { | interface IFeatureStrategyRemoveProps { | ||||||
|     projectId: string; |     projectId: string; | ||||||
| @ -23,19 +25,81 @@ interface IFeatureStrategyRemoveProps { | |||||||
|     icon?: boolean; |     icon?: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const FeatureStrategyRemove = ({ | interface IFeatureStrategyRemoveDialogueProps { | ||||||
|  |     onRemove: (event: React.FormEvent) => Promise<void>; | ||||||
|  |     onClose: () => void; | ||||||
|  |     isOpen: boolean; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const RemoveAlert: FC = () => ( | ||||||
|  |     <Alert severity="error"> | ||||||
|  |         Removing the strategy will change which users receive access to the | ||||||
|  |         feature. | ||||||
|  |     </Alert> | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | const FeatureStrategyRemoveDialogue: FC< | ||||||
|  |     IFeatureStrategyRemoveDialogueProps | ||||||
|  | > = ({ onRemove, onClose, isOpen }) => { | ||||||
|  |     return ( | ||||||
|  |         <Dialogue | ||||||
|  |             title="Are you sure you want to delete this strategy?" | ||||||
|  |             open={isOpen} | ||||||
|  |             primaryButtonText="Remove strategy" | ||||||
|  |             secondaryButtonText="Cancel" | ||||||
|  |             onClick={onRemove} | ||||||
|  |             onClose={onClose} | ||||||
|  |         > | ||||||
|  |             <RemoveAlert /> | ||||||
|  |         </Dialogue> | ||||||
|  |     ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const MsgContainer = styled('div')(({ theme }) => ({ | ||||||
|  |     marginTop: theme.spacing(3), | ||||||
|  |     marginBottom: theme.spacing(1), | ||||||
|  | })); | ||||||
|  | 
 | ||||||
|  | const SuggestFeatureStrategyRemoveDialogue: FC< | ||||||
|  |     IFeatureStrategyRemoveDialogueProps | ||||||
|  | > = ({ onRemove, onClose, isOpen }) => { | ||||||
|  |     return ( | ||||||
|  |         <Dialogue | ||||||
|  |             title="Suggest changes" | ||||||
|  |             open={isOpen} | ||||||
|  |             primaryButtonText="Add suggestion to draft" | ||||||
|  |             secondaryButtonText="Cancel" | ||||||
|  |             onClick={onRemove} | ||||||
|  |             onClose={onClose} | ||||||
|  |         > | ||||||
|  |             <RemoveAlert /> | ||||||
|  |             <MsgContainer> | ||||||
|  |                 <Typography variant="body2" color="text.secondary"> | ||||||
|  |                     Your suggestion: | ||||||
|  |                 </Typography> | ||||||
|  |             </MsgContainer> | ||||||
|  |             <Typography fontWeight="bold">Remove strategy</Typography> | ||||||
|  |         </Dialogue> | ||||||
|  |     ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | interface IRemoveProps { | ||||||
|  |     projectId: string; | ||||||
|  |     featureId: string; | ||||||
|  |     environmentId: string; | ||||||
|  |     strategyId: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const useOnRemove = ({ | ||||||
|     projectId, |     projectId, | ||||||
|     featureId, |     featureId, | ||||||
|     environmentId, |     environmentId, | ||||||
|     strategyId, |     strategyId, | ||||||
|     disabled, | }: IRemoveProps) => { | ||||||
|     icon, |  | ||||||
| }: IFeatureStrategyRemoveProps) => { |  | ||||||
|     const [openDialogue, setOpenDialogue] = useState(false); |  | ||||||
|     const { deleteStrategyFromFeature } = useFeatureStrategyApi(); |     const { deleteStrategyFromFeature } = useFeatureStrategyApi(); | ||||||
|     const { refetchFeature } = useFeature(projectId, featureId); |  | ||||||
|     const { setToastData, setToastApiError } = useToast(); |     const { setToastData, setToastApiError } = useToast(); | ||||||
|     const navigate = useNavigate(); |     const navigate = useNavigate(); | ||||||
|  |     const { refetchFeature } = useFeature(projectId, featureId); | ||||||
| 
 | 
 | ||||||
|     const onRemove = async (event: React.FormEvent) => { |     const onRemove = async (event: React.FormEvent) => { | ||||||
|         try { |         try { | ||||||
| @ -56,6 +120,63 @@ export const FeatureStrategyRemove = ({ | |||||||
|             setToastApiError(formatUnknownError(error)); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |     return onRemove; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const useOnSuggestRemove = ({ | ||||||
|  |     projectId, | ||||||
|  |     featureId, | ||||||
|  |     environmentId, | ||||||
|  |     strategyId, | ||||||
|  | }: IRemoveProps) => { | ||||||
|  |     const { addChangeRequest } = useChangeRequestApi(); | ||||||
|  |     const { setToastData, setToastApiError } = useToast(); | ||||||
|  |     const onSuggestRemove = async (event: React.FormEvent) => { | ||||||
|  |         try { | ||||||
|  |             event.preventDefault(); | ||||||
|  |             await addChangeRequest(projectId, environmentId, { | ||||||
|  |                 action: 'deleteStrategy', | ||||||
|  |                 feature: featureId, | ||||||
|  |                 payload: { | ||||||
|  |                     id: strategyId, | ||||||
|  |                 }, | ||||||
|  |             }); | ||||||
|  |             setToastData({ | ||||||
|  |                 title: 'Changes added to the draft!', | ||||||
|  |                 type: 'success', | ||||||
|  |             }); | ||||||
|  |         } catch (error: unknown) { | ||||||
|  |             setToastApiError(formatUnknownError(error)); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     return onSuggestRemove; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const FeatureStrategyRemove = ({ | ||||||
|  |     projectId, | ||||||
|  |     featureId, | ||||||
|  |     environmentId, | ||||||
|  |     strategyId, | ||||||
|  |     disabled, | ||||||
|  |     icon, | ||||||
|  | }: IFeatureStrategyRemoveProps) => { | ||||||
|  |     const [openDialogue, setOpenDialogue] = useState(false); | ||||||
|  | 
 | ||||||
|  |     const { uiConfig } = useUiConfig(); | ||||||
|  |     const suggestChangesEnabled = Boolean(uiConfig?.flags?.changeRequests); | ||||||
|  | 
 | ||||||
|  |     const onRemove = useOnRemove({ | ||||||
|  |         featureId, | ||||||
|  |         projectId, | ||||||
|  |         strategyId, | ||||||
|  |         environmentId, | ||||||
|  |     }); | ||||||
|  |     const onSuggestRemove = useOnSuggestRemove({ | ||||||
|  |         featureId, | ||||||
|  |         projectId, | ||||||
|  |         strategyId, | ||||||
|  |         environmentId, | ||||||
|  |     }); | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <> |         <> | ||||||
| @ -91,19 +212,26 @@ export const FeatureStrategyRemove = ({ | |||||||
|                     </PermissionButton> |                     </PermissionButton> | ||||||
|                 } |                 } | ||||||
|             /> |             /> | ||||||
|             <Dialogue |             <ConditionallyRender | ||||||
|                 title="Are you sure you want to delete this strategy?" |                 condition={suggestChangesEnabled} | ||||||
|                 open={openDialogue} |                 show={ | ||||||
|                 primaryButtonText="Remove strategy" |                     <SuggestFeatureStrategyRemoveDialogue | ||||||
|                 secondaryButtonText="Cancel" |                         isOpen={openDialogue} | ||||||
|                 onClick={onRemove} |                         onClose={() => setOpenDialogue(false)} | ||||||
|                 onClose={() => setOpenDialogue(false)} |                         onRemove={async e => { | ||||||
|             > |                             await onSuggestRemove(e); | ||||||
|                 <Alert severity="error"> |                             setOpenDialogue(false); | ||||||
|                     Removing the strategy will change which users receive access |                         }} | ||||||
|                     to the feature. |                     /> | ||||||
|                 </Alert> |                 } | ||||||
|             </Dialogue> |                 elseShow={ | ||||||
|  |                     <FeatureStrategyRemoveDialogue | ||||||
|  |                         isOpen={openDialogue} | ||||||
|  |                         onClose={() => setOpenDialogue(false)} | ||||||
|  |                         onRemove={onRemove} | ||||||
|  |                     /> | ||||||
|  |                 } | ||||||
|  |             /> | ||||||
|         </> |         </> | ||||||
|     ); |     ); | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -4,9 +4,9 @@ interface IChangeRequestsSchema { | |||||||
|     feature: string; |     feature: string; | ||||||
|     action: |     action: | ||||||
|         | 'updateEnabled' |         | 'updateEnabled' | ||||||
|         | 'strategyAdd' |         | 'addStrategy' | ||||||
|         | 'strategyUpdate' |         | 'updateStrategy' | ||||||
|         | 'strategyDelete'; |         | 'deleteStrategy'; | ||||||
|     payload: string | boolean | object | number; |     payload: string | boolean | object | number; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user