Skip to content

Commit

Permalink
Feat: users list page with deletion button for admins.
Browse files Browse the repository at this point in the history
  • Loading branch information
dleclercpro committed May 16, 2024
1 parent cc42b14 commit 5fddef0
Show file tree
Hide file tree
Showing 17 changed files with 213 additions and 73 deletions.
8 changes: 8 additions & 0 deletions Apps/Client/src/components/pages/AuthPageStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ const useAuthPageStyles = makeStyles()(({ breakpoints, spacing }: Theme) => ({
marginBottom: spacing(1 * SPACING),
},

table: {
marginBottom: spacing(SPACING),
},

fields: {
border: 0,
padding: 0,
Expand Down Expand Up @@ -89,6 +93,10 @@ const useAuthPageStyles = makeStyles()(({ breakpoints, spacing }: Theme) => ({
},
},

button: {
width: '100%',
},

linkButton: {
height: spacing(BUTTON_HEIGHT),
},
Expand Down
1 change: 1 addition & 0 deletions Apps/Client/src/components/pages/sign-up/SignUpPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ const SignUpPage: React.FC<Props> = () => {
to={getURL(Page.SignIn)}
color='secondary'
startIcon={<BackIcon />}
disabled={loading}
>
Back
</Button>
Expand Down
168 changes: 116 additions & 52 deletions Apps/Client/src/components/pages/users/UsersPage.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { Button, Paper, Typography } from '@mui/material';
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import useAuthPageStyles from '../AuthPageStyles';
import useDatabase from '../../../hooks/useDatabase';
import { Page, getURL } from '../../../routes/Router';
import { Link } from 'react-router-dom';
import BackIcon from '@mui/icons-material/ArrowBack';
import DeleteIcon from '@mui/icons-material/Delete';
import LoadingButton from '../../buttons/LoadingButton';
import YesNoDialog from '../../dialogs/YesNoDialog';

interface Props {

Expand All @@ -13,64 +16,125 @@ interface Props {
const UsersPage: React.FC<Props> = () => {
const { classes } = useAuthPageStyles();

const { users, admins, getUsers } = useDatabase();
const [version, setVersion] = useState(0);

const [userEmail, setUserEmail] = useState('');

const [isDeleteUserConfirmDialogOpen, setIsDeleteUserConfirmDialogOpen] = useState(false);

const openDeleteUserConfirmDialog = (email: string) => {
setUserEmail(email);
setIsDeleteUserConfirmDialogOpen(true);
}
const closeDeleteUserConfirmDialog = () => {
setUserEmail('');
setIsDeleteUserConfirmDialogOpen(false);
}

const handleDeleteUser = async () => {
setIsDeleteUserConfirmDialogOpen(false);

await deleteUser(userEmail);

setVersion(version + 1);
}

const { users, admins, isDeletingUser, deleteUser, getUsers } = useDatabase();

useEffect(() => {
getUsers();
}, []);

}, [version]);

if (!users || !admins) {
return null;
}

return (
<Paper elevation={8} className={classes.root}>
<div
className={`${classes.form} users`}
>
<Typography variant='h1' className={classes.title}>
Users
</Typography>

<Typography className={classes.text}>
Here is the list of admin users:
</Typography>

<ul>
{admins.map((admin: string) => (
<li>
<Typography>
<strong>{admin}</strong>
</Typography>
</li>
))}
</ul>

<Typography className={classes.text}>
Here is the list of regular users:
</Typography>

<ul>
{users.map((user: string) => (
<li>
<Typography>
<strong>{user}</strong>
<>
<YesNoDialog
open={isDeleteUserConfirmDialogOpen}
title='Delete user'
text={`Are you sure you want to delete user '${userEmail}'?`}
handleYes={handleDeleteUser}
handleNo={closeDeleteUserConfirmDialog}
handleClose={closeDeleteUserConfirmDialog}
/>

<Paper elevation={8} className={classes.root}>
<div
className={`${classes.form} users`}
>
<Typography variant='h1' className={classes.title}>
Users
</Typography>

<Typography className={classes.text}>
Here is the list of admin users:
</Typography>

<table className={classes.table}>
<tbody>
{admins.map((admin) => (
<tr>
<td>
<Typography>
<strong>{admin}</strong>
</Typography>
</td>
</tr>
))}
</tbody>
</table>

{users.length > 0 && (
<>
<Typography className={classes.text}>
Here is the list of regular users:
</Typography>
</li>
))}
</ul>
</div>

<div className={classes.buttons}>
<div className='top'>
<Button
className={classes.linkButton}
component={Link}
to={getURL(Page.Home)}
color='secondary'
startIcon={<BackIcon />}
>
Back
</Button>
</div>

<table className={classes.table}>
{users.map((email) => (
<tr>
<td>
<Typography>
<strong>{email}</strong>
</Typography>
</td>
<td>
<LoadingButton
className={classes.button}
variant='contained'
color='error'
icon={<DeleteIcon />}
loading={isDeletingUser && email === userEmail}
onClick={() => openDeleteUserConfirmDialog(email)}
>
Delete
</LoadingButton>
</td>
</tr>
))}
</table>
</>
)}
</div>
</Paper>

<div className={classes.buttons}>
<div className='top'>
<Button
className={classes.linkButton}
component={Link}
to={getURL(Page.Home)}
color='secondary'
startIcon={<BackIcon />}
>
Back
</Button>
</div>
</div>
</Paper>
</>
);
}

Expand Down
2 changes: 1 addition & 1 deletion Apps/Client/src/hooks/useDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const useDatabase = () => {
const deleteUser = async (email: string) => {
setIsDeletingUser(true);

return await new CallDeleteUser.default().execute()
return await new CallDeleteUser.default().execute({ email })
.then(() => {

})
Expand Down
2 changes: 1 addition & 1 deletion Apps/Client/src/hooks/useToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const useToken = <T extends Token> (value: string) => {

return new CallValidateToken.default().execute({ token: value })
.then(({ data }) => {
setValidatedValue(data!.string);
setValidatedValue((data as CallValidateToken.ResponseData).string);
setToken(data as T);
})
.catch(({ code, error, data }) => {
Expand Down
2 changes: 1 addition & 1 deletion Apps/Client/src/models/calls/admin/CallFlushDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface ErrorResponseData {

}

export default class CallFlushDatabase extends CallDELETE {
export default class CallFlushDatabase extends CallDELETE<Data, ResponseData, ErrorResponseData> {

constructor() {
super(`/database`);
Expand Down
14 changes: 13 additions & 1 deletion Apps/Client/src/models/calls/auth/CallConfirmEmail.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import CallPUT from '../base/CallPUT';

export default class CallConfirmEmail extends CallPUT<void, void> {
export type Data = {

};

export type ResponseData = {

};

export type ErrorResponseData = {

};

export default class CallConfirmEmail extends CallPUT<Data, ResponseData, ErrorResponseData> {

constructor(token: string) {
super(`/confirm-email?token=${token}`);
Expand Down
10 changes: 9 additions & 1 deletion Apps/Client/src/models/calls/auth/CallForgotPassword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ export interface Data {
email: string,
}

export default class CallForgotPassword extends CallPOST<Data, void> {
export type ResponseData = {

};

export type ErrorResponseData = {

};

export default class CallForgotPassword extends CallPOST<Data, ResponseData, ErrorResponseData> {

constructor() {
super(`/forgot-password`);
Expand Down
2 changes: 1 addition & 1 deletion Apps/Client/src/models/calls/auth/CallPing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface ErrorResponseData {

}

export default class CallPing extends CallGET<void, ResponseData> {
export default class CallPing extends CallGET<Data, ResponseData, ErrorResponseData> {

constructor() {
super(`/ping`);
Expand Down
10 changes: 9 additions & 1 deletion Apps/Client/src/models/calls/auth/CallResetPassword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ export interface Data {
password: string,
}

export default class CallResetPassword extends CallPOST<Data, void> {
export type ResponseData = {

};

export type ErrorResponseData = {

};

export default class CallResetPassword extends CallPOST<Data, ResponseData, ErrorResponseData> {

constructor(token?: string) {
const url = `/reset-password`;
Expand Down
14 changes: 13 additions & 1 deletion Apps/Client/src/models/calls/auth/CallSignOut.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import CallGET from '../base/CallGET';

export default class CallSignOut extends CallGET<void> {
export type Data = {

};

export type ResponseData = {

};

export type ErrorResponseData = {

};

export default class CallSignOut extends CallGET<Data, ResponseData, ErrorResponseData> {

constructor() {
super(`/sign-out`);
Expand Down
10 changes: 9 additions & 1 deletion Apps/Client/src/models/calls/auth/CallSignUp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ export interface Data {
password: string,
}

export default class CallSignUp extends CallPOST<Data, void> {
export type ResponseData = {

};

export type ErrorResponseData = {

};

export default class CallSignUp extends CallPOST<Data, ResponseData, ErrorResponseData> {

constructor() {
super(`/sign-up`);
Expand Down
6 changes: 5 additions & 1 deletion Apps/Client/src/models/calls/auth/CallValidateToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ export interface ResponseData {
content: object,
}

export default class CallValidateToken extends CallPUT<Data, ResponseData> {
export type ErrorResponseData = {

};

export default class CallValidateToken extends CallPUT<Data, ResponseData, ErrorResponseData> {

constructor() {
super(`/token`);
Expand Down
4 changes: 2 additions & 2 deletions Apps/Client/src/models/calls/user/CallDeleteUser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import CallDELETE from '../base/CallDELETE';

export interface Data {

email: string,
}

export type ResponseData = {
Expand All @@ -12,7 +12,7 @@ export interface ErrorResponseData {

}

export default class CallDeleteUser extends CallDELETE {
export default class CallDeleteUser extends CallDELETE<Data, ResponseData, ErrorResponseData> {

constructor() {
super(`/user`);
Expand Down
6 changes: 5 additions & 1 deletion Apps/Client/src/models/calls/user/CallGetSecret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ export type Data = {

export type ResponseData = string;

export default class CallGetSecret extends CallPOST<Data, ResponseData> {
export type ErrorResponseData = {

};

export default class CallGetSecret extends CallPOST<Data, ResponseData, ErrorResponseData> {

constructor() {
super(`/secret`);
Expand Down
Loading

0 comments on commit 5fddef0

Please sign in to comment.