mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: Feature toggle type - edit form (#4269)
## About the changes  Task: https://linear.app/unleash/issue/1-1127/add-front-end ### Important files frontend/src/component/featureTypes/FeatureTypeForm/FeatureTypeForm.tsx ## Discussion points **`FIXME`** will be addressed when integrating with API
This commit is contained in:
		
							parent
							
								
									77a365e667
								
							
						
					
					
						commit
						d3708297cf
					
				| @ -0,0 +1,173 @@ | ||||
| import { type FormEventHandler, type VFC, useState } from 'react'; | ||||
| import { Box, Button, Typography, Checkbox, styled } from '@mui/material'; | ||||
| import { useNavigate, useParams } from 'react-router-dom'; | ||||
| import FormTemplate from 'component/common/FormTemplate/FormTemplate'; | ||||
| import NotFound from 'component/common/NotFound/NotFound'; | ||||
| import PermissionButton from 'component/common/PermissionButton/PermissionButton'; | ||||
| import { ADMIN } from 'component/providers/AccessProvider/permissions'; | ||||
| import { GO_BACK } from 'constants/navigate'; | ||||
| import Input from 'component/common/Input/Input'; | ||||
| import { FeatureTypeSchema } from 'openapi'; | ||||
| import { trim } from 'component/common/util'; | ||||
| import { HelpIcon } from 'component/common/HelpIcon/HelpIcon'; | ||||
| 
 | ||||
| type FeatureTypeFormProps = { | ||||
|     featureTypes: FeatureTypeSchema[]; | ||||
|     loading: boolean; | ||||
| }; | ||||
| 
 | ||||
| const StyledButtons = styled(Box)(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     justifyContent: 'flex-end', | ||||
|     marginTop: 'auto', | ||||
|     gap: theme.spacing(2), | ||||
|     paddingTop: theme.spacing(4), | ||||
| })); | ||||
| 
 | ||||
| const StyledForm = styled(Box)(() => ({ | ||||
|     display: 'flex', | ||||
|     flexDirection: 'column', | ||||
|     flexGrow: 1, | ||||
| })); | ||||
| 
 | ||||
