From ef495a35ee341c2eb6a50854b897b570496cdfe1 Mon Sep 17 00:00:00 2001
From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com>
Date: Tue, 18 Jul 2023 13:46:06 +0200
Subject: [PATCH] Feature toggle types list (#4260)
## About the changes

---
.../admin/roles/RolesTable/RolesTable.tsx | 4 +-
.../featureTypes/FeatureTypesList.tsx | 190 ++++++++++++++++++
.../__snapshots__/routes.test.tsx.snap | 11 +
frontend/src/component/menu/routes.ts | 11 +
frontend/src/interfaces/uiConfig.ts | 1 +
5 files changed, 215 insertions(+), 2 deletions(-)
create mode 100644 frontend/src/component/featureTypes/FeatureTypesList.tsx
diff --git a/frontend/src/component/admin/roles/RolesTable/RolesTable.tsx b/frontend/src/component/admin/roles/RolesTable/RolesTable.tsx
index 32f4ada533..b4824391c3 100644
--- a/frontend/src/component/admin/roles/RolesTable/RolesTable.tsx
+++ b/frontend/src/component/admin/roles/RolesTable/RolesTable.tsx
@@ -5,12 +5,11 @@ import { IRole, PredefinedRoleType } from 'interfaces/role';
import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError';
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 { useFlexLayout, useSortBy, useTable } from 'react-table';
import { sortTypes } from 'utils/sortTypes';
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
-import theme from 'themes/theme';
import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
import { useSearch } from 'hooks/useSearch';
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
@@ -42,6 +41,7 @@ export const RolesTable = ({
setSelectedRole,
}: IRolesTableProps) => {
const { setToastData, setToastApiError } = useToast();
+ const theme = useTheme();
const { roles, projectRoles, refetch, loading } = useRoles();
const { removeRole } = useRolesApi();
diff --git a/frontend/src/component/featureTypes/FeatureTypesList.tsx b/frontend/src/component/featureTypes/FeatureTypesList.tsx
new file mode 100644
index 0000000000..29ef1c954c
--- /dev/null
+++ b/frontend/src/component/featureTypes/FeatureTypesList.tsx
@@ -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 (
+
+ }
+ />
+ );
+ },
+ width: 50,
+ disableSortBy: true,
+ },
+ {
+ Header: 'Name',
+ accessor: 'name',
+ minWidth: 125,
+ Cell: TextCell,
+ },
+ {
+ Header: 'Description',
+ accessor: 'description',
+ width: '80%',
+ Cell: ({ value }: { value: string }) => (
+
+ {value}
+
+ ),
+ disableSortBy: true,
+ },
+ {
+ Header: 'Lifetime',
+ accessor: 'lifetimeDays',
+ Cell: ({ value }: { value: number }) => {
+ if (value) {
+ return (
+
+ {value === 1 ? '1 day' : `${value} days`}
+
+ );
+ }
+
+ return doesn't expire;
+ },
+ sortInverted: true,
+ minWidth: 150,
+ },
+ {
+ Header: 'Actions',
+ Cell: ({ row: { original: featureType } }: any) => (
+ ({ padding: theme.spacing(0.5, 0) })}>
+
+ {}}
+ permission={ADMIN}
+ tooltipProps={{
+ title: 'Edit feature toggle type',
+ }}
+ >
+
+
+
+
+ ),
+ 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 (
+
+ ({
+ fontSize: theme.fontSizes.mainHeader,
+ })}
+ >
+ Feature toggle types
+
+
+ }
+ >
+
+
+
+ {rows.map(row => {
+ prepareRow(row);
+ return (
+
+ {row.cells.map(cell => (
+
+ {cell.render('Cell')}
+
+ ))}
+
+ );
+ })}
+
+
+
+ );
+};
diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
index 9dde07c560..7a384324e5 100644
--- a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
+++ b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
@@ -193,6 +193,17 @@ exports[`returns all baseRoutes 1`] = `
"title": "Context fields",
"type": "protected",
},
+ {
+ "component": [Function],
+ "flag": "configurableFeatureTypeLifetimes",
+ "menu": {
+ "advanced": true,
+ "mobile": true,
+ },
+ "path": "/feature-toggle-type",
+ "title": "Feature toggle types",
+ "type": "protected",
+ },
{
"component": [Function],
"menu": {},
diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts
index b782a76bb7..f882be40b6 100644
--- a/frontend/src/component/menu/routes.ts
+++ b/frontend/src/component/menu/routes.ts
@@ -44,6 +44,7 @@ import { LazyAdmin } from 'component/admin/LazyAdmin';
import { LazyProject } from 'component/project/Project/LazyProject';
import { AdminRedirect } from 'component/admin/AdminRedirect';
import { LoginHistory } from 'component/loginHistory/LoginHistory';
+import { FeatureTypesList } from 'component/featureTypes/FeatureTypesList';
export const routes: IRoute[] = [
// Splash
@@ -209,6 +210,16 @@ export const routes: IRoute[] = [
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
{
path: '/strategies/create',
diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts
index ac620831d4..eea3f36acc 100644
--- a/frontend/src/interfaces/uiConfig.ts
+++ b/frontend/src/interfaces/uiConfig.ts
@@ -53,6 +53,7 @@ export interface IFlags {
customRootRoles?: boolean;
strategyVariant?: boolean;
newProjectLayout?: boolean;
+ configurableFeatureTypeLifetimes?: boolean;
}
export interface IVersionInfo {