mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +01:00
feat: Feature toggle type - edit form (#4269)
## About the changes ![image](https://github.com/Unleash/unleash/assets/2625371/f09bb538-9bb1-4c6b-85d7-e7895486e794) 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