diff --git a/frontend/src/component/admin/groups/Group/Group.tsx b/frontend/src/component/admin/groups/Group/Group.tsx
index d7ccb7d91c..a3330c49e6 100644
--- a/frontend/src/component/admin/groups/Group/Group.tsx
+++ b/frontend/src/component/admin/groups/Group/Group.tsx
@@ -255,7 +255,7 @@ export const Group: VFC = () => {
onClick={() => setRemoveOpen(true)}
permission={ADMIN}
tooltipProps={{
- title: 'Remove group',
+ title: 'Delete group',
}}
>
diff --git a/frontend/src/component/admin/groups/GroupForm/GroupForm.tsx b/frontend/src/component/admin/groups/GroupForm/GroupForm.tsx
index d5ef3190c3..6e6e2d9bb2 100644
--- a/frontend/src/component/admin/groups/GroupForm/GroupForm.tsx
+++ b/frontend/src/component/admin/groups/GroupForm/GroupForm.tsx
@@ -81,6 +81,7 @@ export const GroupForm: FC = ({
value={name}
onChange={e => setName(e.target.value)}
data-testid={UG_NAME_ID}
+ required
/>
How would you describe your group?
diff --git a/frontend/src/component/admin/groups/GroupUserRoleCell/GroupUserRoleCell.tsx b/frontend/src/component/admin/groups/GroupUserRoleCell/GroupUserRoleCell.tsx
index 3d26d2d3a5..6382e56dc7 100644
--- a/frontend/src/component/admin/groups/GroupUserRoleCell/GroupUserRoleCell.tsx
+++ b/frontend/src/component/admin/groups/GroupUserRoleCell/GroupUserRoleCell.tsx
@@ -2,25 +2,11 @@ import { capitalize, MenuItem, Select, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
import { Role } from 'interfaces/group';
+import { Badge } from 'component/common/Badge/Badge';
+import { StarRounded } from '@mui/icons-material';
-const StyledBadge = styled('div')(({ theme }) => ({
- padding: theme.spacing(0.5, 1),
- textDecoration: 'none',
- color: theme.palette.text.secondary,
- border: `1px solid ${theme.palette.dividerAlternative}`,
- background: theme.palette.activityIndicators.unknown,
- display: 'inline-block',
- borderRadius: theme.shape.borderRadius,
- marginLeft: theme.spacing(1.5),
- fontSize: theme.fontSizes.smallerBody,
- fontWeight: theme.fontWeight.bold,
- lineHeight: 1,
-}));
-
-const StyledOwnerBadge = styled(StyledBadge)(({ theme }) => ({
- color: theme.palette.success.dark,
- border: `1px solid ${theme.palette.success.border}`,
- background: theme.palette.success.light,
+const StyledPopupStar = styled(StarRounded)(({ theme }) => ({
+ color: theme.palette.warning.main,
}));
interface IGroupUserRoleCellProps {
@@ -35,8 +21,12 @@ export const GroupUserRoleCell = ({
const renderBadge = () => (
{capitalize(value)}}
- elseShow={{capitalize(value)}}
+ show={{capitalize(value)}}
+ elseShow={
+ }>
+ {capitalize(value)}
+
+ }
/>
);
diff --git a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCard.tsx b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCard.tsx
index 8373b42d2e..6f9309b624 100644
--- a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCard.tsx
+++ b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCard.tsx
@@ -1,6 +1,6 @@
import { styled, Tooltip } from '@mui/material';
import { IGroup } from 'interfaces/group';
-import { Link } from 'react-router-dom';
+import { Link, useNavigate } from 'react-router-dom';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { GroupCardAvatars } from './GroupCardAvatars/GroupCardAvatars';
import { Badge } from 'component/common/Badge/Badge';
@@ -20,9 +20,15 @@ const StyledGroupCard = styled('aside')(({ theme }) => ({
border: `1px solid ${theme.palette.dividerAlternative}`,
borderRadius: theme.shape.borderRadiusLarge,
boxShadow: theme.boxShadows.card,
+ display: 'flex',
+ flexDirection: 'column',
[theme.breakpoints.up('md')]: {
padding: theme.spacing(4),
},
+ '&:hover': {
+ transition: 'background-color 0.2s ease-in-out',
+ backgroundColor: theme.palette.neutral.light,
+ },
}));
const StyledRow = styled('div')(() => ({
@@ -31,6 +37,14 @@ const StyledRow = styled('div')(() => ({
justifyContent: 'space-between',
}));
+const StyledTitleRow = styled(StyledRow)(() => ({
+ alignItems: 'flex-start',
+}));
+
+const StyledBottomRow = styled(StyledRow)(() => ({
+ marginTop: 'auto',
+}));
+
const StyledHeaderTitle = styled('h2')(({ theme }) => ({
fontSize: theme.fontSizes.mainHeader,
fontWeight: theme.fontWeight.medium,
@@ -55,7 +69,13 @@ const StyledCounterDescription = styled('span')(({ theme }) => ({
marginLeft: theme.spacing(1),
}));
-const ProjectBadgeContainer = styled('div')(() => ({}));
+const ProjectBadgeContainer = styled('div')(() => ({
+ maxWidth: '50%',
+}));
+
+const StyledBadge = styled(Badge)(() => ({
+ marginRight: 0.5,
+}));
interface IGroupCardProps {
group: IGroup;
@@ -63,12 +83,12 @@ interface IGroupCardProps {
export const GroupCard = ({ group }: IGroupCardProps) => {
const [removeOpen, setRemoveOpen] = useState(false);
-
+ const navigate = useNavigate();
return (
<>
-
+
{group.name}
{
onRemove={() => setRemoveOpen(true)}
/>
-
+
{group.description}
-
+
0}
show={}
@@ -92,13 +112,26 @@ export const GroupCard = ({ group }: IGroupCardProps) => {
0}
show={group.projects.map(project => (
- }
- sx={{ marginRight: 0.5 }}
+
- {project}
-
+ {
+ e.preventDefault();
+ navigate(
+ `/projects/${project}/access`
+ );
+ }}
+ color="secondary"
+ icon={}
+ >
+ {project}
+
+
))}
elseShow={
{
}
/>
-
+
= ({
- Remove group
+ Delete group
diff --git a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupCardAvatars.tsx b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupCardAvatars.tsx
index 73e6245bd0..01933b912f 100644
--- a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupCardAvatars.tsx
+++ b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupCardAvatars.tsx
@@ -15,6 +15,9 @@ const StyledAvatars = styled('div')(({ theme }) => ({
const StyledAvatar = styled(UserAvatar)(({ theme }) => ({
outline: `${theme.spacing(0.25)} solid ${theme.palette.background.paper}`,
marginLeft: theme.spacing(-1),
+ '&:hover': {
+ outlineColor: theme.palette.primary.main,
+ },
}));
interface IGroupCardAvatarsProps {
@@ -44,6 +47,7 @@ export const GroupCardAvatars = ({ users }: IGroupCardAvatarsProps) => {
{shownUsers.map(user => (
{
diff --git a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupPopover/GroupPopover.tsx b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupPopover/GroupPopover.tsx
index 5e59c2e9a5..665ee8bb74 100644
--- a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupPopover/GroupPopover.tsx
+++ b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupPopover/GroupPopover.tsx
@@ -1,21 +1,18 @@
-import { Badge, Popover, styled } from '@mui/material';
+import { Popover, styled } from '@mui/material';
import { IGroupUser, Role } from 'interfaces/group';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-import { Badge as StyledBadge } from 'component/common/Badge/Badge';
-import StarIcon from '@mui/icons-material/Star';
+import { Badge } from 'component/common/Badge/Badge';
+import { StarRounded } from '@mui/icons-material';
const StyledPopover = styled(Popover)(({ theme }) => ({
pointerEvents: 'none',
'.MuiPaper-root': {
- padding: '12px',
+ padding: theme.spacing(2),
},
}));
-const StyledPopupStar = styled(StarIcon)(({ theme }) => ({
+const StyledPopupStar = styled(StarRounded)(({ theme }) => ({
color: theme.palette.warning.main,
- fontSize: theme.fontSizes.smallBody,
- marginLeft: theme.spacing(0.1),
- marginTop: theme.spacing(2),
}));
const StyledName = styled('div')(({ theme }) => ({
@@ -55,22 +52,10 @@ export const GroupPopover = ({
>
{user?.role}}
+ show={{user?.role}}
elseShow={
- }
- >
-
- {user?.role}
-
+ }>
+ {user?.role}
}
/>
diff --git a/frontend/src/component/admin/groups/GroupsList/GroupEmpty/GroupEmpty.tsx b/frontend/src/component/admin/groups/GroupsList/GroupEmpty/GroupEmpty.tsx
new file mode 100644
index 0000000000..768127474d
--- /dev/null
+++ b/frontend/src/component/admin/groups/GroupsList/GroupEmpty/GroupEmpty.tsx
@@ -0,0 +1,35 @@
+import { Button, styled, Typography } from '@mui/material';
+import { Link } from 'react-router-dom';
+
+export const GroupEmpty = () => {
+ const StyledContainerDiv = styled('div')(({ theme }) => ({
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ justifyContent: 'center',
+ margin: theme.spacing(6),
+ marginLeft: 'auto',
+ marginRight: 'auto',
+ }));
+
+ const StyledTitle = styled(Typography)(({ theme }) => ({
+ fontSize: theme.fontSizes.bodySize,
+ marginBottom: theme.spacing(2.5),
+ }));
+
+ return (
+
+
+ No groups available. Get started by adding a new group.
+
+
+
+ );
+};
diff --git a/frontend/src/component/admin/groups/GroupsList/GroupsList.tsx b/frontend/src/component/admin/groups/GroupsList/GroupsList.tsx
index 8f61209922..18d1756406 100644
--- a/frontend/src/component/admin/groups/GroupsList/GroupsList.tsx
+++ b/frontend/src/component/admin/groups/GroupsList/GroupsList.tsx
@@ -11,6 +11,7 @@ import theme from 'themes/theme';
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
import { TablePlaceholder } from 'component/common/Table';
import { GroupCard } from './GroupCard/GroupCard';
+import { GroupEmpty } from './GroupEmpty/GroupEmpty';
type PageQueryType = Partial>;
@@ -123,12 +124,7 @@ export const GroupsList: VFC = () => {
”
}
- elseShow={
-
- No groups available. Get started by adding a new
- group.
-
- }
+ elseShow={}
/>
}
/>
diff --git a/frontend/src/component/admin/groups/RemoveGroup/RemoveGroup.tsx b/frontend/src/component/admin/groups/RemoveGroup/RemoveGroup.tsx
index ddf7d5a34d..3dfed93853 100644
--- a/frontend/src/component/admin/groups/RemoveGroup/RemoveGroup.tsx
+++ b/frontend/src/component/admin/groups/RemoveGroup/RemoveGroup.tsx
@@ -48,10 +48,10 @@ export const RemoveGroup: FC = ({
onClose={() => {
setOpen(false);
}}
- title="Remove group"
+ title="Delete group"
>
- Are you sure you wish to remove {group.name}?
+ Are you sure you wish to delete {group.name}?
If this group is currently assigned to one or more projects then
users belonging to this group may lose access to those projects.
diff --git a/frontend/src/component/common/Badge/Badge.tsx b/frontend/src/component/common/Badge/Badge.tsx
index bbcce6fa69..b944542e25 100644
--- a/frontend/src/component/common/Badge/Badge.tsx
+++ b/frontend/src/component/common/Badge/Badge.tsx
@@ -1,5 +1,5 @@
import { styled, SxProps, Theme } from '@mui/material';
-import {
+import React, {
cloneElement,
FC,
ForwardedRef,
@@ -17,6 +17,8 @@ interface IBadgeProps {
className?: string;
sx?: SxProps;
children?: ReactNode;
+ title?: string;
+ onClick?: (event: React.SyntheticEvent) => void;
}
interface IBadgeIconProps {
diff --git a/frontend/src/component/common/MainHeader/MainHeader.tsx b/frontend/src/component/common/MainHeader/MainHeader.tsx
index 2711d6df12..2b46d0237b 100644
--- a/frontend/src/component/common/MainHeader/MainHeader.tsx
+++ b/frontend/src/component/common/MainHeader/MainHeader.tsx
@@ -1,6 +1,7 @@
import { Paper, styled } from '@mui/material';
import { usePageTitle } from 'hooks/usePageTitle';
import { ReactNode } from 'react';
+import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender';
const StyledMainHeader = styled(Paper)(({ theme }) => ({
borderRadius: theme.shape.borderRadiusLarge,
@@ -49,7 +50,15 @@ export const MainHeader = ({
{title}
{actions}
- Description:{description}
+
+ Description:
+ {description}
+ >
+ }
+ />
);
};
diff --git a/frontend/src/component/common/UserAvatar/UserAvatar.tsx b/frontend/src/component/common/UserAvatar/UserAvatar.tsx
index 523cdbab1f..c2ec2288ae 100644
--- a/frontend/src/component/common/UserAvatar/UserAvatar.tsx
+++ b/frontend/src/component/common/UserAvatar/UserAvatar.tsx
@@ -9,11 +9,11 @@ import {
import { IUser } from 'interfaces/user';
import { FC } from 'react';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-import StarIcon from '@mui/icons-material/Star';
+import { StarRounded } from '@mui/icons-material';
const StyledAvatar = styled(Avatar)(({ theme }) => ({
- width: theme.spacing(4),
- height: theme.spacing(4),
+ width: theme.spacing(3.5),
+ height: theme.spacing(3.5),
margin: 'auto',
backgroundColor: theme.palette.secondary.light,
color: theme.palette.text.primary,
@@ -21,7 +21,7 @@ const StyledAvatar = styled(Avatar)(({ theme }) => ({
fontWeight: theme.fontWeight.bold,
}));
-const StyledStar = styled(StarIcon)(({ theme }) => ({
+const StyledStar = styled(StarRounded)(({ theme }) => ({
color: theme.palette.warning.main,
backgroundColor: theme.palette.background.paper,
borderRadius: theme.shape.borderRadiusExtraLarge,
diff --git a/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectRoleDescription/ProjectRoleDescription.tsx b/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectRoleDescription/ProjectRoleDescription.tsx
index d4f66881d8..f9d2276693 100644
--- a/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectRoleDescription/ProjectRoleDescription.tsx
+++ b/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectRoleDescription/ProjectRoleDescription.tsx
@@ -44,27 +44,44 @@ export const ProjectRoleDescription: VFC = ({
const environments = useMemo(() => {
const environments = new Set();
role.permissions
- ?.filter((permission: any) => permission.environment !== '')
+ ?.filter((permission: any) => permission.environment)
.forEach((permission: any) => {
environments.add(permission.environment);
});
return [...environments].sort();
}, [role]);
+ const projectPermissions = useMemo(() => {
+ return role.permissions?.filter(
+ (permission: any) => !permission.environment
+ );
+ }, [role]);
+
return (
-
- Project permissions
-
-
- {role.permissions
- ?.filter((permission: any) => permission.environment === '')
- .map((permission: any) => permission.displayName)
- .sort()
- .map((permission: any) => (
- {permission}
- ))}
-
+
+
+ Project permissions
+
+
+ {role.permissions
+ ?.filter(
+ (permission: any) => !permission.environment
+ )
+ .map(
+ (permission: any) => permission.displayName
+ )
+ .sort()
+ .map((permission: any) => (
+ {permission}
+ ))}
+
+ >
+ }
+ />