1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-04 00:18:01 +01:00

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)
This commit is contained in:
Nuno Góis 2023-09-08 11:42:58 +01:00 committed by GitHub
parent 10a62642d7
commit 61174a1d9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 37 deletions

View File

@ -179,7 +179,7 @@ export const GroupForm: FC<IGroupForm> = ({
<StyledInputDescription>
<Box sx={{ display: 'flex' }}>
Do you want to associate a root role with this group?
<HelpIcon tooltip="When you associate an Admin or Editor role with this group, users in this group will automatically inherit the role globally. Note that groups with a root role association cannot be assigned to projects." />
<HelpIcon tooltip="When you associate a root role with this group, users in this group will automatically inherit the role globally." />
</Box>
</StyledInputDescription>
<StyledAutocompleteWrapper>

View File

@ -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))

View File

@ -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 (
<StyledDescription tooltip={tooltip} {...rest}>
<StyledDescriptionHeader sx={{ mb: 0 }}>
<StyledRoleHeader>
<StyledSupervisedUserCircle color="disabled" />
{name}
</StyledDescriptionHeader>
</StyledRoleHeader>
<StyledDescriptionSubHeader>
{description}
</StyledDescriptionSubHeader>
<ConditionallyRender
condition={!PREDEFINED_ROLE_TYPES.includes(role.type)}
show={() =>
categories.map(({ label, permissions }) => (
<StyledDescriptionBlock key={label}>
<StyledDescriptionHeader>
{label}
</StyledDescriptionHeader>
{permissions.map(permission => (
<p key={permission.id}>
{permission.displayName}
</p>
show={() => (
<>
<StyledPermissionsLabel>
Role permissions:
</StyledPermissionsLabel>
<StyledPermissions>
{categories.map(({ label, permissions }) => (
<div key={label}>
<StyledDescriptionHeader>
{label}
</StyledDescriptionHeader>
<StyledPermissionsList>
{permissions.map(permission => (
<li key={permission.id}>
{permission.displayName}
</li>
))}
</StyledPermissionsList>
</div>
))}
</StyledDescriptionBlock>
))
}
</StyledPermissions>
</>
)}
/>
</StyledDescription>
);

View File

@ -98,6 +98,11 @@ export const ProjectAccessTable: VFC = () => {
const [groupOpen, setGroupOpen] = useState(false);
const [selectedRow, setSelectedRow] = useState<IProjectAccess>();
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:'}
<RoleCell
value={roleText(selectedRow?.entity.roles || [])}
roles={selectedRow?.entity.roles || []}
/>
</>
}
onEdit={() => {
navigate(`edit/group/${selectedRow?.entity.id}`);
}}

View File

@ -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<React.SetStateAction<boolean>>;
group: IGroup;
projectId: string;
subtitle: string;
subtitle: React.ReactNode;
onEdit: () => void;
onRemove: () => void;
}