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:
210
web/src/components/ak-nav-buttons.ts
Normal file
210
web/src/components/ak-nav-buttons.ts
Normal 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`
|
||||
<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>`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user