From 61174a1d9c8fb50d6944a032d3e5e4c5ce301268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20G=C3=B3is?= Date: Fri, 8 Sep 2023 11:42:58 +0100 Subject: [PATCH] fix: multi project roles UI improvements (#4646) https://linear.app/unleash/issue/2-1373/small-ui-fixes-and-improvements - Update group root role `HelpIcon` tooltip to reflect the new behavior; - Fixes a crash on groups search where `description` could be `undefined`; - Improves `RoleDescription` design; - Fixes the role label in `ProjectGroupView`; ![image](https://github.com/Unleash/unleash/assets/14320932/f16ebe98-9408-4edd-8e2b-1e56ba2ad5c6) --- .../admin/groups/GroupForm/GroupForm.tsx | 2 +- .../admin/groups/GroupsList/GroupsList.tsx | 2 +- .../RoleDescription/RoleDescription.tsx | 78 ++++++++++++++----- .../ProjectAccessTable/ProjectAccessTable.tsx | 30 +++---- .../ProjectGroupView/ProjectGroupView.tsx | 4 +- 5 files changed, 79 insertions(+), 37 deletions(-) diff --git a/frontend/src/component/admin/groups/GroupForm/GroupForm.tsx b/frontend/src/component/admin/groups/GroupForm/GroupForm.tsx index 9fa4765986..1d80b6ad33 100644 --- a/frontend/src/component/admin/groups/GroupForm/GroupForm.tsx +++ b/frontend/src/component/admin/groups/GroupForm/GroupForm.tsx @@ -179,7 +179,7 @@ export const GroupForm: FC = ({ Do you want to associate a root role with this group? - + diff --git a/frontend/src/component/admin/groups/GroupsList/GroupsList.tsx b/frontend/src/component/admin/groups/GroupsList/GroupsList.tsx index 11b1ff1594..025f920fb2 100644 --- a/frontend/src/component/admin/groups/GroupsList/GroupsList.tsx +++ b/frontend/src/component/admin/groups/GroupsList/GroupsList.tsx @@ -30,7 +30,7 @@ const groupsSearch = (group: IGroup, searchValue: string) => { }; return ( group.name.toLowerCase().includes(search) || - group.description.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)) diff --git a/frontend/src/component/common/RoleDescription/RoleDescription.tsx b/frontend/src/component/common/RoleDescription/RoleDescription.tsx index 2ace95f1f4..34c9c6267b 100644 --- a/frontend/src/component/common/RoleDescription/RoleDescription.tsx +++ b/frontend/src/component/common/RoleDescription/RoleDescription.tsx @@ -1,4 +1,5 @@ import { SxProps, Theme, styled } from '@mui/material'; +import { SupervisedUserCircle } from '@mui/icons-material'; import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender'; import { useRole } from 'hooks/api/getters/useRole/useRole'; import { @@ -24,20 +25,45 @@ const StyledDescription = styled('div', { borderRadius: tooltip ? 0 : theme.shape.borderRadiusMedium, })); -const StyledDescriptionBlock = styled('div')(({ theme }) => ({ +const StyledPermissionsLabel = styled('p')(({ theme }) => ({ + color: theme.palette.text.primary, marginTop: theme.spacing(2), + marginBottom: theme.spacing(0.5), })); -const StyledDescriptionHeader = styled('p')(({ theme }) => ({ +const StyledPermissions = styled('div')(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(1), +})); + +const StyledRoleHeader = styled('p')(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(0.5), color: theme.palette.text.primary, - fontSize: theme.fontSizes.smallBody, + fontSize: theme.fontSizes.bodySize, + fontWeight: theme.fontWeight.bold, +})); + +const StyledSupervisedUserCircle = styled(SupervisedUserCircle)( + ({ theme }) => ({ + fontSize: theme.fontSizes.mainHeader, + }) +); + +const StyledDescriptionHeader = styled('p')(({ theme }) => ({ + color: theme.palette.text.secondary, fontWeight: theme.fontWeight.bold, - marginBottom: theme.spacing(1), })); const StyledDescriptionSubHeader = styled('p')(({ theme }) => ({ - fontSize: theme.fontSizes.smallBody, - marginTop: theme.spacing(1), + marginTop: theme.spacing(0.5), +})); + +const StyledPermissionsList = styled('ul')(({ theme }) => ({ + margin: 0, + paddingLeft: theme.spacing(2), })); interface IRoleDescriptionProps { @@ -66,28 +92,38 @@ export const RoleDescription = ({ return ( - + + {name} - + {description} - categories.map(({ label, permissions }) => ( - - - {label} - - {permissions.map(permission => ( -

- {permission.displayName} -

+ show={() => ( + <> + + Role permissions: + + + {categories.map(({ label, permissions }) => ( +
+ + {label} + + + {permissions.map(permission => ( +
  • + {permission.displayName} +
  • + ))} +
    +
    ))} -
    - )) - } + + + )} />
    ); diff --git a/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable.tsx b/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable.tsx index a30a9174b8..c2202d5db5 100644 --- a/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable.tsx +++ b/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable.tsx @@ -98,6 +98,11 @@ export const ProjectAccessTable: VFC = () => { const [groupOpen, setGroupOpen] = useState(false); const [selectedRow, setSelectedRow] = useState(); + const roleText = (roles: number[]): string => + roles.length > 1 + ? `${roles.length} roles` + : access?.roles.find(({ id }) => id === roles[0])?.name || ''; + const columns = useMemo( () => [ { @@ -150,14 +155,7 @@ export const ProjectAccessTable: VFC = () => { { id: 'role', Header: 'Role', - accessor: (row: IProjectAccess) => - row.entity.roles - ? row.entity.roles.length > 1 - ? `${row.entity.roles.length} roles` - : access?.roles.find( - ({ id }) => id === row.entity.roleId - )?.name - : 'No Roles!', + accessor: (row: IProjectAccess) => roleText(row.entity.roles), Cell: ({ value, row: { original: row }, @@ -490,11 +488,17 @@ export const ProjectAccessTable: VFC = () => { setOpen={setGroupOpen} group={selectedRow?.entity as IGroup} projectId={projectId} - subtitle={`Role: ${ - access?.roles.find( - ({ id }) => id === selectedRow?.entity.roleId - )?.name - }`} + subtitle={ + <> + {selectedRow && selectedRow.entity.roles.length > 1 + ? 'Roles:' + : 'Role:'} + + + } onEdit={() => { navigate(`edit/group/${selectedRow?.entity.id}`); }} diff --git a/frontend/src/component/project/ProjectAccess/ProjectGroupView/ProjectGroupView.tsx b/frontend/src/component/project/ProjectAccess/ProjectGroupView/ProjectGroupView.tsx index f9b2c66ba1..b7eef3f289 100644 --- a/frontend/src/component/project/ProjectAccess/ProjectGroupView/ProjectGroupView.tsx +++ b/frontend/src/component/project/ProjectAccess/ProjectGroupView/ProjectGroupView.tsx @@ -41,6 +41,8 @@ const StyledTitle = styled('div')(({ theme }) => ({ display: 'flex', flexDirection: 'column', '& > span': { + display: 'flex', + alignItems: 'center', color: theme.palette.text.secondary, fontSize: theme.fontSizes.bodySize, }, @@ -113,7 +115,7 @@ interface IProjectGroupViewProps { setOpen: React.Dispatch>; group: IGroup; projectId: string; - subtitle: string; + subtitle: React.ReactNode; onEdit: () => void; onRemove: () => void; }