web: migrate admin overview cards to separate files
This commit is contained in:
parent
48438e28fd
commit
2d9efe035e
|
@ -1,20 +0,0 @@
|
|||
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;
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(): Promise<AdminOverview> {
|
||||
return DefaultClient.fetch<AdminOverview>(["admin", "overview"]);
|
||||
}
|
||||
|
||||
}
|
21
web/src/api/version.ts
Normal file
21
web/src/api/version.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { DefaultClient } from "./client";
|
||||
|
||||
export class Version {
|
||||
|
||||
version_current: string;
|
||||
version_latest: string;
|
||||
outdated: boolean;
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(): Promise<Version> {
|
||||
return DefaultClient.fetch<Version>(["admin", "version"]);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.version_current;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,20 +1,22 @@
|
|||
import { gettext } from "django";
|
||||
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import { AdminOverview } from "../../api/admin_overview";
|
||||
import { DefaultClient } from "../../api/client";
|
||||
import { User } from "../../api/user";
|
||||
import { COMMON_STYLES } from "../../common/styles";
|
||||
import { SpinnerSize } from "../../elements/Spinner";
|
||||
|
||||
import "../../elements/AdminLoginsChart";
|
||||
import "../../elements/cards/AggregatePromiseCard";
|
||||
import "./TopApplicationsTable";
|
||||
import "./OverviewCards";
|
||||
import "./cards/AdminStatusCard";
|
||||
import "./cards/FlowCacheStatusCard";
|
||||
import "./cards/PolicyCacheStatusCard";
|
||||
import "./cards/PolicyUnboundStatusCard";
|
||||
import "./cards/ProviderStatusCard";
|
||||
import "./cards/VersionStatusCard";
|
||||
import "./cards/WorkerStatusCard";
|
||||
|
||||
@customElement("ak-admin-overview")
|
||||
export class AdminOverviewPage extends LitElement {
|
||||
@property({attribute: false})
|
||||
data?: AdminOverview;
|
||||
|
||||
@property({attribute: false})
|
||||
users?: Promise<number>;
|
||||
|
||||
|
@ -23,7 +25,6 @@ export class AdminOverviewPage extends LitElement {
|
|||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
AdminOverview.get().then(value => this.data = value);
|
||||
this.users = User.count();
|
||||
}
|
||||
|
||||
|
@ -51,19 +52,10 @@ export class AdminOverviewPage extends LitElement {
|
|||
headerLink="#/administration/users/"
|
||||
.promise=${this.users}>
|
||||
</ak-aggregate-card-promise>
|
||||
<!-- Version card -->
|
||||
<ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Workers">
|
||||
${this.data ?
|
||||
this.data?.worker_count < 1 ?
|
||||
html`<p class="ak-aggregate-card">
|
||||
<i class="fa fa-exclamation-triangle"></i> ${this.data?.worker_count}
|
||||
</p>
|
||||
<p class="subtext">${gettext("No workers connected.")}</p>` :
|
||||
html`<p class="ak-aggregate-card">
|
||||
<i class="fa fa-check-circle"></i> ${this.data?.worker_count}
|
||||
</p>`
|
||||
: html`<ak-spinner size=${SpinnerSize.Large}></ak-spinner>`}
|
||||
</ak-aggregate-card>
|
||||
<ak-admin-status-version class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-bundle" header="Version">
|
||||
</ak-admin-status-version>
|
||||
<ak-admin-status-card-workers class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Workers">
|
||||
</ak-admin-status-card-workers>
|
||||
<ak-admin-status-card-policy-cache class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Cached Policies">
|
||||
</ak-admin-status-card-policy-cache>
|
||||
<ak-admin-status-card-flow-cache class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Cached Flows">
|
||||
|
|
|
@ -1,153 +0,0 @@
|
|||
import { gettext } from "django";
|
||||
import { customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { Flow } from "../../api/flow";
|
||||
import { Policy } from "../../api/policy";
|
||||
import { Provider } from "../../api/provider";
|
||||
import { AggregateCard } from "../../elements/cards/AggregateCard";
|
||||
import { SpinnerSize } from "../../elements/Spinner";
|
||||
|
||||
interface AdminStatus {
|
||||
icon: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
abstract class AdminStatusCard extends AggregateCard {
|
||||
|
||||
abstract getPrimaryCounter(): Promise<number>;
|
||||
|
||||
abstract getStatus(counter: number): Promise<AdminStatus>;
|
||||
|
||||
@property({type: Number})
|
||||
counter = 0;
|
||||
|
||||
renderInner(): TemplateResult {
|
||||
return html`<p class="center-value">
|
||||
${until(this.getPrimaryCounter().then((c) => {
|
||||
this.counter = c;
|
||||
return this.getStatus(c);
|
||||
}).then((status) => {
|
||||
return html`<p class="ak-aggregate-card">
|
||||
<i class="${status.icon}"></i> ${this.counter}
|
||||
</p>
|
||||
${status.message ? html`<p class="subtext">${status.message}</p>` : html``}`;
|
||||
}), html`<ak-spinner size="${SpinnerSize.Large}"></ak-spinner>`)}
|
||||
</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("ak-admin-status-card-provider")
|
||||
export class ProviderStatusCard extends AdminStatusCard {
|
||||
|
||||
getPrimaryCounter(): Promise<number> {
|
||||
return Provider.list({
|
||||
"application__isnull": true
|
||||
}).then((response) => {
|
||||
return response.pagination.count;
|
||||
});
|
||||
}
|
||||
|
||||
getStatus(counter: number): Promise<AdminStatus> {
|
||||
if (counter > 0) {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-exclamation-triangle",
|
||||
message: gettext("Warning: At least one Provider has no application assigned."),
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@customElement("ak-admin-status-card-policy-unbound")
|
||||
export class PolicyUnboundStatusCard extends AdminStatusCard {
|
||||
|
||||
getPrimaryCounter(): Promise<number> {
|
||||
return Policy.list({
|
||||
"bindings__isnull": true,
|
||||
"promptstage__isnull": true,
|
||||
}).then((response) => {
|
||||
return response.pagination.count;
|
||||
});
|
||||
}
|
||||
|
||||
getStatus(counter: number): Promise<AdminStatus> {
|
||||
if (counter > 0) {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-exclamation-triangle",
|
||||
message: gettext("Policies without binding exist."),
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@customElement("ak-admin-status-card-policy-cache")
|
||||
export class PolicyCacheStatusCard extends AdminStatusCard {
|
||||
|
||||
getPrimaryCounter(): Promise<number> {
|
||||
return Policy.cached();
|
||||
}
|
||||
|
||||
getStatus(counter: number): Promise<AdminStatus> {
|
||||
if (counter < 1) {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-exclamation-triangle",
|
||||
message: gettext("No policies cached. Users may experience slow response times."),
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderHeaderLink(): TemplateResult {
|
||||
return html`<ak-modal-button href="/administration/overview/cache/policy/">
|
||||
<a slot="trigger">
|
||||
<i class="fa fa-trash"> </i>
|
||||
</a>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@customElement("ak-admin-status-card-flow-cache")
|
||||
export class FlowCacheStatusCard extends AdminStatusCard {
|
||||
|
||||
getPrimaryCounter(): Promise<number> {
|
||||
return Flow.cached();
|
||||
}
|
||||
|
||||
getStatus(counter: number): Promise<AdminStatus> {
|
||||
if (counter < 1) {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-exclamation-triangle",
|
||||
message: gettext("No flows cached."),
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderHeaderLink(): TemplateResult {
|
||||
return html`<ak-modal-button href="/administration/overview/cache/flow/">
|
||||
<a slot="trigger">
|
||||
<i class="fa fa-trash"> </i>
|
||||
</a>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>`;
|
||||
}
|
||||
|
||||
}
|
33
web/src/pages/admin-overview/cards/AdminStatusCard.ts
Normal file
33
web/src/pages/admin-overview/cards/AdminStatusCard.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { html, TemplateResult } from "lit-html";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { AggregateCard } from "../../../elements/cards/AggregateCard";
|
||||
import { SpinnerSize } from "../../../elements/Spinner";
|
||||
|
||||
export interface AdminStatus {
|
||||
icon: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export abstract class AdminStatusCard<T> extends AggregateCard {
|
||||
|
||||
abstract getPrimaryValue(): Promise<T>;
|
||||
|
||||
abstract getStatus(value: T): Promise<AdminStatus>;
|
||||
|
||||
value?: T;
|
||||
|
||||
renderInner(): TemplateResult {
|
||||
return html`<p class="center-value">
|
||||
${until(this.getPrimaryValue().then((v) => {
|
||||
this.value = v;
|
||||
return this.getStatus(v);
|
||||
}).then((status) => {
|
||||
return html`<p class="ak-aggregate-card">
|
||||
<i class="${status.icon}"></i> ${this.value}
|
||||
</p>
|
||||
${status.message ? html`<p class="subtext">${status.message}</p>` : html``}`;
|
||||
}), html`<ak-spinner size="${SpinnerSize.Large}"></ak-spinner>`)}
|
||||
</p>`;
|
||||
}
|
||||
}
|
||||
|
36
web/src/pages/admin-overview/cards/FlowCacheStatusCard.ts
Normal file
36
web/src/pages/admin-overview/cards/FlowCacheStatusCard.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { gettext } from "django";
|
||||
import { customElement, html, TemplateResult } from "lit-element";
|
||||
import { Flow } from "../../../api/flow";
|
||||
import { AdminStatus, AdminStatusCard } from "./AdminStatusCard";
|
||||
import "../../../elements/buttons/ModalButton";
|
||||
|
||||
@customElement("ak-admin-status-card-flow-cache")
|
||||
export class FlowCacheStatusCard extends AdminStatusCard<number> {
|
||||
|
||||
getPrimaryValue(): Promise<number> {
|
||||
return Flow.cached();
|
||||
}
|
||||
|
||||
getStatus(value: number): Promise<AdminStatus> {
|
||||
if (value < 1) {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-exclamation-triangle",
|
||||
message: gettext("No flows cached."),
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderHeaderLink(): TemplateResult {
|
||||
return html`<ak-modal-button href="/administration/overview/cache/flow/">
|
||||
<a slot="trigger">
|
||||
<i class="fa fa-trash"> </i>
|
||||
</a>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>`;
|
||||
}
|
||||
|
||||
}
|
37
web/src/pages/admin-overview/cards/PolicyCacheStatusCard.ts
Normal file
37
web/src/pages/admin-overview/cards/PolicyCacheStatusCard.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { gettext } from "django";
|
||||
import { customElement } from "lit-element";
|
||||
import { TemplateResult, html } from "lit-html";
|
||||
import { Policy } from "../../../api/policy";
|
||||
import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
|
||||
import "../../../elements/buttons/ModalButton";
|
||||
|
||||
@customElement("ak-admin-status-card-policy-cache")
|
||||
export class PolicyCacheStatusCard extends AdminStatusCard<number> {
|
||||
|
||||
getPrimaryValue(): Promise<number> {
|
||||
return Policy.cached();
|
||||
}
|
||||
|
||||
getStatus(value: number): Promise<AdminStatus> {
|
||||
if (value < 1) {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-exclamation-triangle",
|
||||
message: gettext("No policies cached. Users may experience slow response times."),
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderHeaderLink(): TemplateResult {
|
||||
return html`<ak-modal-button href="/administration/overview/cache/policy/">
|
||||
<a slot="trigger">
|
||||
<i class="fa fa-trash"> </i>
|
||||
</a>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>`;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import { gettext } from "django";
|
||||
import { customElement } from "lit-element";
|
||||
import { Policy } from "../../../api/policy";
|
||||
import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
|
||||
|
||||
@customElement("ak-admin-status-card-policy-unbound")
|
||||
export class PolicyUnboundStatusCard extends AdminStatusCard<number> {
|
||||
|
||||
getPrimaryValue(): Promise<number> {
|
||||
return Policy.list({
|
||||
"bindings__isnull": true,
|
||||
"promptstage__isnull": true,
|
||||
}).then((response) => {
|
||||
return response.pagination.count;
|
||||
});
|
||||
}
|
||||
|
||||
getStatus(value: number): Promise<AdminStatus> {
|
||||
if (value > 0) {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-exclamation-triangle",
|
||||
message: gettext("Policies without binding exist."),
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
30
web/src/pages/admin-overview/cards/ProviderStatusCard.ts
Normal file
30
web/src/pages/admin-overview/cards/ProviderStatusCard.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { gettext } from "django";
|
||||
import { customElement } from "lit-element";
|
||||
import { Provider } from "../../../api/provider";
|
||||
import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
|
||||
|
||||
@customElement("ak-admin-status-card-provider")
|
||||
export class ProviderStatusCard extends AdminStatusCard<number> {
|
||||
|
||||
getPrimaryValue(): Promise<number> {
|
||||
return Provider.list({
|
||||
"application__isnull": true
|
||||
}).then((response) => {
|
||||
return response.pagination.count;
|
||||
});
|
||||
}
|
||||
|
||||
getStatus(value: number): Promise<AdminStatus> {
|
||||
if (value > 0) {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-exclamation-triangle",
|
||||
message: gettext("Warning: At least one Provider has no application assigned."),
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
27
web/src/pages/admin-overview/cards/VersionStatusCard.ts
Normal file
27
web/src/pages/admin-overview/cards/VersionStatusCard.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { gettext } from "django";
|
||||
import { customElement } from "lit-element";
|
||||
import { Version } from "../../../api/version";
|
||||
import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
|
||||
|
||||
@customElement("ak-admin-status-version")
|
||||
export class VersionStatusCard extends AdminStatusCard<Version> {
|
||||
|
||||
getPrimaryValue(): Promise<Version> {
|
||||
return Version.get();
|
||||
}
|
||||
|
||||
getStatus(value: Version): Promise<AdminStatus> {
|
||||
if (value.outdated) {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-exclamation-triangle",
|
||||
message: gettext(`${value.version_latest} is available!`),
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle",
|
||||
message: gettext("Up-to-date!")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
28
web/src/pages/admin-overview/cards/WorkerStatusCard.ts
Normal file
28
web/src/pages/admin-overview/cards/WorkerStatusCard.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { gettext } from "django";
|
||||
import { customElement } from "lit-element";
|
||||
import { DefaultClient, PBResponse } from "../../../api/client";
|
||||
import { AdminStatus, AdminStatusCard } from "./AdminStatusCard";
|
||||
|
||||
@customElement("ak-admin-status-card-workers")
|
||||
export class WorkersStatusCard extends AdminStatusCard<number> {
|
||||
|
||||
getPrimaryValue(): Promise<number> {
|
||||
return DefaultClient.fetch<PBResponse<number>>(["admins", "workers"]).then((r) => {
|
||||
return r.pagination.count;
|
||||
});
|
||||
}
|
||||
|
||||
getStatus(value: number): Promise<AdminStatus> {
|
||||
if (value < 1) {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-exclamation-triangle",
|
||||
message: gettext("No workers connected. Background tasks will not run."),
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue