284 lines
7.8 KiB
TypeScript
284 lines
7.8 KiB
TypeScript
import {
|
|
Alert,
|
|
Box,
|
|
Button,
|
|
Card,
|
|
CardActions,
|
|
CardContent,
|
|
Checkbox,
|
|
FormControlLabel,
|
|
TextField,
|
|
Typography,
|
|
} from "@mui/material";
|
|
import React from "react";
|
|
import { ServerApi } from "../api/ServerApi";
|
|
import { ReplacePasswordResponse, User, UserApi } from "../api/UserApi";
|
|
import { useAlert } from "../hooks/context_providers/AlertDialogProvider";
|
|
import { useUser } from "../widgets/BaseAuthenticatedPage";
|
|
import { useConfirm } from "../hooks/context_providers/ConfirmDialogProvider";
|
|
import { PasswordInput } from "../widgets/PasswordInput";
|
|
import { formatDate } from "../widgets/TimeWidget";
|
|
|
|
export function ProfileRoute(): React.ReactElement {
|
|
const user = useUser();
|
|
|
|
return (
|
|
<div style={{ maxWidth: "500px", margin: "auto" }}>
|
|
<Typography variant="h3">Profil</Typography>
|
|
|
|
<ProfileSettingsCard
|
|
user={user.user}
|
|
onUpdate={() => user.reloadUserInfo()}
|
|
/>
|
|
{user.user.has_password && <ChangePasswordCard />}
|
|
<DeleteAccountButton />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ProfileSettingsCard(p: { user: User; onUpdate: () => void }) {
|
|
const [newName, setNewName] = React.useState(p.user.name);
|
|
|
|
const [error, setError] = React.useState<string | null>(null);
|
|
const [success, setSuccess] = React.useState<string | null>(null);
|
|
|
|
const updateProfile = async () => {
|
|
try {
|
|
setSuccess(null);
|
|
setError(null);
|
|
|
|
await UserApi.UpdateProfile(newName);
|
|
|
|
p.onUpdate();
|
|
setSuccess("Informations du profil enregistrées avec succès !");
|
|
} catch (e) {
|
|
console.error(e);
|
|
setError("Echec de la mise à jour du profil !");
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<Card style={{ marginTop: "10px" }}>
|
|
{error && <Alert severity="error">{error}</Alert>}
|
|
{success && <Alert severity="success">{success}</Alert>}
|
|
|
|
<CardContent>
|
|
<Typography gutterBottom variant="h5" component="div">
|
|
Paramètres du compte
|
|
</Typography>
|
|
|
|
<Box
|
|
component="form"
|
|
sx={{
|
|
"& .MuiTextField-root": { my: 1 },
|
|
}}
|
|
noValidate
|
|
autoComplete="off"
|
|
>
|
|
<TextField
|
|
disabled
|
|
fullWidth
|
|
label="Identifiant"
|
|
value={p.user.id}
|
|
/>
|
|
|
|
<TextField
|
|
disabled
|
|
fullWidth
|
|
label="Création du compte"
|
|
value={formatDate(p.user.time_create)}
|
|
/>
|
|
|
|
<TextField
|
|
disabled
|
|
fullWidth
|
|
label="Activation du compte"
|
|
value={formatDate(p.user.time_activate)}
|
|
/>
|
|
|
|
<TextField
|
|
disabled
|
|
fullWidth
|
|
label="Adresse mail"
|
|
value={p.user.email}
|
|
/>
|
|
|
|
<TextField
|
|
fullWidth
|
|
label="Nom d'utilisateur"
|
|
value={newName}
|
|
onChange={(e) => setNewName(e.target.value)}
|
|
inputProps={{
|
|
maxLength: ServerApi.Config.constraints.user_name_len.max,
|
|
}}
|
|
/>
|
|
|
|
<FormControlLabel
|
|
disabled
|
|
control={<Checkbox checked={p.user.admin} />}
|
|
label="Compte administrateur"
|
|
/>
|
|
</Box>
|
|
</CardContent>
|
|
<CardActions>
|
|
<Button onClick={updateProfile} style={{ marginLeft: "auto" }}>
|
|
Enregistrer
|
|
</Button>
|
|
</CardActions>
|
|
</Card>
|
|
</>
|
|
);
|
|
}
|
|
|
|
function ChangePasswordCard(): React.ReactElement {
|
|
const [loading, setLoading] = React.useState(false);
|
|
const [error, setError] = React.useState<string | null>(null);
|
|
const [success, setSuccess] = React.useState<string | null>(null);
|
|
|
|
const [oldPassword, setOldpassword] = React.useState("");
|
|
const [newPassword, setNewpassword] = React.useState("");
|
|
const [confirmNewPassword, setConfirmNewpassword] = React.useState("");
|
|
|
|
const isValid =
|
|
ServerApi.CheckPassword(newPassword) === null &&
|
|
oldPassword.length > 0 &&
|
|
confirmNewPassword === newPassword;
|
|
|
|
const updatePassword = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
|
|
if (!isValid || loading) return;
|
|
|
|
setLoading(true);
|
|
setSuccess(null);
|
|
setError(null);
|
|
|
|
try {
|
|
const result = await UserApi.ReplacePassword(oldPassword, newPassword);
|
|
|
|
switch (result) {
|
|
case ReplacePasswordResponse.Error:
|
|
setError("Echec du changement de mot de passe !");
|
|
break;
|
|
case ReplacePasswordResponse.Success:
|
|
setSuccess("Mot de passe changé avec succès !");
|
|
break;
|
|
case ReplacePasswordResponse.InvalidOldPassword:
|
|
setError("Ancien mot de passe saisi invalide !");
|
|
break;
|
|
case ReplacePasswordResponse.InvalidNewPassword:
|
|
setError("Nouveau mot de passe saisi invalide !");
|
|
break;
|
|
case ReplacePasswordResponse.TooManyRequests:
|
|
setError(
|
|
"Trop de tentatives de changement de mot de passe, veuillez réessayer ultérieurement !"
|
|
);
|
|
break;
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
setError("Echec de la mise à jour du mot de passe !");
|
|
}
|
|
|
|
setLoading(false);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<Card style={{ marginTop: "10px" }}>
|
|
{error && <Alert severity="error">{error}</Alert>}
|
|
{success && <Alert severity="success">{success}</Alert>}
|
|
<Box
|
|
component="form"
|
|
sx={{
|
|
"& .MuiTextField-root": { my: 1 },
|
|
}}
|
|
noValidate
|
|
autoComplete="off"
|
|
onSubmit={updatePassword}
|
|
>
|
|
<CardContent>
|
|
<Typography gutterBottom variant="h5" component="div">
|
|
Changement du mot de passe
|
|
</Typography>
|
|
|
|
<TextField
|
|
fullWidth
|
|
label="Mot de passe actuel"
|
|
value={oldPassword}
|
|
type="password"
|
|
onChange={(e) => setOldpassword(e.target.value)}
|
|
/>
|
|
|
|
<PasswordInput
|
|
value={newPassword}
|
|
onChange={(n) => setNewpassword(n)}
|
|
label={"Nouveau mot de passe"}
|
|
/>
|
|
|
|
<TextField
|
|
fullWidth
|
|
error={
|
|
confirmNewPassword !== "" && confirmNewPassword !== newPassword
|
|
}
|
|
helperText={
|
|
confirmNewPassword !== newPassword
|
|
? "Le nouveau mot de passe et sa confirmation doivent être identiques !"
|
|
: ""
|
|
}
|
|
label="Confirmation du nouveau mot de passe"
|
|
value={confirmNewPassword}
|
|
type="password"
|
|
onChange={(e) => setConfirmNewpassword(e.target.value)}
|
|
/>
|
|
</CardContent>
|
|
<CardActions>
|
|
<Button
|
|
disabled={!isValid && !loading}
|
|
type="submit"
|
|
style={{ marginLeft: "auto" }}
|
|
>
|
|
Enregistrer
|
|
</Button>
|
|
</CardActions>{" "}
|
|
</Box>
|
|
</Card>
|
|
</>
|
|
);
|
|
}
|
|
|
|
function DeleteAccountButton(): React.ReactElement {
|
|
const alert = useAlert();
|
|
const confirm = useConfirm();
|
|
|
|
const requestDelete = async () => {
|
|
try {
|
|
if (
|
|
!(await confirm(
|
|
"Voulez-vous initier la suppression de votre compte ?",
|
|
"Suppression de compte"
|
|
))
|
|
)
|
|
return;
|
|
|
|
await UserApi.RequestAccountDeletion();
|
|
|
|
await alert(
|
|
"Demande de suppression de compte enregistrée avec succès. Veuillez consulter votre boîte mail."
|
|
);
|
|
} catch (e) {
|
|
console.error(e);
|
|
alert("Echec de la demande de suppression de compte !");
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div style={{ textAlign: "center", margin: "15px 0px" }}>
|
|
<Button onClick={requestDelete} color="error">
|
|
Supprimer mon compte
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|