enterprise: UI improvements, better handling of expiry (#10828)
* web/admin: show enterprise banner on the very top Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rework license Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix a bunch of things Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add some more tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add more tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix middleware Signed-off-by: Jens Langhammer <jens@goauthentik.io> * better api Signed-off-by: Jens Langhammer <jens@goauthentik.io> * format Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add tests for and fix read only mode Signed-off-by: Jens Langhammer <jens@goauthentik.io> * field name consistency Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -71,6 +71,12 @@ export class AdminInterface extends EnterpriseAwareInterface {
|
||||
:host([theme="dark"]) .pf-c-page {
|
||||
--pf-c-page--BackgroundColor: var(--ak-dark-background);
|
||||
}
|
||||
ak-enterprise-status {
|
||||
grid-area: header;
|
||||
}
|
||||
ak-admin-sidebar {
|
||||
grid-area: nav;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
@ -118,6 +124,7 @@ export class AdminInterface extends EnterpriseAwareInterface {
|
||||
|
||||
return html` <ak-locale-context>
|
||||
<div class="pf-c-page">
|
||||
<ak-enterprise-status interface="admin"></ak-enterprise-status>
|
||||
<ak-admin-sidebar
|
||||
class="pf-c-page__sidebar ${classMap(sidebarClasses)}"
|
||||
></ak-admin-sidebar>
|
||||
|
@ -29,6 +29,7 @@ import {
|
||||
License,
|
||||
LicenseForecast,
|
||||
LicenseSummary,
|
||||
LicenseSummaryStatusEnum,
|
||||
RbacPermissionsAssignedByUsersListModelEnum,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
@ -182,7 +183,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
header=${msg("Expiry")}
|
||||
subtext=${msg("Cumulative license expiry")}
|
||||
>
|
||||
${this.summary?.hasLicense
|
||||
${this.summary?.status === LicenseSummaryStatusEnum.Unlicensed
|
||||
? html`<div>${getRelativeTime(this.summary.latestValid)}</div>
|
||||
<small>${this.summary.latestValid.toLocaleString()}</small>`
|
||||
: "-"}
|
||||
|
@ -4,7 +4,7 @@ import { Constructor } from "@goauthentik/elements/types.js";
|
||||
import { consume } from "@lit/context";
|
||||
import type { LitElement } from "lit";
|
||||
|
||||
import type { LicenseSummary } from "@goauthentik/api";
|
||||
import { type LicenseSummary, LicenseSummaryStatusEnum } from "@goauthentik/api";
|
||||
|
||||
export function WithLicenseSummary<T extends Constructor<LitElement>>(
|
||||
superclass: T,
|
||||
@ -15,7 +15,7 @@ export function WithLicenseSummary<T extends Constructor<LitElement>>(
|
||||
public licenseSummary!: LicenseSummary;
|
||||
|
||||
get hasEnterpriseLicense() {
|
||||
return this.licenseSummary?.hasLicense;
|
||||
return this.licenseSummary?.status !== LicenseSummaryStatusEnum.Unlicensed;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,63 +138,62 @@ export class PageHeader extends WithBrandConfig(AKElement) {
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html` <ak-enterprise-status interface="admin"></ak-enterprise-status>
|
||||
<div class="bar">
|
||||
<button
|
||||
class="sidebar-trigger pf-c-button pf-m-plain"
|
||||
@click=${() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_SIDEBAR_TOGGLE, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<i class="fas fa-bars"></i>
|
||||
</button>
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<slot name="icon">${this.renderIcon()}</slot>
|
||||
<slot name="header">${this.header}</slot>
|
||||
</h1>
|
||||
${this.description ? html`<p>${this.description}</p>` : html``}
|
||||
</div>
|
||||
</section>
|
||||
<button
|
||||
class="notification-trigger pf-c-button pf-m-plain"
|
||||
@click=${() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_API_DRAWER_TOGGLE, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<pf-tooltip position="top" content=${msg("Open API drawer")}>
|
||||
<i class="fas fa-code"></i>
|
||||
</pf-tooltip>
|
||||
</button>
|
||||
<button
|
||||
class="notification-trigger pf-c-button pf-m-plain ${this.hasNotifications
|
||||
? "has-notifications"
|
||||
: ""}"
|
||||
@click=${() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_NOTIFICATION_DRAWER_TOGGLE, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<pf-tooltip position="top" content=${msg("Open Notification drawer")}>
|
||||
<i class="fas fa-bell"></i>
|
||||
</pf-tooltip>
|
||||
</button>
|
||||
</div>`;
|
||||
return html`<div class="bar">
|
||||
<button
|
||||
class="sidebar-trigger pf-c-button pf-m-plain"
|
||||
@click=${() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_SIDEBAR_TOGGLE, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<i class="fas fa-bars"></i>
|
||||
</button>
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<slot name="icon">${this.renderIcon()}</slot>
|
||||
<slot name="header">${this.header}</slot>
|
||||
</h1>
|
||||
${this.description ? html`<p>${this.description}</p>` : html``}
|
||||
</div>
|
||||
</section>
|
||||
<button
|
||||
class="notification-trigger pf-c-button pf-m-plain"
|
||||
@click=${() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_API_DRAWER_TOGGLE, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<pf-tooltip position="top" content=${msg("Open API drawer")}>
|
||||
<i class="fas fa-code"></i>
|
||||
</pf-tooltip>
|
||||
</button>
|
||||
<button
|
||||
class="notification-trigger pf-c-button pf-m-plain ${this.hasNotifications
|
||||
? "has-notifications"
|
||||
: ""}"
|
||||
@click=${() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_NOTIFICATION_DRAWER_TOGGLE, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<pf-tooltip position="top" content=${msg("Open Notification drawer")}>
|
||||
<i class="fas fa-bell"></i>
|
||||
</pf-tooltip>
|
||||
</button>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@ import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||
|
||||
import { LicenseSummaryStatusEnum } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-enterprise-status")
|
||||
export class EnterpriseStatusBanner extends WithLicenseSummary(AKElement) {
|
||||
@property()
|
||||
@ -17,26 +19,58 @@ export class EnterpriseStatusBanner extends WithLicenseSummary(AKElement) {
|
||||
}
|
||||
|
||||
renderBanner(): TemplateResult {
|
||||
let message = "";
|
||||
switch (this.licenseSummary.status) {
|
||||
case LicenseSummaryStatusEnum.LimitExceededAdmin:
|
||||
case LicenseSummaryStatusEnum.LimitExceededUser:
|
||||
message = msg(
|
||||
"Warning: The current user count has exceeded the configured licenses.",
|
||||
);
|
||||
break;
|
||||
case LicenseSummaryStatusEnum.Expired:
|
||||
message = msg("Warning: One or more license(s) have expired.");
|
||||
break;
|
||||
case LicenseSummaryStatusEnum.ExpirySoon:
|
||||
message = msg(
|
||||
"Warning: One or more license(s) will expire within the next 2 weeks.",
|
||||
);
|
||||
break;
|
||||
case LicenseSummaryStatusEnum.ReadOnly:
|
||||
message = msg(
|
||||
"Caution: This authentik instance has entered read-only mode due to expired/exceeded licenses.",
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return html`<div
|
||||
class="pf-c-banner ${this.licenseSummary?.readOnly ? "pf-m-red" : "pf-m-gold"}"
|
||||
class="pf-c-banner ${this.licenseSummary?.status === LicenseSummaryStatusEnum.ReadOnly
|
||||
? "pf-m-red"
|
||||
: "pf-m-gold"}"
|
||||
>
|
||||
${msg("Warning: The current user count has exceeded the configured licenses.")}
|
||||
${message}
|
||||
<a href="/if/admin/#/enterprise/licenses"> ${msg("Click here for more info.")} </a>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
switch (this.interface.toLowerCase()) {
|
||||
case "admin":
|
||||
if (this.licenseSummary?.showAdminWarning || this.licenseSummary?.readOnly) {
|
||||
switch (this.licenseSummary.status) {
|
||||
case LicenseSummaryStatusEnum.LimitExceededUser:
|
||||
if (this.interface.toLowerCase() === "user") {
|
||||
return this.renderBanner();
|
||||
}
|
||||
break;
|
||||
case "user":
|
||||
if (this.licenseSummary?.showUserWarning || this.licenseSummary?.readOnly) {
|
||||
case LicenseSummaryStatusEnum.ExpirySoon:
|
||||
case LicenseSummaryStatusEnum.Expired:
|
||||
case LicenseSummaryStatusEnum.LimitExceededAdmin:
|
||||
if (this.interface.toLowerCase() === "admin") {
|
||||
return this.renderBanner();
|
||||
}
|
||||
break;
|
||||
case LicenseSummaryStatusEnum.ReadOnly:
|
||||
return this.renderBanner();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return html``;
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ export class Sidebar extends AKElement {
|
||||
nav {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 100vh;
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
Reference in New Issue
Block a user