mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	copy all strategies into an environment (#1182)
This commit is contained in:
		
							parent
							
								
									ee3f5be522
								
							
						
					
					
						commit
						826c8ff116
					
				| @ -0,0 +1,106 @@ | ||||
| import { MouseEvent, useContext, useState, VFC } from 'react'; | ||||
| import { | ||||
|     Button, | ||||
|     ListItemIcon, | ||||
|     ListItemText, | ||||
|     Menu, | ||||
|     MenuItem, | ||||
|     Tooltip, | ||||
| } from '@mui/material'; | ||||
| import { Lock } from '@mui/icons-material'; | ||||
| import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; | ||||
| import { IFeatureEnvironment } from 'interfaces/featureToggle'; | ||||
| import AccessContext from 'contexts/AccessContext'; | ||||
| import { CREATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions'; | ||||
| import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | ||||
| 
 | ||||
| interface ICopyButtonProps { | ||||
|     environmentId: IFeatureEnvironment['name']; | ||||
|     environments: IFeatureEnvironment['name'][]; | ||||
|     onClick: (environmentId: string) => void; | ||||
| } | ||||
| 
 | ||||
| export const CopyButton: VFC<ICopyButtonProps> = ({ | ||||
|     environmentId, | ||||
|     environments, | ||||
|     onClick, | ||||
| }) => { | ||||
|     const projectId = useRequiredPathParam('projectId'); | ||||
|     const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); | ||||
|     const open = Boolean(anchorEl); | ||||
|     const { hasAccess } = useContext(AccessContext); | ||||
|     const enabled = environments.some(environment => | ||||
|         hasAccess(CREATE_FEATURE_STRATEGY, projectId, environment) | ||||
|     ); | ||||
| 
 | ||||
|     return ( | ||||
|         <div> | ||||
|             <Tooltip title={enabled ? '' : '(Access denied)'}> | ||||
|                 <div> | ||||
|                     <Button | ||||
|                         id={`copy-all-strategies-${environmentId}`} | ||||
|                         aria-controls={open ? 'basic-menu' : undefined} | ||||
|                         aria-haspopup="true" | ||||
|                         aria-expanded={open ? 'true' : undefined} | ||||
|                         onClick={(event: MouseEvent<HTMLButtonElement>) => { | ||||
|                             setAnchorEl(event.currentTarget); | ||||
|                         }} | ||||
|                         disabled={!enabled} | ||||
|                         variant="outlined" | ||||
|                     > | ||||
|                         Copy from another environment | ||||
|                     </Button> | ||||
|                 </div> | ||||
|             </Tooltip> | ||||
|             <Menu | ||||
|                 id="basic-menu" | ||||
|                 anchorEl={anchorEl} | ||||
|                 open={open} | ||||
|                 onClose={() => { | ||||
|                     setAnchorEl(null); | ||||
|                 }} | ||||
|                 MenuListProps={{ | ||||
|                     'aria-labelledby': `copy-all-strategies-${environmentId}`, | ||||
|                 }} | ||||
|             > | ||||
|                 {environments.map(environment => { | ||||
|                     const access = hasAccess( | ||||
|                         CREATE_FEATURE_STRATEGY, | ||||
|                         projectId, | ||||
|                         environment | ||||
|                     ); | ||||
| 
 | ||||
|                     return ( | ||||
|                         <Tooltip | ||||
|                             title={ | ||||
|                                 access | ||||
|                                     ? '' | ||||
|                                     : "You don't have access to add a strategy to this environment" | ||||
|                             } | ||||
|                             key={environment} | ||||
|                         > | ||||
|                             <div> | ||||
|                                 <MenuItem | ||||
|                                     onClick={() => onClick(environment)} | ||||
|                                     disabled={!access} | ||||
|                                 > | ||||
|                                     <ConditionallyRender | ||||
|                                         condition={!access} | ||||
|                                         show={ | ||||
|                                             <ListItemIcon> | ||||
|                                                 <Lock fontSize="small" /> | ||||
|                                             </ListItemIcon> | ||||
|                                         } | ||||
|                                     /> | ||||
|                                     <ListItemText> | ||||
|                                         Copy from {environment} | ||||
|                                     </ListItemText> | ||||
|                                 </MenuItem> | ||||
|                             </div> | ||||
|                         </Tooltip> | ||||
|                     ); | ||||
|                 })} | ||||
|             </Menu> | ||||
|         </div> | ||||
|     ); | ||||
| }; | ||||
| @ -10,6 +10,8 @@ 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'; | ||||
| 
 | ||||
| interface IFeatureStrategyEmptyProps { | ||||
|     projectId: string; | ||||
| @ -30,18 +32,55 @@ export const FeatureStrategyEmpty = ({ | ||||
|         projectId, | ||||
|         featureId | ||||
|     ); | ||||
|     const { feature } = useFeature(projectId, featureId); | ||||
|     const otherAvailableEnvironments = feature?.environments.filter( | ||||
|         environment => | ||||
|             environment.name !== environmentId && | ||||
|             environment.strategies && | ||||
|             environment.strategies.length > 0 | ||||
|     ); | ||||
| 
 | ||||
|     const onAfterAddStrategy = () => { | ||||
|     const onAfterAddStrategy = (multiple = false) => { | ||||
|         refetchFeature(); | ||||
|         refetchFeatureImmutable(); | ||||
| 
 | ||||
|         setToastData({ | ||||
|             title: 'Strategy created', | ||||
|             text: 'Successfully created strategy', | ||||
|             title: multiple ? 'Strategies created' : 'Strategy created', | ||||
|             text: multiple | ||||
|                 ? 'Successfully copied from another environment' | ||||
|                 : 'Successfully created strategy', | ||||
|             type: 'success', | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     const onCopyStrategies = async (fromEnvironmentName: string) => { | ||||
|         const strategies = | ||||
|             otherAvailableEnvironments?.find( | ||||
|                 environment => environment.name === fromEnvironmentName | ||||
|             )?.strategies || []; | ||||
| 
 | ||||
|         try { | ||||
|             await Promise.all( | ||||
|                 strategies.map(strategy => { | ||||
|                     const { id, ...strategyCopy } = { | ||||
|                         ...strategy, | ||||
|                         environment: environmentId, | ||||
|                     }; | ||||
| 
 | ||||
|                     return addStrategyToFeature( | ||||
|                         projectId, | ||||
|                         featureId, | ||||
|                         environmentId, | ||||
|                         strategyCopy | ||||
|                     ); | ||||
|                 }) | ||||
|             ); | ||||
|             onAfterAddStrategy(true); | ||||
|         } catch (error) { | ||||
|             setToastApiError(formatUnknownError(error)); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     const onAddSimpleStrategy = async () => { | ||||
|         try { | ||||
|             await addStrategyToFeature(projectId, featureId, environmentId, { | ||||
| @ -82,12 +121,38 @@ export const FeatureStrategyEmpty = ({ | ||||
|                 <Link to="/admin/api">API key configured</Link> for this | ||||
|                 environment. | ||||
|             </p> | ||||
|             <FeatureStrategyMenu | ||||
|                 label="Add your first strategy" | ||||
|                 projectId={projectId} | ||||
|                 featureId={featureId} | ||||
|                 environmentId={environmentId} | ||||
|             /> | ||||
|             <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} | ||||
|                 /> | ||||
|                 <ConditionallyRender | ||||
|                     condition={ | ||||
|                         otherAvailableEnvironments && | ||||
|                         otherAvailableEnvironments.length > 0 | ||||
|                     } | ||||
|                     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> | ||||
|  | ||||
| @ -40,10 +40,7 @@ export const CopyStrategyIconMenu: VFC<ICopyStrategyIconMenuProps> = ({ | ||||
|         projectId, | ||||
|         featureId | ||||
|     ); | ||||
|     const handleClick = (event: MouseEvent<HTMLButtonElement>) => { | ||||
|         setAnchorEl(event.currentTarget); | ||||
|     }; | ||||
|     const handleClose = () => { | ||||
|     const onClose = () => { | ||||
|         setAnchorEl(null); | ||||
|     }; | ||||
|     const { hasAccess } = useContext(AccessContext); | ||||
| @ -70,7 +67,7 @@ export const CopyStrategyIconMenu: VFC<ICopyStrategyIconMenuProps> = ({ | ||||
|         } catch (error) { | ||||
|             setToastApiError(formatUnknownError(error)); | ||||
|         } | ||||
|         handleClose(); | ||||
|         onClose(); | ||||
|     }; | ||||
| 
 | ||||
|     const enabled = environments.some(environment => | ||||
| @ -87,11 +84,13 @@ export const CopyStrategyIconMenu: VFC<ICopyStrategyIconMenuProps> = ({ | ||||
|                 <div> | ||||
|                     <IconButton | ||||
|                         size="large" | ||||
|                         id="basic-button" | ||||
|                         id={`copy-strategy-icon-menu-${strategy.id}`} | ||||
|                         aria-controls={open ? 'basic-menu' : undefined} | ||||
|                         aria-haspopup="true" | ||||
|                         aria-expanded={open ? 'true' : undefined} | ||||
|                         onClick={handleClick} | ||||
|                         onClick={(event: MouseEvent<HTMLButtonElement>) => { | ||||
|                             setAnchorEl(event.currentTarget); | ||||
|                         }} | ||||
|                         disabled={!enabled} | ||||
|                     > | ||||
|                         <CopyIcon /> | ||||
| @ -102,9 +101,9 @@ export const CopyStrategyIconMenu: VFC<ICopyStrategyIconMenuProps> = ({ | ||||
|                 id="basic-menu" | ||||
|                 anchorEl={anchorEl} | ||||
|                 open={open} | ||||
|                 onClose={handleClose} | ||||
|                 onClose={onClose} | ||||
|                 MenuListProps={{ | ||||
|                     'aria-labelledby': 'basic-button', | ||||
|                     'aria-labelledby': `copy-strategy-icon-menu-${strategy.id}`, | ||||
|                 }} | ||||
|             > | ||||
|                 {environments.map(environment => { | ||||
| @ -136,7 +135,9 @@ export const CopyStrategyIconMenu: VFC<ICopyStrategyIconMenuProps> = ({ | ||||
|                                             </ListItemIcon> | ||||
|                                         } | ||||
|                                     /> | ||||
|                                     <ListItemText>{environment}</ListItemText> | ||||
|                                     <ListItemText> | ||||
|                                         Copy to {environment} | ||||
|                                     </ListItemText> | ||||
|                                 </MenuItem> | ||||
|                             </div> | ||||
|                         </Tooltip> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user