| export const FeatureTypeForm: VFC<FeatureTypeFormProps> = ({ | ||||
|     featureTypes, | ||||
|     loading, | ||||
| }) => { | ||||
|     const { featureTypeId } = useParams(); | ||||
|     const navigate = useNavigate(); | ||||
|     const featureType = featureTypes.find( | ||||
|         featureType => featureType.id === featureTypeId | ||||
|     ); | ||||
|     const [lifetime, setLifetime] = useState<number>( | ||||
|         featureType?.lifetimeDays || 0 | ||||
|     ); | ||||
|     const [doesntExpire, setDoesntExpire] = useState<boolean>( | ||||
|         !featureType?.lifetimeDays | ||||
|     ); | ||||
| 
 | ||||
|     if (!loading && !featureType) { | ||||
|         return <NotFound />; | ||||
|     } | ||||
| 
 | ||||
|     const onChangeLifetime = (e: React.ChangeEvent<HTMLInputElement>) => { | ||||
|         const value = parseInt(trim(e.target.value), 10); | ||||
|         setLifetime(value); | ||||
|         if (value === 0) { | ||||
|             setDoesntExpire(true); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     const onChangeDoesntExpire = (e: React.ChangeEvent<HTMLInputElement>) => { | ||||
|         setDoesntExpire(e.target.checked); | ||||
|         if (lifetime === 0) { | ||||
|             setLifetime(featureType?.lifetimeDays || 1); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     const isIncorrect = | ||||
|         !doesntExpire && (Number.isNaN(lifetime) || lifetime < 0); | ||||
| 
 | ||||
|     const onSubmit: FormEventHandler = e => { | ||||
|         e.preventDefault(); | ||||
|         if (isIncorrect) return; | ||||
| 
 | ||||
|         const value = doesntExpire ? 0 : lifetime; | ||||
|         console.log('FIXME: onSubmit', value); | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|         <FormTemplate | ||||
|             modal | ||||
|             title={ | ||||
|                 loading | ||||
|                     ? 'Edit toggle type' | ||||
|                     : `Edit toggle type: ${featureType?.name}` | ||||
|             } | ||||
|             description={featureType?.description || ''} | ||||
|             documentationLink="https://docs.getunleash.io/reference/feature-toggle-types" | ||||
|             documentationLinkLabel="Feature toggle types documentation" | ||||
|             formatApiCode={() => 'FIXME: formatApiCode'} | ||||
|         > | ||||
|             <StyledForm component="form" onSubmit={onSubmit}> | ||||
|                 <Typography | ||||
|                     sx={theme => ({ | ||||
|                         margin: theme.spacing(3, 0, 1), | ||||
|                         display: 'flex', | ||||
|                         alignItems: 'center', | ||||
|                     })} | ||||
|                 > | ||||
|                     <Box component="label" htmlFor="feature-toggle-lifetime"> | ||||
|                         Expected lifetime | ||||
|                     </Box> | ||||
|                     <HelpIcon | ||||
|                         htmlTooltip | ||||
|                         tooltip={ | ||||
|                             <> | ||||
|                                 <p> | ||||
|                                     If your toggle exceeded lifetime of it's | ||||
|                                     type it will be marked as potencially stale. | ||||
|                                 </p> | ||||
|                                 <br /> | ||||
|                                 <a | ||||
|                                     href="https://docs.getunleash.io/reference/feature-toggle-types#expected-lifetime" | ||||
|                                     target="_blank" | ||||
|                                     rel="noreferrer" | ||||
|                                 > | ||||
|                                     Read more in the documentation | ||||
|                                 </a> | ||||
|                             </> | ||||
|                         } | ||||
|                     /> | ||||
|                 </Typography> | ||||
|                 <Box | ||||
|                     component="label" | ||||
|                     sx={theme => ({ | ||||
|                         display: 'flex', | ||||
|                         alignItems: 'center', | ||||
|                         cursor: 'pointer', | ||||
|                         marginBottom: theme.spacing(1), | ||||
|                         marginRight: 'auto', | ||||
|                     })} | ||||
|                     htmlFor="feature-toggle-expire" | ||||
|                 > | ||||
|                     <Checkbox | ||||
|                         checked={doesntExpire || lifetime === 0} | ||||
|                         id="feature-toggle-expire" | ||||
|                         onChange={onChangeDoesntExpire} | ||||
|                     /> | ||||
|                     <Box>doesn't expire</Box> | ||||
|                 </Box> | ||||
|                 <Input | ||||
|                     autoFocus | ||||
|                     disabled={doesntExpire} | ||||
|                     type="number" | ||||
|                     label="Lifetime in days" | ||||
|                     id="feature-toggle-lifetime" | ||||
|                     value={doesntExpire ? '0' : `${lifetime}`} | ||||
|                     onChange={onChangeLifetime} | ||||
|                     error={isIncorrect} | ||||
|                 /> | ||||
|                 <StyledButtons> | ||||
|                     <PermissionButton | ||||
|                         permission={ADMIN} | ||||
|                         variant="contained" | ||||
|                         color="primary" | ||||
|                         type="submit" | ||||
|                         disabled={loading || isIncorrect} | ||||
|                     > | ||||
|                         Save feature toggle type | ||||
|                     </PermissionButton> | ||||
|                     <Button | ||||
|                         type="button" | ||||
|                         color="primary" | ||||
|                         onClick={() => navigate(GO_BACK)} | ||||
|                         disabled={loading} | ||||
|                     > | ||||
|                         Cancel | ||||
|                     </Button> | ||||
|                 </StyledButtons> | ||||
|             </StyledForm> | ||||
|         </FormTemplate> | ||||
|     ); | ||||
| }; | ||||
| @ -1,4 +1,5 @@ | ||||
| import { useMemo } from 'react'; | ||||
| import { Route, Routes, useNavigate } from 'react-router-dom'; | ||||
| import { useSortBy, useTable } from 'react-table'; | ||||
| import { sortTypes } from 'utils/sortTypes'; | ||||
| import { PageContent } from 'component/common/PageContent/PageContent'; | ||||
| @ -20,11 +21,16 @@ import PermissionIconButton from 'component/common/PermissionIconButton/Permissi | ||||
| import { ADMIN } from 'component/providers/AccessProvider/permissions'; | ||||
| import { Edit } from '@mui/icons-material'; | ||||
| import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns'; | ||||
| import { SidebarModal } from 'component/common/SidebarModal/SidebarModal'; | ||||
| import { FeatureTypeForm } from './FeatureTypeForm/FeatureTypeForm'; | ||||
| 
 | ||||
| const basePath = '/feature-toggle-type'; | ||||
| 
 | ||||
| export const FeatureTypesList = () => { | ||||
|     const { featureTypes, loading } = useFeatureTypes(); | ||||
|     const theme = useTheme(); | ||||
|     const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); | ||||
|     const navigate = useNavigate(); | ||||
| 
 | ||||
|     const columns = useMemo( | ||||
|         () => [ | ||||
| @ -93,10 +99,14 @@ export const FeatureTypesList = () => { | ||||
|                             <PermissionIconButton | ||||
|                                 disabled={!featureType.id} | ||||
|                                 data-loading="true" | ||||
|                                 onClick={() => {}} | ||||
|                                 onClick={() => | ||||
|                                     navigate( | ||||
|                                         `/feature-toggle-type/edit/${featureType.id}` | ||||
|                                     ) | ||||
|                                 } | ||||
|                                 permission={ADMIN} | ||||
|                                 tooltipProps={{ | ||||
|                                     title: 'Edit feature toggle type', | ||||
|                                     title: `Edit ${featureType.name} feature toggle type`, | ||||
|                                 }} | ||||
|                             > | ||||
|                                 <Edit /> | ||||
| @ -107,7 +117,7 @@ export const FeatureTypesList = () => { | ||||
|                 disableSortBy: true, | ||||
|             }, | ||||
|         ], | ||||
|         [] | ||||
|         [navigate] | ||||
|     ); | ||||
| 
 | ||||
|     const data = useMemo( | ||||
| @ -185,6 +195,23 @@ export const FeatureTypesList = () => { | ||||
|                     })} | ||||
|                 </TableBody> | ||||
|             </Table> | ||||
|             <Routes> | ||||
|                 <Route | ||||
|                     path="edit/:featureTypeId" | ||||
|                     element={ | ||||
|                         <SidebarModal | ||||
|                             label="Edit feature toggle type" | ||||
|                             onClose={() => navigate(basePath)} | ||||
|                             open | ||||
|                         > | ||||
|                             <FeatureTypeForm | ||||
|                                 featureTypes={featureTypes} | ||||
|                                 loading={loading} | ||||
|                             /> | ||||
|                         </SidebarModal> | ||||
|                     } | ||||
|                 /> | ||||
|             </Routes> | ||||
|         </PageContent> | ||||
|     ); | ||||
| }; | ||||
|  | ||||
| @ -107,6 +107,11 @@ const StyledIconButton = styled(IconButton)(({ theme }) => ({ | ||||
|     borderRadius: 100, | ||||
| })); | ||||
| 
 | ||||
| const mapRouteLink = (route: INavigationMenuItem) => ({ | ||||
|     ...route, | ||||
|     path: route.path.replace('/*', ''), | ||||
| }); | ||||
| 
 | ||||
| const Header: VFC = () => { | ||||
|     const { onSetThemeMode, themeMode } = useThemeMode(); | ||||
|     const theme = useTheme(); | ||||
| @ -143,7 +148,8 @@ const Header: VFC = () => { | ||||
|                     menu: {}, | ||||
|                 }, | ||||
|             ]) | ||||
|             .filter(filterByConfig(uiConfig)), | ||||
|             .filter(filterByConfig(uiConfig)) | ||||
|             .map(mapRouteLink), | ||||
|         mobileRoutes: getCondensedRoutes(routes.mobileRoutes) | ||||
|             .concat([ | ||||
|                 { | ||||
| @ -152,14 +158,12 @@ const Header: VFC = () => { | ||||
|                     menu: {}, | ||||
|                 }, | ||||
|             ]) | ||||
|             .filter(filterByConfig(uiConfig)), | ||||
|             .filter(filterByConfig(uiConfig)) | ||||
|             .map(mapRouteLink), | ||||
|         adminRoutes: adminMenuRoutes | ||||
|             .filter(filterByConfig(uiConfig)) | ||||
|             .filter(filterByMode) | ||||
|             .map(route => ({ | ||||
|                 ...route, | ||||
|                 path: route.path.replace('/*', ''), | ||||
|             })), | ||||
|             .map(mapRouteLink), | ||||
|     }; | ||||
| 
 | ||||
|     if (smallScreen) { | ||||
|  | ||||
| @ -200,7 +200,7 @@ exports[`returns all baseRoutes 1`] = ` | ||||
|       "advanced": true, | ||||
|       "mobile": true, | ||||
|     }, | ||||
|     "path": "/feature-toggle-type", | ||||
|     "path": "/feature-toggle-type/*", | ||||
|     "title": "Feature toggle types", | ||||
|     "type": "protected", | ||||
|   }, | ||||
|  | ||||
| @ -212,7 +212,7 @@ export const routes: IRoute[] = [ | ||||
| 
 | ||||
|     // Feature types
 | ||||
|     { | ||||
|         path: '/feature-toggle-type', | ||||
|         path: '/feature-toggle-type/*', | ||||
|         title: 'Feature toggle types', | ||||
|         component: FeatureTypesList, | ||||
|         type: 'protected', | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user