mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Feat/groups refinements (#1197)
* Improvements * Double icon for group * Hide columns * Refinements * Refinements * Reduce padding * Add projectId * Fixes * Make useHiddenColumns component
This commit is contained in:
		
							parent
							
								
									6eb3922741
								
							
						
					
					
						commit
						c99470ec4e
					
				| @ -18,18 +18,19 @@ import { CopyApiTokenButton } from 'component/admin/apiToken/CopyApiTokenButton/ | |||||||
| import { RemoveApiTokenButton } from 'component/admin/apiToken/RemoveApiTokenButton/RemoveApiTokenButton'; | import { RemoveApiTokenButton } from 'component/admin/apiToken/RemoveApiTokenButton/RemoveApiTokenButton'; | ||||||
| import { DateCell } from 'component/common/Table/cells/DateCell/DateCell'; | import { DateCell } from 'component/common/Table/cells/DateCell/DateCell'; | ||||||
| import { sortTypes } from 'utils/sortTypes'; | import { sortTypes } from 'utils/sortTypes'; | ||||||
| import { useEffect, useMemo } from 'react'; | import { useMemo } from 'react'; | ||||||
| import theme from 'themes/theme'; | import theme from 'themes/theme'; | ||||||
| import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; | import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; | ||||||
| import { ProjectsList } from 'component/admin/apiToken/ProjectsList/ProjectsList'; | import { ProjectsList } from 'component/admin/apiToken/ProjectsList/ProjectsList'; | ||||||
| import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | ||||||
| import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell'; | import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell'; | ||||||
| import { Search } from 'component/common/Search/Search'; | import { Search } from 'component/common/Search/Search'; | ||||||
|  | import useHiddenColumns from 'hooks/useHiddenColumns'; | ||||||
| 
 | 
 | ||||||
| export const ApiTokenTable = () => { | export const ApiTokenTable = () => { | ||||||
|     const { tokens, loading } = useApiTokens(); |     const { tokens, loading } = useApiTokens(); | ||||||
|     const hiddenColumns = useHiddenColumns(); |  | ||||||
|     const initialState = useMemo(() => ({ sortBy: [{ id: 'createdAt' }] }), []); |     const initialState = useMemo(() => ({ sortBy: [{ id: 'createdAt' }] }), []); | ||||||
|  |     const { uiConfig } = useUiConfig(); | ||||||
| 
 | 
 | ||||||
|     const { |     const { | ||||||
|         getTableProps, |         getTableProps, | ||||||
| @ -52,9 +53,16 @@ export const ApiTokenTable = () => { | |||||||
|         useSortBy |         useSortBy | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     useEffect(() => { |     useHiddenColumns( | ||||||
|         setHiddenColumns(hiddenColumns); |         setHiddenColumns, | ||||||
|     }, [setHiddenColumns, hiddenColumns]); |         ['Icon', 'createdAt'], | ||||||
|  |         useMediaQuery(theme.breakpoints.down('md')) | ||||||
|  |     ); | ||||||
|  |     useHiddenColumns( | ||||||
|  |         setHiddenColumns, | ||||||
|  |         ['projects', 'environment'], | ||||||
|  |         !uiConfig.flags.E | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <PageContent |         <PageContent | ||||||
| @ -124,27 +132,6 @@ export const ApiTokenTable = () => { | |||||||
|     ); |     ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const useHiddenColumns = (): string[] => { |  | ||||||
|     const { uiConfig } = useUiConfig(); |  | ||||||
|     const isMediumScreen = useMediaQuery(theme.breakpoints.down('md')); |  | ||||||
| 
 |  | ||||||
|     return useMemo(() => { |  | ||||||
|         const hidden: string[] = []; |  | ||||||
| 
 |  | ||||||
|         if (!uiConfig.flags.E) { |  | ||||||
|             hidden.push('projects'); |  | ||||||
|             hidden.push('environment'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (isMediumScreen) { |  | ||||||
|             hidden.push('Icon'); |  | ||||||
|             hidden.push('createdAt'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return hidden; |  | ||||||
|     }, [uiConfig, isMediumScreen]); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const COLUMNS = [ | const COLUMNS = [ | ||||||
|     { |     { | ||||||
|         id: 'Icon', |         id: 'Icon', | ||||||
|  | |||||||
| @ -1,13 +1,12 @@ | |||||||
| import { useEffect, useMemo, useState, VFC } from 'react'; | import { useEffect, useMemo, useState, VFC } from 'react'; | ||||||
| import { | import { | ||||||
|     Button, |  | ||||||
|     IconButton, |     IconButton, | ||||||
|     styled, |     styled, | ||||||
|     Tooltip, |     Tooltip, | ||||||
|     useMediaQuery, |     useMediaQuery, | ||||||
|     useTheme, |     useTheme, | ||||||
| } from '@mui/material'; | } from '@mui/material'; | ||||||
| import { useSearchParams } from 'react-router-dom'; | import { useSearchParams, Link } from 'react-router-dom'; | ||||||
| import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table'; | import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table'; | ||||||
| import { TablePlaceholder, VirtualizedTable } from 'component/common/Table'; | import { TablePlaceholder, VirtualizedTable } from 'component/common/Table'; | ||||||
| import { useGroup } from 'hooks/api/getters/useGroup/useGroup'; | import { useGroup } from 'hooks/api/getters/useGroup/useGroup'; | ||||||
| @ -26,17 +25,17 @@ import { HighlightCell } from 'component/common/Table/cells/HighlightCell/Highli | |||||||
| import { TimeAgoCell } from 'component/common/Table/cells/TimeAgoCell/TimeAgoCell'; | import { TimeAgoCell } from 'component/common/Table/cells/TimeAgoCell/TimeAgoCell'; | ||||||
| import { GroupUserRoleCell } from 'component/admin/groups/GroupUserRoleCell/GroupUserRoleCell'; | import { GroupUserRoleCell } from 'component/admin/groups/GroupUserRoleCell/GroupUserRoleCell'; | ||||||
| import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; | import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; | ||||||
| import { Delete, Edit } from '@mui/icons-material'; | import { Add, Delete, Edit } from '@mui/icons-material'; | ||||||
| import { ADMIN } from 'component/providers/AccessProvider/permissions'; | import { ADMIN } from 'component/providers/AccessProvider/permissions'; | ||||||
| import { MainHeader } from 'component/common/MainHeader/MainHeader'; | import { MainHeader } from 'component/common/MainHeader/MainHeader'; | ||||||
| import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; | import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; | ||||||
| import { RemoveGroup } from 'component/admin/groups/RemoveGroup/RemoveGroup'; | import { RemoveGroup } from 'component/admin/groups/RemoveGroup/RemoveGroup'; | ||||||
| import { Link } from 'react-router-dom'; |  | ||||||
| import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell'; | import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell'; | ||||||
| import { AddGroupUser } from './AddGroupUser/AddGroupUser'; | import { AddGroupUser } from './AddGroupUser/AddGroupUser'; | ||||||
| import { EditGroupUser } from './EditGroupUser/EditGroupUser'; | import { EditGroupUser } from './EditGroupUser/EditGroupUser'; | ||||||
| import { RemoveGroupUser } from './RemoveGroupUser/RemoveGroupUser'; | import { RemoveGroupUser } from './RemoveGroupUser/RemoveGroupUser'; | ||||||
| import { UserAvatar } from 'component/common/UserAvatar/UserAvatar'; | import { UserAvatar } from 'component/common/UserAvatar/UserAvatar'; | ||||||
|  | import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton'; | ||||||
| import { | import { | ||||||
|     UG_EDIT_BTN_ID, |     UG_EDIT_BTN_ID, | ||||||
|     UG_DELETE_BTN_ID, |     UG_DELETE_BTN_ID, | ||||||
| @ -140,30 +139,36 @@ export const Group: VFC = () => { | |||||||
|                 Cell: ({ row: { original: rowUser } }: any) => ( |                 Cell: ({ row: { original: rowUser } }: any) => ( | ||||||
|                     <ActionCell> |                     <ActionCell> | ||||||
|                         <Tooltip title="Edit user" arrow describeChild> |                         <Tooltip title="Edit user" arrow describeChild> | ||||||
|                             <IconButton |                             <span> | ||||||
|                                 data-testid={`${UG_EDIT_USER_BTN_ID}-${rowUser.id}`} |                                 <IconButton | ||||||
|                                 onClick={() => { |                                     data-testid={`${UG_EDIT_USER_BTN_ID}-${rowUser.id}`} | ||||||
|                                     setSelectedUser(rowUser); |                                     disabled={group?.users.length === 1} | ||||||
|                                     setEditUserOpen(true); |                                     onClick={() => { | ||||||
|                                 }} |                                         setSelectedUser(rowUser); | ||||||
|                             > |                                         setEditUserOpen(true); | ||||||
|                                 <Edit /> |                                     }} | ||||||
|                             </IconButton> |                                 > | ||||||
|  |                                     <Edit /> | ||||||
|  |                                 </IconButton> | ||||||
|  |                             </span> | ||||||
|                         </Tooltip> |                         </Tooltip> | ||||||
|                         <Tooltip |                         <Tooltip | ||||||
|                             title="Remove user from group" |                             title="Remove user from group" | ||||||
|                             arrow |                             arrow | ||||||
|                             describeChild |                             describeChild | ||||||
|                         > |                         > | ||||||
|                             <IconButton |                             <span> | ||||||
|                                 data-testid={`${UG_REMOVE_USER_BTN_ID}-${rowUser.id}`} |                                 <IconButton | ||||||
|                                 onClick={() => { |                                     data-testid={`${UG_REMOVE_USER_BTN_ID}-${rowUser.id}`} | ||||||
|                                     setSelectedUser(rowUser); |                                     disabled={group?.users.length === 1} | ||||||
|                                     setRemoveUserOpen(true); |                                     onClick={() => { | ||||||
|                                 }} |                                         setSelectedUser(rowUser); | ||||||
|                             > |                                         setRemoveUserOpen(true); | ||||||
|                                 <Delete /> |                                     }} | ||||||
|                             </IconButton> |                                 > | ||||||
|  |                                     <Delete /> | ||||||
|  |                                 </IconButton> | ||||||
|  |                             </span> | ||||||
|                         </Tooltip> |                         </Tooltip> | ||||||
|                     </ActionCell> |                     </ActionCell> | ||||||
|                 ), |                 ), | ||||||
| @ -171,7 +176,7 @@ export const Group: VFC = () => { | |||||||
|                 disableSortBy: true, |                 disableSortBy: true, | ||||||
|             }, |             }, | ||||||
|         ], |         ], | ||||||
|         [setSelectedUser, setRemoveUserOpen] |         [setSelectedUser, setRemoveUserOpen, group?.users.length] | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     const [searchParams, setSearchParams] = useSearchParams(); |     const [searchParams, setSearchParams] = useSearchParams(); | ||||||
| @ -306,16 +311,17 @@ export const Group: VFC = () => { | |||||||
|                                                 </> |                                                 </> | ||||||
|                                             } |                                             } | ||||||
|                                         /> |                                         /> | ||||||
|                                         <Button |                                         <ResponsiveButton | ||||||
|                                             data-testid={UG_ADD_USER_BTN_ID} |                                             data-testid={UG_ADD_USER_BTN_ID} | ||||||
|                                             variant="contained" |  | ||||||
|                                             color="primary" |  | ||||||
|                                             onClick={() => { |                                             onClick={() => { | ||||||
|                                                 setAddUserOpen(true); |                                                 setAddUserOpen(true); | ||||||
|                                             }} |                                             }} | ||||||
|  |                                             maxWidth="700px" | ||||||
|  |                                             Icon={Add} | ||||||
|  |                                             permission={ADMIN} | ||||||
|                                         > |                                         > | ||||||
|                                             Add user |                                             Add user | ||||||
|                                         </Button> |                                         </ResponsiveButton> | ||||||
|                                     </> |                                     </> | ||||||
|                                 } |                                 } | ||||||
|                             > |                             > | ||||||
| @ -376,13 +382,13 @@ export const Group: VFC = () => { | |||||||
|                         <EditGroupUser |                         <EditGroupUser | ||||||
|                             open={editUserOpen} |                             open={editUserOpen} | ||||||
|                             setOpen={setEditUserOpen} |                             setOpen={setEditUserOpen} | ||||||
|                             user={selectedUser!} |                             user={selectedUser} | ||||||
|                             group={group!} |                             group={group!} | ||||||
|                         /> |                         /> | ||||||
|                         <RemoveGroupUser |                         <RemoveGroupUser | ||||||
|                             open={removeUserOpen} |                             open={removeUserOpen} | ||||||
|                             setOpen={setRemoveUserOpen} |                             setOpen={setRemoveUserOpen} | ||||||
|                             user={selectedUser!} |                             user={selectedUser} | ||||||
|                             group={group!} |                             group={group!} | ||||||
|                         /> |                         /> | ||||||
|                     </PageContent> |                     </PageContent> | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import { useMemo, VFC } from 'react'; | import { useMemo, VFC } from 'react'; | ||||||
| import { IconButton, Tooltip } from '@mui/material'; | import { IconButton, Tooltip, useMediaQuery } from '@mui/material'; | ||||||
| import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; | import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; | ||||||
| import { IGroupUser } from 'interfaces/group'; | import { IGroupUser } from 'interfaces/group'; | ||||||
| import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell'; | import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell'; | ||||||
| @ -11,6 +11,8 @@ import { VirtualizedTable } from 'component/common/Table'; | |||||||
| 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 { UserAvatar } from 'component/common/UserAvatar/UserAvatar'; | import { UserAvatar } from 'component/common/UserAvatar/UserAvatar'; | ||||||
|  | import theme from 'themes/theme'; | ||||||
|  | import useHiddenColumns from 'hooks/useHiddenColumns'; | ||||||
| 
 | 
 | ||||||
| interface IGroupFormUsersTableProps { | interface IGroupFormUsersTableProps { | ||||||
|     users: IGroupUser[]; |     users: IGroupUser[]; | ||||||
| @ -106,7 +108,7 @@ export const GroupFormUsersTable: VFC<IGroupFormUsersTableProps> = ({ | |||||||
|         [setUsers] |         [setUsers] | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     const { headerGroups, rows, prepareRow } = useTable( |     const { headerGroups, rows, prepareRow, setHiddenColumns } = useTable( | ||||||
|         { |         { | ||||||
|             columns: columns as any[], |             columns: columns as any[], | ||||||
|             data: users as any[], |             data: users as any[], | ||||||
| @ -119,6 +121,12 @@ export const GroupFormUsersTable: VFC<IGroupFormUsersTableProps> = ({ | |||||||
|         useFlexLayout |         useFlexLayout | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |     useHiddenColumns( | ||||||
|  |         setHiddenColumns, | ||||||
|  |         ['imageUrl', 'name'], | ||||||
|  |         useMediaQuery(theme.breakpoints.down('md')) | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|     return ( |     return ( | ||||||
|         <ConditionallyRender |         <ConditionallyRender | ||||||
|             condition={rows.length > 0} |             condition={rows.length > 0} | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { useEffect, useMemo, useState, VFC } from 'react'; | import { useEffect, useMemo, useState, VFC } from 'react'; | ||||||
| import { useGroups } from 'hooks/api/getters/useGroups/useGroups'; | import { useGroups } from 'hooks/api/getters/useGroups/useGroups'; | ||||||
| import { Link, useSearchParams } from 'react-router-dom'; | import { Link, useNavigate, useSearchParams } from 'react-router-dom'; | ||||||
| import { IGroup } from 'interfaces/group'; | import { IGroup } from 'interfaces/group'; | ||||||
| import { PageContent } from 'component/common/PageContent/PageContent'; | import { PageContent } from 'component/common/PageContent/PageContent'; | ||||||
| import { PageHeader } from 'component/common/PageHeader/PageHeader'; | import { PageHeader } from 'component/common/PageHeader/PageHeader'; | ||||||
| @ -12,6 +12,9 @@ import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightC | |||||||
| import { TablePlaceholder } from 'component/common/Table'; | import { TablePlaceholder } from 'component/common/Table'; | ||||||
| import { GroupCard } from './GroupCard/GroupCard'; | import { GroupCard } from './GroupCard/GroupCard'; | ||||||
| import { GroupEmpty } from './GroupEmpty/GroupEmpty'; | 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'; | import { NAVIGATE_TO_CREATE_GROUP } from 'utils/testIds'; | ||||||
| 
 | 
 | ||||||
| type PageQueryType = Partial<Record<'search', string>>; | type PageQueryType = Partial<Record<'search', string>>; | ||||||
| @ -33,6 +36,7 @@ const groupsSearch = (group: IGroup, searchValue: string) => { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const GroupsList: VFC = () => { | export const GroupsList: VFC = () => { | ||||||
|  |     const navigate = useNavigate(); | ||||||
|     const { groups = [], loading } = useGroups(); |     const { groups = [], loading } = useGroups(); | ||||||
|     const [searchParams, setSearchParams] = useSearchParams(); |     const [searchParams, setSearchParams] = useSearchParams(); | ||||||
|     const [searchValue, setSearchValue] = useState( |     const [searchValue, setSearchValue] = useState( | ||||||
| @ -81,15 +85,17 @@ export const GroupsList: VFC = () => { | |||||||
|                                     </> |                                     </> | ||||||
|                                 } |                                 } | ||||||
|                             /> |                             /> | ||||||
|                             <Button |                             <ResponsiveButton | ||||||
|                                 to="/admin/groups/create-group" |                                 onClick={() => | ||||||
|                                 component={Link} |                                     navigate('/admin/groups/create-group') | ||||||
|                                 variant="contained" |                                 } | ||||||
|                                 color="primary" |                                 maxWidth="700px" | ||||||
|  |                                 Icon={Add} | ||||||
|  |                                 permission={ADMIN} | ||||||
|                                 data-testid={NAVIGATE_TO_CREATE_GROUP} |                                 data-testid={NAVIGATE_TO_CREATE_GROUP} | ||||||
|                             > |                             > | ||||||
|                                 New group |                                 New group | ||||||
|                             </Button> |                             </ResponsiveButton> | ||||||
|                         </> |                         </> | ||||||
|                     } |                     } | ||||||
|                 > |                 > | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ const StyledMainHeader = styled(Paper)(({ theme }) => ({ | |||||||
| 
 | 
 | ||||||
| const StyledTitleHeader = styled('div')(({ theme }) => ({ | const StyledTitleHeader = styled('div')(({ theme }) => ({ | ||||||
|     display: 'flex', |     display: 'flex', | ||||||
|     alignItems: 'center', |     alignItems: 'flex-start', | ||||||
|     justifyContent: 'space-between', |     justifyContent: 'space-between', | ||||||
| })); | })); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -47,8 +47,8 @@ const StyledAutocompleteWrapper = styled('div')(({ theme }) => ({ | |||||||
|     }, |     }, | ||||||
| })); | })); | ||||||
| 
 | 
 | ||||||
| const StyledButtonContainer = styled('div')(() => ({ | const StyledButtonContainer = styled('div')(({ theme }) => ({ | ||||||
|     marginTop: 'auto', |     marginTop: theme.spacing(6), | ||||||
|     display: 'flex', |     display: 'flex', | ||||||
|     justifyContent: 'flex-end', |     justifyContent: 'flex-end', | ||||||
| })); | })); | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ const StyledDescription = styled('div')(({ theme }) => ({ | |||||||
|     padding: theme.spacing(3), |     padding: theme.spacing(3), | ||||||
|     backgroundColor: theme.palette.neutral.light, |     backgroundColor: theme.palette.neutral.light, | ||||||
|     color: theme.palette.text.secondary, |     color: theme.palette.text.secondary, | ||||||
|     fontSize: theme.fontSizes.smallerBody, |     fontSize: theme.fontSizes.smallBody, | ||||||
|     borderRadius: theme.shape.borderRadiusMedium, |     borderRadius: theme.shape.borderRadiusMedium, | ||||||
| })); | })); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| import { useEffect, useMemo, useState, VFC } from 'react'; | import { useEffect, useMemo, useState, VFC } from 'react'; | ||||||
| import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table'; | import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table'; | ||||||
| import { VirtualizedTable, TablePlaceholder } from 'component/common/Table'; | import { VirtualizedTable, TablePlaceholder } from 'component/common/Table'; | ||||||
| import { Button, useMediaQuery, useTheme } from '@mui/material'; | import { styled, useMediaQuery, useTheme } from '@mui/material'; | ||||||
| import { Delete, Edit } from '@mui/icons-material'; | import { Add, Delete, Edit } from '@mui/icons-material'; | ||||||
| import { sortTypes } from 'utils/sortTypes'; | import { sortTypes } from 'utils/sortTypes'; | ||||||
| import useProjectAccess, { | import useProjectAccess, { | ||||||
|     ENTITY_TYPE, |     ENTITY_TYPE, | ||||||
| @ -38,9 +38,11 @@ import { IUser } from 'interfaces/user'; | |||||||
| import { IGroup } from 'interfaces/group'; | import { IGroup } from 'interfaces/group'; | ||||||
| import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; | import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; | ||||||
| import { UserAvatar } from 'component/common/UserAvatar/UserAvatar'; | import { UserAvatar } from 'component/common/UserAvatar/UserAvatar'; | ||||||
|  | import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton'; | ||||||
| import { ProjectAccessCreate } from 'component/project/ProjectAccess/ProjectAccessCreate/ProjectAccessCreate'; | import { ProjectAccessCreate } from 'component/project/ProjectAccess/ProjectAccessCreate/ProjectAccessCreate'; | ||||||
| import { ProjectAccessEditUser } from 'component/project/ProjectAccess/ProjectAccessEditUser/ProjectAccessEditUser'; | import { ProjectAccessEditUser } from 'component/project/ProjectAccess/ProjectAccessEditUser/ProjectAccessEditUser'; | ||||||
| import { ProjectAccessEditGroup } from 'component/project/ProjectAccess/ProjectAccessEditGroup/ProjectAccessEditGroup'; | import { ProjectAccessEditGroup } from 'component/project/ProjectAccess/ProjectAccessEditGroup/ProjectAccessEditGroup'; | ||||||
|  | import useHiddenColumns from 'hooks/useHiddenColumns'; | ||||||
| 
 | 
 | ||||||
| export type PageQueryType = Partial< | export type PageQueryType = Partial< | ||||||
|     Record<'sort' | 'order' | 'search', string> |     Record<'sort' | 'order' | 'search', string> | ||||||
| @ -53,6 +55,20 @@ const { value: storedParams, setValue: setStoredParams } = createLocalStorage( | |||||||
|     defaultSort |     defaultSort | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
|  | const StyledUserAvatars = styled('div')(({ theme }) => ({ | ||||||
|  |     display: 'inline-flex', | ||||||
|  |     alignItems: 'center', | ||||||
|  |     flexWrap: 'wrap', | ||||||
|  |     marginLeft: theme.spacing(1), | ||||||
|  | })); | ||||||
|  | 
 | ||||||
|  | const StyledEmptyAvatar = styled(UserAvatar)(({ theme }) => ({ | ||||||
|  |     marginRight: theme.spacing(-3.5), | ||||||
|  | })); | ||||||
|  | const StyledGroupAvatar = styled(UserAvatar)(({ theme }) => ({ | ||||||
|  |     outline: `${theme.spacing(0.25)} solid ${theme.palette.background.paper}`, | ||||||
|  | })); | ||||||
|  | 
 | ||||||
| export const ProjectAccessTable: VFC = () => { | export const ProjectAccessTable: VFC = () => { | ||||||
|     const projectId = useRequiredPathParam('projectId'); |     const projectId = useRequiredPathParam('projectId'); | ||||||
| 
 | 
 | ||||||
| @ -77,11 +93,15 @@ export const ProjectAccessTable: VFC = () => { | |||||||
|                 Header: 'Avatar', |                 Header: 'Avatar', | ||||||
|                 accessor: 'imageUrl', |                 accessor: 'imageUrl', | ||||||
|                 Cell: ({ row: { original: row } }: any) => ( |                 Cell: ({ row: { original: row } }: any) => ( | ||||||
|                     <TextCell> |                     <StyledUserAvatars> | ||||||
|                         <UserAvatar user={row.entity}> |                         <ConditionallyRender | ||||||
|  |                             condition={row.type === ENTITY_TYPE.GROUP} | ||||||
|  |                             show={<StyledEmptyAvatar />} | ||||||
|  |                         /> | ||||||
|  |                         <StyledGroupAvatar user={row.entity}> | ||||||
|                             {row.entity.users?.length} |                             {row.entity.users?.length} | ||||||
|                         </UserAvatar> |                         </StyledGroupAvatar> | ||||||
|                     </TextCell> |                     </StyledUserAvatars> | ||||||
|                 ), |                 ), | ||||||
|                 maxWidth: 85, |                 maxWidth: 85, | ||||||
|                 disableSortBy: true, |                 disableSortBy: true, | ||||||
| @ -124,6 +144,7 @@ export const ProjectAccessTable: VFC = () => { | |||||||
|                 searchable: true, |                 searchable: true, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|  |                 id: 'role', | ||||||
|                 Header: 'Role', |                 Header: 'Role', | ||||||
|                 accessor: (row: IProjectAccess) => |                 accessor: (row: IProjectAccess) => | ||||||
|                     access?.roles.find(({ id }) => id === row.entity.roleId) |                     access?.roles.find(({ id }) => id === row.entity.roleId) | ||||||
| @ -145,6 +166,7 @@ export const ProjectAccessTable: VFC = () => { | |||||||
|                 maxWidth: 150, |                 maxWidth: 150, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|  |                 id: 'lastLogin', | ||||||
|                 Header: 'Last login', |                 Header: 'Last login', | ||||||
|                 accessor: (row: IProjectAccess) => { |                 accessor: (row: IProjectAccess) => { | ||||||
|                     if (row.type === ENTITY_TYPE.USER) { |                     if (row.type === ENTITY_TYPE.USER) { | ||||||
| @ -240,6 +262,7 @@ export const ProjectAccessTable: VFC = () => { | |||||||
|         headerGroups, |         headerGroups, | ||||||
|         rows, |         rows, | ||||||
|         prepareRow, |         prepareRow, | ||||||
|  |         setHiddenColumns, | ||||||
|         state: { sortBy }, |         state: { sortBy }, | ||||||
|     } = useTable( |     } = useTable( | ||||||
|         { |         { | ||||||
| @ -258,6 +281,12 @@ export const ProjectAccessTable: VFC = () => { | |||||||
|         useFlexLayout |         useFlexLayout | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |     useHiddenColumns( | ||||||
|  |         setHiddenColumns, | ||||||
|  |         ['imageUrl', 'username', 'role', 'added', 'lastLogin'], | ||||||
|  |         isSmallScreen | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|     useEffect(() => { |     useEffect(() => { | ||||||
|         const tableState: PageQueryType = {}; |         const tableState: PageQueryType = {}; | ||||||
|         tableState.sort = sortBy[0].id; |         tableState.sort = sortBy[0].id; | ||||||
| @ -332,14 +361,15 @@ export const ProjectAccessTable: VFC = () => { | |||||||
|                                     </> |                                     </> | ||||||
|                                 } |                                 } | ||||||
|                             /> |                             /> | ||||||
|                             <Button |                             <ResponsiveButton | ||||||
|                                 component={Link} |                                 onClick={() => navigate('create')} | ||||||
|                                 to={`create`} |                                 maxWidth="700px" | ||||||
|                                 variant="contained" |                                 Icon={Add} | ||||||
|                                 color="primary" |                                 permission={UPDATE_PROJECT} | ||||||
|  |                                 projectId={projectId} | ||||||
|                             > |                             > | ||||||
|                                 Assign {entityType} |                                 Assign {entityType} | ||||||
|                             </Button> |                             </ResponsiveButton> | ||||||
|                         </> |                         </> | ||||||
|                     } |                     } | ||||||
|                 > |                 > | ||||||
|  | |||||||
| @ -20,17 +20,22 @@ import { IGroup, IGroupUser } from 'interfaces/group'; | |||||||
| import { VFC, useState } from 'react'; | import { VFC, useState } from 'react'; | ||||||
| import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table'; | import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table'; | ||||||
| import { sortTypes } from 'utils/sortTypes'; | import { sortTypes } from 'utils/sortTypes'; | ||||||
|  | import useHiddenColumns from 'hooks/useHiddenColumns'; | ||||||
| 
 | 
 | ||||||
| const StyledPageContent = styled(PageContent)(({ theme }) => ({ | const StyledPageContent = styled(PageContent)(({ theme }) => ({ | ||||||
|     height: '100vh', |     height: '100vh', | ||||||
|     overflow: 'auto', |     overflow: 'auto', | ||||||
|     padding: theme.spacing(7.5, 6), |     padding: theme.spacing(7.5, 6), | ||||||
|  |     [theme.breakpoints.down('md')]: { | ||||||
|  |         padding: theme.spacing(4, 2), | ||||||
|  |     }, | ||||||
|     '& .header': { |     '& .header': { | ||||||
|         padding: theme.spacing(0, 0, 2, 0), |         padding: theme.spacing(0, 0, 2, 0), | ||||||
|     }, |     }, | ||||||
|     '& .body': { |     '& .body': { | ||||||
|         padding: theme.spacing(3, 0, 0, 0), |         padding: theme.spacing(3, 0, 0, 0), | ||||||
|     }, |     }, | ||||||
|  |     borderRadius: `${theme.spacing(1.5, 0, 0, 1.5)} !important`, | ||||||
| })); | })); | ||||||
| 
 | 
 | ||||||
| const StyledTitle = styled('div')(({ theme }) => ({ | const StyledTitle = styled('div')(({ theme }) => ({ | ||||||
| @ -38,7 +43,7 @@ const StyledTitle = styled('div')(({ theme }) => ({ | |||||||
|     flexDirection: 'column', |     flexDirection: 'column', | ||||||
|     '& > span': { |     '& > span': { | ||||||
|         color: theme.palette.text.secondary, |         color: theme.palette.text.secondary, | ||||||
|         fontSize: theme.fontSizes.smallBody, |         fontSize: theme.fontSizes.bodySize, | ||||||
|     }, |     }, | ||||||
| })); | })); | ||||||
| 
 | 
 | ||||||
| @ -80,6 +85,7 @@ const columns = [ | |||||||
|         filterName: 'type', |         filterName: 'type', | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|  |         id: 'joined', | ||||||
|         Header: 'Joined', |         Header: 'Joined', | ||||||
|         accessor: 'joinedAt', |         accessor: 'joinedAt', | ||||||
|         Cell: DateCell, |         Cell: DateCell, | ||||||
| @ -87,6 +93,7 @@ const columns = [ | |||||||
|         maxWidth: 150, |         maxWidth: 150, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|  |         id: 'lastLogin', | ||||||
|         Header: 'Last login', |         Header: 'Last login', | ||||||
|         accessor: (row: IGroupUser) => row.seenAt || '', |         accessor: (row: IGroupUser) => row.seenAt || '', | ||||||
|         Cell: ({ row: { original: user } }: any) => ( |         Cell: ({ row: { original: user } }: any) => ( | ||||||
| @ -135,7 +142,7 @@ export const ProjectGroupView: VFC<IProjectGroupViewProps> = ({ | |||||||
|         group?.users ?? [] |         group?.users ?? [] | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     const { headerGroups, rows, prepareRow } = useTable( |     const { headerGroups, rows, prepareRow, setHiddenColumns } = useTable( | ||||||
|         { |         { | ||||||
|             columns: columns as any[], |             columns: columns as any[], | ||||||
|             data, |             data, | ||||||
| @ -149,6 +156,12 @@ export const ProjectGroupView: VFC<IProjectGroupViewProps> = ({ | |||||||
|         useFlexLayout |         useFlexLayout | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |     useHiddenColumns( | ||||||
|  |         setHiddenColumns, | ||||||
|  |         ['imageUrl', 'name', 'joined', 'lastLogin'], | ||||||
|  |         useMediaQuery(theme.breakpoints.down('md')) | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|     return ( |     return ( | ||||||
|         <SidebarModal |         <SidebarModal | ||||||
|             open={open} |             open={open} | ||||||
|  | |||||||
							
								
								
									
										15
									
								
								frontend/src/hooks/useHiddenColumns.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								frontend/src/hooks/useHiddenColumns.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | import { useEffect } from 'react'; | ||||||
|  | import { IdType } from 'react-table'; | ||||||
|  | 
 | ||||||
|  | const useHiddenColumns = ( | ||||||
|  |     setHiddenColumns: <D>(param: Array<IdType<D>>) => void, | ||||||
|  |     hiddenColumns: string[], | ||||||
|  |     condition: boolean | ||||||
|  | ) => { | ||||||
|  |     useEffect(() => { | ||||||
|  |         const hidden = condition ? hiddenColumns : []; | ||||||
|  |         setHiddenColumns(hidden); | ||||||
|  |     }, [setHiddenColumns, condition]); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export default useHiddenColumns; | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user