mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-28 17:55:15 +02:00
* fix: remove group owner concept * fix: adapt e2e tests accordingly * refactor users select to match improvement * refactor: add user -> edit users * feat: add edit users to group card actions * add a few more UI improvements * fix: edit group users icon * improve loading behaviour * fix group users refresh on card view * improvement: create group form validation * fix edit group, some refactoring * fix: e2e tests, minor bugs * fix: infinite re-renders due to useHiddenColumns useEffect array dependency * fix re-rendering on useHiddenColumns for some tables * refactor: validations into functions / variables
142 lines
5.6 KiB
TypeScript
142 lines
5.6 KiB
TypeScript
import { useEffect, useMemo, useState, VFC } from 'react';
|
|
import { useGroups } from 'hooks/api/getters/useGroups/useGroups';
|
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
|
import { IGroup } from 'interfaces/group';
|
|
import { PageContent } from 'component/common/PageContent/PageContent';
|
|
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
import { Search } from 'component/common/Search/Search';
|
|
import { Grid, useMediaQuery } from '@mui/material';
|
|
import theme from 'themes/theme';
|
|
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
|
import { TablePlaceholder } from 'component/common/Table';
|
|
import { GroupCard } from './GroupCard/GroupCard';
|
|
import { GroupEmpty } from './GroupEmpty/GroupEmpty';
|
|
import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
|
|
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
|
import { Add } from '@mui/icons-material';
|
|
import { NAVIGATE_TO_CREATE_GROUP } from 'utils/testIds';
|
|
|
|
type PageQueryType = Partial<Record<'search', string>>;
|
|
|
|
const groupsSearch = (group: IGroup, searchValue: string) => {
|
|
const search = searchValue.toLowerCase();
|
|
const users = {
|
|
names: group.users?.map(user => user.name?.toLowerCase() || ''),
|
|
usernames: group.users?.map(user => user.username?.toLowerCase() || ''),
|
|
emails: group.users?.map(user => user.email?.toLowerCase() || ''),
|
|
};
|
|
return (
|
|
group.name.toLowerCase().includes(search) ||
|
|
group.description.toLowerCase().includes(search) ||
|
|
users.names?.some(name => name.includes(search)) ||
|
|
users.usernames?.some(username => username.includes(search)) ||
|
|
users.emails?.some(email => email.includes(search))
|
|
);
|
|
};
|
|
|
|
export const GroupsList: VFC = () => {
|
|
const navigate = useNavigate();
|
|
const { groups = [], loading } = useGroups();
|
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
const [searchValue, setSearchValue] = useState(
|
|
searchParams.get('search') || ''
|
|
);
|
|
|
|
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
|
|
|
useEffect(() => {
|
|
const tableState: PageQueryType = {};
|
|
if (searchValue) {
|
|
tableState.search = searchValue;
|
|
}
|
|
|
|
setSearchParams(tableState, {
|
|
replace: true,
|
|
});
|
|
}, [searchValue, setSearchParams]);
|
|
|
|
const data = useMemo(() => {
|
|
const sortedGroups = groups.sort((a, b) =>
|
|
a.name.localeCompare(b.name)
|
|
);
|
|
return searchValue
|
|
? sortedGroups.filter(group => groupsSearch(group, searchValue))
|
|
: sortedGroups;
|
|
}, [groups, searchValue]);
|
|
|
|
return (
|
|
<PageContent
|
|
isLoading={loading}
|
|
header={
|
|
<PageHeader
|
|
title={`Groups (${data.length})`}
|
|
actions={
|
|
<>
|
|
<ConditionallyRender
|
|
condition={!isSmallScreen}
|
|
show={
|
|
<>
|
|
<Search
|
|
initialValue={searchValue}
|
|
onChange={setSearchValue}
|
|
/>
|
|
<PageHeader.Divider />
|
|
</>
|
|
}
|
|
/>
|
|
<ResponsiveButton
|
|
onClick={() =>
|
|
navigate('/admin/groups/create-group')
|
|
}
|
|
maxWidth="700px"
|
|
Icon={Add}
|
|
permission={ADMIN}
|
|
data-testid={NAVIGATE_TO_CREATE_GROUP}
|
|
>
|
|
New group
|
|
</ResponsiveButton>
|
|
</>
|
|
}
|
|
>
|
|
<ConditionallyRender
|
|
condition={isSmallScreen}
|
|
show={
|
|
<Search
|
|
initialValue={searchValue}
|
|
onChange={setSearchValue}
|
|
/>
|
|
}
|
|
/>
|
|
</PageHeader>
|
|
}
|
|
>
|
|
<SearchHighlightProvider value={searchValue}>
|
|
<Grid container spacing={2}>
|
|
{data.map(group => (
|
|
<Grid key={group.id} item xs={12} md={6}>
|
|
<GroupCard group={group} />
|
|
</Grid>
|
|
))}
|
|
</Grid>
|
|
</SearchHighlightProvider>
|
|
<ConditionallyRender
|
|
condition={!loading && data.length === 0}
|
|
show={
|
|
<ConditionallyRender
|
|
condition={searchValue?.length > 0}
|
|
show={
|
|
<TablePlaceholder>
|
|
No groups found matching “
|
|
{searchValue}
|
|
”
|
|
</TablePlaceholder>
|
|
}
|
|
elseShow={<GroupEmpty />}
|
|
/>
|
|
}
|
|
/>
|
|
</PageContent>
|
|
);
|
|
};
|