mirror of
https://gitlab.com/comunic/comunicconsole
synced 2024-11-23 13:59:23 +00:00
Display the list of registered keys
This commit is contained in:
parent
41d568e493
commit
7ac554e90e
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
}}
|
||||
|
@ -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<
|
||||
|
46
src/ui/widgets/TimestampWidget.tsx
Normal file
46
src/ui/widgets/TimestampWidget.tsx
Normal 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>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user