mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-18 00:19:49 +01:00
chore: access overview search (#9547)
https://linear.app/unleash/issue/2-3407/add-search-to-the-access-overview Adds search to Access Overview. 
This commit is contained in:
parent
76b3e06fe5
commit
3a59886206
@ -3,7 +3,7 @@ import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import useUserInfo from 'hooks/api/getters/useUserInfo/useUserInfo';
|
||||
import { styled, useMediaQuery } from '@mui/material';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import theme from 'themes/theme';
|
||||
@ -18,16 +18,21 @@ import {
|
||||
} from 'utils/permissions';
|
||||
import type { IAccessOverviewPermissionCategory } from './AccessOverviewAccordion/AccessOverviewList';
|
||||
import { createProjectPermissionsStructure } from 'component/admin/roles/RoleForm/RolePermissionCategories/createProjectPermissionsStructure';
|
||||
import { Search } from 'component/common/Search/Search';
|
||||
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
||||
|
||||
const StyledActionsContainer = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
gap: theme.spacing(1),
|
||||
maxWidth: 600,
|
||||
maxWidth: 800,
|
||||
[theme.breakpoints.down('md')]: {
|
||||
flexDirection: 'column',
|
||||
maxWidth: '100%',
|
||||
},
|
||||
'& > div': {
|
||||
width: '100%',
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledAccessOverviewContainer = styled('div')(({ theme }) => ({
|
||||
@ -36,6 +41,20 @@ const StyledAccessOverviewContainer = styled('div')(({ theme }) => ({
|
||||
gap: theme.spacing(2),
|
||||
}));
|
||||
|
||||
const filterCategory = (
|
||||
category: IAccessOverviewPermissionCategory,
|
||||
search: string,
|
||||
): IAccessOverviewPermissionCategory | undefined => {
|
||||
const searchLower = search.toLowerCase();
|
||||
const filteredPermissions = category.permissions.filter(({ displayName }) =>
|
||||
displayName.toLowerCase().includes(searchLower),
|
||||
);
|
||||
|
||||
if (filteredPermissions.length) {
|
||||
return { ...category, permissions: filteredPermissions };
|
||||
}
|
||||
};
|
||||
|
||||
export const AccessOverview = () => {
|
||||
const id = useRequiredPathParam('id');
|
||||
const [query, setQuery] = useQueryParams({
|
||||
@ -48,6 +67,7 @@ export const AccessOverview = () => {
|
||||
|
||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
||||
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const [project, setProject] = useState(query.project ?? '');
|
||||
const [environment, setEnvironment] = useState(
|
||||
query.environment ?? undefined,
|
||||
@ -71,6 +91,7 @@ export const AccessOverview = () => {
|
||||
|
||||
const AccessActions = (
|
||||
<StyledActionsContainer>
|
||||
<Search initialValue={searchValue} onChange={setSearchValue} />
|
||||
<AccessOverviewSelect
|
||||
label='Project'
|
||||
options={projects}
|
||||
@ -92,20 +113,40 @@ export const AccessOverview = () => {
|
||||
</StyledActionsContainer>
|
||||
);
|
||||
|
||||
const rootCategories = getCategorizedRootPermissions(
|
||||
overview?.root ?? [],
|
||||
) as IAccessOverviewPermissionCategory[];
|
||||
const rootCategories = useMemo(() => {
|
||||
const categories = getCategorizedRootPermissions(
|
||||
overview?.root ?? [],
|
||||
) as IAccessOverviewPermissionCategory[];
|
||||
|
||||
const projectCategories = createProjectPermissionsStructure(
|
||||
overview?.project ?? [],
|
||||
).map(({ label, permissions }) => ({
|
||||
label,
|
||||
permissions: permissions.map(([permission]) => permission),
|
||||
})) as IAccessOverviewPermissionCategory[];
|
||||
if (!searchValue) return categories;
|
||||
|
||||
const environmentCategories = getCategorizedProjectPermissions(
|
||||
overview?.environment ?? [],
|
||||
) as IAccessOverviewPermissionCategory[];
|
||||
return categories
|
||||
.map((category) => filterCategory(category, searchValue))
|
||||
.filter(Boolean) as IAccessOverviewPermissionCategory[];
|
||||
}, [overview?.root, searchValue]);
|
||||
|
||||
const projectCategories = useMemo(() => {
|
||||
const categories = createProjectPermissionsStructure(
|
||||
overview?.project ?? [],
|
||||
).map(({ label, permissions }) => ({
|
||||
label,
|
||||
permissions: permissions.map(([permission]) => permission),
|
||||
})) as IAccessOverviewPermissionCategory[];
|
||||
|
||||
return categories
|
||||
.map((category) => filterCategory(category, searchValue))
|
||||
.filter(Boolean) as IAccessOverviewPermissionCategory[];
|
||||
}, [overview?.project, searchValue]);
|
||||
|
||||
const environmentCategories = useMemo(() => {
|
||||
const categories = getCategorizedProjectPermissions(
|
||||
overview?.environment ?? [],
|
||||
) as IAccessOverviewPermissionCategory[];
|
||||
|
||||
return categories
|
||||
.map((category) => filterCategory(category, searchValue))
|
||||
.filter(Boolean) as IAccessOverviewPermissionCategory[];
|
||||
}, [overview?.environment, searchValue]);
|
||||
|
||||
return (
|
||||
<PageContent
|
||||
@ -128,20 +169,24 @@ export const AccessOverview = () => {
|
||||
}
|
||||
>
|
||||
<StyledAccessOverviewContainer>
|
||||
<AccessOverviewAccordion categories={rootCategories}>
|
||||
Root permissions for role {rootRole?.name}
|
||||
</AccessOverviewAccordion>
|
||||
<AccessOverviewAccordion categories={projectCategories}>
|
||||
Project permissions
|
||||
{project
|
||||
? ` for project ${project}${projectRoles?.length ? ` with project role${projectRoles.length !== 1 ? 's' : ''} ${projectRoles?.map((role: any) => role.name).join(', ')}` : ''}`
|
||||
: ''}
|
||||
</AccessOverviewAccordion>
|
||||
{environment && (
|
||||
<AccessOverviewAccordion categories={environmentCategories}>
|
||||
Environment permissions for {environment}
|
||||
<SearchHighlightProvider value={searchValue}>
|
||||
<AccessOverviewAccordion categories={rootCategories}>
|
||||
Root permissions for role {rootRole?.name}
|
||||
</AccessOverviewAccordion>
|
||||
)}
|
||||
<AccessOverviewAccordion categories={projectCategories}>
|
||||
Project permissions
|
||||
{project
|
||||
? ` for project ${project}${projectRoles?.length ? ` with project role${projectRoles.length !== 1 ? 's' : ''} ${projectRoles?.map((role: any) => role.name).join(', ')}` : ''}`
|
||||
: ''}
|
||||
</AccessOverviewAccordion>
|
||||
{environment && (
|
||||
<AccessOverviewAccordion
|
||||
categories={environmentCategories}
|
||||
>
|
||||
Environment permissions for {environment}
|
||||
</AccessOverviewAccordion>
|
||||
)}
|
||||
</SearchHighlightProvider>
|
||||
</StyledAccessOverviewContainer>
|
||||
</PageContent>
|
||||
);
|
||||
|
@ -1,6 +1,8 @@
|
||||
import Check from '@mui/icons-material/Check';
|
||||
import Close from '@mui/icons-material/Close';
|
||||
import { Box, styled } from '@mui/material';
|
||||
import { Highlighter } from 'component/common/Highlighter/Highlighter';
|
||||
import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
||||
import type {
|
||||
IAccessOverviewPermission,
|
||||
IPermissionCategory,
|
||||
@ -53,29 +55,37 @@ export const AccessOverviewList = ({
|
||||
categories,
|
||||
}: {
|
||||
categories: IAccessOverviewPermissionCategory[];
|
||||
}) => (
|
||||
<Box sx={{ maxHeight: 500, overflow: 'auto' }}>
|
||||
<StyledList>
|
||||
{categories.map((category) => (
|
||||
<>
|
||||
<li key={category.label}>
|
||||
<strong>{category.label}</strong>
|
||||
</li>
|
||||
<StyledList>
|
||||
{category.permissions.map((permission) => (
|
||||
<li key={permission.name}>
|
||||
<div>{permission.displayName}</div>
|
||||
<PermissionStatus
|
||||
hasPermission={permission.hasPermission}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</StyledList>
|
||||
</>
|
||||
))}
|
||||
</StyledList>
|
||||
</Box>
|
||||
);
|
||||
}) => {
|
||||
const { searchQuery } = useSearchHighlightContext();
|
||||
|
||||
return (
|
||||
<Box sx={{ maxHeight: 500, overflow: 'auto' }}>
|
||||
<StyledList>
|
||||
{categories.map((category) => (
|
||||
<>
|
||||
<li key={category.label}>
|
||||
<strong>{category.label}</strong>
|
||||
</li>
|
||||
<StyledList>
|
||||
{category.permissions.map((permission) => (
|
||||
<li key={permission.name}>
|
||||
<div>
|
||||
<Highlighter search={searchQuery}>
|
||||
{permission.displayName}
|
||||
</Highlighter>
|
||||
</div>
|
||||
<PermissionStatus
|
||||
hasPermission={permission.hasPermission}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</StyledList>
|
||||
</>
|
||||
))}
|
||||
</StyledList>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const PermissionStatus = ({ hasPermission }: { hasPermission: boolean }) => (
|
||||
<StyledPermissionStatus hasPermission={hasPermission}>
|
||||
|
Loading…
Reference in New Issue
Block a user