diff --git a/passbook/core/templates/base/page.html b/passbook/core/templates/base/page.html
index 4e8ff6d87..dca47d93c 100644
--- a/passbook/core/templates/base/page.html
+++ b/passbook/core/templates/base/page.html
@@ -3,7 +3,7 @@
{% load i18n %}
{% block body %}
-
+
{% trans 'Skip to content' %}
{% block page_content %}
diff --git a/passbook/core/templates/shell.html b/passbook/core/templates/shell.html
index 2f4e4431a..ae0b46403 100644
--- a/passbook/core/templates/shell.html
+++ b/passbook/core/templates/shell.html
@@ -1,10 +1,5 @@
-{% extends "base/page.html" %}
+{% extends "base/skeleton.html" %}
-{% block page_content %}
-
-
-
-
-
+{% block body %}
+
{% endblock %}
diff --git a/web/src/api/user.ts b/web/src/api/user.ts
index 6dc555df9..38c5c33dc 100644
--- a/web/src/api/user.ts
+++ b/web/src/api/user.ts
@@ -1,5 +1,7 @@
import { DefaultClient, PBResponse } from "./client";
+let me: User;
+
export class User {
pk: number;
username: string;
@@ -13,7 +15,10 @@ export class User {
}
static me(): Promise
{
- return DefaultClient.fetch(["core", "users", "me"]);
+ if (me) {
+ return Promise.resolve(me);
+ }
+ return DefaultClient.fetch(["core", "users", "me"]).then(u => me = u);
}
static count(): Promise {
diff --git a/web/src/elements/Messages.ts b/web/src/elements/Messages.ts
index 8408fb9ac..9fd990b8d 100644
--- a/web/src/elements/Messages.ts
+++ b/web/src/elements/Messages.ts
@@ -1,4 +1,5 @@
-import { LitElement, html, customElement, property, TemplateResult } from "lit-element";
+import { LitElement, html, customElement, TemplateResult } from "lit-element";
+import { DefaultClient } from "../api/client";
const LEVEL_ICON_MAP: { [key: string]: string } = {
error: "fas fa-exclamation-circle",
@@ -19,8 +20,7 @@ interface Message {
@customElement("pb-messages")
export class Messages extends LitElement {
- @property()
- url = "";
+ url = DefaultClient.makeUrl(["root", "messages"]);
messageSocket?: WebSocket;
retryDelay = 200;
diff --git a/web/src/elements/sidebar/Sidebar.ts b/web/src/elements/sidebar/Sidebar.ts
index 667d3c371..87e60ec82 100644
--- a/web/src/elements/sidebar/Sidebar.ts
+++ b/web/src/elements/sidebar/Sidebar.ts
@@ -6,136 +6,22 @@ import NavStyle from "@patternfly/patternfly/components/Nav/nav.css";
// @ts-ignore
import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
-import { User } from "../../api/user";
+import { until } from "lit-html/directives/until";
export interface SidebarItem {
name: string;
path?: string[];
children?: SidebarItem[];
- condition?: (sb: Sidebar) => boolean;
+ condition?: () => Promise;
}
-export const SIDEBAR_ITEMS: SidebarItem[] = [
- {
- name: "Library",
- path: ["/library/"],
- },
- {
- name: "Monitor",
- path: ["/audit/audit/"],
- condition: (sb: Sidebar): boolean => {
- return sb.user?.is_superuser || false;
- },
- },
- {
- name: "Administration",
- children: [
- {
- name: "Overview",
- path: ["/administration/overview-ng/"],
- },
- {
- name: "System Tasks",
- path: ["/administration/tasks/"],
- },
- {
- name: "Applications",
- path: ["/administration/applications/"],
- },
- {
- name: "Sources",
- path: ["/administration/sources/"],
- },
- {
- name: "Providers",
- path: ["/administration/providers/"],
- },
- {
- name: "User Management",
- children: [
- {
- name: "User",
- path: ["/administration/users/"],
- },
- {
- name: "Groups",
- path: ["/administration/groups/"],
- },
- ],
- },
- {
- name: "Outposts",
- children: [
- {
- name: "Outposts",
- path: ["/administration/outposts/"],
- },
- {
- name: "Service Connections",
- path: ["/administration/outposts/service_connections/"],
- },
- ],
- },
- {
- name: "Policies",
- children: [
- {
- name: "Policies",
- path: ["/administration/policies/"],
- },
- {
- name: "Bindings",
- path: ["/administration/policies/bindings/"],
- },
- ],
- },
- {
- name: "Property Mappings",
- path: ["/administration/property-mappings/"],
- },
- {
- name: "Flows",
- children: [
- {
- name: "Flows",
- path: ["/administration/flows/"],
- },
- {
- name: "Stages",
- path: ["/administration/stages/"],
- },
- {
- name: "Prompts",
- path: ["/administration/stages/prompts/"],
- },
- {
- name: "Invitations",
- path: ["/administration/stages/invitations/"],
- },
- ],
- },
- {
- name: "Certificates",
- path: ["/administration/crypto/certificates/"],
- },
- {
- name: "Tokens",
- path: ["/administration/tokens/"],
- },
- ],
- condition: (sb: Sidebar): boolean => {
- return sb.user?.is_superuser || false;
- },
- },
-];
-
@customElement("pb-sidebar")
export class Sidebar extends LitElement {
- @property()
- activePath: string;
+ @property({attribute: false})
+ items: SidebarItem[] = [];
@property()
- user?: User;
+ activePath: string;
static get styles(): CSSResult[] {
return [
@@ -145,7 +31,7 @@ export class Sidebar extends LitElement {
css`
.pf-c-nav__list .sidebar-brand {
max-height: 82px;
- margin-bottom: 0.5rem;
+ margin-bottom: -0.5rem;
}
.pf-c-nav__link {
--pf-c-nav__link--PaddingTop: 0.5rem;
@@ -167,16 +53,15 @@ export class Sidebar extends LitElement {
constructor() {
super();
- User.me().then((u) => (this.user = u));
this.activePath = window.location.hash.slice(1, Infinity);
window.addEventListener("hashchange", () => {
this.activePath = window.location.hash.slice(1, Infinity);
});
}
- renderItem(item: SidebarItem): TemplateResult {
+ async renderItem(item: SidebarItem): Promise {
if (item.condition) {
- const result = item.condition(this);
+ const result = await item.condition();
if (!result) {
return html``;
}
@@ -194,7 +79,7 @@ export class Sidebar extends LitElement {
- ${item.children?.map((i) => this.renderItem(i))}
+ ${item.children?.map((i) => until(this.renderItem(i), html``))}
`}
`;
@@ -207,9 +92,9 @@ export class Sidebar extends LitElement {
- ${SIDEBAR_ITEMS.map((i) => this.renderItem(i))}
+ ${this.items.map((i) => until(this.renderItem(i), html``))}
-
+
diff --git a/web/src/elements/sidebar/SidebarUser.ts b/web/src/elements/sidebar/SidebarUser.ts
index bfac50070..487c675f6 100644
--- a/web/src/elements/sidebar/SidebarUser.ts
+++ b/web/src/elements/sidebar/SidebarUser.ts
@@ -1,4 +1,4 @@
-import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
+import { css, CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element";
// @ts-ignore
import NavStyle from "@patternfly/patternfly/components/Nav/nav.css";
// @ts-ignore
@@ -9,9 +9,6 @@ import { User } from "../../api/user";
@customElement("pb-sidebar-user")
export class SidebarUser extends LitElement {
- @property()
- user?: User;
-
static get styles(): CSSResult[] {
return [
fa,
@@ -44,14 +41,12 @@ export class SidebarUser extends LitElement {
];
}
- render(): TemplateResult {
- if (!this.user) {
- return html``;
- }
+ async render(): Promise {
+ const user = await User.me();
return html`
-
- ${this.user?.username}
+
+ ${user.username}
diff --git a/web/src/interfaces/AdminInterface.ts b/web/src/interfaces/AdminInterface.ts
new file mode 100644
index 000000000..97e3242aa
--- /dev/null
+++ b/web/src/interfaces/AdminInterface.ts
@@ -0,0 +1,127 @@
+import { customElement } from "lit-element";
+import { User } from "../api/user";
+import { SidebarItem } from "../elements/sidebar/Sidebar";
+import { Interface } from "./Interface";
+
+export const SIDEBAR_ITEMS: SidebarItem[] = [
+ {
+ name: "Library",
+ path: ["/library/"],
+ },
+ {
+ name: "Monitor",
+ path: ["/audit/audit/"],
+ condition: (): Promise => {
+ return User.me().then(u => u.is_superuser);
+ },
+ },
+ {
+ name: "Administration",
+ children: [
+ {
+ name: "Overview",
+ path: ["/administration/overview-ng/"],
+ },
+ {
+ name: "System Tasks",
+ path: ["/administration/tasks/"],
+ },
+ {
+ name: "Applications",
+ path: ["/administration/applications/"],
+ },
+ {
+ name: "Sources",
+ path: ["/administration/sources/"],
+ },
+ {
+ name: "Providers",
+ path: ["/administration/providers/"],
+ },
+ {
+ name: "User Management",
+ children: [
+ {
+ name: "User",
+ path: ["/administration/users/"],
+ },
+ {
+ name: "Groups",
+ path: ["/administration/groups/"],
+ },
+ ],
+ },
+ {
+ name: "Outposts",
+ children: [
+ {
+ name: "Outposts",
+ path: ["/administration/outposts/"],
+ },
+ {
+ name: "Service Connections",
+ path: ["/administration/outposts/service_connections/"],
+ },
+ ],
+ },
+ {
+ name: "Policies",
+ children: [
+ {
+ name: "Policies",
+ path: ["/administration/policies/"],
+ },
+ {
+ name: "Bindings",
+ path: ["/administration/policies/bindings/"],
+ },
+ ],
+ },
+ {
+ name: "Property Mappings",
+ path: ["/administration/property-mappings/"],
+ },
+ {
+ name: "Flows",
+ children: [
+ {
+ name: "Flows",
+ path: ["/administration/flows/"],
+ },
+ {
+ name: "Stages",
+ path: ["/administration/stages/"],
+ },
+ {
+ name: "Prompts",
+ path: ["/administration/stages/prompts/"],
+ },
+ {
+ name: "Invitations",
+ path: ["/administration/stages/invitations/"],
+ },
+ ],
+ },
+ {
+ name: "Certificates",
+ path: ["/administration/crypto/certificates/"],
+ },
+ {
+ name: "Tokens",
+ path: ["/administration/tokens/"],
+ },
+ ],
+ condition: (): Promise => {
+ return User.me().then(u => u.is_superuser);
+ },
+ },
+];
+
+@customElement("pb-interface-admin")
+export class AdminInterface extends Interface {
+
+ get sidebar(): SidebarItem[] {
+ return SIDEBAR_ITEMS;
+ }
+
+}
diff --git a/web/src/interfaces/Interface.ts b/web/src/interfaces/Interface.ts
new file mode 100644
index 000000000..e25ba28bd
--- /dev/null
+++ b/web/src/interfaces/Interface.ts
@@ -0,0 +1,28 @@
+import { gettext } from "django";
+import { CSSResult, html, LitElement, TemplateResult } from "lit-element";
+import { COMMON_STYLES } from "../common/styles";
+import { SidebarItem } from "../elements/sidebar/Sidebar";
+
+// @customElement("pb-interface")
+export abstract class Interface extends LitElement {
+
+ abstract get sidebar(): SidebarItem[];
+
+ static get styles(): CSSResult[] {
+ return COMMON_STYLES;
+ }
+
+ render(): TemplateResult {
+ return html`
+ `;
+ }
+
+}
diff --git a/web/src/main.ts b/web/src/main.ts
index a87a5424e..134d18994 100644
--- a/web/src/main.ts
+++ b/web/src/main.ts
@@ -30,3 +30,5 @@ import "./pages/admin-overview/TopApplicationsTable";
import "./pages/applications/ApplicationListPage";
import "./pages/applications/ApplicationViewPage";
import "./pages/LibraryPage";
+
+import "./interfaces/AdminInterface";
diff --git a/web/src/passbook.css b/web/src/passbook.css
index d9f05521a..7d441a94f 100644
--- a/web/src/passbook.css
+++ b/web/src/passbook.css
@@ -114,7 +114,3 @@ select[multiple] {
.pf-c-content h1 :first-child {
margin-right: var(--pf-global--spacer--sm);
}
-
-.pf-c-empty-state {
- height: 100vh;
-}