web/admin: WIP Fix issue on Safari where sidebar collapse.
- TODO: Add width check.
This commit is contained in:
@ -10,6 +10,7 @@ import { configureSentry } from "@goauthentik/common/sentry";
|
|||||||
import { me } from "@goauthentik/common/users";
|
import { me } from "@goauthentik/common/users";
|
||||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||||
import { AuthenticatedInterface } from "@goauthentik/elements/Interface";
|
import { AuthenticatedInterface } from "@goauthentik/elements/Interface";
|
||||||
|
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider.js";
|
||||||
import "@goauthentik/elements/ak-locale-context";
|
import "@goauthentik/elements/ak-locale-context";
|
||||||
import "@goauthentik/elements/banner/EnterpriseStatusBanner";
|
import "@goauthentik/elements/banner/EnterpriseStatusBanner";
|
||||||
import "@goauthentik/elements/banner/EnterpriseStatusBanner";
|
import "@goauthentik/elements/banner/EnterpriseStatusBanner";
|
||||||
@ -24,25 +25,30 @@ import "@goauthentik/elements/router/RouterOutlet";
|
|||||||
import "@goauthentik/elements/sidebar/Sidebar";
|
import "@goauthentik/elements/sidebar/Sidebar";
|
||||||
import "@goauthentik/elements/sidebar/SidebarItem";
|
import "@goauthentik/elements/sidebar/SidebarItem";
|
||||||
|
|
||||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators.js";
|
import { customElement, property, query, state } from "lit/decorators.js";
|
||||||
import { classMap } from "lit/directives/class-map.js";
|
import { classMap } from "lit/directives/class-map.js";
|
||||||
|
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
|
import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
|
||||||
|
import PFNav from "@patternfly/patternfly/components/Nav/nav.css";
|
||||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
import { SessionUser, UiThemeEnum } from "@goauthentik/api";
|
import { LicenseSummaryStatusEnum, SessionUser, UiThemeEnum } from "@goauthentik/api";
|
||||||
|
|
||||||
import "./AdminSidebar";
|
import {
|
||||||
|
AdminSidebarEnterpriseEntries,
|
||||||
|
AdminSidebarEntries,
|
||||||
|
renderSidebarItems,
|
||||||
|
} from "./AdminSidebar.js";
|
||||||
|
|
||||||
if (process.env.NODE_ENV === "development") {
|
if (process.env.NODE_ENV === "development") {
|
||||||
await import("@goauthentik/esbuild-plugin-live-reload/client");
|
await import("@goauthentik/esbuild-plugin-live-reload/client");
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ak-interface-admin")
|
@customElement("ak-interface-admin")
|
||||||
export class AdminInterface extends AuthenticatedInterface {
|
export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) {
|
||||||
//#region Properties
|
//#region Properties
|
||||||
|
|
||||||
@property({ type: Boolean })
|
@property({ type: Boolean })
|
||||||
@ -59,11 +65,11 @@ export class AdminInterface extends AuthenticatedInterface {
|
|||||||
@query("ak-about-modal")
|
@query("ak-about-modal")
|
||||||
aboutModal?: AboutModal;
|
aboutModal?: AboutModal;
|
||||||
|
|
||||||
@state()
|
@property({ type: Boolean, reflect: true })
|
||||||
sidebarVisible = false;
|
public sidebarOpen = true;
|
||||||
|
|
||||||
#toggleSidebar = () => {
|
#toggleSidebar = () => {
|
||||||
this.sidebarVisible = !this.sidebarVisible;
|
this.sidebarOpen = !this.sidebarOpen;
|
||||||
};
|
};
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
@ -76,6 +82,7 @@ export class AdminInterface extends AuthenticatedInterface {
|
|||||||
PFPage,
|
PFPage,
|
||||||
PFButton,
|
PFButton,
|
||||||
PFDrawer,
|
PFDrawer,
|
||||||
|
PFNav,
|
||||||
css`
|
css`
|
||||||
.pf-c-page__main,
|
.pf-c-page__main,
|
||||||
.pf-c-drawer__content,
|
.pf-c-drawer__content,
|
||||||
@ -103,7 +110,7 @@ export class AdminInterface extends AuthenticatedInterface {
|
|||||||
grid-area: header;
|
grid-area: header;
|
||||||
}
|
}
|
||||||
|
|
||||||
ak-admin-sidebar {
|
.ak-sidebar {
|
||||||
grid-area: nav;
|
grid-area: nav;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +128,12 @@ export class AdminInterface extends AuthenticatedInterface {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.ws = new WebsocketClient();
|
this.ws = new WebsocketClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public connectedCallback(): void {
|
||||||
|
super.connectedCallback();
|
||||||
|
|
||||||
|
window.addEventListener(EVENT_SIDEBAR_TOGGLE, this.#toggleSidebar);
|
||||||
|
|
||||||
window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, () => {
|
window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, () => {
|
||||||
this.notificationDrawerOpen = !this.notificationDrawerOpen;
|
this.notificationDrawerOpen = !this.notificationDrawerOpen;
|
||||||
@ -137,6 +150,11 @@ export class AdminInterface extends AuthenticatedInterface {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public disconnectedCallback(): void {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
window.removeEventListener(EVENT_SIDEBAR_TOGGLE, this.#toggleSidebar);
|
||||||
|
}
|
||||||
|
|
||||||
async firstUpdated(): Promise<void> {
|
async firstUpdated(): Promise<void> {
|
||||||
configureSentry(true);
|
configureSentry(true);
|
||||||
this.user = await me();
|
this.user = await me();
|
||||||
@ -145,27 +163,18 @@ export class AdminInterface extends AuthenticatedInterface {
|
|||||||
this.user.user.isSuperuser ||
|
this.user.user.isSuperuser ||
|
||||||
// TODO: somehow add `access_admin_interface` to the API schema
|
// TODO: somehow add `access_admin_interface` to the API schema
|
||||||
this.user.user.systemPermissions.includes("access_admin_interface");
|
this.user.user.systemPermissions.includes("access_admin_interface");
|
||||||
|
|
||||||
if (!canAccessAdmin && this.user.user.pk > 0) {
|
if (!canAccessAdmin && this.user.user.pk > 0) {
|
||||||
window.location.assign("/if/user/");
|
window.location.assign("/if/user/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async connectedCallback(): Promise<void> {
|
|
||||||
super.connectedCallback();
|
|
||||||
|
|
||||||
window.addEventListener(EVENT_SIDEBAR_TOGGLE, this.#toggleSidebar);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedCallback(): void {
|
|
||||||
super.disconnectedCallback();
|
|
||||||
window.removeEventListener(EVENT_SIDEBAR_TOGGLE, this.#toggleSidebar);
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
const sidebarClasses = {
|
const sidebarClasses = {
|
||||||
|
"pf-c-page__sidebar": true,
|
||||||
"pf-m-light": this.activeTheme === UiThemeEnum.Light,
|
"pf-m-light": this.activeTheme === UiThemeEnum.Light,
|
||||||
"pf-m-expanded": !this.sidebarVisible,
|
"pf-m-expanded": this.sidebarOpen,
|
||||||
"pf-m-collapsed": this.sidebarVisible,
|
"pf-m-collapsed": !this.sidebarOpen,
|
||||||
};
|
};
|
||||||
|
|
||||||
const drawerOpen = this.notificationDrawerOpen || this.apiDrawerOpen;
|
const drawerOpen = this.notificationDrawerOpen || this.apiDrawerOpen;
|
||||||
@ -182,10 +191,12 @@ export class AdminInterface extends AuthenticatedInterface {
|
|||||||
<ak-enterprise-status interface="admin"></ak-enterprise-status>
|
<ak-enterprise-status interface="admin"></ak-enterprise-status>
|
||||||
</ak-page-navbar>
|
</ak-page-navbar>
|
||||||
|
|
||||||
<ak-admin-sidebar
|
<ak-sidebar class="${classMap(sidebarClasses)}">
|
||||||
class="pf-c-page__sidebar
|
${renderSidebarItems(AdminSidebarEntries)}
|
||||||
${classMap(sidebarClasses)}"
|
${this.licenseSummary?.status !== LicenseSummaryStatusEnum.Unlicensed
|
||||||
></ak-admin-sidebar>
|
? renderSidebarItems(AdminSidebarEnterpriseEntries)
|
||||||
|
: nothing}
|
||||||
|
</ak-sidebar>
|
||||||
|
|
||||||
<div class="pf-c-page__drawer">
|
<div class="pf-c-page__drawer">
|
||||||
<div class="pf-c-drawer ${classMap(drawerClasses)}">
|
<div class="pf-c-drawer ${classMap(drawerClasses)}">
|
||||||
|
@ -1,165 +1,98 @@
|
|||||||
import { me } from "@goauthentik/common/users";
|
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
|
||||||
import {
|
|
||||||
CapabilitiesEnum,
|
|
||||||
WithCapabilitiesConfig,
|
|
||||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
|
||||||
import { WithVersion } from "@goauthentik/elements/Interface/versionProvider";
|
|
||||||
import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/Route";
|
import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/Route";
|
||||||
import { getRootStyle } from "@goauthentik/elements/utils/getRootStyle";
|
|
||||||
import { spread } from "@open-wc/lit-helpers";
|
import { spread } from "@open-wc/lit-helpers";
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
import { TemplateResult, html, nothing } from "lit";
|
import { TemplateResult, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators.js";
|
import { repeat } from "lit/directives/repeat.js";
|
||||||
import { map } from "lit/directives/map.js";
|
|
||||||
|
|
||||||
import { UiThemeEnum } from "@goauthentik/api";
|
// The second attribute type is of string[] to help with the 'activeWhen' control, which was
|
||||||
import type { SessionUser, UserSelf } from "@goauthentik/api";
|
// commonplace and singular enough to merit its own handler.
|
||||||
|
type SidebarEntry = [
|
||||||
|
path: string | null,
|
||||||
|
label: string,
|
||||||
|
attributes?: Record<string, any> | string[] | null, // eslint-disable-line
|
||||||
|
children?: SidebarEntry[],
|
||||||
|
];
|
||||||
|
|
||||||
@customElement("ak-admin-sidebar")
|
/**
|
||||||
export class AkAdminSidebar extends WithCapabilitiesConfig(WithVersion(AKElement)) {
|
* Recursively renders a sidebar entry.
|
||||||
@property({ type: Boolean, reflect: true })
|
*/
|
||||||
open = true;
|
export function renderSidebarItem([
|
||||||
|
path,
|
||||||
|
label,
|
||||||
|
attributes,
|
||||||
|
children,
|
||||||
|
]: SidebarEntry): TemplateResult {
|
||||||
|
const properties = Array.isArray(attributes)
|
||||||
|
? { ".activeWhen": attributes }
|
||||||
|
: (attributes ?? {});
|
||||||
|
|
||||||
@state()
|
if (path) {
|
||||||
impersonation: UserSelf["username"] | null = null;
|
properties.path = path;
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
me().then((user: SessionUser) => {
|
|
||||||
this.impersonation = user.original ? user.user.username : null;
|
|
||||||
});
|
|
||||||
this.checkWidth = this.checkWidth.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkWidth() {
|
return html`<ak-sidebar-item ${spread(properties)}>
|
||||||
// This works just fine, but it assumes that the `--ak-sidebar--minimum-auto-width` is in
|
${label ? html`<span slot="label">${label}</span>` : nothing}
|
||||||
// REMs. If that changes, this code will have to be adjusted as well.
|
${children ? renderSidebarItems(children) : nothing}
|
||||||
const minWidth =
|
</ak-sidebar-item>`;
|
||||||
parseFloat(getRootStyle("--ak-sidebar--minimum-auto-width")) *
|
|
||||||
parseFloat(getRootStyle("font-size"));
|
|
||||||
this.open = window.innerWidth >= minWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
|
|
||||||
window.addEventListener("resize", this.checkWidth);
|
|
||||||
// After connecting to the DOM, we can now perform this check to see if the sidebar should
|
|
||||||
// be open by default.
|
|
||||||
this.checkWidth();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The symmetry (☟, ☝) here is critical in that you want to start adding these handlers after
|
|
||||||
// connection, and removing them before disconnection.
|
|
||||||
|
|
||||||
disconnectedCallback() {
|
|
||||||
window.removeEventListener("resize", this.checkWidth);
|
|
||||||
super.disconnectedCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`
|
|
||||||
<ak-sidebar
|
|
||||||
class="pf-c-page__sidebar
|
|
||||||
${this.open ? "pf-m-expanded" : "pf-m-collapsed"} ${this.activeTheme ===
|
|
||||||
UiThemeEnum.Light
|
|
||||||
? "pf-m-light"
|
|
||||||
: ""}"
|
|
||||||
>
|
|
||||||
${this.renderSidebarItems()}
|
|
||||||
</ak-sidebar>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSidebarItems(): TemplateResult {
|
|
||||||
// The second attribute type is of string[] to help with the 'activeWhen' control, which was
|
|
||||||
// commonplace and singular enough to merit its own handler.
|
|
||||||
type SidebarEntry = [
|
|
||||||
path: string | null,
|
|
||||||
label: string,
|
|
||||||
attributes?: Record<string, any> | string[] | null, // eslint-disable-line
|
|
||||||
children?: SidebarEntry[],
|
|
||||||
];
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
const sidebarContent: SidebarEntry[] = [
|
|
||||||
[null, msg("Dashboards"), { "?expanded": true }, [
|
|
||||||
["/administration/overview", msg("Overview")],
|
|
||||||
["/administration/dashboard/users", msg("User Statistics")],
|
|
||||||
["/administration/system-tasks", msg("System Tasks")]]],
|
|
||||||
[null, msg("Applications"), null, [
|
|
||||||
["/core/applications", msg("Applications"), [`^/core/applications/(?<slug>${SLUG_REGEX})$`]],
|
|
||||||
["/core/providers", msg("Providers"), [`^/core/providers/(?<id>${ID_REGEX})$`]],
|
|
||||||
["/outpost/outposts", msg("Outposts")]]],
|
|
||||||
[null, msg("Events"), null, [
|
|
||||||
["/events/log", msg("Logs"), [`^/events/log/(?<id>${UUID_REGEX})$`]],
|
|
||||||
["/events/rules", msg("Notification Rules")],
|
|
||||||
["/events/transports", msg("Notification Transports")]]],
|
|
||||||
[null, msg("Customization"), null, [
|
|
||||||
["/policy/policies", msg("Policies")],
|
|
||||||
["/core/property-mappings", msg("Property Mappings")],
|
|
||||||
["/blueprints/instances", msg("Blueprints")],
|
|
||||||
["/policy/reputation", msg("Reputation scores")]]],
|
|
||||||
[null, msg("Flows and Stages"), null, [
|
|
||||||
["/flow/flows", msg("Flows"), [`^/flow/flows/(?<slug>${SLUG_REGEX})$`]],
|
|
||||||
["/flow/stages", msg("Stages")],
|
|
||||||
["/flow/stages/prompts", msg("Prompts")]]],
|
|
||||||
[null, msg("Directory"), null, [
|
|
||||||
["/identity/users", msg("Users"), [`^/identity/users/(?<id>${ID_REGEX})$`]],
|
|
||||||
["/identity/groups", msg("Groups"), [`^/identity/groups/(?<id>${UUID_REGEX})$`]],
|
|
||||||
["/identity/roles", msg("Roles"), [`^/identity/roles/(?<id>${UUID_REGEX})$`]],
|
|
||||||
["/identity/initial-permissions", msg("Initial Permissions"), [`^/identity/initial-permissions/(?<id>${ID_REGEX})$`]],
|
|
||||||
["/core/sources", msg("Federation and Social login"), [`^/core/sources/(?<slug>${SLUG_REGEX})$`]],
|
|
||||||
["/core/tokens", msg("Tokens and App passwords")],
|
|
||||||
["/flow/stages/invitations", msg("Invitations")]]],
|
|
||||||
[null, msg("System"), null, [
|
|
||||||
["/core/brands", msg("Brands")],
|
|
||||||
["/crypto/certificates", msg("Certificates")],
|
|
||||||
["/outpost/integrations", msg("Outpost Integrations")],
|
|
||||||
["/admin/settings", msg("Settings")]]],
|
|
||||||
];
|
|
||||||
|
|
||||||
// Typescript requires the type here to correctly type the recursive path
|
|
||||||
type SidebarRenderer = (_: SidebarEntry) => TemplateResult;
|
|
||||||
|
|
||||||
const renderOneSidebarItem: SidebarRenderer = ([path, label, attributes, children]) => {
|
|
||||||
const properties = Array.isArray(attributes)
|
|
||||||
? { ".activeWhen": attributes }
|
|
||||||
: (attributes ?? {});
|
|
||||||
if (path) {
|
|
||||||
properties.path = path;
|
|
||||||
}
|
|
||||||
return html`<ak-sidebar-item ${spread(properties)}>
|
|
||||||
${label ? html`<span slot="label">${label}</span>` : nothing}
|
|
||||||
${map(children, renderOneSidebarItem)}
|
|
||||||
</ak-sidebar-item>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
return html`
|
|
||||||
${map(sidebarContent, renderOneSidebarItem)}
|
|
||||||
${this.renderEnterpriseMenu()}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderEnterpriseMenu() {
|
|
||||||
return this.can(CapabilitiesEnum.IsEnterprise)
|
|
||||||
? html`
|
|
||||||
<ak-sidebar-item>
|
|
||||||
<span slot="label">${msg("Enterprise")}</span>
|
|
||||||
<ak-sidebar-item path="/enterprise/licenses">
|
|
||||||
<span slot="label">${msg("Licenses")}</span>
|
|
||||||
</ak-sidebar-item>
|
|
||||||
</ak-sidebar-item>
|
|
||||||
`
|
|
||||||
: nothing;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
/**
|
||||||
interface HTMLElementTagNameMap {
|
* Recursively renders a collection of sidebar entries.
|
||||||
"ak-admin-sidebar": AkAdminSidebar;
|
*/
|
||||||
}
|
export function renderSidebarItems(entries: readonly SidebarEntry[]) {
|
||||||
|
console.debug("authentik/sidebar: Rendering sidebar items", entries);
|
||||||
|
return repeat(entries, ([path, label]) => path || label, renderSidebarItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
export const AdminSidebarEntries: readonly SidebarEntry[] = [
|
||||||
|
[null, msg("Dashboards"), { "?expanded": true }, [
|
||||||
|
["/administration/overview", msg("Overview")],
|
||||||
|
["/administration/dashboard/users", msg("User Statistics")],
|
||||||
|
["/administration/system-tasks", msg("System Tasks")]]
|
||||||
|
],
|
||||||
|
[null, msg("Applications"), null, [
|
||||||
|
["/core/applications", msg("Applications"), [`^/core/applications/(?<slug>${SLUG_REGEX})$`]],
|
||||||
|
["/core/providers", msg("Providers"), [`^/core/providers/(?<id>${ID_REGEX})$`]],
|
||||||
|
["/outpost/outposts", msg("Outposts")]]
|
||||||
|
],
|
||||||
|
[null, msg("Events"), null, [
|
||||||
|
["/events/log", msg("Logs"), [`^/events/log/(?<id>${UUID_REGEX})$`]],
|
||||||
|
["/events/rules", msg("Notification Rules")],
|
||||||
|
["/events/transports", msg("Notification Transports")]]
|
||||||
|
],
|
||||||
|
[null, msg("Customization"), null, [
|
||||||
|
["/policy/policies", msg("Policies")],
|
||||||
|
["/core/property-mappings", msg("Property Mappings")],
|
||||||
|
["/blueprints/instances", msg("Blueprints")],
|
||||||
|
["/policy/reputation", msg("Reputation scores")]]
|
||||||
|
],
|
||||||
|
[null, msg("Flows and Stages"), null, [
|
||||||
|
["/flow/flows", msg("Flows"), [`^/flow/flows/(?<slug>${SLUG_REGEX})$`]],
|
||||||
|
["/flow/stages", msg("Stages")],
|
||||||
|
["/flow/stages/prompts", msg("Prompts")]]
|
||||||
|
],
|
||||||
|
[null, msg("Directory"), null, [
|
||||||
|
["/identity/users", msg("Users"), [`^/identity/users/(?<id>${ID_REGEX})$`]],
|
||||||
|
["/identity/groups", msg("Groups"), [`^/identity/groups/(?<id>${UUID_REGEX})$`]],
|
||||||
|
["/identity/roles", msg("Roles"), [`^/identity/roles/(?<id>${UUID_REGEX})$`]],
|
||||||
|
["/identity/initial-permissions", msg("Initial Permissions"), [`^/identity/initial-permissions/(?<id>${ID_REGEX})$`]],
|
||||||
|
["/core/sources", msg("Federation and Social login"), [`^/core/sources/(?<slug>${SLUG_REGEX})$`]],
|
||||||
|
["/core/tokens", msg("Tokens and App passwords")],
|
||||||
|
["/flow/stages/invitations", msg("Invitations")]]
|
||||||
|
],
|
||||||
|
[null, msg("System"), null, [
|
||||||
|
["/core/brands", msg("Brands")],
|
||||||
|
["/crypto/certificates", msg("Certificates")],
|
||||||
|
["/outpost/integrations", msg("Outpost Integrations")],
|
||||||
|
["/admin/settings", msg("Settings")]]
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
export const AdminSidebarEnterpriseEntries: readonly SidebarEntry[] = [
|
||||||
|
[null, msg("Enterprise"), null, [
|
||||||
|
["/enterprise/licenses", msg("Licenses"), null]
|
||||||
|
],
|
||||||
|
]]
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
} from "@goauthentik/common/constants";
|
} from "@goauthentik/common/constants";
|
||||||
import { globalAK } from "@goauthentik/common/global";
|
import { globalAK } from "@goauthentik/common/global";
|
||||||
import { currentInterface } from "@goauthentik/common/sentry";
|
import { currentInterface } from "@goauthentik/common/sentry";
|
||||||
import { UIConfig, UserDisplay, uiConfig } from "@goauthentik/common/ui/config";
|
import { UIConfig, UserDisplay, getConfigForUser } from "@goauthentik/common/ui/config";
|
||||||
import { me } from "@goauthentik/common/users";
|
import { me } from "@goauthentik/common/users";
|
||||||
import "@goauthentik/components/ak-nav-buttons";
|
import "@goauthentik/components/ak-nav-buttons";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
@ -267,15 +267,28 @@ export class AKPageNavbar extends WithBrandConfig(AKElement) implements PageNavb
|
|||||||
open = true;
|
open = true;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
me?: SessionUser;
|
session?: SessionUser;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
uiConfig!: UIConfig;
|
uiConfig!: UIConfig;
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#endregion
|
//#region Private Methods
|
||||||
//#region Methods
|
|
||||||
|
#setTitle(header?: string) {
|
||||||
|
const currentIf = currentInterface();
|
||||||
|
let title = this.brand?.brandingTitle || TITLE_DEFAULT;
|
||||||
|
|
||||||
|
if (currentIf === "admin") {
|
||||||
|
title = `${msg("Admin")} - ${title}`;
|
||||||
|
}
|
||||||
|
// Prepend the header to the title
|
||||||
|
if (header) {
|
||||||
|
title = `${header} - ${title}`;
|
||||||
|
}
|
||||||
|
document.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
#toggleSidebar() {
|
#toggleSidebar() {
|
||||||
this.open = !this.open;
|
this.open = !this.open;
|
||||||
@ -288,50 +301,38 @@ export class AKPageNavbar extends WithBrandConfig(AKElement) implements PageNavb
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//#region Constructor
|
//#endregion
|
||||||
|
|
||||||
|
//#region Lifecycle
|
||||||
|
|
||||||
|
public connectedCallback(): void {
|
||||||
|
super.connectedCallback();
|
||||||
|
AKPageNavbar.elementRef = this;
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
window.addEventListener(EVENT_WS_MESSAGE, () => {
|
window.addEventListener(EVENT_WS_MESSAGE, () => {
|
||||||
this.firstUpdated();
|
this.firstUpdated();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback(): void {
|
public disconnectedCallback(): void {
|
||||||
super.connectedCallback();
|
|
||||||
AKPageNavbar.elementRef = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedCallback(): void {
|
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
AKPageNavbar.elementRef = null;
|
AKPageNavbar.elementRef = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async firstUpdated() {
|
public async firstUpdated() {
|
||||||
this.me = await me();
|
this.session = await me();
|
||||||
this.uiConfig = await uiConfig();
|
this.uiConfig = getConfigForUser(this.session.user);
|
||||||
this.uiConfig.navbar.userDisplay = UserDisplay.none;
|
this.uiConfig.navbar.userDisplay = UserDisplay.none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#setTitle(header?: string) {
|
|
||||||
const currentIf = currentInterface();
|
|
||||||
let title = this.brand?.brandingTitle || TITLE_DEFAULT;
|
|
||||||
if (currentIf === "admin") {
|
|
||||||
title = `${msg("Admin")} - ${title}`;
|
|
||||||
}
|
|
||||||
// Prepend the header to the title
|
|
||||||
if (header !== undefined && header !== "") {
|
|
||||||
title = `${header} - ${title}`;
|
|
||||||
}
|
|
||||||
document.title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
willUpdate() {
|
willUpdate() {
|
||||||
// Always update title, even if there's no header value set,
|
// Always update title, even if there's no header value set,
|
||||||
// as in that case we still need to return to the generic title
|
// as in that case we still need to return to the generic title
|
||||||
this.#setTitle(this.header);
|
this.#setTitle(this.header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
//#region Render
|
//#region Render
|
||||||
|
|
||||||
renderIcon() {
|
renderIcon() {
|
||||||
@ -389,7 +390,7 @@ export class AKPageNavbar extends WithBrandConfig(AKElement) implements PageNavb
|
|||||||
|
|
||||||
<section class="items secondary">
|
<section class="items secondary">
|
||||||
<div class="pf-c-page__header-tools-group">
|
<div class="pf-c-page__header-tools-group">
|
||||||
<ak-nav-buttons .uiConfig=${this.uiConfig} .me=${this.me}>
|
<ak-nav-buttons .uiConfig=${this.uiConfig} .me=${this.session}>
|
||||||
<a
|
<a
|
||||||
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
|
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/user/"
|
href="${globalAK().api.base}if/user/"
|
||||||
|
@ -1,16 +1,3 @@
|
|||||||
import { AKElement } from "@goauthentik/elements/Base";
|
|
||||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
|
||||||
import { themeImage } from "@goauthentik/elements/utils/images";
|
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
|
||||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
|
||||||
import { customElement } from "lit/decorators.js";
|
|
||||||
|
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
|
||||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
|
||||||
import PFGlobal from "@patternfly/patternfly/patternfly-base.css";
|
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
|
||||||
|
|
||||||
import { CurrentBrand, UiThemeEnum } from "@goauthentik/api";
|
import { CurrentBrand, UiThemeEnum } from "@goauthentik/api";
|
||||||
|
|
||||||
// If the viewport is wider than MIN_WIDTH, the sidebar
|
// If the viewport is wider than MIN_WIDTH, the sidebar
|
||||||
@ -27,56 +14,3 @@ export const DefaultBrand: CurrentBrand = {
|
|||||||
matchedDomain: "",
|
matchedDomain: "",
|
||||||
defaultLocale: "",
|
defaultLocale: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
@customElement("ak-sidebar-brand")
|
|
||||||
export class SidebarBrand extends WithBrandConfig(AKElement) {
|
|
||||||
static get styles(): CSSResult[] {
|
|
||||||
return [
|
|
||||||
PFBase,
|
|
||||||
PFGlobal,
|
|
||||||
PFPage,
|
|
||||||
PFButton,
|
|
||||||
css`
|
|
||||||
:host {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
height: var(--ak-navbar-height);
|
|
||||||
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;
|
|
||||||
height: 42px;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
window.addEventListener("resize", () => {
|
|
||||||
this.requestUpdate();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): TemplateResult {
|
|
||||||
return html` <a href="#/" class="pf-c-page__header-brand-link">
|
|
||||||
<div class="pf-c-brand ak-brand">
|
|
||||||
<img
|
|
||||||
src=${themeImage(this.brand?.brandingLogo ?? DefaultBrand.brandingLogo)}
|
|
||||||
alt="${msg("authentik Logo")}"
|
|
||||||
loading="lazy"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</a>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"ak-sidebar-brand": SidebarBrand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user