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:
parent
10a62642d7
commit
61174a1d9c
@ -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>
|
||||
|
@ -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))
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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}`);
|
||||
}}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user