Skip to content

Commit 5770ae8

Browse files
added user and admin buttons - updates to database
Co-authored-by: Abigail Gerig <[email protected]> Co-authored-by: Christian Looff <[email protected]> Co-authored-by: Ruli Warner-Rosen <[email protected]> Co-authored-by: Trine Medina <[email protected]> Co-authored-by: Matt Dias <[email protected]>
1 parent ad9b7ce commit 5770ae8

File tree

5 files changed

+153
-66
lines changed

5 files changed

+153
-66
lines changed

server/controllers/userController.js

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ userController.createUser = (req, res, next) => {
2525
.catch((err) => {
2626
return next({
2727
log: `Error in userController newUser: ${err}`,
28-
message: { err: 'An error occured creating new user in database. See userController.newUser.' },
28+
message: { err: 'An error occurred creating new user in database. See userController.newUser.' },
2929
});
3030
});
3131
}
@@ -45,7 +45,7 @@ userController.getAllUsers = (req, res, next) => {
4545
.catch((err) => {
4646
return next({
4747
log: `Error in userController getAllUsers: ${err}`,
48-
message: { err: 'An error occured retrieving all users from database. See userController.getAllUsers.' },
48+
message: { err: 'An error occurred retrieving all users from database. See userController.getAllUsers.' },
4949
});
5050
});
5151
}
@@ -65,7 +65,7 @@ userController.getOneUser = (req, res, next) => {
6565
.catch((err) => {
6666
return next({
6767
log: `Error in userController getOneUser: ${err}`,
68-
message: { err: 'An error occured retrieving user from database. See userController.getOneUser.' },
68+
message: { err: 'An error occurred retrieving user from database. See userController.getOneUser.' },
6969
});
7070
});
7171
};
@@ -86,38 +86,55 @@ userController.verifyUser = (req, res, next) => {
8686
.catch((err) => {
8787
return next({
8888
log: `Error in userController checkUserExists: ${err}`,
89-
message: { err: 'An error occured while checking if username exists. See userController.checkUserExists.' },
89+
message: { err: 'An error occurred while checking if username exists. See userController.checkUserExists.' },
90+
});
91+
});
92+
};
93+
94+
// Checks database to ensure at least one person is SysAdmin - there should never be 0 sysAdmins.
95+
userController.checkSysAdmin = (req, res, next) => {
96+
// TODO: Add logic to check length of returned data
97+
const query = 'SELECT * FROM users WHERE role_id = 1';
98+
99+
db.query(query)
100+
.then((data) => {
101+
res.locals.sysAdmins = data.length;
102+
return next();
103+
})
104+
.catch((err) => {
105+
return next({
106+
log: `Error in userController switchUserRole: ${err}`,
107+
message: { err: 'An error occurred while checking number of SysAdmins. See userController.checkSysAdmins.' },
90108
});
91109
});
92110
};
93111

