mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +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:
parent
10a62642d7
commit
61174a1d9c
@ -179,7 +179,7 @@ export const GroupForm: FC<IGroupForm> = ({
|
|||||||
<StyledInputDescription>
|
<StyledInputDescription>
|
||||||
<Box sx={{ display: 'flex' }}>
|
<Box sx={{ display: 'flex' }}>
|
||||||
Do you want to associate a root role with this group?
|
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>
|
</Box>
|
||||||
</StyledInputDescription>
|
</StyledInputDescription>
|
||||||
<StyledAutocompleteWrapper>
|
<StyledAutocompleteWrapper>
|
||||||
|
@ -30,7 +30,7 @@ const groupsSearch = (group: IGroup, searchValue: string) => {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
group.name.toLowerCase().includes(search) ||
|
group.name.toLowerCase().includes(search) ||
|
||||||
group.description.toLowerCase().includes(search) ||
|
group.description?.toLowerCase().includes(search) ||
|
||||||
users.names?.some(name => name.includes(search)) ||
|
users.names?.some(name => name.includes(search)) ||
|
||||||
users.usernames?.some(username => username.includes(search)) ||
|
users.usernames?.some(username => username.includes(search)) ||
|
||||||
users.emails?.some(email => email.includes(search))
|
users.emails?.some(email => email.includes(search))
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { SxProps, Theme, styled } from '@mui/material';
|
import { SxProps, Theme, styled } from '@mui/material';
|
||||||
|
import { SupervisedUserCircle } from '@mui/icons-material';
|
||||||
import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender';
|
||||||
import { useRole } from 'hooks/api/getters/useRole/useRole';
|
import { useRole } from 'hooks/api/getters/useRole/useRole';
|
||||||
import {
|
import {
|
||||||
@ -24,20 +25,45 @@ const StyledDescription = styled('div', {
|
|||||||
borderRadius: tooltip ? 0 : theme.shape.borderRadiusMedium,
|
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),
|
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,
|
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,
|
fontWeight: theme.fontWeight.bold,
|
||||||
marginBottom: theme.spacing(1),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledDescriptionSubHeader = styled('p')(({ theme }) => ({
|
const StyledDescriptionSubHeader = styled('p')(({ theme }) => ({
|
||||||
fontSize: theme.fontSizes.smallBody,
|
marginTop: theme.spacing(0.5),
|
||||||
marginTop: theme.spacing(1),
|
}));
|
||||||
|
|
||||||
|
const StyledPermissionsList = styled('ul')(({ theme }) => ({
|
||||||
|
margin: 0,
|
||||||
|
paddingLeft: theme.spacing(2),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface IRoleDescriptionProps {
|
interface IRoleDescriptionProps {
|
||||||
@ -66,28 +92,38 @@ export const RoleDescription = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledDescription tooltip={tooltip} {...rest}>
|
<StyledDescription tooltip={tooltip} {...rest}>
|
||||||
<StyledDescriptionHeader sx={{ mb: 0 }}>
|
<StyledRoleHeader>
|
||||||
|
<StyledSupervisedUserCircle color="disabled" />
|
||||||
{name}
|
{name}
|
||||||
</StyledDescriptionHeader>
|
</StyledRoleHeader>
|
||||||
<StyledDescriptionSubHeader>
|
<StyledDescriptionSubHeader>
|
||||||
{description}
|
{description}
|
||||||
</StyledDescriptionSubHeader>
|
</StyledDescriptionSubHeader>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={!PREDEFINED_ROLE_TYPES.includes(role.type)}
|
condition={!PREDEFINED_ROLE_TYPES.includes(role.type)}
|
||||||
show={() =>
|
show={() => (
|
||||||
categories.map(({ label, permissions }) => (
|
<>
|
||||||
<StyledDescriptionBlock key={label}>
|
<StyledPermissionsLabel>
|
||||||
<StyledDescriptionHeader>
|
Role permissions:
|
||||||
{label}
|
</StyledPermissionsLabel>
|
||||||
</StyledDescriptionHeader>
|
<StyledPermissions>
|
||||||
{permissions.map(permission => (
|
{categories.map(({ label, permissions }) => (
|
||||||
<p key={permission.id}>
|
<div key={label}>
|
||||||
{permission.displayName}
|
<StyledDescriptionHeader>
|
||||||
</p>
|
{label}
|
||||||
|
</StyledDescriptionHeader>
|
||||||
|
<StyledPermissionsList>
|
||||||
|
{permissions.map(permission => (
|
||||||
|
<li key={permission.id}>
|
||||||
|
{permission.displayName}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</StyledPermissionsList>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</StyledDescriptionBlock>
|
</StyledPermissions>
|
||||||
))
|
</>
|
||||||
}
|
)}
|
||||||
/>
|
/>
|
||||||
</StyledDescription>
|
</StyledDescription>
|
||||||
);
|
);
|
||||||
|
@ -98,6 +98,11 @@ export const ProjectAccessTable: VFC = () => {
|
|||||||
const [groupOpen, setGroupOpen] = useState(false);
|
const [groupOpen, setGroupOpen] = useState(false);
|
||||||
const [selectedRow, setSelectedRow] = useState<IProjectAccess>();
|
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(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@ -150,14 +155,7 @@ export const ProjectAccessTable: VFC = () => {
|
|||||||
{
|
{
|
||||||
id: 'role',
|
id: 'role',
|
||||||
Header: 'Role',
|
Header: 'Role',
|
||||||
accessor: (row: IProjectAccess) =>
|
accessor: (row: IProjectAccess) => roleText(row.entity.roles),
|
||||||
row.entity.roles
|
|
||||||
? row.entity.roles.length > 1
|
|
||||||
? `${row.entity.roles.length} roles`
|
|
||||||
: access?.roles.find(
|
|
||||||
({ id }) => id === row.entity.roleId
|
|
||||||
)?.name
|
|
||||||
: 'No Roles!',
|
|
||||||
Cell: ({
|
Cell: ({
|
||||||
value,
|
value,
|
||||||
row: { original: row },
|
row: { original: row },
|
||||||
@ -490,11 +488,17 @@ export const ProjectAccessTable: VFC = () => {
|
|||||||
setOpen={setGroupOpen}
|
setOpen={setGroupOpen}
|
||||||
group={selectedRow?.entity as IGroup}
|
group={selectedRow?.entity as IGroup}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
subtitle={`Role: ${
|
subtitle={
|
||||||
access?.roles.find(
|
<>
|
||||||
({ id }) => id === selectedRow?.entity.roleId
|
{selectedRow && selectedRow.entity.roles.length > 1
|
||||||
)?.name
|
? 'Roles:'
|
||||||
}`}
|
: 'Role:'}
|
||||||
|
<RoleCell
|
||||||
|
value={roleText(selectedRow?.entity.roles || [])}
|
||||||
|
roles={selectedRow?.entity.roles || []}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
onEdit={() => {
|
onEdit={() => {
|
||||||
navigate(`edit/group/${selectedRow?.entity.id}`);
|
navigate(`edit/group/${selectedRow?.entity.id}`);
|
||||||
}}
|
}}
|
||||||
|
@ -41,6 +41,8 @@ const StyledTitle = styled('div')(({ theme }) => ({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
'& > span': {
|
'& > span': {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
fontSize: theme.fontSizes.bodySize,
|
fontSize: theme.fontSizes.bodySize,
|
||||||
},
|
},
|
||||||
@ -113,7 +115,7 @@ interface IProjectGroupViewProps {
|
|||||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
group: IGroup;
|
group: IGroup;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
subtitle: string;
|
subtitle: React.ReactNode;
|
||||||
onEdit: () => void;
|
onEdit: () => void;
|
||||||
onRemove: () => void;
|
onRemove: () => void;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user