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> ); }