Skip to content

Commit

Permalink
Merge pull request #38 from Honeybrain/error-handling
Browse files Browse the repository at this point in the history
🥅 Add error handling for GRPC
  • Loading branch information
valentinbreiz authored Sep 19, 2023
2 parents a59a7f2 + fd4b3d7 commit ce7081f
Show file tree
Hide file tree
Showing 4 changed files with 303 additions and 218 deletions.
222 changes: 135 additions & 87 deletions src/components/dashboard/UsersManagement.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
import React, { useState, useEffect } from 'react';
import React, { useEffect, useState } from "react";
import {
Typography, TextField, Button, Grid, List, ListItem, ListItemText, Box, Select, MenuItem, Card, CardContent, Snackbar, Alert, IconButton
} from '@mui/material';
import useGetUsersRPC from '@hooks/backend/userService/useGetUsersRPC';
import useInviteUserRPC from '@hooks/backend/userService/useInviteUserRPC';
import useChangeRightsRPC from '@hooks/backend/userService/useChangeRightsRPC';
import useDeleteUserRPC from '@hooks/backend/userService/useDeleteUserRPC';
import DeleteIcon from '@mui/icons-material/Delete';
Alert,
Box,
Button,
Grid,
IconButton,
List,
ListItem,
ListItemText,
MenuItem,
Select,
Snackbar,
TextField,
Typography,
} from "@mui/material";
import useGetUsersRPC from "@hooks/backend/userService/useGetUsersRPC";
import useInviteUserRPC from "@hooks/backend/userService/useInviteUserRPC";
import useChangeRightsRPC from "@hooks/backend/userService/useChangeRightsRPC";
import useDeleteUserRPC from "@hooks/backend/userService/useDeleteUserRPC";
import DeleteIcon from "@mui/icons-material/Delete";
import { RpcError } from "@protobuf-ts/runtime-rpc";

