Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

S2 4.1/history page #39

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions src/components/dashboard/UsersManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ 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";
import { useTranslation } from 'react-i18next';

interface User {
email: string;
Expand All @@ -29,6 +30,7 @@ interface User {
}

const UsersManagement: React.FC = () => {
const { t } = useTranslation();
const [selectedRights, setSelectedRights] = useState<Record<string, string>>(
{},
);
Expand Down Expand Up @@ -57,14 +59,14 @@ const UsersManagement: React.FC = () => {
...prevSelectedRights,
[email]: e.target.value as string,
}));
const admin = e.target.value === "Administrateur"; // Cette ligne transforme la valeur en un booléen
const admin = e.target.value === "Administrateur";
await changeRightsClick(email, admin);
}}
sx={{ width: "150px" }}
label="Changer les droitas"
>
<MenuItem value="Utilisateur">Utilisateur</MenuItem>
<MenuItem value="Administrateur">Administrateur</MenuItem>
<MenuItem value="Utilisateur">User</MenuItem>
<MenuItem value="Administrateur">Admin</MenuItem>
</Select>
</>
);
Expand Down Expand Up @@ -157,17 +159,17 @@ const UsersManagement: React.FC = () => {
return (
<Grid container direction="column" spacing={2}>
<Grid item>
<Typography variant="h4">Gestion des utilisateurs</Typography>
<Typography variant="h4">{t('usersManagement.title')}</Typography>
</Grid>
<Grid item>
<Typography variant="h6" mb={2}>
Inviter un nouvel utilisateur
{t('usersManagement.inviteSection')}
</Typography>
<Grid container spacing={2} direction="column">
<Grid item>
<TextField
type="email"
label="Email nouvel utilisateur"
label={t('usersManagement.emailLabel')}
variant="outlined"
fullWidth
value={email}
Expand All @@ -178,18 +180,16 @@ const UsersManagement: React.FC = () => {
<Button
variant="contained"
color="primary"
onClick={() => {
inviteUserClick(email);
}}
onClick={() => inviteUserClick(email)}
>
Envoyer l'invitation
{t('usersManagement.sendInvitation')}
</Button>
</Grid>
</Grid>
</Grid>
<Grid item>
<Typography variant="h6" gutterBottom>
Liste des utilisateurs
{t('usersManagement.userList')}
</Typography>
</Grid>
<Grid item xs sx={{ marginBottom: 0.4 }}>
Expand Down
23 changes: 23 additions & 0 deletions src/hooks/backend/userService/useFetchHistoryRPC.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from "react";
import { transport } from "../../../environment";
import { HistoryClient } from "@protos/history.client";
import { HistoryRequest } from "@protos/history";
import AuthContext from "@contexts/AuthContext";

const useFetchHistoryRPC = () => {
const client = React.useMemo(() => new HistoryClient(transport), []);
const { token } = React.useContext(AuthContext);

const fetchHistory = React.useCallback(async () => {
const request: HistoryRequest = HistoryRequest.create({});
return await client.getHistory(request, {
meta: { Authorization: `Bearer ${token}` },
});
}, [client, token]);

return {
fetchHistory,
};
};

export default useFetchHistoryRPC;
28 changes: 25 additions & 3 deletions src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@
"description": "Description",
"safe": "Safe",
"danger": "Danger",
"dangerHigh": "High danger level",
"dangerMedium": "Medium danger level",
"dangerHigh": "He changed his email",
"dangerMedium": "He logged into his account",
"dangerLow": "Low danger level"
},
"helpModal": {
Expand All @@ -109,6 +109,28 @@
"danger": "Danger"
},
"AuthContext": {
"logout": "You have been successfully logged out "
"logout": "You have been successfully logged out "
},
"invitationSignup": {
"choosePassword": "Choose your new password",
"accountCreated": "Your account has been successfully created",
"passwordLabel": "Password",
"usedInvitation": "Invitation already used!",
"createAccount": "Create an account"
},
"usersManagement": {
"title": "User Management",
"inviteUser": "Invite a new user",
"inviteSection": "Invite a new user",
"emailLabel": "Email new user",
"sendInvitation": "Send Invitation",
"userList": "List of users",
"activated": "Activated",
"pendingActivation": "Pending Activation",
"changeRightsError": "An error occurred while changing user rights.",
"deleteUserError": "An error occurred while deleting the user.",
"inviteSuccess": "User successfully invited!",
"inviteError": "An error occurred while inviting the user.",
"alreadyExists": "An account with this email already exists."
}
}
26 changes: 24 additions & 2 deletions src/locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@
"description": "Description",
"safe": "Sûr",
"danger": "Dangereux",
"dangerHigh": "Niveau de danger : élevé",
"dangerMedium": "Niveau de danger : moyen",
"dangerHigh": "Il a change son mail",
"dangerMedium": "Il s'est connecté à son compte",
"dangerLow": "Niveau de danger : faible"
},
"helpModal": {
Expand All @@ -110,5 +110,27 @@
},
"AuthContext": {
"logout": "Vous avez été déconnecté avec succès"
},
"invitationSignup": {
"choosePassword": "Choisissez votre nouveau mot de passe",
"accountCreated": "Votre compte a été correctement créé",
"passwordLabel": "Mot de passe",
"usedInvitation": "Invitation déjà utilisée!",
"createAccount": "Créer un compte"
},
"usersManagement": {
"title": "Gestion des utilisateurs",
"inviteUser": "Inviter un nouvel utilisateur",
"inviteSection": "Inviter un nouvel utilisateur",
"emailLabel": "Email nouvel utilisateur",
"sendInvitation": "Envoyer l'invitation",
"userList": "Liste des utilisateurs",
"activated": "Activé",
"pendingActivation": "En attente d'activation",
"changeRightsError": "Une erreur s'est produite lors du changement de droit de l'utilisateur.",
"deleteUserError": "Une erreur s'est produite lors de la suppression de l'utilisateur.",
"inviteSuccess": "Utilisateur invité avec succès!",
"inviteError": "Une erreur s'est produite lors de l'invitation de l'utilisateur.",
"alreadyExists": "Un compte avec cet email existe déjà."
}
}
105 changes: 105 additions & 0 deletions src/pages/HistoryPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React, { useState, useEffect, ChangeEvent } from 'react';
import {Typography, TextField, Chip, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper } from '@mui/material';
import SearchIcon from '@mui/icons-material/Search';
import ErrorIcon from '@mui/icons-material/Error';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import { useTranslation } from 'react-i18next';
import useFetchHistoryRPC from '../hooks/backend/userService/useFetchHistoryRPC';
import { CircularProgress } from '@mui/material';


