1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-04 01:18:20 +02:00

Feature toggle types list (#4260)

## About the changes

![image](https://github.com/Unleash/unleash/assets/2625371/0f66333f-5ed9-4e44-b658-2e7c3606a2c3)
This commit is contained in:
Tymoteusz Czech 2023-07-18 13:46:06 +02:00 committed by GitHub
parent 35fbd8f271
commit ef495a35ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 215 additions and 2 deletions

View File

@ -5,12 +5,11 @@ import { IRole, PredefinedRoleType } from 'interfaces/role';
import useToast from 'hooks/useToast'; import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError'; import { formatUnknownError } from 'utils/formatUnknownError';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import { useMediaQuery } from '@mui/material'; import { useTheme, useMediaQuery } from '@mui/material';
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
import { useFlexLayout, useSortBy, useTable } from 'react-table'; import { useFlexLayout, useSortBy, useTable } from 'react-table';
import { sortTypes } from 'utils/sortTypes'; import { sortTypes } from 'utils/sortTypes';
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
import theme from 'themes/theme';
import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns'; import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
import { useSearch } from 'hooks/useSearch'; import { useSearch } from 'hooks/useSearch';
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell'; import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
@ -42,6 +41,7 @@ export const RolesTable = ({
setSelectedRole, setSelectedRole,
}: IRolesTableProps) => { }: IRolesTableProps) => {
const { setToastData, setToastApiError } = useToast(); const { setToastData, setToastApiError } = useToast();
const theme = useTheme();
const { roles, projectRoles, refetch, loading } = useRoles(); const { roles, projectRoles, refetch, loading } = useRoles();
const { removeRole } = useRolesApi(); const { removeRole } = useRolesApi();

View File

@ -0,0 +1,190 @@
import { useMemo } from 'react';
import { useSortBy, useTable } from 'react-table';
import { sortTypes } from 'utils/sortTypes';
import { PageContent } from 'component/common/PageContent/PageContent';
import useFeatureTypes from 'hooks/api/getters/useFeatureTypes/useFeatureTypes';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { Box, Typography, useMediaQuery, useTheme } from '@mui/material';
import {
Table,
TableBody,
TableCell,
TableRow,
SortableTableHeader,
} from 'component/common/Table';
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons';
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { Edit } from '@mui/icons-material';
import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
export const FeatureTypesList = () => {
const { featureTypes, loading } = useFeatureTypes();
const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
const columns = useMemo(
() => [
{
accessor: 'id',
Cell: ({ value }: { value: string }) => {
const IconComponent = getFeatureTypeIcons(value);
return (
<IconCell
icon={
<IconComponent
data-loading="true"
color="action"
/>
}
/>
);
},
width: 50,
disableSortBy: true,
},
{
Header: 'Name',
accessor: 'name',
minWidth: 125,
Cell: TextCell,
},
{
Header: 'Description',
accessor: 'description',
width: '80%',
Cell: ({ value }: { value: string }) => (
<Typography
component="div"
variant="body2"
color="text.secondary"
lineHeight={2}
>
<TextCell lineClamp={1}>{value}</TextCell>
</Typography>
),
disableSortBy: true,
},
{
Header: 'Lifetime',
accessor: 'lifetimeDays',
Cell: ({ value }: { value: number }) => {
if (value) {
return (
<TextCell>
{value === 1 ? '1 day' : `${value} days`}
</TextCell>
);
}
return <TextCell>doesn't expire</TextCell>;
},
sortInverted: true,
minWidth: 150,
},
{
Header: 'Actions',
Cell: ({ row: { original: featureType } }: any) => (
<Box sx={theme => ({ padding: theme.spacing(0.5, 0) })}>
<ActionCell>
<PermissionIconButton
disabled={!featureType.id}
data-loading="true"
onClick={() => {}}
permission={ADMIN}
tooltipProps={{
title: 'Edit feature toggle type',
}}
>
<Edit />
</PermissionIconButton>
</ActionCell>
</Box>
),
disableSortBy: true,
},
],
[]
);
const data = useMemo(
() =>
loading
? Array(5).fill({
id: '',
name: 'Loading...',
description: 'Loading...',
lifetimeDays: 1,
})
: featureTypes,
[loading, featureTypes]
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
setHiddenColumns,
} = useTable(
{
columns: columns as any[],
data,
sortTypes,
autoResetSortBy: false,
disableSortRemove: true,
},
useSortBy
);
useConditionallyHiddenColumns(
[
{
condition: isSmallScreen,
columns: ['description'],
},
],
setHiddenColumns,
columns
);
return (
<PageContent
isLoading={loading}
header={
<PageHeader>
<Typography
component="h2"
sx={theme => ({
fontSize: theme.fontSizes.mainHeader,
})}
>
Feature toggle types
</Typography>
</PageHeader>
}
>
<Table {...getTableProps()}>
<SortableTableHeader headerGroups={headerGroups} />
<TableBody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row);
return (
<TableRow hover {...row.getRowProps()}>
{row.cells.map(cell => (
<TableCell {...cell.getCellProps()}>
{cell.render('Cell')}
</TableCell>
))}
</TableRow>
);
})}
</TableBody>
</Table>
</PageContent>
);
};

View File

@ -193,6 +193,17 @@ exports[`returns all baseRoutes 1`] = `
"title": "Context fields", "title": "Context fields",
"type": "protected", "type": "protected",
}, },
{
"component": [Function],
"flag": "configurableFeatureTypeLifetimes",
"menu": {
"advanced": true,
"mobile": true,
},
"path": "/feature-toggle-type",
"title": "Feature toggle types",
"type": "protected",
},
{ {
"component": [Function], "component": [Function],
"menu": {}, "menu": {},

View File

@ -44,6 +44,7 @@ import { LazyAdmin } from 'component/admin/LazyAdmin';
import { LazyProject } from 'component/project/Project/LazyProject'; import { LazyProject } from 'component/project/Project/LazyProject';
import { AdminRedirect } from 'component/admin/AdminRedirect'; import { AdminRedirect } from 'component/admin/AdminRedirect';
import { LoginHistory } from 'component/loginHistory/LoginHistory'; import { LoginHistory } from 'component/loginHistory/LoginHistory';
import { FeatureTypesList } from 'component/featureTypes/FeatureTypesList';
export const routes: IRoute[] = [ export const routes: IRoute[] = [
// Splash // Splash
@ -209,6 +210,16 @@ export const routes: IRoute[] = [
menu: { mobile: true, advanced: true }, menu: { mobile: true, advanced: true },
}, },
// Feature types
{
path: '/feature-toggle-type',
title: 'Feature toggle types',
component: FeatureTypesList,
type: 'protected',
menu: { mobile: true, advanced: true },
flag: 'configurableFeatureTypeLifetimes',
},
// Strategies // Strategies
{ {
path: '/strategies/create', path: '/strategies/create',

View File

@ -53,6 +53,7 @@ export interface IFlags {
customRootRoles?: boolean; customRootRoles?: boolean;
strategyVariant?: boolean; strategyVariant?: boolean;
newProjectLayout?: boolean; newProjectLayout?: boolean;
configurableFeatureTypeLifetimes?: boolean;
} }
export interface IVersionInfo { export interface IVersionInfo {