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