interface Action {
id: number;
user: string;
type: 'danger' | 'safe';
dangerKey: string;
date: string;
time: string;
emailChanged?: boolean;
}

const HistoryPage: React.FC = () => {
const [actions, setActions] = useState<Action[]>([]);
const [searchTerm, setSearchTerm] = useState<string>('');
const { t } = useTranslation();
const { fetchHistory } = useFetchHistoryRPC();
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchData = () => {
setTimeout(() => {
const data: Action[] = [
{ id: 1, user: '[email protected]', type: 'safe', dangerKey: 'dangerMedium', date: '21-09-2023', time: '09:43' },
{ id: 2, user: '[email protected]', type: 'safe', dangerKey: 'dangerHigh', date: '20-09-2023', time: '15:43' },
];

data.sort((a, b) => new Date(b.date + 'T' + b.time).getTime() - new Date(a.date + 'T' + a.time).getTime());
setActions(data);
setLoading(false);
}, 2000);
};
fetchData();
}, []);
if (loading) {
return <CircularProgress />;
}
return (

<div className="history-page">
<Typography variant="h4" component="div" gutterBottom className="title">
{t('historyPage.title')}
</Typography>
<div className="search-bar">
<SearchIcon />
<TextField
id="search"
label={t('historyPage.search')}
variant="outlined"
value={searchTerm}
onChange={(event: ChangeEvent<HTMLInputElement>) => setSearchTerm(event.target.value)}
/>
</div>
<TableContainer component={Paper} className="history-table">
<Table>
<TableHead>
<TableRow>
<TableCell>{t('historyPage.date')}</TableCell>
<TableCell>{t('historyPage.time')}</TableCell>
<TableCell>{t('historyPage.type')}</TableCell>
<TableCell>{t('historyPage.user')}</TableCell>
<TableCell>{t('historyPage.description')}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{actions
.filter(action => action.user.includes(searchTerm) || action.type.includes(searchTerm))
.map(action => (
<TableRow key={action.id}>
<TableCell>{action.date}</TableCell>
<TableCell>{action.time}</TableCell>
<TableCell>
<Chip
icon={
action.type === 'safe' ?
<CheckCircleIcon style={{ color: 'white' }} />
:
<ErrorIcon style={{ color: 'white' }} />
}
label={t(`historyPage.${action.type}`)}
style={{
backgroundColor: action.type === 'safe' ? 'green' : 'red',
color: 'white',
}}
/>
</TableCell>
<TableCell>{action.user}</TableCell>
<TableCell>{t(`historyPage.${action.dangerKey}`)}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer> </div>
);
};

export default HistoryPage;
7 changes: 6 additions & 1 deletion src/pages/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import BlockManager from '@components/dashboard/BlockManager';
import Others from '@components/dashboard/Others';
import { useTranslation } from "react-i18next";
import UsersManagement from '../components/dashboard/UsersManagement';
import History from '../pages/HistoryPage';


const HomePage = () => {
const { isLoggedIn, logout } = useContext(AuthContext);
Expand Down Expand Up @@ -51,6 +53,8 @@ const HomePage = () => {
return (<div><BlockManager /></div>);
case 'usersManagement':
return(<UsersManagement />)
case 'history':
return (<History />);
default:

}
Expand All @@ -65,7 +69,8 @@ const HomePage = () => {
<li onClick={() => handleMenuClick('ipManagement')}>{t('homePage.ipManagement')}</li>
<li onClick={() => handleMenuClick('containerManager')}>{t('homePage.containerManager')}</li>
<li onClick={() => handleMenuClick('incomingConnections')}>{t('homePage.incomingConnections')}</li>
<li onClick={() => handleMenuClick('usersManagement')}>Gestion des utilisateurs</li>
<li onClick={() => handleMenuClick('usersManagement')}>{t('usersManagement.title')}</li>
<li onClick={() => handleMenuClick('history')}>{t('homePage.history')}</li>
<li onClick={() => handleMenuClick('otherFeatures')}>{t('homePage.otherFeatures')}</li>
</ul>
</div>
Expand Down
14 changes: 7 additions & 7 deletions src/pages/Invitation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,28 @@ const InvitationSignup: React.FC = () => {

const signIn = async () => {
try {
setError(null); // Clear any previous errors before attempting the operation again
setError(null);
const loginToken = await activateUser(activationToken, password);
loginWithToken(loginToken);
toast.success(t("loginPage.loginSuccess"));
toast.success(t('loginPage.loginSuccess'));
setSubmitted(true);
} catch (err) {
setError("Invitation déjà utilisée!"); // Set the error state
setError(t('invitationSignup.usedInvitation')); // Set the error state
}
};

return (
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: 'calc(100vh - 110px)' }}>
<Paper sx={{ p: 2, width: '25em' }}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<Typography variant="h6">Choisissez votre nouveau mot de passe</Typography>
{submitted && (<Typography>Votre compte a été correctement créé</Typography>)}
<Typography variant="h6">{t('invitationSignup.choosePassword')}</Typography>
{submitted && (<Typography>{t('invitationSignup.accountCreated')}</Typography>)}
{!submitted && (
<>
<TextField
type="password"
name='password'
label="Mot de passe"
label={t('invitationSignup.passwordLabel')}
value={password}
required
onChange={(e) => setPassword(e.target.value)}
Expand All @@ -49,7 +49,7 @@ const InvitationSignup: React.FC = () => {
<Typography color="error" variant="body2">{error}</Typography> // Display error inline
)}
<Button type="submit" variant="contained" color="primary" onClick={signIn} >
Créer un compte
{t('invitationSignup.createAccount')}
</Button>
</>
)}
Expand Down
Empty file added src/pages/save
Empty file.
Loading