Display the list of API tokens
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||
import { Alert, AlertTitle, IconButton, Tooltip } from "@mui/material";
|
||||
import type { GridColDef } from "@mui/x-data-grid";
|
||||
import { DataGrid } from "@mui/x-data-grid";
|
||||
import { QRCodeCanvas } from "qrcode.react";
|
||||
import React from "react";
|
||||
import { APIClient } from "../api/ApiClient";
|
||||
@@ -9,6 +11,7 @@ import { CreateTokenDialog } from "../dialogs/CreateTokenDialog";
|
||||
import { AsyncWidget } from "../widgets/AsyncWidget";
|
||||
import { CopyTextChip } from "../widgets/CopyTextChip";
|
||||
import { MatrixGWRouteContainer } from "../widgets/MatrixGWRouteContainer";
|
||||
import { TimeWidget } from "../widgets/TimeWidget";
|
||||
|
||||
export function APITokensRoute(): React.ReactElement {
|
||||
const count = React.useRef(0);
|
||||
@@ -75,7 +78,9 @@ export function APITokensRoute(): React.ReactElement {
|
||||
ready={list !== undefined}
|
||||
load={load}
|
||||
errMsg="Failed to load the list of tokens!"
|
||||
build={() => <>{list?.length} tokens</>}
|
||||
build={() => (
|
||||
<TokensListGrid list={list!} onReload={handleRefreshTokensList} />
|
||||
)}
|
||||
/>
|
||||
</MatrixGWRouteContainer>
|
||||
);
|
||||
@@ -117,3 +122,92 @@ function CreatedToken(p: { token: TokenWithSecret }): React.ReactElement {
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
function TokensListGrid(p: {
|
||||
list: Token[];
|
||||
onReload: () => void;
|
||||
}): React.ReactElement {
|
||||
const columns: GridColDef<(typeof p.list)[number]>[] = [
|
||||
{ field: "id", headerName: "ID", flex: 1 },
|
||||
{
|
||||
field: "name",
|
||||
headerName: "Name",
|
||||
flex: 3,
|
||||
},
|
||||
{
|
||||
field: "networks",
|
||||
headerName: "Networks restriction",
|
||||
flex: 3,
|
||||
renderCell(params) {
|
||||
return (
|
||||
params.row.networks?.join(", ") ?? (
|
||||
<span style={{ fontStyle: "italic" }}>Unrestricted</span>
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "created",
|
||||
headerName: "Creation",
|
||||
flex: 3,
|
||||
renderCell(params) {
|
||||
return <TimeWidget time={params.row.created} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "last_used",
|
||||
headerName: "Last usage",
|
||||
flex: 3,
|
||||
renderCell(params) {
|
||||
return <TimeWidget time={params.row.last_used} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "max_inactivity",
|
||||
headerName: "Max inactivity",
|
||||
flex: 3,
|
||||
renderCell(params) {
|
||||
return <TimeWidget time={params.row.max_inactivity} isDuration />;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "expiration",
|
||||
headerName: "Expiration",
|
||||
flex: 3,
|
||||
renderCell(params) {
|
||||
return <TimeWidget time={params.row.expiration} showDate />;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "read_only",
|
||||
headerName: "Read only",
|
||||
flex: 2,
|
||||
type: "boolean",
|
||||
},
|
||||
];
|
||||
|
||||
if (p.list.length === 0)
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
You do not have created any token yet!
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<DataGrid
|
||||
style={{ flex: "1" }}
|
||||
rows={p.list}
|
||||
columns={columns}
|
||||
autoPageSize
|
||||
getRowId={(c) => c.id}
|
||||
isCellEditable={() => false}
|
||||
isRowSelectable={() => false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
86
matrixgw_frontend/src/widgets/TimeWidget.tsx
Normal file
86
matrixgw_frontend/src/widgets/TimeWidget.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Tooltip } from "@mui/material";
|
||||
import { format } from "date-and-time";
|
||||
import { time } from "../utils/DateUtils";
|
||||
|
||||
export function formatDateTime(time: number): string {
|
||||
const t = new Date();
|
||||
t.setTime(1000 * time);
|
||||
return format(t, "DD/MM/YYYY HH:mm:ss");
|
||||
}
|
||||
|
||||
export function formatDate(time: number): string {
|
||||
const t = new Date();
|
||||
t.setTime(1000 * time);
|
||||
return format(t, "DD/MM/YYYY");
|
||||
}
|
||||
|
||||
export function timeDiff(a: number, b: number): string {
|
||||
let diff = b - a;
|
||||
|
||||
if (diff === 0) return "now";
|
||||
if (diff === 1) return "1 second";
|
||||
|
||||
if (diff < 60) {
|
||||
return `${diff} seconds`;
|
||||
}
|
||||
|
||||
diff = Math.floor(diff / 60);
|
||||
|
||||
if (diff === 1) return "1 minute";
|
||||
if (diff < 60) {
|
||||
return `${diff} minutes`;
|
||||
}
|
||||
|
||||
diff = Math.floor(diff / 60);
|
||||
|
||||
if (diff === 1) return "1 hour";
|
||||
if (diff < 24) {
|
||||
return `${diff} hours`;
|
||||
}
|
||||
|
||||
const diffDays = Math.floor(diff / 24);
|
||||
|
||||
if (diffDays === 1) return "1 day";
|
||||
if (diffDays < 31) {
|
||||
return `${diffDays} days`;
|
||||
}
|
||||
|
||||
diff = Math.floor(diffDays / 31);
|
||||
|
||||
if (diff < 12) {
|
||||
return `${diff} month`;
|
||||
}
|
||||
|
||||
const diffYears = Math.floor(diffDays / 365);
|
||||
|
||||
if (diffYears === 1) return "1 year";
|
||||
return `${diffYears} years`;
|
||||
}
|
||||
|
||||
export function timeDiffFromNow(t: number): string {
|
||||
return timeDiff(t, time());
|
||||
}
|
||||
|
||||
export function TimeWidget(p: {
|
||||
time?: number;
|
||||
isDuration?: boolean;
|
||||
showDate?: boolean;
|
||||
}): React.ReactElement {
|
||||
if (!p.time) return <></>;
|
||||
return (
|
||||
<Tooltip
|
||||
title={formatDateTime(
|
||||
p.isDuration ? new Date().getTime() / 1000 - p.time : p.time
|
||||
)}
|
||||
arrow
|
||||
>
|
||||
<span>
|
||||
{p.showDate
|
||||
? formatDate(p.time)
|
||||
: p.isDuration
|
||||
? timeDiff(0, p.time)
|
||||
: timeDiffFromNow(p.time)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user