94112
// switches role of user upon designation by system admin
95113
userController.switchUserRole = (req, res, next) => {
96114

97-
const roleMap = {
98-
sysadmin: 1,
99-
admin: 2,
100-
user: 3,
115+
const roleMap = { // TM
116+
1: 'system admin',
117+
2: 'admin',
118+
3: 'user'
101119
};
102120

103-
const { _id, changeToAdmin } = req.body;
121+
const { _id, role_id} = req.body;
104122

105123
const query = 'UPDATE users SET role = $1, role_id = $2 WHERE _id = $3 RETURNING *;';
106-
107-
// Array destructuring assignment and ternary operator
108-
const [ newRole, newRoleId ] = changeToAdmin ? [ 'admin', roleMap.admin ] : [ 'user', roleMap.user ];
109-
110-
// const newRoleId = changeToAdmin ? roleMap.admin : roleMap.user;
111-
const parameters = [ newRole, newRoleId, _id ];
124+
125+
126+
const parameters = [roleMap[role_id], role_id, _id]; // TM
127+
112128
db.query(query, parameters)
113129
.then((data) => {
114130
res.locals.user = data.rows[0];
131+
115132
return next();
116133
})
117134
.catch((err) => {
118135
return next({
119136
log: `Error in userController switchUserRole: ${err}`,
120-
message: { err: 'An error occured while checking if username exists. See userController.switchUserRole.' },
137+
message: { err: 'An error occurred while switching roles. See userController.switchUserRole.' },
121138
});
122139
});
123140
};
@@ -144,7 +161,7 @@ userController.updatePassword = (req, res, next) => {
144161
.catch((err) => {
145162
return next({
146163
log: `Error in userController updatePassword: ${err}`,
147-
message: { err: 'An error occured while checking if username exists. See userController.updatePassword.' },
164+
message: { err: 'An error occurred while checking if username exists. See userController.updatePassword.' },
148165
});
149166
});
150167
};
@@ -164,7 +181,7 @@ userController.updatePhone = (req, res, next) => {
164181
.catch((err) => {
165182
return next({
166183
log: `Error in userController updatePhone: ${err}`,
167-
message: { err: 'An error occured while checking if username exists. See userController.updatePhone.' },
184+
message: { err: 'An error occurred while checking if username exists. See userController.updatePhone.' },
168185
});
169186
});
170187
};
@@ -183,7 +200,7 @@ userController.updateEmail = (req, res, next) => {
183200
.catch((err) => {
184201
return next({
185202
log: `Error in userController updateEmail: ${err}`,
186-
message: { err: 'An error occured while checking if username exists. See userController.updateEmail.' },
203+
message: { err: 'An error occurred while checking if username exists. See userController.updateEmail.' },
187204
});
188205
});
189206
};
@@ -204,7 +221,7 @@ userController.verifySysadmin = (req, res, next) =>{
204221
.catch((err) => {
205222
return next({
206223
log: `Error in userController verifySysadmin: ${err}`,
207-
message: { err: 'An error occured while checking if token exists. See userController.verifySysadmin.' },
224+
message: { err: 'An error occurred while checking if token exists. See userController.verifySysadmin.' },
208225
});
209226
});
210227

server/routes/adminRouter.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ router.post('/',
2222
// Route Handler: Checks if client has sysadmin privilege. Switch user role from 'user' to 'admin' and vice-versa.
2323
router.post('/switch',
2424
userController.verifySysadmin,
25+
userController.checkSysAdmin,
2526
userController.switchUserRole,
2627
(req, res) => {
2728
return res.status(200).json(res.locals.user);

src/components/helper/newUserHelper.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import store from '../../renderer/store';
66
import * as actions from '../../actions/actions';
77
import { NetworkCellSharp, NextWeek } from '@material-ui/icons';
88
import { username } from '../../../security/email';
9-
// TM - no react-redux imports?
109

1110
export const handleNewUser = (e) => {
1211
e.preventDefault();
@@ -136,13 +135,13 @@ export const getUpdatedUserList = () => {
136135
})
137136
.then((response) => response.json())
138137
.then((data) => {
139-
updateUserList(data);
138+
// updateUserList(data);
140139
})
141140
.catch((err) => {
142141
console.log('error in getUpdatedUserList: ', err);
143142
});
144143
};
145144

146-
export const updateUserList = (data) => { // TM: react-redux is not imported but this is still working... redux is stupid
145+
export const updateUserList = (data) => {
147146
store.dispatch(actions.updateUserList(data));
148147
};

src/components/tabs/Users.js

Lines changed: 110 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ const useStyles1 = makeStyles((theme) => ({
3939
},
4040
}));
4141

42-
// Function store of all pagination functions, these functions update the count, the current range of pages, etc. Essentially, any action on the pagination bar will be handled by this function store which is passed into the TablePagination component
42+
// Function store of all pagination functions, these functions update the count, the current range of pages, etc.
43+
// Essentially, any action on the pagination bar will be handled by this function store which is passed into the TablePagination component
4344
function TablePaginationActions(props) {
4445
const classes = useStyles1();
4546
const theme = useTheme();
@@ -117,18 +118,29 @@ const UserTable = () => {
117118
const dispatch = useDispatch();
118119
const updateUserRole = (data) => dispatch(actions.updateUserRole(data));
119120

120-
const tempSelected = {};
121+
// Create an object for each user-role checkbox - assign true/false based on the roles
122+
// from the state array
123+
// TODO: Add react useEffect once updated
124+
const tempAdminSelected = {};
125+
const tempSysAdminSelected = {};
126+
const tempUserSelected = {};
121127
for (let i = 0; i < rows.length; i++) {
122-
tempSelected[rows[i]._id] =
123-
rows[i].role === 'admin' || rows[i].role === 'sysadmin';
128+
tempSysAdminSelected[rows[i]._id] = rows[i].role === 'system admin';
129+
tempAdminSelected[rows[i]._id] = rows[i].role === 'admin';
130+
tempUserSelected[rows[i]._id] = rows[i].role === 'user';
124131
}
132+
console.log(tempAdminSelected);
125133

126134
// Local state variables for table pagination
127135
const [page, setPage] = useState(0);
128136
const [rowsPerPage, setRowsPerPage] = useState(5);
129137

130-
// Local state variable to keep track of which admin checkboxes are selected
131-
const [selected, setSelected] = React.useState(tempSelected);
138+
// Local state variable to keep track of which admin, sysadmin or user checkboxes are selected.
139+
const [selectedUser, setSelectedUser] = React.useState(tempUserSelected);
140+
const [selectedAdmin, setSelectedAdmin] = React.useState(tempAdminSelected); // tm
141+
const [selectedSysAdmin, setSelectedSysAdmin] = React.useState(tempSysAdminSelected);
142+
143+
132144

133145
// Determine how many empty rows to display on a certain page, e.g. if rowsPerPage = 5, and there are only 3 rows, should display 2 rows
134146
const emptyRows =
@@ -144,23 +156,57 @@ const UserTable = () => {
144156
setPage(0);
145157
};
146158

147-
const handleCheckboxClick = (event) => {
148-
// event persist is required to access target property of event. Without this, a synthetic event object would be used where target is re-assigned to null for performance reasons
149-
event.persist();
159+
const setSysAdmin = (id) => {
160+
const invertPreviousValue = !selectedSysAdmin[`S${id}`];
161+
const temp = {
162+
...selectedSysAdmin,
163+
};
164+
// update key of checkbox id to switched/inverted value
165+
temp[id] = invertPreviousValue;
166+
// update local state
167+
setSelectedSysAdmin(temp);
168+
return 1;
169+
};
150170

151-
const id = event.target.getAttribute('id');
152-
const invertPreviousValue = !selected[id];
171+
const setAdmin = (id) => {
172+
const invertPreviousValue = !selectedAdmin[id];
173+
// Create a temp copy of the selected object
174+
const temp = {
175+
...selectedAdmin,
176+
};
177+
temp[id] = invertPreviousValue;
178+
setSelectedAdmin(temp);
179+
return 2;
180+
};
153181

154-
// create temporary copy of selected object
182+
const setUser = (id) => {
183+
const invertPreviousValue = !selectedUser[`U${id}`];
155184
const temp = {
156-
...selected,
185+
...selectedUser,
157186
};
158-
// update key of checkbox id to switched/inverted value
159187
temp[id] = invertPreviousValue;
188+
setSelectedUser(temp);
189+
return 3;
190+
};
160191

161-
// update local state variable selected
162-
setSelected(temp);
192+
const handleCheckboxClick = (event) => {
193+
// event persist is required to access target property of event. Without this, a synthetic event object would be used where target is re-assigned to null for performance reasons
194+
event.persist();
163195

196+
let id = event.target.getAttribute('id');
197+
let role_id;
198+
199+
if(id.charAt(0) === 'S') {
200+
id = id.slice(1);
201+
role_id = setSysAdmin(id);
202+
} else if (id.charAt(0) === 'U'){
203+
id = id.slice(1);
204+
role_id = setUser(id);
205+
}
206+
else {
207+
role_id = setAdmin(id);
208+
}
209+
164210
// send request to endpoint http://localhost:3000/admin/switch
165211
fetch('http://localhost:3000/admin/switch', {
166212
method: 'POST',
@@ -169,7 +215,8 @@ const UserTable = () => {
169215
},
170216
body: JSON.stringify({
171217
_id: id,
172-
changeToAdmin: invertPreviousValue,
218+
role_id: role_id // TM - Need to add info if role needs to be downgraded
219+
// changeToAdmin: invertPreviousValue,
173220
}),
174221
})
175222
.then((response) => {
@@ -215,38 +262,58 @@ const UserTable = () => {
215262
<TableCell>Contact Preference</TableCell>
216263
<TableCell>Memory Threshold</TableCell>
217264
<TableCell>CPU Threshold</TableCell>
265+
<TableCell>User</TableCell>
218266
<TableCell>Admin</TableCell>
267+
<TableCell>SysAdmin</TableCell>
219268
</TableRow>
220269
</TableHead>
221270
<TableBody>
222271
{renderRows.length > 0
223272
? renderRows.map((row, index) => {
224-
// const isItemSelected = isSelected(row.name);
225-
const labelId = `enhanced-table-checkbox-${index}`;
226-
return (
227-
<TableRow key={row._id} hover>
228-
<TableCell component='th' scope='row'>
229-
{row._id}
230-
</TableCell>
231-
<TableCell>{row.username}</TableCell>
232-
<TableCell>{row.email}</TableCell>
233-
<TableCell>{row.phone}</TableCell>
234-
<TableCell>{row.role}</TableCell>
235-
<TableCell>{row.contact_pref}</TableCell>
236-
<TableCell>{row.mem_threshold}</TableCell>
237-
<TableCell>{row.cpu_threshold}</TableCell>
238-
<TableCell>
239-
<Checkbox
240-
id={`${row._id}`}
241-
userid={`${row._id}`}
242-
checked={selected[row._id]}
243-
inputProps={{ 'aria-labelledby': labelId }}
244-
onChange={handleCheckboxClick}
245-
/>
246-
</TableCell>
247-
</TableRow>
248-
);
249-
})
273+
// const isItemSelected = isSelected(row.name);
274+
const labelId = `enhanced-table-checkbox-${index}`;
275+
return (
276+
<TableRow key={row._id} hover>
277+
<TableCell component='th' scope='row'>
278+
{row._id}
279+
</TableCell>
280+
<TableCell>{row.username}</TableCell>
281+
<TableCell>{row.email}</TableCell>
282+
<TableCell>{row.phone}</TableCell>
283+
<TableCell>{row.role}</TableCell>
284+
<TableCell>{row.contact_pref}</TableCell>
285+
<TableCell>{row.mem_threshold}</TableCell>
286+
<TableCell>{row.cpu_threshold}</TableCell>
287+
<TableCell>
288+
<Checkbox
289+
id={`U${row._id}`}
290+
userid={`${row._id}`}
291+
checked={selectedUser[`U${row._id}`]}
292+
inputProps={{ 'aria-labelledby': labelId }}
293+
onChange={handleCheckboxClick}
294+
/>
295+
</TableCell>
296+
<TableCell>
297+
<Checkbox
298+
id={`${row._id}`}
299+
userid={`${row._id}`}
300+
checked={selectedAdmin[row._id]}
301+
inputProps={{ 'aria-labelledby': labelId }}
302+
onChange={handleCheckboxClick}
303+
/>
304+
</TableCell>
305+
<TableCell>
306+
<Checkbox
307+
id={`S${row._id}`} // Edit these details so it links to sysadmin
308+
userid={`${row._id}`}
309+
checked={selectedSysAdmin[`S${row._id}`]}
310+
inputProps={{ 'aria-labelledby': labelId }}
311+
onChange={handleCheckboxClick}
312+
/>
313+
</TableCell>
314+
</TableRow>
315+
);
316+
})
250317
: ''}
251318
</TableBody>
252319
<TableFooter>

src/reducers/sessionReducer.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ const initialState = {
1717
};
1818

1919
export default function (state = initialState, action){
20+
2021
switch(action.type) {
2122

2223
// Change isLoggedIn state variable depending on previous value
2324
case types.UPDATE_SESSION: {
25+
2426
return {
2527
...state,
2628
isLoggedIn : (!state.isLoggedIn)
@@ -29,6 +31,7 @@ export default function (state = initialState, action){
2931

3032
// Upon successful sign-up or login, update session state with all user info
3133
case types.UPDATE_USER: {
34+
3235
const {
3336
_id,
3437
username,

0 commit comments

Comments
 (0)