mirror of
https://gitlab.com/comunic/comunicconsole
synced 2024-11-23 13:59:23 +00:00
Refactor code
This commit is contained in:
parent
69c68f43cb
commit
6c4427d06b
@ -17,6 +17,7 @@ export interface AdminAccount {
|
|||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
time_create: number;
|
time_create: number;
|
||||||
|
roles: Array<"manage_admins" | "manage_users" | "access_full_admin_logs">;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NewAdminGeneralSettings {
|
export interface NewAdminGeneralSettings {
|
||||||
@ -32,7 +33,7 @@ export interface AdminResetToken {
|
|||||||
|
|
||||||
export const SESSION_STORAGE_TOKEN = "auth_token";
|
export const SESSION_STORAGE_TOKEN = "auth_token";
|
||||||
|
|
||||||
let currentAccount: AdminAccount | null = null;
|
let currentAccount: AdminAccount;
|
||||||
|
|
||||||
export class AccountHelper {
|
export class AccountHelper {
|
||||||
/**
|
/**
|
||||||
|
@ -12,7 +12,7 @@ export interface AdminRole {
|
|||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
let RolesList: AdminRole[] = [];
|
export let RolesList: AdminRole[] = [];
|
||||||
|
|
||||||
export class AdminRolesHelper {
|
export class AdminRolesHelper {
|
||||||
/**
|
/**
|
||||||
|
93
src/ui/accountSettings/GeneralSettings.tsx
Normal file
93
src/ui/accountSettings/GeneralSettings.tsx
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
* Account general settings section
|
||||||
|
*
|
||||||
|
* @author Pierre Hubert
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TextField, Button } from "@material-ui/core";
|
||||||
|
import React from "react";
|
||||||
|
import { AdminAccount, AccountHelper } from "../../helpers/AccountHelper";
|
||||||
|
import { snackbar, matAlert } from "../widgets/DialogsProvider";
|
||||||
|
import { SettingsSection } from "./SettingsSection";
|
||||||
|
|
||||||
|
export class GeneralSettings extends React.Component<
|
||||||
|
{ admin: AdminAccount },
|
||||||
|
{ newName: string; newEmail: string }
|
||||||
|
> {
|
||||||
|
constructor(p: any) {
|
||||||
|
super(p);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
newName: this.props.admin.name,
|
||||||
|
newEmail: this.props.admin.email,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.changedName = this.changedName.bind(this);
|
||||||
|
this.changedEmail = this.changedEmail.bind(this);
|
||||||
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
changedName(e: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
this.setState({ newName: e.target.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
changedEmail(e: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
this.setState({ newEmail: e.target.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
get isValid(): boolean {
|
||||||
|
return this.state.newEmail.length > 2 && this.state.newName.length > 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleSubmit() {
|
||||||
|
try {
|
||||||
|
if (!this.isValid) return;
|
||||||
|
|
||||||
|
await AccountHelper.UpdateGeneralSettings({
|
||||||
|
id: this.props.admin.id,
|
||||||
|
name: this.state.newName,
|
||||||
|
email: this.state.newEmail,
|
||||||
|
});
|
||||||
|
|
||||||
|
snackbar("Successfully updated admin settings!");
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
matAlert("Failed to update admin settings!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<SettingsSection title="General settings">
|
||||||
|
<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" }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div style={{ textAlign: "right" }}>
|
||||||
|
<Button
|
||||||
|
style={{ alignSelf: "end", marginRight: "10px" }}
|
||||||
|
disabled={!this.isValid}
|
||||||
|
onClick={this.handleSubmit}
|
||||||
|
>
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SettingsSection>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
186
src/ui/accountSettings/KeySettingsSection.tsx
Normal file
186
src/ui/accountSettings/KeySettingsSection.tsx
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
|
TableCell,
|
||||||
|
TableBody,
|
||||||
|
IconButton,
|
||||||
|
Divider,
|
||||||
|
Button,
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import { Delete } from "@material-ui/icons";
|
||||||
|
import React from "react";
|
||||||
|
import { AdminAccount, AccountHelper } from "../../helpers/AccountHelper";
|
||||||
|
import { AdminAccountKey, AdminKeyHelper } from "../../helpers/AdminKeyHelper";
|
||||||
|
import { CopyToClipboard } from "../../utils/ClipboardUtils";
|
||||||
|
import { AsyncWidget } from "../widgets/AsyncWidget";
|
||||||
|
import {
|
||||||
|
matConfirm,
|
||||||
|
snackbar,
|
||||||
|
matAlert,
|
||||||
|
input,
|
||||||
|
} from "../widgets/DialogsProvider";
|
||||||
|
import { TimestampWidget } from "../widgets/TimestampWidget";
|
||||||
|
import { SettingsSection } from "./SettingsSection";
|
||||||
|
|
||||||
|
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.generateResetToken = this.generateResetToken.bind(this);
|
||||||
|
this.registerNewKey = this.registerNewKey.bind(this);
|
||||||
|
this.deleteKey = this.deleteKey.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
const keys = await AdminKeyHelper.GetAdminKeys(this.props.admin.id);
|
||||||
|
|
||||||
|
this.setState({ keys: keys });
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateResetToken() {
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
!(await matConfirm(
|
||||||
|
"Do you really want to generate a reset token for this account?"
|
||||||
|
))
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const token = await AccountHelper.GenerateResetToken(
|
||||||
|
this.props.admin.id
|
||||||
|
);
|
||||||
|
|
||||||
|
CopyToClipboard(token.token);
|
||||||
|
snackbar("Reset token was successfully copied to the clipboard!");
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
matAlert("Failed to generate a token!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async registerNewKey() {
|
||||||
|
try {
|
||||||
|
const challenge =
|
||||||
|
await AdminKeyHelper.GetKeyRegistrationChallenge();
|
||||||
|
const credential = await navigator.credentials.create(challenge);
|
||||||
|
|
||||||
|
if (credential == null) throw new Error("Operation aborted!");
|
||||||
|
|
||||||
|
const name = await input({
|
||||||
|
label: "Key name",
|
||||||
|
maxLength: 40,
|
||||||
|
minLength: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await AdminKeyHelper.RegisterKey(name, credential);
|
||||||
|
|
||||||
|
snackbar("Successfully enrolled a new key!");
|
||||||
|
|
||||||
|
this.setState({ counter: this.state.counter + 1 });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
matAlert("Failed to register a new key!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteKey(key: AdminAccountKey) {
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
!(await matConfirm(
|
||||||
|
"Do you really want to delete the key '" + key.name + "' ?"
|
||||||
|
))
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await AdminKeyHelper.DeleteAuthKey(this.props.admin.id, key.id);
|
||||||
|
|
||||||
|
snackbar("The key was successfully deleted!");
|
||||||
|
this.setState({ counter: this.state.counter + 1 });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
matAlert("Failed to delete key!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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="Security keys">
|
||||||
|
<Table aria-label="simple table">
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Key name</TableCell>
|
||||||
|
<TableCell align="right">Date added</TableCell>
|
||||||
|
<TableCell align="right"></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">
|
||||||
|
<IconButton
|
||||||
|
aria-label="delete"
|
||||||
|
size="small"
|
||||||
|
onClick={() => this.deleteKey(key)}
|
||||||
|
>
|
||||||
|
<Delete />
|
||||||
|
</IconButton>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
{/* Action buttons */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
textAlign: "right",
|
||||||
|
margin: "5px 10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button onClick={this.generateResetToken}>
|
||||||
|
New reset token
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
disabled={
|
||||||
|
this.props.admin.id !==
|
||||||
|
AccountHelper.currentAccount.id
|
||||||
|
}
|
||||||
|
onClick={this.registerNewKey}
|
||||||
|
>
|
||||||
|
Register a new key
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</SettingsSection>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
26
src/ui/accountSettings/SettingsSection.tsx
Normal file
26
src/ui/accountSettings/SettingsSection.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { Grid, Paper, Typography, Divider } from "@material-ui/core";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export function SettingsSection(p: {
|
||||||
|
title: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Grid item sm={6}>
|
||||||
|
<Paper>
|
||||||
|
<Typography variant="h6" style={{ padding: "10px 15px " }}>
|
||||||
|
{p.title}
|
||||||
|
</Typography>
|
||||||
|
<Divider />
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{p.children}
|
||||||
|
</div>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
@ -4,35 +4,14 @@
|
|||||||
* @author Pierre Hubert
|
* @author Pierre Hubert
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { Grid } from "@material-ui/core";
|
||||||
Button,
|
|
||||||
Divider,
|
|
||||||
Grid,
|
|
||||||
IconButton,
|
|
||||||
Paper,
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
TextField,
|
|
||||||
Typography,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
import { Delete } from "@material-ui/icons";
|
|
||||||
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 } from "../../helpers/AccountHelper";
|
||||||
import { AdminAccountKey, AdminKeyHelper } from "../../helpers/AdminKeyHelper";
|
import { GeneralSettings } from "../accountSettings/GeneralSettings";
|
||||||
import { CopyToClipboard } from "../../utils/ClipboardUtils";
|
import { KeySettingsSection } from "../accountSettings/KeySettingsSection";
|
||||||
import { AsyncWidget } from "../widgets/AsyncWidget";
|
import { AsyncWidget } from "../widgets/AsyncWidget";
|
||||||
import {
|
|
||||||
input,
|
|
||||||
matAlert,
|
|
||||||
matConfirm,
|
|
||||||
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();
|
||||||
@ -93,268 +72,3 @@ class AccountSettingsRouteInner extends React.Component<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GeneralSettings extends React.Component<
|
|
||||||
{ admin: AdminAccount },
|
|
||||||
{ newName: string; newEmail: string }
|
|
||||||
> {
|
|
||||||
constructor(p: any) {
|
|
||||||
super(p);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
newName: this.props.admin.name,
|
|
||||||
newEmail: this.props.admin.email,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.changedName = this.changedName.bind(this);
|
|
||||||
this.changedEmail = this.changedEmail.bind(this);
|
|
||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
changedName(e: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
this.setState({ newName: e.target.value });
|
|
||||||
}
|
|
||||||
|
|
||||||
changedEmail(e: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
this.setState({ newEmail: e.target.value });
|
|
||||||
}
|
|
||||||
|
|
||||||
get isValid(): boolean {
|
|
||||||
return this.state.newEmail.length > 2 && this.state.newName.length > 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleSubmit() {
|
|
||||||
try {
|
|
||||||
if (!this.isValid) return;
|
|
||||||
|
|
||||||
await AccountHelper.UpdateGeneralSettings({
|
|
||||||
id: this.props.admin.id,
|
|
||||||
name: this.state.newName,
|
|
||||||
email: this.state.newEmail,
|
|
||||||
});
|
|
||||||
|
|
||||||
snackbar("Successfully updated admin settings!");
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
matAlert("Failed to update admin settings!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<SettingsSection title="General settings">
|
|
||||||
<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" }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div style={{ textAlign: "right" }}>
|
|
||||||
<Button
|
|
||||||
style={{ alignSelf: "end", marginRight: "10px" }}
|
|
||||||
disabled={!this.isValid}
|
|
||||||
onClick={this.handleSubmit}
|
|
||||||
>
|
|
||||||
Update
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</SettingsSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.generateResetToken = this.generateResetToken.bind(this);
|
|
||||||
this.registerNewKey = this.registerNewKey.bind(this);
|
|
||||||
this.deleteKey = this.deleteKey.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
async load() {
|
|
||||||
const keys = await AdminKeyHelper.GetAdminKeys(this.props.admin.id);
|
|
||||||
|
|
||||||
this.setState({ keys: keys });
|
|
||||||
}
|
|
||||||
|
|
||||||
async generateResetToken() {
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
!(await matConfirm(
|
|
||||||
"Do you really want to generate a reset token for this account?"
|
|
||||||
))
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const token = await AccountHelper.GenerateResetToken(
|
|
||||||
this.props.admin.id
|
|
||||||
);
|
|
||||||
|
|
||||||
CopyToClipboard(token.token);
|
|
||||||
snackbar("Reset token was successfully copied to the clipboard!");
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
matAlert("Failed to generate a token!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async registerNewKey() {
|
|
||||||
try {
|
|
||||||
const challenge =
|
|
||||||
await AdminKeyHelper.GetKeyRegistrationChallenge();
|
|
||||||
const credential = await navigator.credentials.create(challenge);
|
|
||||||
|
|
||||||
if (credential == null) throw new Error("Operation aborted!");
|
|
||||||
|
|
||||||
const name = await input({
|
|
||||||
label: "Key name",
|
|
||||||
maxLength: 40,
|
|
||||||
minLength: 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
await AdminKeyHelper.RegisterKey(name, credential);
|
|
||||||
|
|
||||||
snackbar("Successfully enrolled a new key!");
|
|
||||||
|
|
||||||
this.setState({ counter: this.state.counter + 1 });
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
matAlert("Failed to register a new key!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteKey(key: AdminAccountKey) {
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
!(await matConfirm(
|
|
||||||
"Do you really want to delete the key '" + key.name + "' ?"
|
|
||||||
))
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
await AdminKeyHelper.DeleteAuthKey(this.props.admin.id, key.id);
|
|
||||||
|
|
||||||
snackbar("The key was successfully deleted!");
|
|
||||||
this.setState({ counter: this.state.counter + 1 });
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
matAlert("Failed to delete key!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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="Security keys">
|
|
||||||
<Table aria-label="simple table">
|
|
||||||
<TableHead>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell>Key name</TableCell>
|
|
||||||
<TableCell align="right">Date added</TableCell>
|
|
||||||
<TableCell align="right"></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">
|
|
||||||
<IconButton
|
|
||||||
aria-label="delete"
|
|
||||||
size="small"
|
|
||||||
onClick={() => this.deleteKey(key)}
|
|
||||||
>
|
|
||||||
<Delete />
|
|
||||||
</IconButton>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{/* Action buttons */}
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
textAlign: "right",
|
|
||||||
margin: "5px 10px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button onClick={this.generateResetToken}>
|
|
||||||
New reset token
|
|
||||||
</Button>
|
|
||||||
<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}>
|
|
||||||
<Paper>
|
|
||||||
<Typography variant="h6" style={{ padding: "10px 15px " }}>
|
|
||||||
{p.title}
|
|
||||||
</Typography>
|
|
||||||
<Divider />
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{p.children}
|
|
||||||
</div>
|
|
||||||
</Paper>
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
11
src/utils/AccountUtils.ts
Normal file
11
src/utils/AccountUtils.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Account utilities
|
||||||
|
*
|
||||||
|
* @author Pierre Hubert
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { AccountHelper } from "../helpers/AccountHelper";
|
||||||
|
|
||||||
|
export function canManageAdmins(): boolean {
|
||||||
|
return AccountHelper.currentAccount.roles.includes("manage_admins");
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user