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;
|
email: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AdminAccountKey {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
time_add: number;
|
||||||
|
}
|
||||||
|
|
||||||
const SESSION_STORAGE_TOKEN = "auth_token";
|
const SESSION_STORAGE_TOKEN = "auth_token";
|
||||||
|
|
||||||
let currentAccount: AdminAccount | null = null;
|
let currentAccount: AdminAccount | null = null;
|
||||||
@ -248,4 +254,15 @@ export class AccountHelper {
|
|||||||
|
|
||||||
sessionStorage.setItem(SESSION_STORAGE_TOKEN, res.token);
|
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,
|
Divider,
|
||||||
Grid,
|
Grid,
|
||||||
Paper,
|
Paper,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useParams } from "react-router-dom";
|
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 { AsyncWidget } from "../widgets/AsyncWidget";
|
||||||
import { input, matAlert, snackbar } from "../widgets/DialogsProvider";
|
import { input, matAlert, snackbar } from "../widgets/DialogsProvider";
|
||||||
import { PageTitle } from "../widgets/PageTitle";
|
import { PageTitle } from "../widgets/PageTitle";
|
||||||
|
import { TimestampWidget } from "../widgets/TimestampWidget";
|
||||||
|
|
||||||
export function AccountSettingsRoute() {
|
export function AccountSettingsRoute() {
|
||||||
let params: any = useParams();
|
let params: any = useParams();
|
||||||
@ -70,7 +80,9 @@ class AccountSettingsRouteInner extends React.Component<
|
|||||||
admin={this.state.account}
|
admin={this.state.account}
|
||||||
></GeneralSettings>
|
></GeneralSettings>
|
||||||
|
|
||||||
<KeySettingsSection></KeySettingsSection>
|
<KeySettingsSection
|
||||||
|
admin={this.state.account}
|
||||||
|
></KeySettingsSection>
|
||||||
</Grid>
|
</Grid>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -126,37 +138,63 @@ class GeneralSettings extends React.Component<
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<SettingsSection title="General settings">
|
<SettingsSection title="General settings">
|
||||||
<TextField
|
<div style={{ margin: "10px" }}>
|
||||||
required
|
<TextField
|
||||||
label="Name"
|
required
|
||||||
value={this.state.newName}
|
label="Name"
|
||||||
onChange={this.changedName}
|
value={this.state.newName}
|
||||||
style={{ width: "100%", paddingBottom: "20px" }}
|
onChange={this.changedName}
|
||||||
/>
|
style={{ width: "100%", paddingBottom: "20px" }}
|
||||||
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
required
|
required
|
||||||
label="Email"
|
label="Email"
|
||||||
value={this.state.newEmail}
|
value={this.state.newEmail}
|
||||||
onChange={this.changedEmail}
|
onChange={this.changedEmail}
|
||||||
type="mail"
|
type="mail"
|
||||||
style={{ width: "100%", paddingBottom: "20px" }}
|
style={{ width: "100%", paddingBottom: "20px" }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<div style={{ textAlign: "right" }}>
|
||||||
style={{ alignSelf: "end", marginRight: "10px" }}
|
<Button
|
||||||
disabled={!this.isValid}
|
style={{ alignSelf: "end", marginRight: "10px" }}
|
||||||
onClick={this.handleSubmit}
|
disabled={!this.isValid}
|
||||||
>
|
onClick={this.handleSubmit}
|
||||||
Update
|
>
|
||||||
</Button>
|
Update
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function KeySettingsSection() {
|
export class KeySettingsSection extends React.Component<
|
||||||
const registerNewKey = async () => {
|
{ 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 {
|
try {
|
||||||
const challenge = await AccountHelper.GetKeyRegistrationChallenge();
|
const challenge = await AccountHelper.GetKeyRegistrationChallenge();
|
||||||
const credential = await navigator.credentials.create(challenge);
|
const credential = await navigator.credentials.create(challenge);
|
||||||
@ -176,32 +214,79 @@ function KeySettingsSection() {
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
matAlert("Failed to register a new key!");
|
matAlert("Failed to register a new key!");
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
render() {
|
||||||
<SettingsSection title="Key setttings">
|
return (
|
||||||
<Button
|
<AsyncWidget
|
||||||
style={{ alignSelf: "end", marginRight: "10px" }}
|
errorMessage="Failed to load admin keys!"
|
||||||
disabled={false /* TODO : adapt if other admin*/}
|
load={this.load}
|
||||||
onClick={registerNewKey}
|
onBuild={this.build}
|
||||||
>
|
key={this.props.admin.id + "-" + this.state.counter}
|
||||||
Register a new key
|
></AsyncWidget>
|
||||||
</Button>
|
);
|
||||||
</SettingsSection>
|
}
|
||||||
);
|
|
||||||
|
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 }) {
|
function SettingsSection(p: { title: string; children?: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<Grid item sm={6} spacing={2}>
|
<Grid item sm={6}>
|
||||||
<Paper>
|
<Paper>
|
||||||
<Typography variant="h5" style={{ padding: "10px 10px " }}>
|
<Typography variant="h5" style={{ padding: "10px 10px " }}>
|
||||||
General settings
|
{p.title}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Divider />
|
<Divider />
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
padding: "10px 10px",
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
|
@ -15,7 +15,7 @@ enum Status {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface AsyncWidgetProperties {
|
export interface AsyncWidgetProperties {
|
||||||
key?: number;
|
key?: number | string;
|
||||||
load: () => Promise<void>;
|
load: () => Promise<void>;
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
onBuild: () => ReactNode;
|
onBuild: () => ReactNode;
|
||||||
@ -23,7 +23,7 @@ export interface AsyncWidgetProperties {
|
|||||||
|
|
||||||
interface AsyncWidgetState {
|
interface AsyncWidgetState {
|
||||||
status: Status;
|
status: Status;
|
||||||
key?: number;
|
key?: number | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AsyncWidget extends React.Component<
|
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