Skip to content

Commit 2046791

Browse files
authored
Add delete user button (#541)
* Add frontend * Add api wrappers * Add draft of backend endpoint * Finish backend endpoint * Fix linter * Clean code * Add swal confirmation
1 parent f321315 commit 2046791

File tree

7 files changed

+113
-7
lines changed

7 files changed

+113
-7
lines changed

backend/routes/api/users.js

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ const {
1717
isRoleValid,
1818
} = require('../../utils/roleUtils');
1919
const { requireAdmin } = require('../../middleware/authentication');
20-
const { ADMIN_ID, DEFAULT_USERS_ON_GET_REQUEST } = require('../../utils/constants');
20+
const {
21+
ADMIN_ID,
22+
DEFAULT_USERS_ON_GET_REQUEST,
23+
} = require('../../utils/constants');
2124

2225
/**
2326
* Gets information about the user making this request.
@@ -59,7 +62,12 @@ router.get(
5962
const users = await identityProvider.listUsers(params).promise();
6063
await sendResponse(res, 200, '', users);
6164
} catch (error) {
62-
await sendResponse(res, 400, 'Please send a proper pagination token.', {});
65+
await sendResponse(
66+
res,
67+
400,
68+
'Please send a proper pagination token.',
69+
{},
70+
);
6371
}
6472
}),
6573
);
@@ -108,7 +116,9 @@ router.delete(
108116
// Check if user has this role
109117
const userRoles = await getUserRoles(username);
110118
const roleIndex = userRoles.indexOf(roleId);
111-
if (roleIndex === -1) return sendResponse(res, 400, 'User does not have role');
119+
if (roleIndex === -1) {
120+
return sendResponse(res, 400, 'User does not have role');
121+
}
112122

113123
// Create params for the update in AWS
114124
userRoles.splice(roleIndex, 1);
@@ -150,4 +160,27 @@ router.put(
150160
}),
151161
);
152162

163+
/**
164+
* Deletes a user. The URL param is the user's unique
165+
* username;
166+
*/
167+
router.delete(
168+
'/:username',
169+
requireAdmin,
170+
errorWrap(async (req, res) => {
171+
const { username } = req.params;
172+
173+
// Create the params for the deletion
174+
const params = {
175+
Username: username,
176+
UserPoolId: USER_POOL_ID,
177+
};
178+
179+
// Do the deletion
180+
const identityProvider = getIdentityProvider();
181+
await identityProvider.adminDeleteUser(params).promise();
182+
await sendResponse(res, 200, 'Access updated');
183+
}),
184+
);
185+
153186
module.exports = router;

frontend/src/api/api.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,14 @@ export const editRole = async (userId, updatedRoleInfo) => {
205205
return res.data;
206206
};
207207

208+
// TODO: test endpoint or create issue for it
209+
export const deleteUser = async (username) => {
210+
const requestString = `/users/${username}`;
211+
const res = await instance.delete(requestString);
212+
if (!res?.data?.success) throw new Error(res?.data?.message);
213+
return res.data;
214+
};
215+
208216
export const addUserRole = async (username, roleName) => {
209217
const requestString = `/users/${username}/roles/${roleName}`;
210218
const res = await instance.put(requestString);

frontend/src/components/EditRoleModal/EditRoleModal.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,25 @@ import {
1313
} from '@material-ui/core';
1414
import PropTypes from 'prop-types';
1515
import { trackPromise } from 'react-promise-tracker';
16+
import swal from 'sweetalert';
1617

1718
import { useErrorWrap } from '../../hooks/useErrorWrap';
1819
import TextField from '../Fields/TextField';
1920
import MultiSelectField from '../Fields/MultiSelectField';
2021
import { ACCESS_LEVELS } from '../../utils/constants';
2122
import './EditRoleModal.scss';
2223
import { useTranslations } from '../../hooks/useTranslations';
23-
import { removeUserRole, setUserAccess, addUserRole } from '../../api/api';
24+
import {
25+
removeUserRole,
26+
setUserAccess,
27+
addUserRole,
28+
deleteUser,
29+
} from '../../api/api';
2430

2531
const EditRoleModal = ({
2632
isOpen,
2733
onUserEdited,
34+
onUserDeleted,
2835
onClose,
2936
userInfo,
3037
allRoles,
@@ -78,6 +85,24 @@ const EditRoleModal = ({
7885
onUserEdited(userData.userId, userData.accessLevel, userData.roles);
7986
};
8087

88+
const onDelete = async () => {
89+
swal({
90+
title: translations.components.modal.deleteTitle,
91+
text: translations.components.modal.deleteUserConfirmation,
92+
icon: 'warning',
93+
buttons: true,
94+
dangerMode: true,
95+
}).then(async (willDelete) => {
96+
if (willDelete) {
97+
await errorWrap(async () =>
98+
trackPromise(deleteUser(userData?.userId)),
99+
);
100+
onClose();
101+
onUserDeleted(userData.userId);
102+
}
103+
});
104+
};
105+
81106
const renderAccessDropdown = () => {
82107
return (
83108
<Select
@@ -130,6 +155,11 @@ const EditRoleModal = ({
130155
</InputLabel>
131156
{renderAccessDropdown()}
132157
</FormControl>
158+
<div>
159+
<Button className="delete-user-button" onClick={onDelete}>
160+
{translations.accountManagement.deleteUser}
161+
</Button>
162+
</div>
133163
<div>
134164
<Button className="save-user-button" onClick={onSave}>
135165
{translations.accountManagement.Save}
@@ -147,6 +177,7 @@ EditRoleModal.propTypes = {
147177
isOpen: PropTypes.bool.isRequired,
148178
onClose: PropTypes.func.isRequired,
149179
onUserEdited: PropTypes.func.isRequired,
180+
onUserDeleted: PropTypes.func.isRequired,
150181
allRoles: PropTypes.arrayOf(
151182
PropTypes.shape({
152183
_id: PropTypes.string,

frontend/src/components/EditRoleModal/EditRoleModal.scss

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@
4343
}
4444
}
4545

46+
.delete-user-button {
47+
margin-top: 20px;
48+
background-color: $deleteRed;
49+
color: white;
50+
padding: 0 24px 0 24px;
51+
height: 38px;
52+
width: auto;
53+
font-size: 12px;
54+
font-weight: bold;
55+
transition: all 0.2s;
56+
border-radius: 2px;
57+
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.15);
58+
&:hover {
59+
background-color: $buttonHighlight;
60+
}
61+
}
62+
4663
.text-field {
4764
margin-bottom: 20px;
4865
}

frontend/src/components/ManageRoleModal/ManageRoleModal.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
// TODO: find red on styles
4747
.delete-user-button {
4848
margin-top: 20px;
49-
background-color: red;
49+
background-color: $deleteRed;
5050
color: white;
5151
padding: 0 24px 0 24px;
5252
height: 38px;

frontend/src/pages/AccountManagement/AccountManagment.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,16 @@ const AccountManagement = () => {
205205
});
206206
};
207207

208+
const onUserDeleted = (username) => {
209+
setUserMetaData((metaData) => {
210+
const users = _.cloneDeep(metaData);
211+
const updatedUsers = users.filter(
212+
(user) => user.Username !== username,
213+
);
214+
return updatedUsers;
215+
});
216+
};
217+
208218
/**
209219
* Called when a role's data is deleted
210220
*/
@@ -297,6 +307,7 @@ const AccountManagement = () => {
297307
allRoles={memoizedMultiSelectRoles}
298308
onClose={() => setSelectedUser(null)}
299309
onUserEdited={onUserEdited}
310+
onUserDeleted={onUserDeleted}
300311
/>
301312
);
302313
};

frontend/src/translations.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
},
3434
"modal": {
3535
"deleteTitle": "Are you sure?",
36+
"deleteConfirmation": "Once deleted, you will not be able to see this field. However, it can be recovered in the database.",
37+
"deleteUserConfirmation": "Once deleted, you will not be able to see this user, and the user will not have access to the site.",
3638
"deleteFieldConfirmation": "Once deleted, you will not be able to see this field. However, it can be recovered in the database.",
3739
"deleteStepConfirmation": "Once deleted, you will not be able to see this step. However, it can be recovered in the database.",
3840
"deletePatientConfirmation": "Once deleted, this patient will be gone forever. It CANNOT be recovered in the database."
@@ -236,7 +238,8 @@
236238
"Revoked": "Revoked",
237239
"Pending": "Pending",
238240
"Save": "Save",
239-
"Discard": "Discard"
241+
"Discard": "Discard",
242+
"deleteUser": "Delete User"
240243
},
241244
"roleManagement": {
242245
"roleDatabase": "Role Database",
@@ -286,6 +289,8 @@
286289
},
287290
"modal": {
288291
"deleteTitle": "Are you sure?",
292+
"deleteConfirmation": "Once deleted, you will not be able to see this field. However, it can be recovered in the database.",
293+
"deleteUserConfirmation": "بمجرد الحذف ، لن تتمكن من رؤية هذا المستخدم ولن يتمكن المستخدم من الوصول إلى الموقع.",
289294
"deleteFieldConfirmation": "Once deleted, you will not be able to see this field. However, it can be recovered in the database.",
290295
"deleteStepConfirmation": "Once deleted, you will not be able to see this step. However, it can be recovered in the database.",
291296
"deletePatientConfirmation": "Once deleted, this patient will be gone forever. It CANNOT be recovered in the database."
@@ -489,7 +494,8 @@
489494
"Save": "يحفظ",
490495
"Discard": "ينبذ",
491496
"username": "اسم المستخدم",
492-
"email": "بريد إلكتروني"
497+
"email": "بريد إلكتروني",
498+
"deleteUser": "مسح المستخدم"
493499
},
494500
"roleManagement": {
495501
"roleDatabase": "قاعدة بيانات الدور",

0 commit comments

Comments
 (0)