Less flashing from loading page on org/ssh key pages
Diff
migrations/2021-08-31-214501_create_crates_table/up.sql | 1 +
chartered-frontend/src/pages/Loading.tsx | 12 +++++++++++-
chartered-frontend/src/pages/organisations/ListOrganisations.tsx | 70 +++++++++++++++++++++++++++++++++++++++++++++-------------------------
chartered-frontend/src/pages/ssh-keys/ListSshKeys.tsx | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
4 files changed, 123 insertions(+), 113 deletions(-)
@@ -5,6 +5,7 @@
);
INSERT INTO users (id, uuid, username) VALUES (1, X'936DA01F9ABD4D9D80C702AF85C822A8', "admin");
INSERT INTO users (id, uuid, username) VALUES (2, X'936DA01F9ABD4D9D80C702AF85C822A9', "billy");
CREATE TABLE organisations (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
@@ -1,6 +1,16 @@
import React = require("react");
export default function Loading() {
export function LoadingSpinner() {
return (
<div className="p-4 d-flex justify-content-center align-items-center">
<div className="spinner-border text-primary" role="status">
<span className="visually-hidden">Loading...</span>
</div>
</div>
);
}
export default function LoadingPage() {
return (
<div className="min-vh-100 bg-primary d-flex justify-content-center align-items-center">
<div className="spinner-border text-light" role="status">
@@ -6,7 +6,7 @@
import { useAuth } from "../../useAuth";
import { RoundedPicture, useAuthenticatedRequest } from "../../util";
import ErrorPage from "../ErrorPage";
import Loading from "../Loading";
import { LoadingSpinner } from "../Loading";
interface Response {
organisations: ResponseOrganisations[];
@@ -27,8 +27,6 @@
if (error) {
return <ErrorPage message={error} />;
} else if (!list) {
return <Loading />;
}
return (
@@ -39,38 +37,40 @@
<h1>Your Organisations</h1>
<div className="card border-0 shadow-sm text-black">
{list.organisations.length === 0 ? (
<div className="card-body">
You don't belong to any organisations yet.
</div>
) : (
<table className="table table-striped">
<tbody>
{list.organisations.map((v, i) => (
<tr key={i}>
<td className="align-middle fit">
<RoundedPicture
src="http://placekitten.com/48/48"
height="48px"
width="48px"
/>
</td>
<td className="align-middle" style={{ lineHeight: "1.1" }}>
<div>
<Link to={`/crates/${v.name}`}>{v.name}</Link>
</div>
<div>
<small style={{ fontSize: "0.75rem" }}>
{v.description}
</small>
</div>
</td>
</tr>
))}
</tbody>
</table>
)}
{!list ? <LoadingSpinner /> : <>
{list.organisations.length === 0 ? (
<div className="card-body">
You don't belong to any organisations yet.
</div>
) : (
<table className="table table-striped">
<tbody>
{list.organisations.map((v, i) => (
<tr key={i}>
<td className="align-middle fit">
<RoundedPicture
src="http://placekitten.com/48/48"
height="48px"
width="48px"
/>
</td>
<td className="align-middle" style={{ lineHeight: "1.1" }}>
<div>
<Link to={`/crates/${v.name}`}>{v.name}</Link>
</div>
<div>
<small style={{ fontSize: "0.75rem" }}>
{v.description}
</small>
</div>
</td>
</tr>
))}
</tbody>
</table>
)}
</>}
</div>
<Link
@@ -10,7 +10,7 @@
import { Button, Modal, OverlayTrigger, Tooltip } from "react-bootstrap";
import HumanTime from "react-human-time";
import ErrorPage from "../ErrorPage";
import Loading from "../Loading";
import Loading, { LoadingSpinner } from "../Loading";
interface SshKeysResponse {
keys: SshKeysResponseKey[];
@@ -42,8 +42,6 @@
if (loadError) {
return <ErrorPage message={loadError} />;
} else if (!sshKeys) {
return <Loading />;
}
const deleteKey = async () => {
@@ -99,81 +97,82 @@
</div>
<div className="card border-0 shadow-sm text-black">
{sshKeys.keys.length == 0 ? (
<div className="card-body">You haven't added any SSH keys yet</div>
) : (
<></>
)}
<div className="table-responsive">
<table className="table table-striped">
<tbody>
{sshKeys.keys.map((key) => (
<tr key={key.uuid}>
<td className="align-middle">
<h6 className="m-0 lh-sm">{key.name}</h6>
<pre className="m-0">{key.fingerprint}</pre>
<div className="lh-sm" style={{ fontSize: ".75rem" }}>
<div className="text-muted d-inline-block me-3">
Added{" "}
<OverlayTrigger
overlay={
<Tooltip id={`${key.uuid}-created-at`}>
{new Date(key.created_at).toLocaleString()}
</Tooltip>
}
>
<span className="text-decoration-underline-dotted">
<HumanTime
time={new Date(key.created_at).getTime()}
/>
</span>
</OverlayTrigger>
</div>
<span
className={`text-${
key.last_used_at
? new Date(key.last_used_at) > dateMonthAgo
? "success"
: "danger"
: "muted"
}`}
>
Last used{" "}
{key.last_used_at ? (
<OverlayTrigger
overlay={
<Tooltip id={`${key.uuid}-last-used`}>
{new Date(key.last_used_at).toLocaleString()}
</Tooltip>
}
{!sshKeys ? <LoadingSpinner /> : <>
{sshKeys.keys.length == 0 ? (
<div className="card-body">You haven't added any SSH keys yet.</div>
) : (
<div className="table-responsive">
<table className="table table-striped">
<tbody>
{sshKeys.keys.map((key) => (
<tr key={key.uuid}>
<td className="align-middle">
<h6 className="m-0 lh-sm">{key.name}</h6>
<pre className="m-0">{key.fingerprint}</pre>
<div className="lh-sm" style={{ fontSize: ".75rem" }}>
<div className="text-muted d-inline-block me-3">
Added{" "}
<OverlayTrigger
overlay={
<Tooltip id={`${key.uuid}-created-at`}>
{new Date(key.created_at).toLocaleString()}
</Tooltip>
}
>
<span className="text-decoration-underline-dotted">
<HumanTime
time={new Date(key.created_at).getTime()}
/>
</span>
</OverlayTrigger>
</div>
<span
className={`text-${
key.last_used_at
? new Date(key.last_used_at) > dateMonthAgo
? "success"
: "danger"
: "muted"
}`}
>
<span className="text-decoration-underline-dotted">
<HumanTime
time={new Date(key.last_used_at).getTime()}
/>
</span>
</OverlayTrigger>
) : (
<>never</>
)}
</span>
</div>
</td>
<td className="align-middle fit">
<button
type="button"
className="btn text-danger"
onClick={() => setDeleting(key)}
>
<Trash />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
Last used{" "}
{key.last_used_at ? (
<OverlayTrigger
overlay={
<Tooltip id={`${key.uuid}-last-used`}>
{new Date(key.last_used_at).toLocaleString()}
</Tooltip>
}
>
<span className="text-decoration-underline-dotted">
<HumanTime
time={new Date(key.last_used_at).getTime()}
/>
</span>
</OverlayTrigger>
) : (
<>never</>
)}
</span>
</div>
</td>
<td className="align-middle fit">
<button
type="button"
className="btn text-danger"
onClick={() => setDeleting(key)}
>
<Trash />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</>}
</div>
<Link