web: start implementing admin overview page

This commit is contained in:
Jens Langhammer 2020-12-01 10:21:04 +01:00
parent 760dca0f76
commit 93bf977709
10 changed files with 140 additions and 22 deletions

View File

@ -3,7 +3,7 @@
"scripts": { "scripts": {
"build": "rollup -c ./rollup.config.js", "build": "rollup -c ./rollup.config.js",
"watch": "rollup -c -w", "watch": "rollup -c -w",
"format": "prettier --write ." "lint": "eslint . --ext .js,.jsx,.ts,.tsx --fix"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^5.15.1", "@fortawesome/fontawesome-free": "^5.15.1",

View File

@ -43,6 +43,6 @@ export default [
watch: { watch: {
clearScreen: false, clearScreen: false,
}, },
external: ['django'] external: ["django"]
}, },
]; ];

View File

@ -0,0 +1,16 @@
import { DefaultClient } from "./client";
export class AdminOverview {
version?: string;
version_latest?: string;
worker_count?: number;
providers_without_application?: number;
policies_without_binding?: number;
cached_policies?: number;
cached_flows?: number;
static get(): Promise<AdminOverview> {
return DefaultClient.fetch<AdminOverview>(["admin", "overview"]);
}
}

2
web/src/django.d.ts vendored
View File

