Display the list of registered keys

This commit is contained in:
Pierre HUBERT 2021-05-14 14:47:13 +02:00
parent 41d568e493
commit 7ac554e90e
4 changed files with 191 additions and 43 deletions

View File

@ -33,6 +33,12 @@ export interface NewAdminGeneralSettings {
email: string;
}
export interface AdminAccountKey {
id: number;
name: string;
time_add: number;
}
const SESSION_STORAGE_TOKEN = "auth_token";
let currentAccount: AdminAccount | null = null;
@ -248,4 +254,15 @@ export class AccountHelper {
sessionStorage.setItem(SESSION_STORAGE_TOKEN, res.token);
}
/**
* Get the list of keys of an admin
*
* @param adminID The id of the target administrator
*/
static async GetAdminKeys(adminID: number): Promise<AdminAccountKey[]> {
return await serverRequest("accounts/keys", {
id: adminID,
});
}
}

View File

@ -9,15 +9,25 @@ import {
Divider,
Grid,
Paper,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
TextField,
Typography,
} from "@material-ui/core";
import React from "react";
import { useParams } from "react-router-dom";
import { AccountHelper, AdminAccount } from "../../helpers/AccountHelper";
import {
AccountHelper,
AdminAccount,
AdminAccountKey,
} from "../../helpers/AccountHelper";
import { AsyncWidget } from "../widgets/AsyncWidget";
import { input, matAlert, snackbar } from "../widgets/DialogsProvider";
import { PageTitle } from "../widgets/PageTitle";
import { TimestampWidget } from "../widgets/TimestampWidget";
export function AccountSettingsRoute() {
let params: any = useParams();
@ -70,7 +80,9 @@ class AccountSettingsRouteInner extends React.Component<
admin={this.state.account}
></GeneralSettings>
<KeySettingsSection></KeySettingsSection>
<KeySettingsSection
admin={this.state.account}
></KeySettingsSection>
</Grid>
</div>
);
@ -126,37 +138,63 @@ class GeneralSettings extends React.Component<
render() {
return (
<SettingsSection title="General settings">
<TextField
required
label="Name"
value={this.state.newName}
onChange={this.changedName}
style={{ width: "100%", paddingBottom: "20px" }}
/>
<div style={{ margin: "10px" }}>
<TextField
required
label="Name"
value={this.state.newName}
onChange={this.changedName}
style={{ width: "100%", paddingBottom: "20px" }}
/>
<TextField
required
label="Email"
value={this.state.newEmail}
onChange={this.changedEmail}
type="mail"
style={{ width: "100%", paddingBottom: "20px" }}
/>
<TextField
required
label="Email"
value={this.state.newEmail}
onChange={this.changedEmail}
type="mail"
style={{ width: "100%", paddingBottom: "20px" }}
/>
<Button
style={{ alignSelf: "end", marginRight: "10px" }}
disabled={!this.isValid}
onClick={this.handleSubmit}
>
Update
</Button>
<div style={{ textAlign: "right" }}>
<Button
style={{ alignSelf: "end", marginRight: "10px" }}
disabled={!this.isValid}
onClick={this.handleSubmit}
>
Update
</Button>
</div>
</div>
</SettingsSection>
);
}
}
function KeySettingsSection() {
const registerNewKey = async () => {
export class KeySettingsSection extends React.Component<
{ admin: AdminAccount },
{ keys: AdminAccountKey[]; counter: number }
> {
constructor(props: any) {
super(props);
this.state = {
keys: [],
counter: 1,
};
this.load = this.load.bind(this);
this.build = this.build.bind(this);
this.registerNewKey = this.registerNewKey.bind(this);
}
async load() {
const keys = await AccountHelper.GetAdminKeys(this.props.admin.id);
this.setState({ keys: keys });
}
async registerNewKey() {
try {
const challenge = await AccountHelper.GetKeyRegistrationChallenge();
const credential = await navigator.credentials.create(challenge);
@ -176,32 +214,79 @@ function KeySettingsSection() {
console.error(e);
matAlert("Failed to register a new key!");
}
};
}
return (
<SettingsSection title="Key setttings">
<Button
style={{ alignSelf: "end", marginRight: "10px" }}
disabled={false /* TODO : adapt if other admin*/}
onClick={registerNewKey}
>
Register a new key
</Button>
</SettingsSection>
);
render() {
return (
<AsyncWidget
errorMessage="Failed to load admin keys!"
load={this.load}
onBuild={this.build}
key={this.props.admin.id + "-" + this.state.counter}
></AsyncWidget>
);
}
build() {
return (
<SettingsSection title="Key setttings">
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Key name</TableCell>
<TableCell align="right">Date added</TableCell>
<TableCell align="right">Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.keys.map((key) => (
<TableRow key={key.id}>
<TableCell component="th" scope="row">
{key.name}
</TableCell>
<TableCell align="right">
<TimestampWidget time={key.time_add} />
</TableCell>
<TableCell align="right">Delete</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<Divider />
{/* Action buttons */}
<div
style={{
textAlign: "right",
margin: "5px 10px",
}}
>
<Button
disabled={
this.props.admin.id !==
AccountHelper.currentAccount.id
}
onClick={this.registerNewKey}
>
Register a new key
</Button>
</div>
</SettingsSection>
);
}
}
function SettingsSection(p: { title: string; children?: React.ReactNode }) {
return (
<Grid item sm={6} spacing={2}>
<Grid item sm={6}>
<Paper>
<Typography variant="h5" style={{ padding: "10px 10px " }}>
General settings
{p.title}
</Typography>
<Divider />
<div
style={{
padding: "10px 10px",
display: "flex",
flexDirection: "column",
}}

View File

@ -15,7 +15,7 @@ enum Status {
}
export interface AsyncWidgetProperties {
key?: number;
key?: number | string;
load: () => Promise<void>;
errorMessage: string;
onBuild: () => ReactNode;
@ -23,7 +23,7 @@ export interface AsyncWidgetProperties {
interface AsyncWidgetState {
status: Status;
key?: number;
key?: number | string;
}
export class AsyncWidget extends React.Component<

View File

@ -0,0 +1,46 @@
import { Tooltip } from "@material-ui/core";
import React from "react";
/**
* Display in human-readable way a timestamp
*/
export function TimestampWidget(props: { time: number }) {
const date = new Date(props.time * 1000);
const currDate = new Date();
const diff = Math.floor(currDate.getTime() / 1000 - props.time);
let diffStr = diff + " seconds ago";
if (diff === 0) diffStr = "Just now";
else if (diff === 1) diffStr = "1 second ago";
else if (diff === 60) diffStr = "1 minute ago";
if (diff > 60) diffStr = Math.floor(diff / 60) + " minutes ago";
if (diff === 3600) diffStr = "1 hour ago";
if (diff > 3600) diffStr = Math.floor(diff / 3600) + " hours ago";
const daysAgo = Math.floor(diff / (3600 * 24));
if (daysAgo === 1) diffStr = "1 day ago";
else if (daysAgo > 1) diffStr = daysAgo + " days ago";
const monthsAgo = Math.floor(daysAgo / 30);
if (monthsAgo === 1) diffStr = "1 month ago";
else if (monthsAgo > 1) diffStr = monthsAgo + " months ago";
const yearsAgo = Math.floor(monthsAgo / 12);
if (yearsAgo === 1) diffStr = "1 year ago";
else if (yearsAgo > 1) diffStr = yearsAgo + " years ago";
return (
<Tooltip
disableFocusListener
title={date.toString()}
placement="top"
arrow
>
<span>{diffStr}</span>
</Tooltip>
);
}