Skip to content

Commit

Permalink
feat: add usage info to project role deletion dialog (#4464)
Browse files Browse the repository at this point in the history
## About the changes
<!-- Describe the changes introduced. What are they and why are they
being introduced? Feel free to also add screenshots or steps to view the
changes if they're visual. -->

Adds projects user and group -usage information to the dialog shown when
user wants to delete a project role

<img width="670" alt="Skjermbilde 2023-08-10 kl 08 28 40"
src="https://github.com/Unleash/unleash/assets/707867/a1df961b-2d0f-419d-b9bf-fedef896a84e">

---------

Co-authored-by: Nuno Góis <[email protected]>
  • Loading branch information
daveleek and nunogois authored Aug 17, 2023
1 parent da7829d commit 76d3cc5
Show file tree
Hide file tree
Showing 16 changed files with 453 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -1,21 +1,7 @@
import { Alert, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import { useServiceAccounts } from 'hooks/api/getters/useServiceAccounts/useServiceAccounts';
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
import { IRole } from 'interfaces/role';
import { RoleDeleteDialogUsers } from './RoleDeleteDialogUsers/RoleDeleteDialogUsers';
import { RoleDeleteDialogServiceAccounts } from './RoleDeleteDialogServiceAccounts/RoleDeleteDialogServiceAccounts';
import { useGroups } from 'hooks/api/getters/useGroups/useGroups';
import { RoleDeleteDialogGroups } from './RoleDeleteDialogGroups/RoleDeleteDialogGroups';

const StyledTableContainer = styled('div')(({ theme }) => ({
marginTop: theme.spacing(1.5),
}));

const StyledLabel = styled('p')(({ theme }) => ({
marginTop: theme.spacing(3),
}));
import { RoleDeleteDialogRootRole } from './RoleDeleteDialogRootRole/RoleDeleteDialogRootRole';
import { RoleDeleteDialogProjectRole } from './RoleDeleteDialogProjectRole/RoleDeleteDialogProjectRole';
import { CUSTOM_PROJECT_ROLE_TYPE } from 'constants/roles';

interface IRoleDeleteDialogProps {
role?: IRole;
Expand All @@ -30,98 +16,23 @@ export const RoleDeleteDialog = ({
setOpen,
onConfirm,
}: IRoleDeleteDialogProps) => {
const { users } = useUsers();
const { serviceAccounts } = useServiceAccounts();
const { groups } = useGroups();

const roleUsers = users.filter(({ rootRole }) => rootRole === role?.id);
const roleServiceAccounts = serviceAccounts.filter(
({ rootRole }) => rootRole === role?.id
);
const roleGroups = groups?.filter(({ rootRole }) => rootRole === role?.id);

const entitiesWithRole = Boolean(
roleUsers.length || roleServiceAccounts.length || roleGroups?.length
);
if (role?.type === CUSTOM_PROJECT_ROLE_TYPE) {
return (
<RoleDeleteDialogProjectRole
role={role}
open={open}
setOpen={setOpen}
onConfirm={onConfirm}
/>
);
}

return (
<Dialogue
title="Delete role?"
<RoleDeleteDialogRootRole
role={role}
open={open}
primaryButtonText="Delete role"
secondaryButtonText="Cancel"
disabledPrimaryButton={entitiesWithRole}
onClick={() => onConfirm(role!)}
onClose={() => {
setOpen(false);
}}
>
<ConditionallyRender
condition={entitiesWithRole}
show={
<>
<Alert severity="error">
You are not allowed to delete a role that is
currently in use. Please change the role of the
following entities first:
</Alert>
<ConditionallyRender
condition={Boolean(roleUsers.length)}
show={
<>
<StyledLabel>
Users ({roleUsers.length}):
</StyledLabel>
<StyledTableContainer>
<RoleDeleteDialogUsers
users={roleUsers}
/>
</StyledTableContainer>
</>
}
/>
<ConditionallyRender
condition={Boolean(roleServiceAccounts.length)}
show={
<>
<StyledLabel>
Service accounts (
{roleServiceAccounts.length}):
</StyledLabel>
<StyledTableContainer>
<RoleDeleteDialogServiceAccounts
serviceAccounts={
roleServiceAccounts
}
/>
</StyledTableContainer>
</>
}
/>
<ConditionallyRender
condition={Boolean(roleGroups?.length)}
show={
<>
<StyledLabel>
Groups ({roleGroups?.length}):
</StyledLabel>
<StyledTableContainer>
<RoleDeleteDialogGroups
groups={roleGroups!}
/>
</StyledTableContainer>
</>
}
/>
</>
}
elseShow={
<p>
You are about to delete role:{' '}
<strong>{role?.name}</strong>
</p>
}
/>
</Dialogue>
setOpen={setOpen}
onConfirm={onConfirm}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Alert, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import { IRole } from 'interfaces/role';
import { useProjectRoleAccessUsage } from 'hooks/api/getters/useProjectRoleAccessUsage/useProjectRoleAccessUsage';
import { RoleDeleteDialogProjectRoleTable } from './RoleDeleteDialogProjectRoleTable';

const StyledTableContainer = styled('div')(({ theme }) => ({
marginTop: theme.spacing(1.5),
}));

const StyledLabel = styled('p')(({ theme }) => ({
marginTop: theme.spacing(3),
}));

interface IRoleDeleteDialogProps {
role?: IRole;
open: boolean;
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
onConfirm: (role: IRole) => void;
}

export const RoleDeleteDialogProjectRole = ({
role,
open,
setOpen,
onConfirm,
}: IRoleDeleteDialogProps) => {
const { projects } = useProjectRoleAccessUsage(role?.id);

const entitiesWithRole = Boolean(projects?.length);

return (
<Dialogue
title="Delete project role?"
open={open}
primaryButtonText="Delete role"
secondaryButtonText="Cancel"
disabledPrimaryButton={entitiesWithRole}
onClick={() => onConfirm(role!)}
onClose={() => {
setOpen(false);
}}
maxWidth="md"
>
<ConditionallyRender
condition={entitiesWithRole}
show={
<>
<Alert severity="error">
You are not allowed to delete a role that is
currently in use. Please change the role of the
following entities first:
</Alert>
<ConditionallyRender
condition={Boolean(projects?.length)}
show={
<>
<StyledLabel>
Role assigned in {projects?.length}{' '}
projects:
</StyledLabel>
<StyledTableContainer>
<RoleDeleteDialogProjectRoleTable
projects={projects}
/>
</StyledTableContainer>
</>
}
/>
</>
}
elseShow={
<p>
You are about to delete role:{' '}
<strong>{role?.name}</strong>
</p>
}
/>
</Dialogue>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { VirtualizedTable } from 'component/common/Table';
import { useMemo, useState } from 'react';
import { useTable, useSortBy, useFlexLayout, Column } from 'react-table';
import { sortTypes } from 'utils/sortTypes';
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
import { IProjectRoleUsageCount } from 'interfaces/project';
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';

interface IRoleDeleteDialogProjectRoleTableProps {
projects: IProjectRoleUsageCount[];
}

export const RoleDeleteDialogProjectRoleTable = ({
projects,
}: IRoleDeleteDialogProjectRoleTableProps) => {
const [initialState] = useState(() => ({
sortBy: [{ id: 'name' }],
}));

const columns = useMemo(
() =>
[
{
id: 'name',
Header: 'Project name',
accessor: (row: any) => row.project || '',
minWidth: 200,
Cell: ({ row: { original: item } }: any) => (
<LinkCell
title={item.project}
to={`/projects/${item.project}`}
/>
),
},
{
id: 'users',
Header: 'Assigned users',
accessor: (row: any) =>
row.userCount === 1
? '1 user'
: `${row.userCount} users`,
Cell: TextCell,
maxWidth: 150,
},
{
id: 'serviceAccounts',
Header: 'Service accounts',
accessor: (row: any) =>
row.serviceAccountCount === 1
? '1 account'
: `${row.serviceAccountCount} accounts`,
Cell: TextCell,
maxWidth: 150,
},
{
id: 'groups',
Header: 'Assigned groups',
accessor: (row: any) =>
row.groupCount === 1
? '1 group'
: `${row.groupCount} groups`,
Cell: TextCell,
maxWidth: 150,
},
] as Column<IProjectRoleUsageCount>[],
[]
);

const { headerGroups, rows, prepareRow } = useTable(
{
columns,
data: projects,
initialState,
sortTypes,
autoResetHiddenColumns: false,
autoResetSortBy: false,
disableSortRemove: true,
disableMultiSort: true,
},
useSortBy,
useFlexLayout
);

return (
<VirtualizedTable
rows={rows}
headerGroups={headerGroups}
prepareRow={prepareRow}
/>
);
};
Loading

0 comments on commit 76d3cc5

Please sign in to comment.