@ -1,4 +1,4 @@
declare module 'django' { declare module "django" {
export = django; export = django;
} }
declare namespace django { declare namespace django {

View File

@ -0,0 +1,33 @@
import { gettext } from "django";
import { customElement, html, LitElement, property, TemplateResult } from "lit-element";
// @ts-ignore
import SpinnerStyle from "@patternfly/patternfly/components/Spinner/spinner.css";
export enum SpinnerSize {
Small = "pf-m-sm",
Medium = "pf-m-md",
Large = "pf-m-lg",
XLarge = "pf-m-xl",
}
@customElement("pb-spinner")
export class Spinner extends LitElement {
@property()
size: SpinnerSize = SpinnerSize.Medium;
static get styles() {
return [SpinnerStyle];
}
render(): TemplateResult {
return html`<span
class="pf-c-spinner ${this.size.toString()}"
role="progressbar"
aria-valuetext="${gettext("Loading...")}">
<span class="pf-c-spinner__clipper"></span>
<span class="pf-c-spinner__lead-ball"></span>
<span class="pf-c-spinner__tail-ball"></span>
</span>`;
}
}

View File

@ -86,8 +86,8 @@ export abstract class Table<T> extends LitElement {
<thead> <thead>
<tr role="row"> <tr role="row">
${this.columns().map( ${this.columns().map(
(col) => html`<th role="columnheader" scope="col">${gettext(col)}</th>` (col) => html`<th role="columnheader" scope="col">${gettext(col)}</th>`
)} )}
</tr> </tr>
</thead> </thead>
<tbody role="rowgroup"> <tbody role="rowgroup">

View File

@ -13,9 +13,11 @@ import "./elements/sidebar/Sidebar";
import "./elements/sidebar/SidebarBrand"; import "./elements/sidebar/SidebarBrand";
import "./elements/sidebar/SidebarUser"; import "./elements/sidebar/SidebarUser";
import "./elements/Tabs"; import "./elements/Tabs";
import "./elements/Spinner";
import "./elements/table/TablePagination"; import "./elements/table/TablePagination";
import "./pages/applications/ApplicationViewPage"; import "./pages/applications/ApplicationViewPage";
import "./pages/applications/ApplicationListPage"; import "./pages/applications/ApplicationListPage";
import "./pages/AdminOverviewPage";
import "./pages/LibraryPage"; import "./pages/LibraryPage";
import "./pages/FlowShellCard"; import "./pages/FlowShellCard";
import "./pages/RouterOutlet"; import "./pages/RouterOutlet";

View File

@ -0,0 +1,74 @@
import { gettext } from "django";
import { customElement, html, LitElement, property, TemplateResult } from "lit-element";
import { AdminOverview } from "../api/admin_overview";
import { DefaultClient } from "../api/client";
import { COMMON_STYLES } from "../common/styles";
@customElement("pb-aggregate-card")
export class AggregateCard extends LitElement {
@property()
icon?: string;
@property()
header?: string;
static get styles() {
return COMMON_STYLES;
}
render(): TemplateResult {
return html`<div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-4-col" >
<div class="pf-c-card__header">
<div class="pf-c-card__header-main">
<i class="${this.icon}"></i> ${this.header ? gettext(this.header) : ""}
</div>
</div>
<div class="pf-c-card__body">
<slot></slot>
</div>
</div>`;
}
}
@customElement("pb-admin-overview")
export class AdminOverviewPage extends LitElement {
@property()
data?: AdminOverview;
static get styles() {
return COMMON_STYLES;
}
firstUpdated(): void {
AdminOverview.get().then(value => this.data = value);
}
render(): TemplateResult {
return html`<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>${gettext("System Overview")}</h1>
</div>
</section>
<section class="pf-c-page__main-section">
<div class="pf-l-gallery pf-m-gutter">
<pb-aggregate-card icon="pf-icon pf-icon-server" header="Logins over the last 24 hours" style="grid-column-end: span 3;grid-row-end: span 2;">
<pb-admin-logins-chart url="${DefaultClient.makeUrl(["admin", "metrics"])}"></pb-admin-logins-chart>
</pb-aggregate-card>
<pb-aggregate-card icon="pf-icon pf-icon-server" header="Workers">
${this.data ?
this.data?.worker_count! < 1 ?
html`<p class="pb-aggregate-card">
<i class="fa fa-exclamation-triangle"></i> ${this.data.worker_count}
</p>
<p>${gettext("No workers connected.")}</p>` :
html`<p class="pb-aggregate-card">
<i class="fa fa-check-circle"></i> ${this.data.worker_count}
</p>`
: html`<pb-spinner></pb-spinner>`}
</pb-aggregate-card>
</div>
</section>`;
}
}

View File

@ -52,6 +52,7 @@ export const ROUTES: Route[] = [
new Route(new RegExp("^/$")).redirect("/library/"), new Route(new RegExp("^/$")).redirect("/library/"),
new Route(new RegExp("^#.*")).redirect("/library/"), new Route(new RegExp("^#.*")).redirect("/library/"),
new Route(new RegExp("^/library/$"), html`<pb-library></pb-library>`), new Route(new RegExp("^/library/$"), html`<pb-library></pb-library>`),
new Route(new RegExp("^/administration/overview/$"), html`<pb-admin-overview></pb-admin-overview>`),
new Route(new RegExp("^/applications/$"), html`<pb-application-list></pb-application-list>`), new Route(new RegExp("^/applications/$"), html`<pb-application-list></pb-application-list>`),
new Route(new RegExp(`^/applications/(?<slug>${SLUG_REGEX})/$`)).then((args) => { new Route(new RegExp(`^/applications/(?<slug>${SLUG_REGEX})/$`)).then((args) => {
return html`<pb-application-view .args=${args}></pb-application-view>`; return html`<pb-application-view .args=${args}></pb-application-view>`;

View File

@ -98,23 +98,15 @@ export class SiteShell extends LitElement {
} }
render() { render() {
return html` ${this.loading return html` ${this.loading ?
? html`<div class="pf-c-backdrop"> html`<div class="pf-c-backdrop">
<div class="pf-l-bullseye"> <div class="pf-l-bullseye">
<div class="pf-l-bullseye__item"> <div class="pf-l-bullseye__item">
<span <pb-spinner></pb-spinner>
class="pf-c-spinner pf-m-xl" </div>
role="progressbar" </div>
aria-valuetext="Loading..." </div>`
>
<span class="pf-c-spinner__clipper"></span>
<span class="pf-c-spinner__lead-ball"></span>
<span class="pf-c-spinner__tail-ball"></span>
</span>
</div>
</div>
</div>`
: ""} : ""}
<slot name="body"> </slot>`; <slot name="body"></slot>`;
} }
} }