interface User {
email: string;
Expand All @@ -16,13 +29,17 @@ interface User {
}

const UsersManagement: React.FC = () => {
const [selectedRights, setSelectedRights] = useState<Record<string, string>>({});
const [selectedRights, setSelectedRights] = useState<Record<string, string>>(
{},
);
const [email, setEmail] = useState<string>("");
const [users, setUsers] = useState<User[]>([]);

const [open, setOpen] = React.useState(false);
const [alertText, setAlertText] = React.useState('');
const [alertSeverity, setAlertSeverity] = useState<'success' | 'error'>('success');
const [alertText, setAlertText] = React.useState("");
const [alertSeverity, setAlertSeverity] = useState<"success" | "error">(
"success",
);

const { getUsers } = useGetUsersRPC();
const { inviteUser } = useInviteUserRPC();
Expand All @@ -32,16 +49,18 @@ const UsersManagement: React.FC = () => {
const myButton = (email: string, right: boolean) => (
<>
<Select
value={selectedRights[email] || (right ? "Administrateur" : "Utilisateur")}
onChange={(e) => {
value={
selectedRights[email] || (right ? "Administrateur" : "Utilisateur")
}
onChange={async (e) => {
setSelectedRights((prevSelectedRights) => ({
...prevSelectedRights,
[email]: e.target.value as string,
}));
const admin = e.target.value === "Administrateur"; // Cette ligne transforme la valeur en un booléen
changeRightsClick(email, admin);
const admin = e.target.value === "Administrateur"; // Cette ligne transforme la valeur en un booléen
await changeRightsClick(email, admin);
}}
sx={{ width: '150px' }}
sx={{ width: "150px" }}
label="Changer les droitas"
>
<MenuItem value="Utilisateur">Utilisateur</MenuItem>
Expand All @@ -51,62 +70,79 @@ const UsersManagement: React.FC = () => {
);

const deleteButton = (email: string) => (
<IconButton edge="end" sx={{ marginLeft: '3px' }} aria-label="delete" onClick={() => { deleteUserClick(email); }}>
<DeleteIcon color="error" />
</IconButton>
)
<IconButton
edge="end"
sx={{ marginLeft: "3px" }}
aria-label="delete"
onClick={() => {
deleteUserClick(email);
}}
>
<DeleteIcon color="error" />
</IconButton>
);

const changeRightsClick = async (email: string, admin: boolean) => {
try {
changeRights(email, admin);
await changeRights(email, admin);
} catch (error: any) {
setAlertText("Une erreur s'est produite lors du changement de droit de l'utilisateur.");
setAlertSeverity('error');
setAlertText(
"Une erreur s'est produite lors du changement de droit de l'utilisateur.",
);
setAlertSeverity("error");
setOpen(true);
}
};

const deleteUserClick = async (email: string) => {
try {
deleteUser(email);
fetchUsers();
} catch (error: any) {
setAlertText("Une erreur s'est produite lors de la suppression de l'utilisateur.");
setAlertSeverity('error');
await deleteUser(email);
await fetchUsers();
} catch (error) {
setAlertText(
"Une erreur s'est produite lors de la suppression de l'utilisateur.",
);
setAlertSeverity("error");
setOpen(true);
}
};

const inviteUserClick = async (email: string) => {
try {
inviteUser(email);
await inviteUser(email);
setAlertText("Utilisateur invité avec succès!");
setAlertSeverity('success');
setAlertSeverity("success");
setOpen(true);
fetchUsers();
} catch (error: any) {
if (error.toString().toLowerCase().includes("exists")) {
setAlertText("Un compte avec cet email existe déjà.");
} else {
setAlertText("Une erreur s'est produite lors de l'invitation de l'utilisateur.");
}
setAlertSeverity('error');
await fetchUsers();
} catch (error) {
setAlertSeverity("error");
setOpen(true);
if (error instanceof RpcError) {
if (error.code == "ALREADY_EXISTS")
return setAlertText("Un compte avec cet email existe déjà.");
}
setAlertText(
"Une erreur s'est produite lors de l'invitation de l'utilisateur.",
);
}
};

const fetchUsers = async () => {
try {
const fetchedUsers = (await getUsers()).map((userString: string) => JSON.parse(userString) as User);
setUsers(fetchedUsers);

const initialRights: Record<string, string> = {};
fetchedUsers.forEach(user => {
initialRights[user.email] = user.admin ? "Administrateur" : "Utilisateur";
});
setSelectedRights(initialRights);
const fetchedUsers = (await getUsers()).map(
(userString: string) => JSON.parse(userString) as User,
);
setUsers(fetchedUsers);

const initialRights: Record<string, string> = {};
fetchedUsers.forEach((user) => {
initialRights[user.email] = user.admin
? "Administrateur"
: "Utilisateur";
});
setSelectedRights(initialRights);
} catch (error) {
console.error("Erreur lors de la récupération des utilisateurs:", error);
console.error("Erreur lors de la récupération des utilisateurs:", error);
}
};

Expand All @@ -121,31 +157,34 @@ const UsersManagement: React.FC = () => {
return (
<Grid container direction="column" spacing={2}>
<Grid item>
<Typography variant="h4">
Gestion des utilisateurs
</Typography>
<Typography variant="h4">Gestion des utilisateurs</Typography>
</Grid>
<Grid item>
<Typography variant="h6" mb={2}>Inviter un nouvel utilisateur</Typography>
<Typography variant="h6" mb={2}>
Inviter un nouvel utilisateur
</Typography>
<Grid container spacing={2} direction="column">
<Grid item>
<TextField
type="email"
label="Email nouvel utilisateur"
variant="outlined"
fullWidth
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</Grid>
<Grid item>
<Button
variant="contained"
color="primary"
onClick={() => { inviteUserClick(email) }}>
Envoyer l'invitation
</Button>
</Grid>
<Grid item>
<TextField
type="email"
label="Email nouvel utilisateur"
variant="outlined"
fullWidth
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</Grid>
<Grid item>
<Button
variant="contained"
color="primary"
onClick={() => {
inviteUserClick(email);
}}
>
Envoyer l'invitation
</Button>
</Grid>
</Grid>
</Grid>
<Grid item>
Expand All @@ -156,31 +195,37 @@ const UsersManagement: React.FC = () => {
<Grid item xs sx={{ marginBottom: 0.4 }}>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
alignItems: 'stretch',
height: '100%',
maxHeight: 'calc(100vh - 440px)', // Set max height
overflow: 'auto',
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
alignItems: "stretch",
height: "100%",
maxHeight: "calc(100vh - 440px)", // Set max height
overflow: "auto",
}}
>
<List>
{users.map((user, index) => (
<ListItem key={index} sx={{
my: 1,
px: 2,
bgcolor: index % 2 === 0 ? 'action.hover' : 'background.default',
borderRadius: 1
}}>
<ListItem
key={index}
sx={{
my: 1,
px: 2,
bgcolor:
index % 2 === 0 ? "action.hover" : "background.default",
borderRadius: 1,
}}
>
<ListItemText
primary={`${user.email}`}
secondary={`${user.activated ? "Activé" : "En attente d'activation"}`}
secondary={`${
user.activated ? "Activé" : "En attente d'activation"
}`}
/>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
{myButton(user.email, user.admin)}
</Box>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
{deleteButton(user.email)}
</Box>
</ListItem>
Expand All @@ -189,13 +234,16 @@ const UsersManagement: React.FC = () => {
</Box>
</Grid>
<Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
<Alert onClose={handleClose} severity={alertSeverity} sx={{ width: '100%' }}>
<Alert
onClose={handleClose}
severity={alertSeverity}
sx={{ width: "100%" }}
>
{alertText}
</Alert>
</Snackbar>
</Grid>
);

};

export default UsersManagement;
13 changes: 5 additions & 8 deletions src/hooks/backend/userService/useInviteUserRPC.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import React from "react";
import { transport } from "../../../environment";
import { UserClient } from '@protos/user.client';
import { InviteUserRequest } from '@protos/user';
import { UserClient } from "@protos/user.client";
import { InviteUserRequest } from "@protos/user";

const useInviteUserRPC = () => {
const client = React.useMemo(() => new UserClient(transport), []);

const inviteUser = React.useCallback( async (email: string) => {
const inviteUser = React.useCallback(async (email: string) => {
const request: InviteUserRequest = InviteUserRequest.create();
request.email = email;
request.admin = true;
await client.inviteUser(request, {});
}, []);

return {
inviteUser,
};

return { inviteUser };
};

export default useInviteUserRPC;
export default useInviteUserRPC;
Loading

0 comments on commit ce7081f

Please sign in to comment.