diff --git a/frontend/src/component/addons/AddonList/AddonList.tsx b/frontend/src/component/addons/AddonList/AddonList.tsx
index a8702d19e7..15c5a0131a 100644
--- a/frontend/src/component/addons/AddonList/AddonList.tsx
+++ b/frontend/src/component/addons/AddonList/AddonList.tsx
@@ -79,8 +79,6 @@ export const AddonList = () => {
condition={addons.length > 0}
show={}
/>
-
-
>
);
diff --git a/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx b/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx
index f00508b52c..f7ddb64cee 100644
--- a/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx
+++ b/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx
@@ -1,19 +1,12 @@
import { useMemo } from 'react';
-import { Box, Table, TableBody, TableCell, TableRow } from '@mui/material';
-import { Delete, Edit, Visibility, VisibilityOff } from '@mui/icons-material';
+import { Table, TableBody, TableCell, TableRow } from 'component/common/Table';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-import {
- DELETE_ADDON,
- UPDATE_ADDON,
-} from 'component/providers/AccessProvider/permissions';
-import { useNavigate } from 'react-router-dom';
import { PageContent } from 'component/common/PageContent/PageContent';
import useAddons from 'hooks/api/getters/useAddons/useAddons';
import useToast from 'hooks/useToast';
import useAddonsApi from 'hooks/api/actions/useAddonsApi/useAddonsApi';
import { useState, useCallback } from 'react';
import { IAddon } from 'interfaces/addons';
-import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import { formatUnknownError } from 'utils/formatUnknownError';
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
@@ -169,6 +162,7 @@ export const ConfiguredAddons = () => {
}
+ sx={theme => ({ marginBottom: theme.spacing(2) })}
>
diff --git a/frontend/src/component/admin/menu/AdminMenu.tsx b/frontend/src/component/admin/menu/AdminMenu.tsx
index bb341cf307..9b38b7a306 100644
--- a/frontend/src/component/admin/menu/AdminMenu.tsx
+++ b/frontend/src/component/admin/menu/AdminMenu.tsx
@@ -59,7 +59,7 @@ function AdminMenu() {
to="/admin/roles"
style={createNavLinkStyle}
>
- Project Roles
+ Project roles
}
/>
@@ -69,7 +69,7 @@ function AdminMenu() {
value="/admin/api"
label={
- API Access
+ API access
}
/>
@@ -77,7 +77,7 @@ function AdminMenu() {
value="/admin/auth"
label={
- Single Sign-On
+ Single sign-on
}
/>
diff --git a/frontend/src/component/admin/projectRoles/ProjectRoles/ProjectRoleList/ProjectRoleList.tsx b/frontend/src/component/admin/projectRoles/ProjectRoles/ProjectRoleList/ProjectRoleList.tsx
index 7960909abe..29e80877cf 100644
--- a/frontend/src/component/admin/projectRoles/ProjectRoles/ProjectRoleList/ProjectRoleList.tsx
+++ b/frontend/src/component/admin/projectRoles/ProjectRoles/ProjectRoleList/ProjectRoleList.tsx
@@ -1,37 +1,47 @@
-import { useContext, useState } from 'react';
+import { useEffect, useMemo, useState } from 'react';
import {
Table,
+ SortableTableHeader,
TableBody,
TableCell,
- TableHead,
TableRow,
-} from '@mui/material';
-import AccessContext from 'contexts/AccessContext';
-import usePagination from 'hooks/usePagination';
+ TablePlaceholder,
+ TableSearch,
+} from 'component/common/Table';
+import { useTable, useGlobalFilter, useSortBy } from 'react-table';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
-import PaginateUI from 'component/common/PaginateUI/PaginateUI';
-import ProjectRoleListItem from './ProjectRoleListItem/ProjectRoleListItem';
import useProjectRoles from 'hooks/api/getters/useProjectRoles/useProjectRoles';
import IRole, { IProjectRole } from 'interfaces/role';
import useProjectRolesApi from 'hooks/api/actions/useProjectRolesApi/useProjectRolesApi';
import useToast from 'hooks/useToast';
import ProjectRoleDeleteConfirm from '../ProjectRoleDeleteConfirm/ProjectRoleDeleteConfirm';
import { formatUnknownError } from 'utils/formatUnknownError';
-import { useStyles } from './ProjectRoleListItem/ProjectRoleListItem.styles';
+import { Box, Button, useMediaQuery } from '@mui/material';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
+import { Delete, Edit, SupervisedUserCircle } from '@mui/icons-material';
+import { useNavigate } from 'react-router-dom';
+import { PageContent } from 'component/common/PageContent/PageContent';
+import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
+import { PageHeader } from 'component/common/PageHeader/PageHeader';
+import { sortTypes } from 'utils/sortTypes';
+import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
+import theme from 'themes/theme';
+import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
const ROOTROLE = 'root';
+const BUILTIN_ROLE_TYPE = 'project';
const ProjectRoleList = () => {
- const { hasAccess } = useContext(AccessContext);
- const { roles } = useProjectRoles();
- const { classes: styles } = useStyles();
+ const navigate = useNavigate();
+ const { roles, refetch, loading } = useProjectRoles();
+
+ const isExtraSmallScreen = useMediaQuery(theme.breakpoints.down('sm'));
const paginationFilter = (role: IRole) => role?.type !== ROOTROLE;
+ const data = roles.filter(paginationFilter);
- const { page, pages, nextPage, prevPage, setPageIndex, pageIndex } =
- usePagination(roles, 10, paginationFilter);
const { deleteRole } = useProjectRolesApi();
- const { refetch } = useProjectRoles();
const [currentRole, setCurrentRole] = useState(null);
const [delDialog, setDelDialog] = useState(false);
const [confirmName, setConfirmName] = useState('');
@@ -54,60 +64,198 @@ const ProjectRoleList = () => {
setConfirmName('');
};
- const renderRoles = () => {
- return page.map((role: IProjectRole) => {
- return (
-
- );
- });
- };
+ const columns = useMemo(
+ () => [
+ {
+ id: 'Icon',
+ Cell: () => (
+ }
+ />
+ ),
+ },
+ {
+ Header: 'Project role',
+ accessor: 'name',
+ },
+ {
+ Header: 'Description',
+ accessor: 'description',
+ width: '90%',
+ },
+ {
+ Header: 'Actions',
+ id: 'Actions',
+ align: 'center',
+ Cell: ({
+ row: {
+ original: { id, type, name, description },
+ },
+ }: any) => (
+
+ {
+ navigate(`/admin/roles/${id}/edit`);
+ }}
+ permission={ADMIN}
+ tooltipProps={{
+ title:
+ type === BUILTIN_ROLE_TYPE
+ ? 'You cannot edit role'
+ : 'Edit role',
+ }}
+ >
+
+
+ {
+ setCurrentRole({
+ id,
+ name,
+ description,
+ } as IProjectRole);
+ setDelDialog(true);
+ }}
+ permission={ADMIN}
+ tooltipProps={{
+ title:
+ type === BUILTIN_ROLE_TYPE
+ ? 'You cannot remove role'
+ : 'Remove role',
+ }}
+ >
+
+
+
+ ),
+ width: 100,
+ disableSortBy: true,
+ },
+ ],
+ [navigate]
+ );
- if (!roles) return null;
+ const initialState = useMemo(
+ () => ({
+ sortBy: [{ id: 'name', desc: false }],
+ }),
+ []
+ );
+
+ const {
+ getTableProps,
+ getTableBodyProps,
+ headerGroups,
+ rows,
+ prepareRow,
+ state: { globalFilter },
+ setGlobalFilter,
+ setHiddenColumns,
+ } = useTable(
+ {
+ columns: columns as any[], // TODO: fix after `react-table` v8 update
+ data,
+ initialState,
+ sortTypes,
+ autoResetGlobalFilter: false,
+ autoResetSortBy: false,
+ disableSortRemove: true,
+ defaultColumn: {
+ Cell: HighlightCell,
+ },
+ },
+ useGlobalFilter,
+ useSortBy
+ );
+
+ useEffect(() => {
+ setHiddenColumns([]);
+ if (isExtraSmallScreen) {
+ setHiddenColumns(['Icon']);
+ }
+ }, [setHiddenColumns, isExtraSmallScreen]);
return (
-
-
-
-
-
- Project Role
-
- Description
-
-
- {hasAccess(ADMIN) ? 'Action' : ''}
-
-
-
- {renderRoles()}
-
+
+
+
+ >
+ }
/>
-
-
+ }
+ >
+
+
+
+
+ {rows.map(row => {
+ prepareRow(row);
+ return (
+
+ {row.cells.map(cell => (
+
+ {cell.render('Cell')}
+
+ ))}
+
+ );
+ })}
+
+
+
+
0}
+ show={
+
+ No project roles found matching “
+ {globalFilter}
+ ”
+
+ }
+ elseShow={
+
+ No project roles available. Get started by
+ adding one.
+
+ }
+ />
+ }
+ />
+
-
+
);
};
diff --git a/frontend/src/component/admin/projectRoles/ProjectRoles/ProjectRoles.tsx b/frontend/src/component/admin/projectRoles/ProjectRoles/ProjectRoles.tsx
index 3d14e7e7e4..8b538acac8 100644
--- a/frontend/src/component/admin/projectRoles/ProjectRoles/ProjectRoles.tsx
+++ b/frontend/src/component/admin/projectRoles/ProjectRoles/ProjectRoles.tsx
@@ -1,61 +1,22 @@
-import { Button } from '@mui/material';
import { useContext } from 'react';
-import { useNavigate } from 'react-router-dom';
import AccessContext from 'contexts/AccessContext';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-import { PageHeader } from 'component/common/PageHeader/PageHeader';
-import { PageContent } from 'component/common/PageContent/PageContent';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import AdminMenu from 'component/admin/menu/AdminMenu';
-import { useStyles } from './ProjectRoles.styles';
import ProjectRoleList from './ProjectRoleList/ProjectRoleList';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
const ProjectRoles = () => {
const { hasAccess } = useContext(AccessContext);
- const { classes: styles } = useStyles();
- const navigate = useNavigate();
return (
-
- navigate(
- '/admin/create-project-role'
- )
- }
- >
- New Project role
-
- }
- elseShow={
-
- PS! Only admins can add/remove roles.
-
- }
- />
- }
- />
- }
- >
- }
- elseShow={}
- />
-
+
}
+ elseShow={
}
+ />
);
};
diff --git a/frontend/src/component/common/Table/cells/HighlightCell/HighlightCell.tsx b/frontend/src/component/common/Table/cells/HighlightCell/HighlightCell.tsx
new file mode 100644
index 0000000000..0292fa1034
--- /dev/null
+++ b/frontend/src/component/common/Table/cells/HighlightCell/HighlightCell.tsx
@@ -0,0 +1,29 @@
+import { VFC } from 'react';
+import { Box } from '@mui/material';
+import { Highlighter } from 'component/common/Highlighter/Highlighter';
+import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
+
+interface IHighlightCellProps {
+ value?: string | null;
+ children?: string | null;
+}
+
+export const HighlightCell: VFC = ({
+ value,
+ children,
+}) => {
+ const { searchQuery } = useSearchHighlightContext();
+
+ const text = children ?? value;
+ if (!text) {
+ return ;
+ }
+
+ return (
+
+
+ {text}
+
+
+ );
+};
diff --git a/frontend/src/component/environments/EnvironmentActionCell/EnvironmentActionCell.tsx b/frontend/src/component/environments/EnvironmentActionCell/EnvironmentActionCell.tsx
index c8464b303f..9ba499b8ad 100644
--- a/frontend/src/component/environments/EnvironmentActionCell/EnvironmentActionCell.tsx
+++ b/frontend/src/component/environments/EnvironmentActionCell/EnvironmentActionCell.tsx
@@ -138,7 +138,14 @@ export const EnvironmentActionCell = ({
+
+
{
+ const { searchQuery } = useSearchHighlightContext();
+
return (
- {environment.name}
+ {environment.name}
Disabled}