Skip to content

Commit 4588bfb

Browse files
feat: delete role functionality added to user table
1 parent 4aadf28 commit 4588bfb

12 files changed

Lines changed: 307 additions & 144 deletions

File tree

src/authz-module/audit-user/CustomCells.tsx

Lines changed: 0 additions & 44 deletions
This file was deleted.

src/authz-module/audit-user/index.tsx

Lines changed: 113 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import React, { useMemo } from 'react';
1+
import React, { useContext, useMemo, useState } from 'react';
22
import { useIntl } from '@edx/frontend-platform/i18n';
3+
import { AppContext } from '@edx/frontend-platform/react';
4+
import type { AppContextType } from '@edx/frontend-platform/react';
35
import debounce from 'lodash.debounce';
46
import {
57
Container, DataTable,
@@ -12,19 +14,30 @@ import { useUserAccount } from '@src/data/hooks';
1214
import baseMessages from '@src/authz-module/messages';
1315
import AddRoleButton from '@src/authz-module/components/AddRoleButton';
1416
import {
15-
OrgCell, RoleCell, ScopeCell, PermissionsCell, ViewAllPermissionsCell, ActionsCell,
17+
OrgCell, RoleCell, ScopeCell, PermissionsCell, ViewAllPermissionsCell,
18+
createActionsCell,
1619
} from '@src/authz-module/components/TableCells';
1720
import { useQuerySettings } from '@src/authz-module/hooks/useQuerySettings';
18-
import { useUserAssignedRoles } from '@src/authz-module/data/hooks';
21+
import { useRevokeUserRoles, useUserAssignedRoles } from '@src/authz-module/data/hooks';
22+
import { Role } from 'types';
23+
import { useToastManager } from 'authz-module/libraries-manager/ToastManagerContext';
1924
import messages from './messages';
25+
import ConfirmDeletionModal from '../components/ConfirmDeletionModal';
2026

2127
const AuditUserPage = () => {
2228
const { formatMessage } = useIntl();
2329
const { username } = useParams();
30+
const { authenticatedUser } = useContext(AppContext) as AppContextType;
2431
const navigate = useNavigate();
32+
const [roleToDelete, setRoleToDelete] = useState<Role | null>(null);
33+
const [showConfirmDeletionModal, setShowConfirmDeletionModal] = useState(false);
34+
const {
35+
showToast, showErrorToast, Bold, Br,
36+
} = useToastManager();
37+
const { mutate: revokeUserRoles, isPending: isRevokingUserRolePending } = useRevokeUserRoles();
2538
const { isLoading: isLoadingUser, data: user } = useUserAccount(username ?? '');
2639
const { querySettings, handleTableFetch } = useQuerySettings();
27-
const { data: { results: userAssignments } = { results: [] } } = useUserAssignedRoles(username ?? '', querySettings);
40+
const { data: { results: userAssignments, count } = { results: [] } } = useUserAssignedRoles(username ?? '', querySettings);
2841

2942
if (!user && !isLoadingUser) {
3043
navigate(AUTHZ_HOME_PATH);
@@ -35,18 +48,7 @@ const AuditUserPage = () => {
3548
to: AUTHZ_HOME_PATH,
3649
},
3750
];
38-
const additionalColumns = [
39-
{
40-
id: 'view_permissions',
41-
Header: '',
42-
Cell: ViewAllPermissionsCell,
43-
},
44-
{
45-
id: 'action',
46-
Header: formatMessage(messages['authz.user.table.action.column.header']),
47-
Cell: ActionsCell,
48-
},
49-
];
51+
5052
const columns = [
5153
{
5254
Header: formatMessage(messages['authz.user.table.role.column.header']),
@@ -75,8 +77,103 @@ const AuditUserPage = () => {
7577

7678
const fetchData = useMemo(() => debounce(handleTableFetch, 500), [handleTableFetch]);
7779

80+
const handleShowConfirmDeletionModal = (role: Role) => {
81+
if (isRevokingUserRolePending) { return; }
82+
83+
setRoleToDelete(role);
84+
setShowConfirmDeletionModal(true);
85+
};
86+
87+
const handleCloseConfirmDeletionModal = () => {
88+
setRoleToDelete(null);
89+
setShowConfirmDeletionModal(false);
90+
};
91+
92+
const handleRevokeUserRole = () => {
93+
if (!user || !roleToDelete) { return; }
94+
95+
const data = {
96+
users: user.username,
97+
role: roleToDelete.role,
98+
scope: roleToDelete.scope,
99+
};
100+
101+
const runRevokeRole = (variables) => {
102+
const variablesData = {
103+
data: {
104+
...variables.data,
105+
querySettings,
106+
},
107+
108+
};
109+
revokeUserRoles(variablesData, {
110+
onSuccess: (response) => {
111+
const { errors } = response;
112+
113+
if (errors.length) {
114+
showToast({
115+
type: 'error',
116+
message: formatMessage(
117+
baseMessages['authz.team.toast.default.error.message'],
118+
{ Bold, Br },
119+
),
120+
});
121+
// authzQueryKeys.userRoles(username, querySettings),
122+
return;
123+
}
124+
125+
const remainingRolesCount = count ? count - 1 : 0;
126+
showToast({
127+
message: formatMessage(
128+
baseMessages['authz.team.remove.user.toast.success.description'],
129+
{
130+
role: roleToDelete.name,
131+
rolesCount: remainingRolesCount,
132+
},
133+
),
134+
type: 'success',
135+
});
136+
handleCloseConfirmDeletionModal();
137+
},
138+
onError: (error, retryVariables) => {
139+
showErrorToast(error, () => runRevokeRole(retryVariables));
140+
},
141+
});
142+
};
143+
144+
runRevokeRole({ data });
145+
};
146+
147+
const additionalColumns = [
148+
{
149+
id: 'view_permissions',
150+
Header: '',
151+
Cell: ViewAllPermissionsCell,
152+
},
153+
{
154+
id: 'action',
155+
Header: formatMessage(messages['authz.user.table.action.column.header']),
156+
Cell: createActionsCell({
157+
onClickDeleteButton: handleShowConfirmDeletionModal,
158+
isUserAuthenticatedPage: username === authenticatedUser.username,
159+
}),
160+
},
161+
];
162+
78163
return (
79164
<div className="authz-module">
165+
<ConfirmDeletionModal
166+
isOpen={showConfirmDeletionModal}
167+
close={handleCloseConfirmDeletionModal}
168+
onSave={handleRevokeUserRole}
169+
isDeleting={isRevokingUserRolePending}
170+
context={{
171+
userName: user?.username || '',
172+
scope: roleToDelete?.scope || '',
173+
role: roleToDelete?.name || '',
174+
rolesCount: count || 0,
175+
}}
176+
/>
80177
<AuthZLayout
81178
context={{
82179
id: '',

src/authz-module/audit-user/utils.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import {
2+
ActionRow, AlertModal, Icon, ModalDialog, Stack,
3+
StatefulButton,
4+
} from '@openedx/paragon';
5+
import { useIntl } from '@edx/frontend-platform/i18n';
6+
7+
import { SpinnerSimple } from '@openedx/paragon/icons';
8+
import messages from './messages';
9+
10+
interface ConfirmDeletionModalProps {
11+
isOpen: boolean;
12+
close: () => void;
13+
onSave: () => void;
14+
isDeleting?: boolean;
15+
context: {
16+
userName: string;
17+
scope: string;
18+
role: string;
19+
rolesCount: number;
20+
}
21+
}
22+
23+
const ConfirmDeletionModal = ({
24+
isOpen, close, onSave, isDeleting, context,
25+
}: ConfirmDeletionModalProps) => {
26+
const intl = useIntl();
27+
return (
28+
<AlertModal
29+
title={intl.formatMessage(messages['authz.team.remove.user.modal.title'])}
30+
isOpen={isOpen}
31+
onClose={close}
32+
size="lg"
33+
footerNode={(
34+
<ActionRow>
35+
<ModalDialog.CloseButton variant="tertiary">
36+
{intl.formatMessage(messages['authz.manage.cancel.button'])}
37+
</ModalDialog.CloseButton>
38+
<StatefulButton
39+
className="px-4"
40+
variant="danger"
41+
labels={{
42+
default: intl.formatMessage(messages['authz.manage.remove.button']),
43+
pending: intl.formatMessage(messages['authz.manage.removing.button']),
44+
}}
45+
icons={{
46+
pending: <Icon src={SpinnerSimple} />,
47+
}}
48+
state={isDeleting ? 'pending' : 'default'}
49+
onClick={() => onSave()}
50+
disabledStates={['pending']}
51+
/>
52+
</ActionRow>
53+
)}
54+
isOverflowVisible={false}
55+
>
56+
<Stack gap={3}>
57+
<p>{intl.formatMessage(messages['authz.team.remove.user.modal.body.1'], {
58+
userName: context.userName,
59+
scope: context.scope,
60+
role: context.role,
61+
})}
62+
</p>
63+
{context.rolesCount === 1 && (
64+
<p>{intl.formatMessage(messages['authz.team.remove.user.modal.body.2'])}</p>
65+
)}
66+
<p>{intl.formatMessage(messages['authz.team.remove.user.modal.body.3'])}</p>
67+
</Stack>
68+
69+
</AlertModal>
70+
);
71+
};
72+
73+
export default ConfirmDeletionModal;

src/authz-module/components/ProtectedRoute.tsx

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)