mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Feat/visual enhancements (#404)
* fix: stale style * fix: execution plan styling * fix: paths * fix: remove console logs * fix: snapshots * fix: add comma * fix: update snapshots
This commit is contained in:
		
							parent
							
								
									6fc30d3a79
								
							
						
					
					
						commit
						7da3573edb
					
				
							
								
								
									
										16
									
								
								frontend/src/assets/icons/dots.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								frontend/src/assets/icons/dots.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | <svg | ||||||
|  |     id="dots" | ||||||
|  |     width="50px" | ||||||
|  |     height="21px" | ||||||
|  |     viewBox="0 0 132 58" | ||||||
|  |     version="1.1" | ||||||
|  |     xmlns="http://www.w3.org/2000/svg" | ||||||
|  | > | ||||||
|  |     <g stroke="none" fill="none"> | ||||||
|  |     <g id="chatbot-loader" fill="#fff"> | ||||||
|  |     <circle id="chatbot-loader-dot1" cx="25" cy="30" r="13"></circle> | ||||||
|  |     <circle id="chatbot-loader-dot2" cx="65" cy="30" r="13"></circle> | ||||||
|  |     <circle id="chatbot-loader-dot3" cx="105" cy="30" r="13"></circle> | ||||||
|  |     </g> | ||||||
|  |     </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 447 B | 
| @ -3,9 +3,14 @@ import { useTheme } from '@material-ui/core'; | |||||||
| interface IPercentageCircleProps { | interface IPercentageCircleProps { | ||||||
|     styles?: object; |     styles?: object; | ||||||
|     percentage: number; |     percentage: number; | ||||||
|  |     secondaryPieColor?: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const PercentageCircle = ({ styles, percentage }: IPercentageCircleProps) => { | const PercentageCircle = ({ | ||||||
|  |     styles, | ||||||
|  |     percentage, | ||||||
|  |     secondaryPieColor, | ||||||
|  | }: IPercentageCircleProps) => { | ||||||
|     const theme = useTheme(); |     const theme = useTheme(); | ||||||
| 
 | 
 | ||||||
|     let circle = { |     let circle = { | ||||||
| @ -14,7 +19,9 @@ const PercentageCircle = ({ styles, percentage }: IPercentageCircleProps) => { | |||||||
|         borderRadius: '50%', |         borderRadius: '50%', | ||||||
|         color: '#fff', |         color: '#fff', | ||||||
|         backgroundColor: theme.palette.grey[200], |         backgroundColor: theme.palette.grey[200], | ||||||
|         backgroundImage: `conic-gradient(${theme.palette.primary.main} ${percentage}%, ${theme.palette.grey[200]} 1%)`, |         backgroundImage: `conic-gradient(${ | ||||||
|  |             theme.palette.primary.main | ||||||
|  |         } ${percentage}%, ${secondaryPieColor || theme.palette.grey[200]} 1%)`,
 | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     if (percentage === 100) { |     if (percentage === 100) { | ||||||
|  | |||||||
| @ -51,18 +51,20 @@ const FeatureEnvironmentMetrics = ({ | |||||||
|         return ( |         return ( | ||||||
|             <div className={containerClasses}> |             <div className={containerClasses}> | ||||||
|                 <div className={styles.headerContainer}> |                 <div className={styles.headerContainer}> | ||||||
|                     <h2 className={styles.title}>Traffic in {metric.name}</h2> |                     <h2 data-loading className={styles.title}> | ||||||
|  |                         Traffic in {metric.name} | ||||||
|  |                     </h2> | ||||||
|                 </div> |                 </div> | ||||||
| 
 | 
 | ||||||
|                 <div className={styles.bodyContainer}> |                 <div className={styles.bodyContainer}> | ||||||
|                     <div className={styles.textContainer}> |                     <div className={styles.textContainer}> | ||||||
|                         <p className={styles.paragraph}> |                         <p className={styles.paragraph} data-loading> | ||||||
|                             No metrics available for this environment. |                             No metrics available for this environment. | ||||||
|                         </p> |                         </p> | ||||||
|                     </div> |                     </div> | ||||||
| 
 | 
 | ||||||
|                     <div className={styles.chartContainer}> |                     <div className={styles.chartContainer}> | ||||||
|                         <PieChartIcon className={styles.icon} /> |                         <PieChartIcon className={styles.icon} data-loading /> | ||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
| @ -72,30 +74,32 @@ const FeatureEnvironmentMetrics = ({ | |||||||
|     return ( |     return ( | ||||||
|         <div className={containerClasses}> |         <div className={containerClasses}> | ||||||
|             <div className={styles.headerContainer}> |             <div className={styles.headerContainer}> | ||||||
|                 <h2 className={styles.title}>Traffic in {metric.name}</h2> |                 <h2 data-loading className={styles.title}> | ||||||
|  |                     Traffic in {metric.name} | ||||||
|  |                 </h2> | ||||||
|             </div> |             </div> | ||||||
| 
 | 
 | ||||||
|             <div className={styles.bodyContainer}> |             <div className={styles.bodyContainer}> | ||||||
|                 <div className={styles.textContainer}> |                 <div className={styles.textContainer}> | ||||||
|                     <div className={styles.trueCountContainer}> |                     <div className={styles.trueCountContainer}> | ||||||
|                         <div> |                         <div> | ||||||
|                             <div className={styles.trueCount} /> |                             <div className={styles.trueCount} data-loading /> | ||||||
|                         </div> |                         </div> | ||||||
|                         <p className={styles.paragraph}> |                         <p className={styles.paragraph} data-loading> | ||||||
|                             {metric.yes} users received this feature |                             {metric.yes} users received this feature | ||||||
|                         </p> |                         </p> | ||||||
|                     </div> |                     </div> | ||||||
| 
 | 
 | ||||||
|                     <div className={styles.trueCountContainer}> |                     <div className={styles.trueCountContainer}> | ||||||
|                         <div> |                         <div> | ||||||
|                             <div className={styles.falseCount} /> |                             <div className={styles.falseCount} data-loading /> | ||||||
|                         </div> |                         </div> | ||||||
|                         <p className={styles.paragraph}> |                         <p className={styles.paragraph} data-loading> | ||||||
|                             {metric.no} users did not receive this feature |                             {metric.no} users did not receive this feature | ||||||
|                         </p> |                         </p> | ||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
|                 <div className={styles.chartContainer}> |                 <div className={styles.chartContainer} data-loading> | ||||||
|                     <PercentageCircle |                     <PercentageCircle | ||||||
|                         percentage={calculatePercentage()} |                         percentage={calculatePercentage()} | ||||||
|                         styles={{ |                         styles={{ | ||||||
|  | |||||||
| @ -18,9 +18,9 @@ const FeatureOverview = () => { | |||||||
|             </div> |             </div> | ||||||
|             <div className={styles.mainContent}> |             <div className={styles.mainContent}> | ||||||
|                 <div className={styles.trafficContainer}> |                 <div className={styles.trafficContainer}> | ||||||
|                     <FeatureOverviewMetrics /> |                     <FeatureOverviewMetrics data-loading /> | ||||||
|                 </div> |                 </div> | ||||||
|                 <FeatureOverviewStrategies /> |                 <FeatureOverviewStrategies data-loading /> | ||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
|     ); |     ); | ||||||
|  | |||||||
| @ -22,18 +22,20 @@ const FeatureOverviewMetaData = () => { | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <div className={classnames(styles.container)}> |         <div className={classnames(styles.container)}> | ||||||
|             <div className={styles.metaDataHeader}> |             <div className={styles.metaDataHeader} data-loading> | ||||||
|                 <IconComponent className={styles.headerIcon} />{' '} |                 <IconComponent className={styles.headerIcon} />{' '} | ||||||
|                 <h3 className={styles.header}> |                 <h3 className={styles.header}> | ||||||
|                     {capitalize(type || '')} toggle |                     {capitalize(type || '')} toggle | ||||||
|                 </h3> |                 </h3> | ||||||
|             </div> |             </div> | ||||||
|             <div className={styles.body}> |             <div className={styles.body}> | ||||||
|                 <span className={styles.bodyItem}>Project: {project}</span> |                 <span className={styles.bodyItem} data-loading> | ||||||
|  |                     Project: {project} | ||||||
|  |                 </span> | ||||||
|                 <ConditionallyRender |                 <ConditionallyRender | ||||||
|                     condition={description} |                     condition={description} | ||||||
|                     show={ |                     show={ | ||||||
|                         <span className={styles.bodyItem}> |                         <span className={styles.bodyItem} data-loading> | ||||||
|                             <div>Description:</div> |                             <div>Description:</div> | ||||||
|                             <div className={styles.descriptionContainer}> |                             <div className={styles.descriptionContainer}> | ||||||
|                                 <p>{description}</p> |                                 <p>{description}</p> | ||||||
| @ -47,7 +49,7 @@ const FeatureOverviewMetaData = () => { | |||||||
|                         </span> |                         </span> | ||||||
|                     } |                     } | ||||||
|                     elseShow={ |                     elseShow={ | ||||||
|                         <span> |                         <span data-loading> | ||||||
|                             <div className={styles.descriptionContainer}> |                             <div className={styles.descriptionContainer}> | ||||||
|                                 No description.{' '} |                                 No description.{' '} | ||||||
|                                 <IconButton |                                 <IconButton | ||||||
|  | |||||||
| @ -17,6 +17,16 @@ export const useStyles = makeStyles(theme => ({ | |||||||
|             maxWidth: 'none', |             maxWidth: 'none', | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|  |     status: { | ||||||
|  |         height: '12.5px', | ||||||
|  |         width: '12.5px', | ||||||
|  |         backgroundColor: theme.palette.success.main, | ||||||
|  |         borderRadius: '50%', | ||||||
|  |         marginLeft: '0.5rem', | ||||||
|  |     }, | ||||||
|  |     statusStale: { | ||||||
|  |         backgroundColor: theme.palette.error.main, | ||||||
|  |     }, | ||||||
|     staleHeaderContainer: { |     staleHeaderContainer: { | ||||||
|         display: 'flex', |         display: 'flex', | ||||||
|         alignItems: 'center', |         alignItems: 'center', | ||||||
| @ -41,6 +51,8 @@ export const useStyles = makeStyles(theme => ({ | |||||||
|     bodyItem: { |     bodyItem: { | ||||||
|         margin: '0.5rem 0', |         margin: '0.5rem 0', | ||||||
|         fontSize: theme.fontSizes.bodySize, |         fontSize: theme.fontSizes.bodySize, | ||||||
|  |         display: 'flex', | ||||||
|  |         alignItems: 'center', | ||||||
|     }, |     }, | ||||||
|     headerIcon: { |     headerIcon: { | ||||||
|         marginRight: '1rem', |         marginRight: '1rem', | ||||||
|  | |||||||
| @ -3,11 +3,11 @@ import classnames from 'classnames'; | |||||||
| import useFeature from '../../../../../hooks/api/getters/useFeature/useFeature'; | import useFeature from '../../../../../hooks/api/getters/useFeature/useFeature'; | ||||||
| import { useParams } from 'react-router-dom'; | import { useParams } from 'react-router-dom'; | ||||||
| import { IFeatureViewParams } from '../../../../../interfaces/params'; | import { IFeatureViewParams } from '../../../../../interfaces/params'; | ||||||
| import PermissionIconButton from '../../../../common/PermissionIconButton/PermissionIconButton'; |  | ||||||
| import { UPDATE_FEATURE } from '../../../../AccessProvider/permissions'; | import { UPDATE_FEATURE } from '../../../../AccessProvider/permissions'; | ||||||
| import { Check, Close } from '@material-ui/icons'; |  | ||||||
| import { useState } from 'react'; | import { useState } from 'react'; | ||||||
| import StaleDialog from './StaleDialog/StaleDialog'; | import StaleDialog from './StaleDialog/StaleDialog'; | ||||||
|  | import PermissionButton from '../../../../common/PermissionButton/PermissionButton'; | ||||||
|  | import classNames from 'classnames'; | ||||||
| 
 | 
 | ||||||
| const FeatureOverviewStale = () => { | const FeatureOverviewStale = () => { | ||||||
|     const styles = useStyles(); |     const styles = useStyles(); | ||||||
| @ -15,27 +15,36 @@ const FeatureOverviewStale = () => { | |||||||
|     const { projectId, featureId } = useParams<IFeatureViewParams>(); |     const { projectId, featureId } = useParams<IFeatureViewParams>(); | ||||||
|     const { feature } = useFeature(projectId, featureId); |     const { feature } = useFeature(projectId, featureId); | ||||||
| 
 | 
 | ||||||
|     const FlipStateButton = () => (feature.stale ? <Close /> : <Check />); |     const flipStateButtonText = () => | ||||||
|  |         feature.stale ? 'Set to active' : 'Set to stale'; | ||||||
|  | 
 | ||||||
|  |     const statusClasses = classNames(styles.status, { | ||||||
|  |         [styles.statusStale]: feature.stale, | ||||||
|  |     }); | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <div className={classnames(styles.container)}> |         <div className={classnames(styles.container)}> | ||||||
|             <div className={styles.staleHeaderContainer}> |             <div className={styles.staleHeaderContainer}> | ||||||
|                 <div className={styles.staleHeader}> |                 <div className={styles.staleHeader}> | ||||||
|                     <h3 className={styles.header}>Status</h3> |                     <h3 className={styles.header} data-loading> | ||||||
|  |                         Status | ||||||
|  |                     </h3> | ||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
|             <div className={styles.body}> |             <div className={styles.body}> | ||||||
|                 <span className={styles.bodyItem}> |                 <span className={styles.bodyItem} data-loading> | ||||||
|                     Feature is {feature.stale ? 'stale' : 'active'} |                     Feature is {feature.stale ? 'stale' : 'active'} | ||||||
|  |                     <div className={statusClasses} /> | ||||||
|                 </span> |                 </span> | ||||||
|                 <div className={styles.staleButton}> |                 <div className={styles.staleButton} data-loading> | ||||||
|                     <PermissionIconButton |                     <PermissionButton | ||||||
|                         onClick={() => setOpenStaleDialog(true)} |                         onClick={() => setOpenStaleDialog(true)} | ||||||
|                         permission={UPDATE_FEATURE} |                         permission={UPDATE_FEATURE} | ||||||
|                         tooltip="Flip status" |                         tooltip="Flip status" | ||||||
|  |                         variant="text" | ||||||
|                     > |                     > | ||||||
|                         <FlipStateButton /> |                         {flipStateButtonText()} | ||||||
|                     </PermissionIconButton> |                     </PermissionButton> | ||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
|             <StaleDialog |             <StaleDialog | ||||||
|  | |||||||
| @ -31,6 +31,7 @@ const FeatureOverviewEnvironment = ({ | |||||||
|         return strategies.map(strategy => { |         return strategies.map(strategy => { | ||||||
|             return ( |             return ( | ||||||
|                 <FeatureOverviewStrategyCard |                 <FeatureOverviewStrategyCard | ||||||
|  |                     data-loading | ||||||
|                     strategy={strategy} |                     strategy={strategy} | ||||||
|                     key={strategy.id} |                     key={strategy.id} | ||||||
|                     onClick={handleClick} |                     onClick={handleClick} | ||||||
|  | |||||||
| @ -24,13 +24,15 @@ const FeatureOverviewStrategyCard = ({ | |||||||
|     const { parameters } = strategy; |     const { parameters } = strategy; | ||||||
|     return ( |     return ( | ||||||
|         <button className={styles.card} onClick={onClick}> |         <button className={styles.card} onClick={onClick}> | ||||||
|             <Icon className={styles.icon} /> |             <Icon className={styles.icon} data-loading /> | ||||||
|             <p className={styles.cardHeader}>{strategyName}</p> |             <p data-loading className={styles.cardHeader}> | ||||||
|  |                 {strategyName} | ||||||
|  |             </p> | ||||||
| 
 | 
 | ||||||
|             <ConditionallyRender |             <ConditionallyRender | ||||||
|                 condition={Boolean(parameters?.rollout) && !smallScreen} |                 condition={Boolean(parameters?.rollout) && !smallScreen} | ||||||
|                 show={ |                 show={ | ||||||
|                     <p className={styles.rollout}> |                     <p className={styles.rollout} data-loading> | ||||||
|                         Rolling out to {parameters?.rollout}% |                         Rolling out to {parameters?.rollout}% | ||||||
|                     </p> |                     </p> | ||||||
|                 } |                 } | ||||||
|  | |||||||
| @ -26,7 +26,9 @@ const FeatureOverviewStrategies = () => { | |||||||
|         <div className={styles.container}> |         <div className={styles.container}> | ||||||
|             <div className={styles.headerContainer}> |             <div className={styles.headerContainer}> | ||||||
|                 <div className={styles.headerInnerContainer}> |                 <div className={styles.headerInnerContainer}> | ||||||
|                     <h3 className={styles.headerTitle}>Toggle Strategies</h3> |                     <h3 className={styles.headerTitle} data-loading> | ||||||
|  |                         Toggle Strategies | ||||||
|  |                     </h3> | ||||||
|                     <div className={styles.actions}> |                     <div className={styles.actions}> | ||||||
|                         <ResponsiveButton |                         <ResponsiveButton | ||||||
|                             maxWidth="700px" |                             maxWidth="700px" | ||||||
|  | |||||||
| @ -27,7 +27,7 @@ const AddTagDialog = ({ open, setOpen }: IAddTagDialogProps) => { | |||||||
|     const DEFAULT_TAG: IDefaultTag = { type: 'simple', value: '' }; |     const DEFAULT_TAG: IDefaultTag = { type: 'simple', value: '' }; | ||||||
|     const styles = useStyles(); |     const styles = useStyles(); | ||||||
|     const { featureId } = useParams<IFeatureViewParams>(); |     const { featureId } = useParams<IFeatureViewParams>(); | ||||||
|     const { addTagToFeature } = useFeatureApi(); |     const { addTagToFeature, loading } = useFeatureApi(); | ||||||
|     const { refetch } = useTags(featureId); |     const { refetch } = useTags(featureId); | ||||||
|     const [errors, setErrors] = useState({ tagError: '' }); |     const [errors, setErrors] = useState({ tagError: '' }); | ||||||
|     const { toast, setToastData } = useToast(); |     const { toast, setToastData } = useToast(); | ||||||
| @ -74,6 +74,7 @@ const AddTagDialog = ({ open, setOpen }: IAddTagDialogProps) => { | |||||||
|                 primaryButtonText="Add tag" |                 primaryButtonText="Add tag" | ||||||
|                 title="Add tags to feature toggle" |                 title="Add tags to feature toggle" | ||||||
|                 onClick={onSubmit} |                 onClick={onSubmit} | ||||||
|  |                 disabledPrimaryButton={loading} | ||||||
|                 onClose={onCancel} |                 onClose={onCancel} | ||||||
|             > |             > | ||||||
|                 <> |                 <> | ||||||
|  | |||||||
| @ -99,6 +99,7 @@ const FeatureOverviewTags = () => { | |||||||
|         <Chip |         <Chip | ||||||
|             icon={tagIcon(t.type)} |             icon={tagIcon(t.type)} | ||||||
|             className={styles.tagChip} |             className={styles.tagChip} | ||||||
|  |             data-loading | ||||||
|             label={t.value} |             label={t.value} | ||||||
|             key={`${t.type}:${t.value}`} |             key={`${t.type}:${t.value}`} | ||||||
|             onDelete={() => { |             onDelete={() => { | ||||||
| @ -112,7 +113,9 @@ const FeatureOverviewTags = () => { | |||||||
|         <div className={styles.container}> |         <div className={styles.container}> | ||||||
|             <div className={styles.tagheaderContainer}> |             <div className={styles.tagheaderContainer}> | ||||||
|                 <div className={styles.tagHeader}> |                 <div className={styles.tagHeader}> | ||||||
|                     <h4 className={styles.tagHeaderText}>Tags</h4> |                     <h4 className={styles.tagHeaderText} data-loading> | ||||||
|  |                         Tags | ||||||
|  |                     </h4> | ||||||
|                 </div> |                 </div> | ||||||
| 
 | 
 | ||||||
|                 <AddTagDialog open={openTagDialog} setOpen={setOpenTagDialog} /> |                 <AddTagDialog open={openTagDialog} setOpen={setOpenTagDialog} /> | ||||||
| @ -120,6 +123,7 @@ const FeatureOverviewTags = () => { | |||||||
|                     onClick={() => setOpenTagDialog(true)} |                     onClick={() => setOpenTagDialog(true)} | ||||||
|                     permission={UPDATE_FEATURE} |                     permission={UPDATE_FEATURE} | ||||||
|                     tooltip="Add tag" |                     tooltip="Add tag" | ||||||
|  |                     data-loading | ||||||
|                 > |                 > | ||||||
|                     <Add /> |                     <Add /> | ||||||
|                 </PermissionIconButton> |                 </PermissionIconButton> | ||||||
| @ -143,7 +147,7 @@ const FeatureOverviewTags = () => { | |||||||
|                 <ConditionallyRender |                 <ConditionallyRender | ||||||
|                     condition={tags.length > 0} |                     condition={tags.length > 0} | ||||||
|                     show={tags.map(renderTag)} |                     show={tags.map(renderTag)} | ||||||
|                     elseShow={<p>No tags to display</p>} |                     elseShow={<p data-loading>No tags to display</p>} | ||||||
|                 /> |                 /> | ||||||
|             </div> |             </div> | ||||||
|             {toast} |             {toast} | ||||||
|  | |||||||
| @ -9,9 +9,10 @@ export const useStyles = makeStyles(theme => ({ | |||||||
|         display: 'flex', |         display: 'flex', | ||||||
|         flexDirection: 'column', |         flexDirection: 'column', | ||||||
|         alignItems: 'center', |         alignItems: 'center', | ||||||
|  |         backgroundColor: theme.palette.grey[100], | ||||||
|     }, |     }, | ||||||
|     header: { |     header: { | ||||||
|         color: theme.palette.primary.main, |         color: theme.palette.grey[700], | ||||||
|         textAlign: 'center', |         textAlign: 'center', | ||||||
|         margin: '0.5rem 0', |         margin: '0.5rem 0', | ||||||
|         fontSize: theme.fontSizes.bodySize, |         fontSize: theme.fontSizes.bodySize, | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ const FeatureStrategiesConfigure = ({ | |||||||
|     const [strategyParams, setStrategyParams] = useState( |     const [strategyParams, setStrategyParams] = useState( | ||||||
|         configureNewStrategy.parameters |         configureNewStrategy.parameters | ||||||
|     ); |     ); | ||||||
|     const { addStrategyToFeature } = useFeatureStrategyApi(); |     const { addStrategyToFeature, loading } = useFeatureStrategyApi(); | ||||||
| 
 | 
 | ||||||
|     const handleCancel = () => { |     const handleCancel = () => { | ||||||
|         setConfigureNewStrategy(null); |         setConfigureNewStrategy(null); | ||||||
| @ -71,6 +71,7 @@ const FeatureStrategiesConfigure = ({ | |||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|  |             if (loading) return; | ||||||
|             const res = await addStrategyToFeature( |             const res = await addStrategyToFeature( | ||||||
|                 projectId, |                 projectId, | ||||||
|                 featureId, |                 featureId, | ||||||
| @ -163,15 +164,21 @@ const FeatureStrategiesConfigure = ({ | |||||||
|                     className={styles.btn} |                     className={styles.btn} | ||||||
|                     onClick={resolveSubmit} |                     onClick={resolveSubmit} | ||||||
|                     data-test={ADD_NEW_STRATEGY_SAVE_ID} |                     data-test={ADD_NEW_STRATEGY_SAVE_ID} | ||||||
|  |                     disabled={loading} | ||||||
|                 > |                 > | ||||||
|                     Save |                     Save | ||||||
|                 </Button> |                 </Button> | ||||||
|                 <Button className={styles.btn} onClick={handleCancel}> |                 <Button | ||||||
|  |                     className={styles.btn} | ||||||
|  |                     onClick={handleCancel} | ||||||
|  |                     disabled={loading} | ||||||
|  |                 > | ||||||
|                     Cancel |                     Cancel | ||||||
|                 </Button> |                 </Button> | ||||||
|             </div> |             </div> | ||||||
|             <FeatureStrategiesProductionGuard |             <FeatureStrategiesProductionGuard | ||||||
|                 primaryButtonText="Save changes" |                 primaryButtonText="Save changes" | ||||||
|  |                 loading={loading} | ||||||
|                 show={productionGuard} |                 show={productionGuard} | ||||||
|                 onClick={() => { |                 onClick={() => { | ||||||
|                     handleSubmit(); |                     handleSubmit(); | ||||||
|  | |||||||
| @ -10,9 +10,6 @@ export const useStyles = makeStyles(theme => ({ | |||||||
|         backgroundColor: theme.palette.primary.light, |         backgroundColor: theme.palette.primary.light, | ||||||
|         opacity: '0.75', |         opacity: '0.75', | ||||||
|     }, |     }, | ||||||
|     strategiesContainer: { |  | ||||||
|         maxWidth: '627px', |  | ||||||
|     }, |  | ||||||
|     dropbox: { |     dropbox: { | ||||||
|         textAlign: 'center', |         textAlign: 'center', | ||||||
|         fontSize: theme.fontSizes.smallBody, |         fontSize: theme.fontSizes.smallBody, | ||||||
| @ -39,5 +36,6 @@ export const useStyles = makeStyles(theme => ({ | |||||||
|     }, |     }, | ||||||
|     environmentList: { |     environmentList: { | ||||||
|         marginTop: 0, |         marginTop: 0, | ||||||
|  |         marginBottom: 0, | ||||||
|     }, |     }, | ||||||
| })); | })); | ||||||
|  | |||||||
| @ -59,7 +59,6 @@ const FeatureStrategiesEnvironments = () => { | |||||||
|         if (addStrategy) { |         if (addStrategy) { | ||||||
|             setExpandedSidebar(true); |             setExpandedSidebar(true); | ||||||
|         } |         } | ||||||
|         console.log(feature); |  | ||||||
|         if (!feature) return; |         if (!feature) return; | ||||||
| 
 | 
 | ||||||
|         if (environmentTab) { |         if (environmentTab) { | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ interface IFeatureStrategiesProductionGuard { | |||||||
|     onClick: () => void; |     onClick: () => void; | ||||||
|     onClose: () => void; |     onClose: () => void; | ||||||
|     primaryButtonText: string; |     primaryButtonText: string; | ||||||
|  |     loading: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const FeatureStrategiesProductionGuard = ({ | const FeatureStrategiesProductionGuard = ({ | ||||||
| @ -13,6 +14,7 @@ const FeatureStrategiesProductionGuard = ({ | |||||||
|     onClick, |     onClick, | ||||||
|     onClose, |     onClose, | ||||||
|     primaryButtonText, |     primaryButtonText, | ||||||
|  |     loading, | ||||||
| }: IFeatureStrategiesProductionGuard) => { | }: IFeatureStrategiesProductionGuard) => { | ||||||
|     return ( |     return ( | ||||||
|         <Dialogue |         <Dialogue | ||||||
| @ -22,6 +24,7 @@ const FeatureStrategiesProductionGuard = ({ | |||||||
|             secondaryButtonText="Cancel" |             secondaryButtonText="Cancel" | ||||||
|             onClick={onClick} |             onClick={onClick} | ||||||
|             onClose={onClose} |             onClose={onClose} | ||||||
|  |             disabledPrimaryButton={loading} | ||||||
|         > |         > | ||||||
|             <Alert severity="error"> |             <Alert severity="error"> | ||||||
|                 WARNING. You are about to make changes to a production |                 WARNING. You are about to make changes to a production | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ import { | |||||||
| } from '../../../../../../testIds'; | } from '../../../../../../testIds'; | ||||||
| import AccessContext from '../../../../../../contexts/AccessContext'; | import AccessContext from '../../../../../../contexts/AccessContext'; | ||||||
| import { UPDATE_FEATURE } from '../../../../../AccessProvider/permissions'; | import { UPDATE_FEATURE } from '../../../../../AccessProvider/permissions'; | ||||||
|  | import useFeatureApi from '../../../../../../hooks/api/actions/useFeatureApi/useFeatureApi'; | ||||||
| 
 | 
 | ||||||
| interface IFeatureStrategyEditable { | interface IFeatureStrategyEditable { | ||||||
|     currentStrategy: IFeatureStrategy; |     currentStrategy: IFeatureStrategy; | ||||||
| @ -38,6 +39,7 @@ const FeatureStrategyEditable = ({ | |||||||
|     index, |     index, | ||||||
| }: IFeatureStrategyEditable) => { | }: IFeatureStrategyEditable) => { | ||||||
|     const { hasAccess } = useContext(AccessContext); |     const { hasAccess } = useContext(AccessContext); | ||||||
|  |     const { loading } = useFeatureApi(); | ||||||
| 
 | 
 | ||||||
|     const { projectId, featureId } = useParams<IFeatureViewParams>(); |     const { projectId, featureId } = useParams<IFeatureViewParams>(); | ||||||
|     const { activeEnvironment, featureCache, dirty, setDirty } = useContext( |     const { activeEnvironment, featureCache, dirty, setDirty } = useContext( | ||||||
| @ -171,12 +173,14 @@ const FeatureStrategyEditable = ({ | |||||||
|                                     className={styles.editButton} |                                     className={styles.editButton} | ||||||
|                                     onClick={updateFeatureStrategy} |                                     onClick={updateFeatureStrategy} | ||||||
|                                     data-test={UPDATE_STRATEGY_BUTTON_ID} |                                     data-test={UPDATE_STRATEGY_BUTTON_ID} | ||||||
|  |                                     disabled={loading} | ||||||
|                                 > |                                 > | ||||||
|                                     Save changes |                                     Save changes | ||||||
|                                 </Button> |                                 </Button> | ||||||
|                                 <Button |                                 <Button | ||||||
|                                     onClick={discardChanges} |                                     onClick={discardChanges} | ||||||
|                                     className={styles.editButton} |                                     className={styles.editButton} | ||||||
|  |                                     disabled={loading} | ||||||
|                                 > |                                 > | ||||||
|                                     Discard changes |                                     Discard changes | ||||||
|                                 </Button> |                                 </Button> | ||||||
|  | |||||||
| @ -10,9 +10,10 @@ export const useStyles = makeStyles(theme => ({ | |||||||
|         display: 'flex', |         display: 'flex', | ||||||
|         flexDirection: 'column', |         flexDirection: 'column', | ||||||
|         alignItems: 'center', |         alignItems: 'center', | ||||||
|  |         backgroundColor: theme.palette.grey[100], | ||||||
|     }, |     }, | ||||||
|     header: { |     header: { | ||||||
|         color: theme.palette.primary.main, |         color: theme.palette.grey[600], | ||||||
|         textAlign: 'center', |         textAlign: 'center', | ||||||
|         margin: '0.5rem 0', |         margin: '0.5rem 0', | ||||||
|         fontSize: theme.fontSizes.bodySize, |         fontSize: theme.fontSizes.bodySize, | ||||||
|  | |||||||
| @ -8,7 +8,6 @@ export const useStyles = makeStyles(theme => ({ | |||||||
|         marginTop: '0.5rem', |         marginTop: '0.5rem', | ||||||
|         width: '100%', |         width: '100%', | ||||||
|     }, |     }, | ||||||
| 
 |  | ||||||
|     constraint: { |     constraint: { | ||||||
|         fontSize: theme.fontSizes.smallBody, |         fontSize: theme.fontSizes.smallBody, | ||||||
|         alignItems: 'center;', |         alignItems: 'center;', | ||||||
|  | |||||||
| @ -70,7 +70,10 @@ const FeatureStrategyExecution = ({ | |||||||
|                                 are included. |                                 are included. | ||||||
|                             </p> |                             </p> | ||||||
| 
 | 
 | ||||||
|                             <PercentageCircle percentage={parameters[key]} /> |                             <PercentageCircle | ||||||
|  |                                 percentage={parameters[key]} | ||||||
|  |                                 secondaryPieColor={'#fff'} | ||||||
|  |                             /> | ||||||
|                         </Fragment> |                         </Fragment> | ||||||
|                     ); |                     ); | ||||||
|                 case 'userIds': |                 case 'userIds': | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ export const useStyles = makeStyles(theme => ({ | |||||||
|         alignItems: 'center;', |         alignItems: 'center;', | ||||||
|         margin: '0.5rem 0', |         margin: '0.5rem 0', | ||||||
|         display: 'flex', |         display: 'flex', | ||||||
|         border: `1px solid ${theme.palette.grey[300]}`, |         border: `1px solid ${theme.palette.grey[600]}`, | ||||||
|         padding: '0.2rem', |         padding: '0.2rem', | ||||||
|         borderRadius: '5px', |         borderRadius: '5px', | ||||||
|         width: '100%', |         width: '100%', | ||||||
|  | |||||||
| @ -17,16 +17,18 @@ import FeatureStrategies from './FeatureStrategies/FeatureStrategies'; | |||||||
| import FeatureVariants from './FeatureVariants/FeatureVariants'; | import FeatureVariants from './FeatureVariants/FeatureVariants'; | ||||||
| import { useStyles } from './FeatureView2.styles'; | import { useStyles } from './FeatureView2.styles'; | ||||||
| import FeatureSettings from './FeatureSettings/FeatureSettings'; | import FeatureSettings from './FeatureSettings/FeatureSettings'; | ||||||
|  | import useLoading from '../../../hooks/useLoading'; | ||||||
| 
 | 
 | ||||||
| const FeatureView2 = () => { | const FeatureView2 = () => { | ||||||
|     const { projectId, featureId } = useParams<IFeatureViewParams>(); |     const { projectId, featureId } = useParams<IFeatureViewParams>(); | ||||||
|     const { feature } = useFeature(projectId, featureId); |     const { feature, loading } = useFeature(projectId, featureId); | ||||||
|     const { a11yProps } = useTabs(0); |     const { a11yProps } = useTabs(0); | ||||||
|     const { archiveFeatureToggle } = useFeatureApi(); |     const { archiveFeatureToggle } = useFeatureApi(); | ||||||
|     const { toast, setToastData } = useToast(); |     const { toast, setToastData } = useToast(); | ||||||
|     const [showDelDialog, setShowDelDialog] = useState(false); |     const [showDelDialog, setShowDelDialog] = useState(false); | ||||||
|     const styles = useStyles(); |     const styles = useStyles(); | ||||||
|     const history = useHistory(); |     const history = useHistory(); | ||||||
|  |     const ref = useLoading(loading); | ||||||
| 
 | 
 | ||||||
|     const basePath = `/projects/${projectId}/features2/${featureId}`; |     const basePath = `/projects/${projectId}/features2/${featureId}`; | ||||||
| 
 | 
 | ||||||
| @ -55,7 +57,7 @@ const FeatureView2 = () => { | |||||||
|     const tabData = [ |     const tabData = [ | ||||||
|         { |         { | ||||||
|             title: 'Overview', |             title: 'Overview', | ||||||
|             path: `${basePath}/overview`, |             path: `${basePath}`, | ||||||
|             name: 'overview', |             name: 'overview', | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @ -81,6 +83,7 @@ const FeatureView2 = () => { | |||||||
|         return tabData.map((tab, index) => { |         return tabData.map((tab, index) => { | ||||||
|             return ( |             return ( | ||||||
|                 <Tab |                 <Tab | ||||||
|  |                     data-loading | ||||||
|                     key={tab.title} |                     key={tab.title} | ||||||
|                     label={tab.title} |                     label={tab.title} | ||||||
|                     value={tab.path} |                     value={tab.path} | ||||||
| @ -95,14 +98,17 @@ const FeatureView2 = () => { | |||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <> |         <div ref={ref}> | ||||||
|             <div className={styles.header}> |             <div className={styles.header}> | ||||||
|                 <div className={styles.innerContainer}> |                 <div className={styles.innerContainer}> | ||||||
|                     <h2 className={styles.featureViewHeader}>{feature.name}</h2> |                     <h2 className={styles.featureViewHeader} data-loading> | ||||||
|  |                         {feature.name} | ||||||
|  |                     </h2> | ||||||
|                     <div className={styles.actions}> |                     <div className={styles.actions}> | ||||||
|                         <PermissionIconButton |                         <PermissionIconButton | ||||||
|                             permission={UPDATE_FEATURE} |                             permission={UPDATE_FEATURE} | ||||||
|                             tooltip="Copy" |                             tooltip="Copy" | ||||||
|  |                             data-loading | ||||||
|                             component={Link} |                             component={Link} | ||||||
|                             to={`${history.location.pathname}/copy`} |                             to={`${history.location.pathname}/copy`} | ||||||
|                         > |                         > | ||||||
| @ -111,6 +117,7 @@ const FeatureView2 = () => { | |||||||
|                         <PermissionIconButton |                         <PermissionIconButton | ||||||
|                             permission={UPDATE_FEATURE} |                             permission={UPDATE_FEATURE} | ||||||
|                             tooltip="Archive feature toggle" |                             tooltip="Archive feature toggle" | ||||||
|  |                             data-loading | ||||||
|                             onClick={() => setShowDelDialog(true)} |                             onClick={() => setShowDelDialog(true)} | ||||||
|                         > |                         > | ||||||
|                             <Archive /> |                             <Archive /> | ||||||
| @ -130,7 +137,8 @@ const FeatureView2 = () => { | |||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
|             <Route |             <Route | ||||||
|                 path={`/projects/:projectId/features2/:featureId/overview`} |                 exact | ||||||
|  |                 path={`/projects/:projectId/features2/:featureId`} | ||||||
|                 component={FeatureOverview} |                 component={FeatureOverview} | ||||||
|             /> |             /> | ||||||
|             <Route |             <Route | ||||||
| @ -164,7 +172,7 @@ const FeatureView2 = () => { | |||||||
|                 Are you sure you want to archive this feature toggle? |                 Are you sure you want to archive this feature toggle? | ||||||
|             </Dialogue> |             </Dialogue> | ||||||
|             {toast} |             {toast} | ||||||
|         </> |         </div> | ||||||
|     ); |     ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -96,19 +96,22 @@ const FeatureViewEnvironment: FC<IFeatureViewEnvironmentProps> = ({ | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <div className={containerClasses}> |         <div className={containerClasses}> | ||||||
|             <div className={environmentIdentifierClasses}> |             <div className={environmentIdentifierClasses} data-loading> | ||||||
|                 <div className={iconContainerClasses}> |                 <div className={iconContainerClasses}> | ||||||
|                     <Cloud className={iconClasses} /> |                     <Cloud className={iconClasses} /> | ||||||
|                 </div> |                 </div> | ||||||
|                 <p className={styles.environmentBadgeParagraph}>{env.type}</p> |                 <p className={styles.environmentBadgeParagraph}>{env.type}</p> | ||||||
|             </div> |             </div> | ||||||
|  | 
 | ||||||
|             <div className={styles.header}> |             <div className={styles.header}> | ||||||
|                 <div className={styles.headerInfo}> |                 <div className={styles.headerInfo}> | ||||||
|                     <Tooltip title={env.name}> |                     <Tooltip title={env.name}> | ||||||
|                         <p className={styles.environmentTitle}>{env.name}</p> |                         <p data-loading className={styles.environmentTitle}> | ||||||
|  |                             {env.name} | ||||||
|  |                         </p> | ||||||
|                     </Tooltip> |                     </Tooltip> | ||||||
|                 </div> |                 </div> | ||||||
|                 <div className={styles.environmentStatus}> |                 <div className={styles.environmentStatus} data-loading> | ||||||
|                     <ConditionallyRender |                     <ConditionallyRender | ||||||
|                         condition={env?.strategies?.length > 0} |                         condition={env?.strategies?.length > 0} | ||||||
|                         show={ |                         show={ | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ import { ITag } from '../../../../interfaces/tags'; | |||||||
| import useAPI from '../useApi/useApi'; | import useAPI from '../useApi/useApi'; | ||||||
| 
 | 
 | ||||||
| const useFeatureApi = () => { | const useFeatureApi = () => { | ||||||
|     const { makeRequest, createRequest, errors } = useAPI({ |     const { makeRequest, createRequest, errors, loading } = useAPI({ | ||||||
|         propagateErrors: true, |         propagateErrors: true, | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
| @ -146,13 +146,13 @@ const useFeatureApi = () => { | |||||||
|     const cloneFeatureToggle = async ( |     const cloneFeatureToggle = async ( | ||||||
|         projectId: string, |         projectId: string, | ||||||
|         featureId: string, |         featureId: string, | ||||||
|         payload: {name: string, replaceGroupId: boolean} |         payload: { name: string; replaceGroupId: boolean } | ||||||
|     ) => { |     ) => { | ||||||
|         const path = `api/admin/projects/${projectId}/features/${featureId}/clone`; |         const path = `api/admin/projects/${projectId}/features/${featureId}/clone`; | ||||||
|         const req = createRequest( |         const req = createRequest(path, { | ||||||
|             path, |             method: 'POST', | ||||||
|             { method: 'POST',  body: JSON.stringify(payload) }, |             body: JSON.stringify(payload), | ||||||
|         ); |         }); | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             const res = await makeRequest(req.caller, req.id); |             const res = await makeRequest(req.caller, req.id); | ||||||
| @ -172,7 +172,8 @@ const useFeatureApi = () => { | |||||||
|         deleteTagFromFeature, |         deleteTagFromFeature, | ||||||
|         archiveFeatureToggle, |         archiveFeatureToggle, | ||||||
|         patchFeatureToggle, |         patchFeatureToggle, | ||||||
|         cloneFeatureToggle |         cloneFeatureToggle, | ||||||
|  |         loading, | ||||||
|     }; |     }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user