Can update profile information

This commit is contained in:
Pierre HUBERT 2023-06-14 14:14:46 +02:00
parent b64bc8fb6d
commit e3bec527f0
6 changed files with 197 additions and 6 deletions

View File

@ -22,6 +22,7 @@
"@types/node": "^16.18.34",
"@types/react": "^18.2.8",
"@types/react-dom": "^18.2.4",
"date-and-time": "^3.0.1",
"jotai": "^2.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@ -6846,6 +6847,11 @@
"node": ">=10"
}
},
"node_modules/date-and-time": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-3.0.1.tgz",
"integrity": "sha512-Az1hy3IPuh7LR4duPGlgkUzZXtjoWH8pd+XVLpLXMNd+DD48sRdcsWIPbqdHCXg4izb9lPzrKtqUXPXevS9g4Q=="
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -22546,6 +22552,11 @@
"whatwg-url": "^8.0.0"
}
},
"date-and-time": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-3.0.1.tgz",
"integrity": "sha512-Az1hy3IPuh7LR4duPGlgkUzZXtjoWH8pd+XVLpLXMNd+DD48sRdcsWIPbqdHCXg4izb9lPzrKtqUXPXevS9g4Q=="
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",

View File

@ -17,6 +17,7 @@
"@types/node": "^16.18.34",
"@types/react": "^18.2.8",
"@types/react-dom": "^18.2.4",
"date-and-time": "^3.0.1",
"jotai": "^2.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@ -47,8 +47,7 @@ export class APIClient {
// Handle expired tokens
if (res.status === 412) {
AuthApi.RemoveAuthToken();
// eslint-disable-next-line no-self-assign
window.location.href = window.location.href;
window.location.href = "/";
}
if (!args.allowFail && !res.ok)

View File

@ -23,4 +23,17 @@ export class UserApi {
})
).data;
}
/**
* Update user profile
*/
static async UpdateProfile(name: string): Promise<void> {
await APIClient.exec({
uri: "/user/update_profile",
method: "POST",
jsonData: {
name: name,
},
});
}
}

View File

@ -1,25 +1,129 @@
import React, { useRef } from "react";
import { AsyncWidget } from "../widgets/AsyncWidget";
import { User, UserApi } from "../api/UserApi";
import {
Alert,
Box,
Button,
Card,
CardActions,
CardContent,
Checkbox,
FormControlLabel,
TextField,
Typography,
} from "@mui/material";
import { TimeWidget, formatDate } from "../widgets/TimeWidget";
import { ServerApi } from "../api/ServerApi";
export function ProfileRoute(): React.ReactElement {
const [user, setUser] = React.useState<null | User>(null);
const [newName, setNewName] = React.useState("");
const [error, setError] = React.useState<string | null>(null);
const [success, setSuccess] = React.useState<string | null>(null);
const load = async () => {
setUser(await UserApi.GetUserInfo());
const u = await UserApi.GetUserInfo();
setUser(u);
setNewName(u.name);
};
const counter = useRef(0);
const updateProfile = async () => {
try {
setSuccess(null);
setError(null);
await UserApi.UpdateProfile(newName);
counter.current += 1;
setSuccess("Informations du profil mises à jour avec succès !");
} catch (e) {
console.error(e);
setError("Echec de la mise à jour du profil !");
}
};
return (
<AsyncWidget
loadKey={counter.current}
load={load}
errMsg="Echec du chargement des informations du compte utilisateur !"
build={() => (
<>
<p>ready !!! {user!.name}</p>
</>
<div style={{ maxWidth: "500px", margin: "auto" }}>
<Typography variant="h3">Profil</Typography>
{error && <Alert severity="error">{error}</Alert>}
{success && <Alert severity="success">{success}</Alert>}
<Card style={{ marginTop: "10px" }}>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
Paramètres du compte
</Typography>
<Box
component="form"
sx={{
"& .MuiTextField-root": { m: 1 },
}}
noValidate
autoComplete="off"
>
<TextField
disabled
fullWidth
label="Identifiant"
value={user?.id}
/>
<TextField
disabled
fullWidth
label="Création du compte"
value={formatDate(user!.time_create)}
/>
<TextField
disabled
fullWidth
label="Activation du compte"
value={formatDate(user!.time_activate)}
/>
<TextField
disabled
fullWidth
label="Adresse mail"
value={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={user!.admin} />}
label="Compte administrateur"
/>
</Box>
</CardContent>
<CardActions>
<Button onClick={updateProfile} style={{ marginLeft: "auto" }}>
Enregistrer
</Button>
</CardActions>
</Card>
</div>
)}
/>
);

View File

@ -0,0 +1,63 @@
import { Tooltip } from "@mui/material";
import date from "date-and-time";
export function formatDate(time: number): string {
const t = new Date();
t.setTime(1000 * time);
return date.format(t, "DD/MM/YYYY HH:mm:ss");
}
export function timeDiff(a: number, b: number): string {
let diff = b - a;
if (diff === 0) return "maintenant";
if (diff === 1) return "1 seconde";
if (diff < 60) {
return `${diff} secondes`;
}
diff = Math.floor(diff / 60);
if (diff === 1) return "1 minute";
if (diff < 24) {
return `${diff} minutes`;
}
diff = Math.floor(diff / 60);
if (diff === 1) return "1 heure";
if (diff < 24) {
return `${diff} heures`;
}
const diffDays = Math.floor(diff / 24);
if (diffDays === 1) return "1 jour";
if (diffDays < 31) {
return `${diffDays} jours`;
}
diff = Math.floor(diffDays / 31);
if (diff < 12) {
return `${diff} mois`;
}
const diffYears = Math.floor(diffDays / 365);
if (diffYears === 1) return "1 an";
return `${diffYears} ans`;
}
export function timeDiffFromNow(time: number): string {
return timeDiff(time, Math.floor(new Date().getTime() / 1000));
}
export function TimeWidget(p: { time: number }): React.ReactElement {
return (
<Tooltip title={formatDate(p.time)}>
<span>{timeDiffFromNow(p.time)}</span>
</Tooltip>
);
}