web/admin: Refine navigation (#12441)

* fix spacing if there's no icon in page header

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add a very slight bar

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* rework navigation to be similar between interfaces

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix subpath and rendering

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix display

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add version to sidebar

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* make page header sticky?

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* unrelated: hide session in system api

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* unrelated: add unidecode for policies

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

#5859

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L.
2024-12-21 22:12:47 +01:00
committed by GitHub
parent 5fccbd7c04
commit 02bd699917
13 changed files with 382 additions and 284 deletions

View File

@ -96,7 +96,7 @@ export class AdminOverviewPage extends AdminOverviewBase {
render(): TemplateResult {
const name = this.user?.user.name ?? this.user?.user.username;
return html`<ak-page-header icon="" header="" description=${msg("General system status")}>
return html`<ak-page-header description=${msg("General system status")} ?hasIcon=${false}>
<span slot="header"> ${msg(str`Welcome, ${name || ""}.`)} </span>
</ak-page-header>
<section class="pf-c-page__main-section">

View File

@ -3,6 +3,7 @@
--ak-global--Color--100: var(--ak-dark-foreground) !important;
--pf-c-page__main-section--m-light--BackgroundColor: var(--ak-dark-background-darker);
--pf-global--link--Color: var(--ak-dark-foreground-link) !important;
--pf-global--BorderColor--100: var(--ak-dark-background-lighter) !important;
}
body {
background-color: var(--ak-dark-background) !important;
@ -31,9 +32,6 @@ body {
.notification-trigger {
background-color: transparent !important;
}
.pf-c-page__main-section.pf-m-light {
background-color: transparent;
}
.pf-c-content {
color: var(--ak-dark-foreground);
}

View File

@ -7,6 +7,7 @@ export enum UserDisplay {
username = "username",
name = "name",
email = "email",
none = "none",
}
export enum LayoutType {

View File

@ -0,0 +1,210 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import {
EVENT_API_DRAWER_TOGGLE,
EVENT_NOTIFICATION_DRAWER_TOGGLE,
} from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { UIConfig, UserDisplay, uiConfig } from "@goauthentik/common/ui/config";
import { me } from "@goauthentik/common/users";
import { AKElement } from "@goauthentik/elements/Base";
import { match } from "ts-pattern";
import { msg } from "@lit/localize";
import { css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
import PFBrand from "@patternfly/patternfly/components/Brand/brand.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";
import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
import { CoreApi, EventsApi, SessionUser } from "@goauthentik/api";
@customElement("ak-nav-buttons")
export class NavigationButtons extends AKElement {
@property({ type: Object })
uiConfig?: UIConfig;
@property({ type: Object })
me?: SessionUser;
@property({ type: Boolean, reflect: true })
notificationDrawerOpen = false;
@property({ type: Boolean, reflect: true })
apiDrawerOpen = false;
@property({ type: Number })
notificationsCount = 0;
static get styles() {
return [
PFBase,
PFDisplay,
PFBrand,
PFPage,
PFAvatar,
PFButton,
PFDrawer,
PFDropdown,
PFNotificationBadge,
css`
.pf-c-page__header-tools {
display: flex;
}
`,
];
}
async firstUpdated() {
this.me = await me();
const notifications = await new EventsApi(DEFAULT_CONFIG).eventsNotificationsList({
seen: false,
ordering: "-created",
pageSize: 1,
user: this.me.user.pk,
});
this.notificationsCount = notifications.pagination.count;
this.uiConfig = await uiConfig();
}
renderApiDrawerTrigger() {
if (!this.uiConfig?.enabledFeatures.apiDrawer) {
return nothing;
}
const onClick = (ev: Event) => {
ev.stopPropagation();
this.dispatchEvent(
new Event(EVENT_API_DRAWER_TOGGLE, { bubbles: true, composed: true }),
);
};
return html`<div class="pf-c-page__header-tools-item pf-m-hidden pf-m-visible-on-lg">
<button class="pf-c-button pf-m-plain" type="button" @click=${onClick}>
<pf-tooltip position="top" content=${msg("Open API drawer")}>
<i class="fas fa-code" aria-hidden="true"></i>
</pf-tooltip>
</button>
</div>`;
}
renderNotificationDrawerTrigger() {
if (!this.uiConfig?.enabledFeatures.notificationDrawer) {
return nothing;
}
const onClick = (ev: Event) => {
ev.stopPropagation();
this.dispatchEvent(
new Event(EVENT_NOTIFICATION_DRAWER_TOGGLE, { bubbles: true, composed: true }),
);
};
return html`<div class="pf-c-page__header-tools-item pf-m-hidden pf-m-visible-on-lg">
<button
class="pf-c-button pf-m-plain"
type="button"
aria-label="${msg("Unread notifications")}"
@click=${onClick}
>
<span
class="pf-c-notification-badge ${this.notificationsCount > 0
? "pf-m-unread"
: ""}"
>
<pf-tooltip position="top" content=${msg("Open Notification drawer")}>
<i class="fas fa-bell" aria-hidden="true"></i>
</pf-tooltip>
<span class="pf-c-notification-badge__count">${this.notificationsCount}</span>
</span>
</button>
</div> `;
}
renderSettings() {
if (!this.uiConfig?.enabledFeatures.settings) {
return nothing;
}
return html`<div class="pf-c-page__header-tools-item">
<a class="pf-c-button pf-m-plain" type="button" href="#/settings">
<pf-tooltip position="top" content=${msg("Settings")}>
<i class="fas fa-cog" aria-hidden="true"></i>
</pf-tooltip>
</a>
</div>`;
}
renderImpersonation() {
if (!this.me?.original) {
return nothing;
}
const onClick = () => {
return new CoreApi(DEFAULT_CONFIG).coreUsersImpersonateEndRetrieve().then(() => {
window.location.reload();
});
};
return html`&nbsp;
<div class="pf-c-page__header-tools">
<div class="pf-c-page__header-tools-group">
<ak-action-button class="pf-m-warning pf-m-small" .apiRequest=${onClick}>
${msg("Stop impersonation")}
</ak-action-button>
</div>
</div>`;
}
get userDisplayName() {
return match<UserDisplay | undefined, string | undefined>(this.uiConfig?.navbar.userDisplay)
.with(UserDisplay.username, () => this.me?.user.username)
.with(UserDisplay.name, () => this.me?.user.name)
.with(UserDisplay.email, () => this.me?.user.email || "")
.with(UserDisplay.none, () => "")
.otherwise(() => this.me?.user.username);
}
render() {
return html`<div class="pf-c-page__header-tools">
<div class="pf-c-page__header-tools-group">
${this.renderApiDrawerTrigger()}
<!-- -->
${this.renderNotificationDrawerTrigger()}
<!-- -->
${this.renderSettings()}
<div class="pf-c-page__header-tools-item">
<a
href="${globalAK().api.base}flows/-/default/invalidation/"
class="pf-c-button pf-m-plain"
>
<pf-tooltip position="top" content=${msg("Sign out")}>
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
</pf-tooltip>
</a>
</div>
<slot name="extra"></slot>
</div>
${this.renderImpersonation()}
${this.userDisplayName != ""
? html`<div class="pf-c-page__header-tools-group">
<div class="pf-c-page__header-tools-item pf-m-hidden pf-m-visible-on-md">
${this.userDisplayName}
</div>
</div>`
: nothing}
<img
class="pf-c-avatar"
src=${ifDefined(this.me?.user.avatar)}
alt="${msg("Avatar image")}"
/>
</div>`;
}
}

View File

@ -1,27 +1,30 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import {
EVENT_API_DRAWER_TOGGLE,
EVENT_NOTIFICATION_DRAWER_TOGGLE,
EVENT_SIDEBAR_TOGGLE,
EVENT_WS_MESSAGE,
TITLE_DEFAULT,
} from "@goauthentik/common/constants";
import { currentInterface } from "@goauthentik/common/sentry";
import { UIConfig, UserDisplay, uiConfig } from "@goauthentik/common/ui/config";
import { me } from "@goauthentik/common/users";
import "@goauthentik/components/ak-nav-buttons";
import { AKElement } from "@goauthentik/elements/Base";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";
import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { EventsApi } from "@goauthentik/api";
import { EventsApi, SessionUser } from "@goauthentik/api";
@customElement("ak-page-header")
export class PageHeader extends WithBrandConfig(AKElement) {
@ -31,8 +34,8 @@ export class PageHeader extends WithBrandConfig(AKElement) {
@property({ type: Boolean })
iconImage = false;
@property({ type: Boolean })
hasNotifications = false;
@state()
notificationsCount = 0;
@property()
header = "";
@ -40,21 +43,39 @@ export class PageHeader extends WithBrandConfig(AKElement) {
@property()
description?: string;
@property({ type: Boolean })
hasIcon = true;
@state()
me?: SessionUser;
@state()
uiConfig!: UIConfig;
static get styles(): CSSResult[] {
return [
PFBase,
PFButton,
PFPage,
PFNotificationBadge,
PFContent,
PFAvatar,
PFDropdown,
css`
:host {
position: sticky;
top: 0;
z-index: 100;
}
.bar {
border-bottom: var(--pf-global--BorderWidth--sm);
border-bottom-style: solid;
border-bottom-color: var(--pf-global--BorderColor--100);
display: flex;
flex-direction: row;
min-height: 114px;
}
.pf-c-button.pf-m-plain {
background-color: transparent;
border-radius: 0px;
max-height: 114px;
background-color: var(--pf-c-page--BackgroundColor);
}
.pf-c-page__main-section.pf-m-light {
background-color: transparent;
@ -81,6 +102,15 @@ export class PageHeader extends WithBrandConfig(AKElement) {
flex-direction: row;
align-items: center !important;
}
.pf-c-page__header-tools {
flex-shrink: 0;
}
.pf-c-page__header-tools-group {
height: 100%;
}
:host([theme="dark"]) .pf-c-page__header-tools {
color: var(--ak-dark-foreground) !important;
}
`,
];
}
@ -92,19 +122,17 @@ export class PageHeader extends WithBrandConfig(AKElement) {
});
}
firstUpdated(): void {
me().then((user) => {
new EventsApi(DEFAULT_CONFIG)
.eventsNotificationsList({
seen: false,
ordering: "-created",
pageSize: 1,
user: user.user.pk,
})
.then((r) => {
this.hasNotifications = r.pagination.count > 0;
});
async firstUpdated() {
this.me = await me();
this.uiConfig = await uiConfig();
this.uiConfig.navbar.userDisplay = UserDisplay.none;
const notifications = await new EventsApi(DEFAULT_CONFIG).eventsNotificationsList({
seen: false,
ordering: "-created",
pageSize: 1,
user: this.me.user.pk,
});
this.notificationsCount = notifications.pagination.count;
}
setTitle(header?: string) {
@ -126,7 +154,7 @@ export class PageHeader extends WithBrandConfig(AKElement) {
this.setTitle(this.header);
}
renderIcon(): TemplateResult {
renderIcon() {
if (this.icon) {
if (this.iconImage && !this.icon.startsWith("fa://")) {
return html`<img class="pf-icon" src="${this.icon}" alt="page icon" />`;
@ -134,7 +162,7 @@ export class PageHeader extends WithBrandConfig(AKElement) {
const icon = this.icon.replaceAll("fa://", "fa ");
return html`<i class=${icon}></i>`;
}
return html``;
return nothing;
}
render(): TemplateResult {
@ -155,44 +183,19 @@ export class PageHeader extends WithBrandConfig(AKElement) {
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<slot name="icon">${this.renderIcon()}</slot>&nbsp;
${this.hasIcon
? html`<slot name="icon">${this.renderIcon()}</slot>&nbsp;`
: nothing}
<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 class="pf-c-page__header-tools">
<div class="pf-c-page__header-tools-group">
<ak-nav-buttons .uiConfig=${this.uiConfig} .me=${this.me}></ak-nav-buttons>
</div>
</div>
</div>`;
}
}

View File

@ -1,6 +1,6 @@
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/sidebar/SidebarBrand";
import "@goauthentik/elements/sidebar/SidebarUser";
import "@goauthentik/elements/sidebar/SidebarVersion";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit";
@ -74,7 +74,7 @@ export class Sidebar extends AKElement {
<ul class="pf-c-nav__list">
<slot></slot>
</ul>
<ak-sidebar-user></ak-sidebar-user>
<ak-sidebar-version></ak-sidebar-version>
</nav>`;
}
}

View File

@ -42,6 +42,9 @@ export class SidebarBrand extends WithBrandConfig(AKElement) {
align-items: center;
height: 114px;
min-height: 114px;
border-bottom: var(--pf-global--BorderWidth--sm);
border-bottom-style: solid;
border-bottom-color: var(--pf-global--BorderColor--100);
}
.pf-c-brand img {
padding: 0 0.5rem;

View File

@ -1,70 +0,0 @@
import { globalAK } from "@goauthentik/common/global";
import { me } from "@goauthentik/common/users";
import { AKElement } from "@goauthentik/elements/Base";
import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { until } from "lit/directives/until.js";
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
import PFNav from "@patternfly/patternfly/components/Nav/nav.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-sidebar-user")
export class SidebarUser extends AKElement {
static get styles(): CSSResult[] {
return [
PFBase,
PFNav,
PFAvatar,
css`
:host {
display: flex;
width: 100%;
flex-direction: row;
justify-content: space-between;
}
.pf-c-nav__link {
align-items: center;
display: flex;
justify-content: center;
}
`,
];
}
render(): TemplateResult {
return html`
<a
href="${globalAK().api.base}if/user/#/settings"
class="pf-c-nav__link user-avatar"
id="user-settings"
>
${until(
me().then((u) => {
return html`<img
class="pf-c-avatar"
src="${ifDefined(u.user.avatar)}"
alt=""
/>`;
}),
html``,
)}
</a>
<a
href="${globalAK().api.base}flows/-/default/invalidation/"
class="pf-c-nav__link user-logout"
id="logout"
>
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
</a>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-sidebar-user": SidebarUser;
}
}

View File

@ -0,0 +1,61 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { globalAK } from "@goauthentik/common/global";
import { AKElement } from "@goauthentik/elements/Base";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
import { msg, str } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, state } from "lit/decorators.js";
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
import PFNav from "@patternfly/patternfly/components/Nav/nav.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { AdminApi, LicenseSummaryStatusEnum, Version } from "@goauthentik/api";
@customElement("ak-sidebar-version")
export class SidebarVersion extends WithLicenseSummary(AKElement) {
static get styles(): CSSResult[] {
return [
PFBase,
PFNav,
PFAvatar,
css`
:host {
display: flex;
width: 100%;
flex-direction: column;
justify-content: space-between;
padding: 1rem !important;
}
p {
text-align: center;
width: 100%;
font-size: var(--pf-global--FontSize--xs);
}
`,
];
}
@state()
version?: Version;
async firstUpdated() {
this.version = await new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
}
render(): TemplateResult {
let product = globalAK().brand.brandingTitle;
if (this.licenseSummary.status != LicenseSummaryStatusEnum.Unlicensed) {
product += ` ${msg("Enterprise")}`;
}
return html`<p class="pf-c-title">${product}</p>
<p class="pf-c-title">${msg(str`Version ${this.version?.versionCurrent}`)}</p> `;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-sidebar-version": SidebarVersion;
}
}

View File

@ -4,11 +4,11 @@ import {
EVENT_NOTIFICATION_DRAWER_TOGGLE,
EVENT_WS_MESSAGE,
} from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { configureSentry } from "@goauthentik/common/sentry";
import { UIConfig, UserDisplay } from "@goauthentik/common/ui/config";
import { UIConfig } from "@goauthentik/common/ui/config";
import { me } from "@goauthentik/common/users";
import { WebsocketClient } from "@goauthentik/common/ws";
import "@goauthentik/components/ak-nav-buttons";
import { AKElement } from "@goauthentik/elements/Base";
import { EnterpriseAwareInterface } from "@goauthentik/elements/Interface";
import "@goauthentik/elements/ak-locale-context";
@ -25,7 +25,6 @@ import "@goauthentik/elements/sidebar/SidebarItem";
import { themeImage } from "@goauthentik/elements/utils/images";
import { ROUTES } from "@goauthentik/user/Routes";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { match } from "ts-pattern";
import { msg } from "@lit/localize";
import { css, html, nothing } from "lit";
@ -41,7 +40,7 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
import { CoreApi, CurrentBrand, EventsApi, SessionUser } from "@goauthentik/api";
import { CurrentBrand, EventsApi, SessionUser } from "@goauthentik/api";
const customStyles = css`
.pf-c-page__main,
@ -152,14 +151,6 @@ class UserInterfacePresentation extends AKElement {
@property({ type: Object })
brand!: CurrentBrand;
get userDisplayName() {
return match<UserDisplay, string>(this.uiConfig.navbar.userDisplay)
.with(UserDisplay.username, () => this.me.user.username)
.with(UserDisplay.name, () => this.me.user.name)
.with(UserDisplay.email, () => this.me.user.email || "")
.otherwise(() => this.me.user.username);
}
get canAccessAdmin() {
return (
this.me.user.isSuperuser ||
@ -172,6 +163,19 @@ class UserInterfacePresentation extends AKElement {
return Boolean(this.uiConfig && this.me && this.brand);
}
renderAdminInterfaceLink() {
if (!this.canAccessAdmin) {
return nothing;
}
return html`<a
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
href="/if/admin/"
slot="extra"
>
${msg("Admin interface")}
</a>`;
}
render() {
// The `!` in the field definitions above only re-assure typescript and eslint that the
// values *should* be available, not that they *are*. Thus this contract check; it asserts
@ -181,7 +185,7 @@ class UserInterfacePresentation extends AKElement {
throw new Error("ak-interface-user-presentation misused; no valid values passed");
}
return html` <ak-locale-context>
return html`<ak-locale-context>
<ak-enterprise-status interface="user"></ak-enterprise-status>
<div class="pf-c-page">
<div class="background-wrapper" style="${this.uiConfig.theme.background}">
@ -199,39 +203,9 @@ class UserInterfacePresentation extends AKElement {
/>
</a>
</div>
<div class="pf-c-page__header-tools">
<div class="pf-c-page__header-tools-group">
${this.renderApiDrawerTrigger()}
<!-- -->
${this.renderNotificationDrawerTrigger()}
<!-- -->
${this.renderSettings()}
<div class="pf-c-page__header-tools-item">
<a
href="${globalAK().api.base}flows/-/default/invalidation/"
class="pf-c-button pf-m-plain"
>
<pf-tooltip position="top" content=${msg("Sign out")}>
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
</pf-tooltip>
</a>
</div>
${this.renderAdminInterfaceLink()}
</div>
${this.renderImpersonation()}
<div class="pf-c-page__header-tools-group">
<div
class="pf-c-page__header-tools-item pf-m-hidden pf-m-visible-on-md"
>
${this.userDisplayName}
</div>
</div>
<img
class="pf-c-avatar"
src=${this.me.user.avatar}
alt="${msg("Avatar image")}"
/>
</div>
<ak-nav-buttons .uiConfig=${this.uiConfig} .me=${this.me}
>${this.renderAdminInterfaceLink()}</ak-nav-buttons
>
</header>
<div class="pf-c-page__drawer">
<div
@ -274,108 +248,6 @@ class UserInterfacePresentation extends AKElement {
</div>
</ak-locale-context>`;
}
renderApiDrawerTrigger() {
if (!this.uiConfig.enabledFeatures.apiDrawer) {
return nothing;
}
const onClick = (ev: Event) => {
ev.stopPropagation();
this.dispatchEvent(
new Event(EVENT_API_DRAWER_TOGGLE, { bubbles: true, composed: true }),
);
};
return html`<div class="pf-c-page__header-tools-item pf-m-hidden pf-m-visible-on-lg">
<button class="pf-c-button pf-m-plain" type="button" @click=${onClick}>
<pf-tooltip position="top" content=${msg("Open API drawer")}>
<i class="fas fa-code" aria-hidden="true"></i>
</pf-tooltip>
</button>
</div>`;
}
renderNotificationDrawerTrigger() {
if (!this.uiConfig.enabledFeatures.notificationDrawer) {
return nothing;
}
const onClick = (ev: Event) => {
ev.stopPropagation();
this.dispatchEvent(
new Event(EVENT_NOTIFICATION_DRAWER_TOGGLE, { bubbles: true, composed: true }),
);
};
return html`<div class="pf-c-page__header-tools-item pf-m-hidden pf-m-visible-on-lg">
<button
class="pf-c-button pf-m-plain"
type="button"
aria-label="${msg("Unread notifications")}"
@click=${onClick}
>
<span
class="pf-c-notification-badge ${this.notificationsCount > 0
? "pf-m-unread"
: ""}"
>
<pf-tooltip position="top" content=${msg("Open Notification drawer")}>
<i class="fas fa-bell" aria-hidden="true"></i>
</pf-tooltip>
<span class="pf-c-notification-badge__count">${this.notificationsCount}</span>
</span>
</button>
</div> `;
}
renderSettings() {
if (!this.uiConfig.enabledFeatures.settings) {
return nothing;
}
return html` <div class="pf-c-page__header-tools-item">
<a class="pf-c-button pf-m-plain" type="button" href="#/settings">
<pf-tooltip position="top" content=${msg("Settings")}>
<i class="fas fa-cog" aria-hidden="true"></i>
</pf-tooltip>
</a>
</div>`;
}
renderAdminInterfaceLink() {
if (!this.canAccessAdmin) {
return nothing;
}
return html`<a
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
href="${globalAK().api.base}if/admin/"
>
${msg("Admin interface")}
</a>`;
}
renderImpersonation() {
if (!this.me.original) {
return nothing;
}
const onClick = () => {
return new CoreApi(DEFAULT_CONFIG).coreUsersImpersonateEndRetrieve().then(() => {
window.location.reload();
});
};
return html`&nbsp;
<div class="pf-c-page__header-tools">
<div class="pf-c-page__header-tools-group">
<ak-action-button class="pf-m-warning pf-m-small" .apiRequest=${onClick}>
${msg("Stop impersonation")}
</ak-action-button>
</div>
</div>`;
}
}
// ___ _