web: switch to eslint
This commit is contained in:
parent
a312ad2ad1
commit
0231bcf685
|
@ -0,0 +1,6 @@
|
||||||
|
# don't ever lint node_modules
|
||||||
|
node_modules
|
||||||
|
# don't lint build output (make sure it's set to your correct build folder name)
|
||||||
|
dist
|
||||||
|
# don't lint nyc coverage output
|
||||||
|
coverage
|
|
@ -0,0 +1,19 @@
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es2021: true,
|
||||||
|
},
|
||||||
|
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||||
|
parser: "@typescript-eslint/parser",
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 12,
|
||||||
|
sourceType: "module",
|
||||||
|
},
|
||||||
|
plugins: ["@typescript-eslint"],
|
||||||
|
rules: {
|
||||||
|
indent: ["error", 4],
|
||||||
|
"linebreak-style": ["error", "unix"],
|
||||||
|
quotes: ["error", "double"],
|
||||||
|
semi: ["error", "always"],
|
||||||
|
},
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
|
@ -24,11 +24,14 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-typescript": "^8.0.0",
|
"@rollup/plugin-typescript": "^8.0.0",
|
||||||
"prettier": "2.2.1",
|
"@typescript-eslint/eslint-plugin": "^4.9.0",
|
||||||
|
"@typescript-eslint/parser": "^4.9.0",
|
||||||
|
"eslint": "^7.14.0",
|
||||||
"rollup-plugin-commonjs": "^10.1.0",
|
"rollup-plugin-commonjs": "^10.1.0",
|
||||||
"rollup-plugin-minify-html-literals": "^1.2.5",
|
"rollup-plugin-minify-html-literals": "^1.2.5",
|
||||||
"rollup-plugin-node-resolve": "^5.2.0",
|
"rollup-plugin-node-resolve": "^5.2.0",
|
||||||
"rollup-plugin-sourcemaps": "^0.6.3",
|
"rollup-plugin-sourcemaps": "^0.6.3",
|
||||||
"rollup-plugin-terser": "^7.0.2"
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
|
"typescript": "^4.1.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ export class Client {
|
||||||
makeUrl(url: string[], query?: { [key: string]: string }): string {
|
makeUrl(url: string[], query?: { [key: string]: string }): string {
|
||||||
let builtUrl = `/api/${VERSION}/${url.join("/")}/`;
|
let builtUrl = `/api/${VERSION}/${url.join("/")}/`;
|
||||||
if (query) {
|
if (query) {
|
||||||
let queryString = Object.keys(query)
|
const queryString = Object.keys(query)
|
||||||
.map((k) => encodeURIComponent(k) + "=" + encodeURIComponent(query[k]))
|
.map((k) => encodeURIComponent(k) + "=" + encodeURIComponent(query[k]))
|
||||||
.join("&");
|
.join("&");
|
||||||
builtUrl += `?${queryString}`;
|
builtUrl += `?${queryString}`;
|
||||||
|
@ -20,10 +20,10 @@ export class Client {
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
if (r.status > 300) {
|
if (r.status > 300) {
|
||||||
switch (r.status) {
|
switch (r.status) {
|
||||||
case 404:
|
case 404:
|
||||||
throw new NotFoundError(`URL ${finalUrl} not found`);
|
throw new NotFoundError(`URL ${finalUrl} not found`);
|
||||||
default:
|
default:
|
||||||
throw new RequestError(r.statusText);
|
throw new RequestError(r.statusText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
|
|
|
@ -21,7 +21,7 @@ export class Config {
|
||||||
tracesSampleRate: 1.0,
|
tracesSampleRate: 1.0,
|
||||||
environment: config.error_reporting_environment,
|
environment: config.error_reporting_environment,
|
||||||
});
|
});
|
||||||
console.debug(`passbook/config: Sentry enabled.`);
|
console.debug("passbook/config: Sentry enabled.");
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
export interface Policy {
|
||||||
|
pk: string;
|
||||||
|
name: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PolicyBinding {
|
||||||
|
pk: string;
|
||||||
|
policy: string,
|
||||||
|
policy_obj: Policy;
|
||||||
|
target: string;
|
||||||
|
enabled: boolean;
|
||||||
|
order: number;
|
||||||
|
timeout: number;
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ interface TickValue {
|
||||||
@customElement("pb-admin-logins-chart")
|
@customElement("pb-admin-logins-chart")
|
||||||
export class AdminLoginsChart extends LitElement {
|
export class AdminLoginsChart extends LitElement {
|
||||||
@property()
|
@property()
|
||||||
url: string = "";
|
url = "";
|
||||||
|
|
||||||
chart: any;
|
chart: any;
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ export class AdminLoginsChart extends LitElement {
|
||||||
.then((r) => r.json())
|
.then((r) => r.json())
|
||||||
.catch((e) => console.error(e))
|
.catch((e) => console.error(e))
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
let ctx = (<HTMLCanvasElement>this.shadowRoot?.querySelector("canvas")).getContext(
|
const ctx = (<HTMLCanvasElement>this.shadowRoot?.querySelector("canvas")).getContext(
|
||||||
"2d"
|
"2d"
|
||||||
)!;
|
)!;
|
||||||
this.chart = new Chart(ctx, {
|
this.chart = new Chart(ctx, {
|
||||||
|
|
|
@ -10,10 +10,10 @@ import "codemirror/mode/python/python.js";
|
||||||
@customElement("pb-codemirror")
|
@customElement("pb-codemirror")
|
||||||
export class CodeMirrorTextarea extends LitElement {
|
export class CodeMirrorTextarea extends LitElement {
|
||||||
@property()
|
@property()
|
||||||
readOnly: boolean = false;
|
readOnly = false;
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
mode: string = "yaml";
|
mode = "yaml";
|
||||||
|
|
||||||
editor?: CodeMirror.EditorFromTextArea;
|
editor?: CodeMirror.EditorFromTextArea;
|
||||||
|
|
||||||
|
|
|
@ -7,16 +7,16 @@ interface ComparisonHash {
|
||||||
@customElement("fetch-fill-slot")
|
@customElement("fetch-fill-slot")
|
||||||
export class FetchFillSlot extends LitElement {
|
export class FetchFillSlot extends LitElement {
|
||||||
@property()
|
@property()
|
||||||
url: string = "";
|
url = "";
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
key: string = "";
|
key = "";
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
value: string = "";
|
value = "";
|
||||||
|
|
||||||
comparison(slotName: string) {
|
comparison(slotName: string) {
|
||||||
let comparisonOperatorsHash = <ComparisonHash>{
|
const comparisonOperatorsHash = <ComparisonHash>{
|
||||||
"<": function (a: any, b: any) {
|
"<": function (a: any, b: any) {
|
||||||
return a < b;
|
return a < b;
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,7 +7,7 @@ const LEVEL_ICON_MAP: { [key: string]: string } = {
|
||||||
info: "fas fa-info",
|
info: "fas fa-info",
|
||||||
};
|
};
|
||||||
|
|
||||||
let ID = function (prefix: string) {
|
const ID = function (prefix: string) {
|
||||||
return prefix + Math.random().toString(36).substr(2, 9);
|
return prefix + Math.random().toString(36).substr(2, 9);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,10 +20,10 @@ interface Message {
|
||||||
@customElement("pb-messages")
|
@customElement("pb-messages")
|
||||||
export class Messages extends LitElement {
|
export class Messages extends LitElement {
|
||||||
@property()
|
@property()
|
||||||
url: string = "";
|
url = "";
|
||||||
|
|
||||||
messageSocket?: WebSocket;
|
messageSocket?: WebSocket;
|
||||||
retryDelay: number = 200;
|
retryDelay = 200;
|
||||||
|
|
||||||
createRenderRoot() {
|
createRenderRoot() {
|
||||||
return this;
|
return this;
|
||||||
|
@ -72,7 +72,7 @@ export class Messages extends LitElement {
|
||||||
* This mostly gets messages which were created when the user arrives/leaves the site
|
* This mostly gets messages which were created when the user arrives/leaves the site
|
||||||
* and especially the login flow */
|
* and especially the login flow */
|
||||||
fetchMessages() {
|
fetchMessages() {
|
||||||
console.debug(`passbook/messages: fetching messages over direct api`);
|
console.debug("passbook/messages: fetching messages over direct api");
|
||||||
return fetch(this.url)
|
return fetch(this.url)
|
||||||
.then((r) => r.json())
|
.then((r) => r.json())
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
|
@ -93,8 +93,8 @@ export class Messages extends LitElement {
|
||||||
const el = document.createElement("template");
|
const el = document.createElement("template");
|
||||||
el.innerHTML = `<li id=${id} class="pf-c-alert-group__item">
|
el.innerHTML = `<li id=${id} class="pf-c-alert-group__item">
|
||||||
<div class="pf-c-alert pf-m-${message.levelTag} ${
|
<div class="pf-c-alert pf-m-${message.levelTag} ${
|
||||||
message.levelTag === "error" ? "pf-m-danger" : ""
|
message.levelTag === "error" ? "pf-m-danger" : ""
|
||||||
}">
|
}">
|
||||||
<div class="pf-c-alert__icon">
|
<div class="pf-c-alert__icon">
|
||||||
<i class="${LEVEL_ICON_MAP[message.levelTag]}"></i>
|
<i class="${LEVEL_ICON_MAP[message.levelTag]}"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,7 +15,7 @@ export class Tabs extends LitElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let pages = Array.from(this.querySelectorAll("[slot]")!);
|
const pages = Array.from(this.querySelectorAll("[slot]")!);
|
||||||
if (!this.currentPage) {
|
if (!this.currentPage) {
|
||||||
if (pages.length < 1) {
|
if (pages.length < 1) {
|
||||||
return html`<h1>no tabs defined</h1>`;
|
return html`<h1>no tabs defined</h1>`;
|
||||||
|
@ -25,24 +25,24 @@ export class Tabs extends LitElement {
|
||||||
return html`<div class="pf-c-tabs">
|
return html`<div class="pf-c-tabs">
|
||||||
<ul class="pf-c-tabs__list">
|
<ul class="pf-c-tabs__list">
|
||||||
${pages.map((page) => {
|
${pages.map((page) => {
|
||||||
const slot = page.attributes.getNamedItem("slot")?.value;
|
const slot = page.attributes.getNamedItem("slot")?.value;
|
||||||
return html` <li
|
return html` <li
|
||||||
class="pf-c-tabs__item ${slot === this.currentPage
|
class="pf-c-tabs__item ${slot === this.currentPage
|
||||||
? CURRENT_CLASS
|
? CURRENT_CLASS
|
||||||
: ""}"
|
: ""}"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="pf-c-tabs__link"
|
class="pf-c-tabs__link"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
this.currentPage = slot;
|
this.currentPage = slot;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span class="pf-c-tabs__item-text">
|
<span class="pf-c-tabs__item-text">
|
||||||
${page.attributes.getNamedItem("tab-title")?.value}
|
${page.attributes.getNamedItem("tab-title")?.value}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</li>`;
|
</li>`;
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<slot name="${this.currentPage}"></slot>`;
|
<slot name="${this.currentPage}"></slot>`;
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { SpinnerButton } from "./SpinnerButton";
|
||||||
@customElement("pb-action-button")
|
@customElement("pb-action-button")
|
||||||
export class ActionButton extends SpinnerButton {
|
export class ActionButton extends SpinnerButton {
|
||||||
@property()
|
@property()
|
||||||
url: string = "";
|
url = "";
|
||||||
|
|
||||||
callAction() {
|
callAction() {
|
||||||
if (this.isRunning === true) {
|
if (this.isRunning === true) {
|
||||||
|
|
|
@ -20,7 +20,7 @@ export class ModalButton extends LitElement {
|
||||||
href?: string;
|
href?: string;
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
open: boolean = false;
|
open = false;
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [
|
||||||
|
@ -79,7 +79,7 @@ export class ModalButton extends LitElement {
|
||||||
this.querySelectorAll<HTMLFormElement>("[slot=modal] form").forEach((form) => {
|
this.querySelectorAll<HTMLFormElement>("[slot=modal] form").forEach((form) => {
|
||||||
form.addEventListener("submit", (e) => {
|
form.addEventListener("submit", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let formData = new FormData(form);
|
const formData = new FormData(form);
|
||||||
fetch(this.href ? this.href : form.action, {
|
fetch(this.href ? this.href : form.action, {
|
||||||
method: form.method,
|
method: form.method,
|
||||||
body: formData,
|
body: formData,
|
||||||
|
@ -91,11 +91,11 @@ export class ModalButton extends LitElement {
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.indexOf("csrfmiddlewaretoken") !== -1) {
|
if (data.indexOf("csrfmiddlewaretoken") !== -1) {
|
||||||
this.querySelector("[slot=modal]")!.innerHTML = data;
|
this.querySelector("[slot=modal]")!.innerHTML = data;
|
||||||
console.debug(`passbook/modalbutton: re-showing form`);
|
console.debug("passbook/modalbutton: re-showing form");
|
||||||
this.updateHandlers();
|
this.updateHandlers();
|
||||||
} else {
|
} else {
|
||||||
this.open = false;
|
this.open = false;
|
||||||
console.debug(`passbook/modalbutton: successful submit`);
|
console.debug("passbook/modalbutton: successful submit");
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("hashchange", {
|
new CustomEvent("hashchange", {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
|
|
|
@ -70,7 +70,7 @@ export class SpinnerButton extends LitElement {
|
||||||
@click=${() => this.callAction()}
|
@click=${() => this.callAction()}
|
||||||
>
|
>
|
||||||
${this.isRunning
|
${this.isRunning
|
||||||
? html` <span class="pf-c-button__progress">
|
? html` <span class="pf-c-button__progress">
|
||||||
<span
|
<span
|
||||||
class="pf-c-spinner pf-m-md"
|
class="pf-c-spinner pf-m-md"
|
||||||
role="progressbar"
|
role="progressbar"
|
||||||
|
@ -81,7 +81,7 @@ export class SpinnerButton extends LitElement {
|
||||||
<span class="pf-c-spinner__tail-ball"></span>
|
<span class="pf-c-spinner__tail-ball"></span>
|
||||||
</span>
|
</span>
|
||||||
</span>`
|
</span>`
|
||||||
: ""}
|
: ""}
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</button>`;
|
</button>`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,15 +181,15 @@ export class Sidebar extends LitElement {
|
||||||
class="pf-c-nav__item ${item.children ? "pf-m-expandable pf-m-expanded" : ""}"
|
class="pf-c-nav__item ${item.children ? "pf-m-expandable pf-m-expanded" : ""}"
|
||||||
>
|
>
|
||||||
${item.path
|
${item.path
|
||||||
? html`<a
|
? html`<a
|
||||||
href="#${item.path}"
|
href="#${item.path}"
|
||||||
class="pf-c-nav__link ${item.path.some((v) => v === this.activePath)
|
class="pf-c-nav__link ${item.path.some((v) => v === this.activePath)
|
||||||
? "pf-m-current"
|
? "pf-m-current"
|
||||||
: ""}"
|
: ""}"
|
||||||
>
|
>
|
||||||
${item.name}
|
${item.name}
|
||||||
</a>`
|
</a>`
|
||||||
: html`<a class="pf-c-nav__link" aria-expanded="true"
|
: html`<a class="pf-c-nav__link" aria-expanded="true"
|
||||||
>${item.name}
|
>${item.name}
|
||||||
<span class="pf-c-nav__toggle">
|
<span class="pf-c-nav__toggle">
|
||||||
<i class="fas fa-angle-right" aria-hidden="true"></i>
|
<i class="fas fa-angle-right" aria-hidden="true"></i>
|
||||||
|
|
|
@ -48,8 +48,8 @@ export class SidebarBrand extends LitElement {
|
||||||
<div class="pf-c-brand pb-brand">
|
<div class="pf-c-brand pb-brand">
|
||||||
<img src="${this.config?.branding_logo}" alt="passbook icon" loading="lazy" />
|
<img src="${this.config?.branding_logo}" alt="passbook icon" loading="lazy" />
|
||||||
${this.config?.branding_title
|
${this.config?.branding_title
|
||||||
? html`<span>${this.config.branding_title}</span>`
|
? html`<span>${this.config.branding_title}</span>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
</a>`;
|
</a>`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ export abstract class Table<T> extends LitElement {
|
||||||
data?: PBResponse<T>;
|
data?: PBResponse<T>;
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
page: number = 1;
|
page = 1;
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [COMMON_STYLES];
|
return [COMMON_STYLES];
|
||||||
|
@ -51,12 +51,12 @@ export abstract class Table<T> extends LitElement {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return this.data.results.map((item) => {
|
return this.data.results.map((item) => {
|
||||||
const fullRow = [`<tr role="row">`].concat(
|
const fullRow = ["<tr role=\"row\">"].concat(
|
||||||
this.row(item).map((col) => {
|
this.row(item).map((col) => {
|
||||||
return `<td role="cell">${col}</td>`;
|
return `<td role="cell">${col}</td>`;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
fullRow.push(`</tr>`);
|
fullRow.push("</tr>");
|
||||||
return html(<any>fullRow);
|
return html(<any>fullRow);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -71,8 +71,8 @@ export abstract class Table<T> extends LitElement {
|
||||||
<slot name="create-button"></slot>
|
<slot name="create-button"></slot>
|
||||||
<button
|
<button
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
this.fetch();
|
this.fetch();
|
||||||
}}
|
}}
|
||||||
class="pf-c-button pf-m-primary"
|
class="pf-c-button pf-m-primary"
|
||||||
>
|
>
|
||||||
Refresh
|
Refresh
|
||||||
|
@ -88,8 +88,8 @@ export abstract class Table<T> extends LitElement {
|
||||||
<thead>
|
<thead>
|
||||||
<tr role="row">
|
<tr role="row">
|
||||||
${this.columns().map(
|
${this.columns().map(
|
||||||
(col) => html`<th role="columnheader" scope="col">${col}</th>`
|
(col) => html`<th role="columnheader" scope="col">${col}</th>`
|
||||||
)}
|
)}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody role="rowgroup">
|
<tbody role="rowgroup">
|
||||||
|
|
|
@ -13,7 +13,7 @@ export class TablePagination extends LitElement {
|
||||||
|
|
||||||
previousHandler() {
|
previousHandler() {
|
||||||
if (!this.table?.data?.pagination.previous) {
|
if (!this.table?.data?.pagination.previous) {
|
||||||
console.debug(`passbook/tables: no previous`);
|
console.debug("passbook/tables: no previous");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.table.page = this.table?.data?.pagination.previous;
|
this.table.page = this.table?.data?.pagination.previous;
|
||||||
|
@ -21,7 +21,7 @@ export class TablePagination extends LitElement {
|
||||||
|
|
||||||
nextHandler() {
|
nextHandler() {
|
||||||
if (!this.table?.data?.pagination.next) {
|
if (!this.table?.data?.pagination.next) {
|
||||||
console.debug(`passbook/tables: no next`);
|
console.debug("passbook/tables: no next");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.table.page = this.table?.data?.pagination.next;
|
this.table.page = this.table?.data?.pagination.next;
|
||||||
|
@ -44,8 +44,8 @@ export class TablePagination extends LitElement {
|
||||||
<button
|
<button
|
||||||
class="pf-c-button pf-m-plain"
|
class="pf-c-button pf-m-plain"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
this.previousHandler();
|
this.previousHandler();
|
||||||
}}
|
}}
|
||||||
disabled="${this.table?.data?.pagination.previous ? "true" : "false"}"
|
disabled="${this.table?.data?.pagination.previous ? "true" : "false"}"
|
||||||
aria-label="{% trans 'Go to previous page' %}"
|
aria-label="{% trans 'Go to previous page' %}"
|
||||||
>
|
>
|
||||||
|
@ -56,8 +56,8 @@ export class TablePagination extends LitElement {
|
||||||
<button
|
<button
|
||||||
class="pf-c-button pf-m-plain"
|
class="pf-c-button pf-m-plain"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
this.nextHandler();
|
this.nextHandler();
|
||||||
}}
|
}}
|
||||||
disabled="${this.table?.data?.pagination.next ? "true" : "false"}"
|
disabled="${this.table?.data?.pagination.next ? "true" : "false"}"
|
||||||
aria-label="{% trans 'Go to next page' %}"
|
aria-label="{% trans 'Go to next page' %}"
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { LitElement, html, customElement, property } from "lit-element";
|
import { LitElement, html, customElement, property, TemplateResult } from "lit-element";
|
||||||
|
|
||||||
enum ResponseType {
|
enum ResponseType {
|
||||||
redirect = "redirect",
|
redirect = "redirect",
|
||||||
|
@ -14,16 +14,16 @@ interface Response {
|
||||||
@customElement("pb-flow-shell-card")
|
@customElement("pb-flow-shell-card")
|
||||||
export class FlowShellCard extends LitElement {
|
export class FlowShellCard extends LitElement {
|
||||||
@property()
|
@property()
|
||||||
flowBodyUrl: string = "";
|
flowBodyUrl = "";
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
flowBody?: string;
|
flowBody?: string;
|
||||||
|
|
||||||
createRenderRoot() {
|
createRenderRoot(): Element | ShadowRoot {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated(): void {
|
||||||
fetch(this.flowBodyUrl)
|
fetch(this.flowBodyUrl)
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
if (r.status === 404) {
|
if (r.status === 404) {
|
||||||
|
@ -46,40 +46,40 @@ export class FlowShellCard extends LitElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateCard(data: Response) {
|
async updateCard(data: Response): Promise<void> {
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case ResponseType.redirect:
|
case ResponseType.redirect:
|
||||||
window.location.assign(data.to!);
|
window.location.assign(data.to!);
|
||||||
break;
|
break;
|
||||||
case ResponseType.template:
|
case ResponseType.template:
|
||||||
this.flowBody = data.body;
|
this.flowBody = data.body;
|
||||||
await this.requestUpdate();
|
await this.requestUpdate();
|
||||||
this.checkAutofocus();
|
this.checkAutofocus();
|
||||||
this.loadFormCode();
|
this.loadFormCode();
|
||||||
this.setFormSubmitHandlers();
|
this.setFormSubmitHandlers();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.debug(`passbook/flows: unexpected data type ${data.type}`);
|
console.debug(`passbook/flows: unexpected data type ${data.type}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadFormCode() {
|
loadFormCode(): void {
|
||||||
this.querySelectorAll("script").forEach((script) => {
|
this.querySelectorAll("script").forEach((script) => {
|
||||||
let newScript = document.createElement("script");
|
const newScript = document.createElement("script");
|
||||||
newScript.src = script.src;
|
newScript.src = script.src;
|
||||||
document.head.appendChild(newScript);
|
document.head.appendChild(newScript);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAutofocus() {
|
checkAutofocus(): void {
|
||||||
const autofocusElement = <HTMLElement>this.querySelector("[autofocus]");
|
const autofocusElement = <HTMLElement>this.querySelector("[autofocus]");
|
||||||
if (autofocusElement !== null) {
|
if (autofocusElement !== null) {
|
||||||
autofocusElement.focus();
|
autofocusElement.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFormAction(form: HTMLFormElement) {
|
updateFormAction(form: HTMLFormElement): boolean {
|
||||||
for (let index = 0; index < form.elements.length; index++) {
|
for (let index = 0; index < form.elements.length; index++) {
|
||||||
const element = <HTMLInputElement>form.elements[index];
|
const element = <HTMLInputElement>form.elements[index];
|
||||||
if (element.value === form.action) {
|
if (element.value === form.action) {
|
||||||
|
@ -94,13 +94,13 @@ export class FlowShellCard extends LitElement {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAutosubmit(form: HTMLFormElement) {
|
checkAutosubmit(form: HTMLFormElement): void {
|
||||||
if ("autosubmit" in form.attributes) {
|
if ("autosubmit" in form.attributes) {
|
||||||
return form.submit();
|
return form.submit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setFormSubmitHandlers() {
|
setFormSubmitHandlers(): void {
|
||||||
this.querySelectorAll("form").forEach((form) => {
|
this.querySelectorAll("form").forEach((form) => {
|
||||||
console.debug(`passbook/flows: Checking for autosubmit attribute ${form}`);
|
console.debug(`passbook/flows: Checking for autosubmit attribute ${form}`);
|
||||||
this.checkAutosubmit(form);
|
this.checkAutosubmit(form);
|
||||||
|
@ -109,7 +109,7 @@ export class FlowShellCard extends LitElement {
|
||||||
console.debug(`passbook/flows: Adding handler for form ${form}`);
|
console.debug(`passbook/flows: Adding handler for form ${form}`);
|
||||||
form.addEventListener("submit", (e) => {
|
form.addEventListener("submit", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let formData = new FormData(form);
|
const formData = new FormData(form);
|
||||||
this.flowBody = undefined;
|
this.flowBody = undefined;
|
||||||
fetch(this.flowBodyUrl, {
|
fetch(this.flowBodyUrl, {
|
||||||
method: "post",
|
method: "post",
|
||||||
|
@ -129,7 +129,7 @@ export class FlowShellCard extends LitElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
errorMessage(error: string) {
|
errorMessage(error: string): void {
|
||||||
this.flowBody = `
|
this.flowBody = `
|
||||||
<style>
|
<style>
|
||||||
.pb-exception {
|
.pb-exception {
|
||||||
|
@ -150,7 +150,7 @@ export class FlowShellCard extends LitElement {
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
loading() {
|
loading(): TemplateResult {
|
||||||
return html` <div class="pf-c-login__main-body pb-loading">
|
return html` <div class="pf-c-login__main-body pb-loading">
|
||||||
<span class="pf-c-spinner" role="progressbar" aria-valuetext="Loading...">
|
<span class="pf-c-spinner" role="progressbar" aria-valuetext="Loading...">
|
||||||
<span class="pf-c-spinner__clipper"></span>
|
<span class="pf-c-spinner__clipper"></span>
|
||||||
|
@ -160,7 +160,7 @@ export class FlowShellCard extends LitElement {
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render(): TemplateResult {
|
||||||
if (this.flowBody) {
|
if (this.flowBody) {
|
||||||
return html(<TemplateStringsArray>(<unknown>[this.flowBody]));
|
return html(<TemplateStringsArray>(<unknown>[this.flowBody]));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { css, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
import { css, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||||
import { Application } from "../api/application";
|
import { Application } from "../api/application";
|
||||||
import { DefaultClient, PBResponse } from "../api/client";
|
import { PBResponse } from "../api/client";
|
||||||
import { COMMON_STYLES } from "../common/styles";
|
import { COMMON_STYLES } from "../common/styles";
|
||||||
import { truncate } from "../utils";
|
import { truncate } from "../utils";
|
||||||
|
|
||||||
|
@ -19,18 +19,17 @@ export class ApplicationViewPage extends LitElement {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated(): void {
|
||||||
Application.list().then((r) => (this.apps = r));
|
Application.list().then((r) => (this.apps = r));
|
||||||
}
|
}
|
||||||
|
|
||||||
renderEmptyState() {
|
renderEmptyState(): TemplateResult {
|
||||||
return html` <div class="pf-c-empty-state pf-m-full-height">
|
return html` <div class="pf-c-empty-state pf-m-full-height">
|
||||||
<div class="pf-c-empty-state__content">
|
<div class="pf-c-empty-state__content">
|
||||||
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
|
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||||
<h1 class="pf-c-title pf-m-lg">{% trans 'No Applications available.' %}</h1>
|
<h1 class="pf-c-title pf-m-lg">No Applications available.</h1>
|
||||||
<div class="pf-c-empty-state__body">
|
<div class="pf-c-empty-state__body">
|
||||||
{% trans "Either no applications are defined, or you don't have access to any."
|
Either no applications are defined, or you don't have access to any.
|
||||||
%}
|
|
||||||
</div>
|
</div>
|
||||||
{% if perms.passbook_core.add_application %}
|
{% if perms.passbook_core.add_application %}
|
||||||
<a
|
<a
|
||||||
|
@ -49,12 +48,12 @@ export class ApplicationViewPage extends LitElement {
|
||||||
return html` <a href="${app.launch_url}" class="pf-c-card pf-m-hoverable pf-m-compact">
|
return html` <a href="${app.launch_url}" class="pf-c-card pf-m-hoverable pf-m-compact">
|
||||||
<div class="pf-c-card__header">
|
<div class="pf-c-card__header">
|
||||||
${app.meta_icon
|
${app.meta_icon
|
||||||
? html`<img
|
? html`<img
|
||||||
class="app-icon pf-c-avatar"
|
class="app-icon pf-c-avatar"
|
||||||
src="${app.meta_icon}"
|
src="${app.meta_icon}"
|
||||||
alt="Application Icon"
|
alt="Application Icon"
|
||||||
/>`
|
/>`
|
||||||
: html`<i class="pf-icon pf-icon-arrow"></i>`}
|
: html`<i class="pf-icon pf-icon-arrow"></i>`}
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card__title">
|
<div class="pf-c-card__title">
|
||||||
<p id="card-1-check-label">${app.name}</p>
|
<p id="card-1-check-label">${app.name}</p>
|
||||||
|
@ -66,7 +65,7 @@ export class ApplicationViewPage extends LitElement {
|
||||||
</a>`;
|
</a>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoading() {
|
renderLoading(): TemplateResult {
|
||||||
return html`<div class="pf-c-empty-state pf-m-full-height">
|
return html`<div class="pf-c-empty-state pf-m-full-height">
|
||||||
<div class="pf-c-empty-state__content">
|
<div class="pf-c-empty-state__content">
|
||||||
<div class="pf-l-bullseye">
|
<div class="pf-l-bullseye">
|
||||||
|
@ -86,7 +85,7 @@ export class ApplicationViewPage extends LitElement {
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render(): TemplateResult {
|
||||||
return html`<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
return html`<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
||||||
<section class="pf-c-page__main-section pf-m-light">
|
<section class="pf-c-page__main-section pf-m-light">
|
||||||
<div class="pf-c-content">
|
<div class="pf-c-content">
|
||||||
|
@ -97,12 +96,12 @@ export class ApplicationViewPage extends LitElement {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
${this.apps
|
${this.apps
|
||||||
? html`<section class="pf-c-page__main-section">
|
? html`<section class="pf-c-page__main-section">
|
||||||
<div class="pf-l-gallery pf-m-gutter">
|
<div class="pf-l-gallery pf-m-gutter">
|
||||||
${this.apps.results.map((app) => this.renderApp(app))}
|
${this.apps.results.map((app) => this.renderApp(app))}
|
||||||
</div>
|
</div>
|
||||||
</section>`
|
</section>`
|
||||||
: this.renderLoading()}
|
: this.renderLoading()}
|
||||||
</main>`;
|
</main>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,10 +49,10 @@ export class Route {
|
||||||
export const SLUG_REGEX = "[-a-zA-Z0-9_]+";
|
export const SLUG_REGEX = "[-a-zA-Z0-9_]+";
|
||||||
export const ROUTES: Route[] = [
|
export const ROUTES: Route[] = [
|
||||||
// Prevent infinite Shell loops
|
// Prevent infinite Shell loops
|
||||||
new Route(new RegExp(`^/$`)).redirect("/library/"),
|
new Route(new RegExp("^/$")).redirect("/library/"),
|
||||||
new Route(new RegExp(`^#.*`)).redirect("/library/"),
|
new Route(new RegExp("^#.*")).redirect("/library/"),
|
||||||
new Route(new RegExp(`^/library/$`), html`<pb-library></pb-library>`),
|
new Route(new RegExp("^/library/$"), html`<pb-library></pb-library>`),
|
||||||
new Route(new RegExp(`^/applications/$`), html`<pb-application-list></pb-application-list>`),
|
new Route(new RegExp("^/applications/$"), html`<pb-application-list></pb-application-list>`),
|
||||||
new Route(new RegExp(`^/applications/(?<slug>${SLUG_REGEX})/$`)).then((args) => {
|
new Route(new RegExp(`^/applications/(?<slug>${SLUG_REGEX})/$`)).then((args) => {
|
||||||
return html`<pb-application-view .args=${args}></pb-application-view>`;
|
return html`<pb-application-view .args=${args}></pb-application-view>`;
|
||||||
}),
|
}),
|
||||||
|
@ -99,14 +99,14 @@ export class RouterOutlet extends LitElement {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
window.addEventListener("hashchange", (e) => this.navigate());
|
window.addEventListener("hashchange", () => this.navigate());
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated(): void {
|
||||||
this.navigate();
|
this.navigate();
|
||||||
}
|
}
|
||||||
|
|
||||||
navigate() {
|
navigate(): void {
|
||||||
let activeUrl = window.location.hash.slice(1, Infinity);
|
let activeUrl = window.location.hash.slice(1, Infinity);
|
||||||
if (activeUrl === "") {
|
if (activeUrl === "") {
|
||||||
activeUrl = this.defaultUrl!;
|
activeUrl = this.defaultUrl!;
|
||||||
|
@ -141,7 +141,7 @@ export class RouterOutlet extends LitElement {
|
||||||
this.current = matchedRoute;
|
this.current = matchedRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render(): TemplateResult {
|
||||||
return this.current?.render();
|
return this.current?.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ export class SiteShell extends LitElement {
|
||||||
_url?: string;
|
_url?: string;
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
loading: boolean = false;
|
loading = false;
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [
|
||||||
|
@ -99,7 +99,7 @@ export class SiteShell extends LitElement {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html` ${this.loading
|
return html` ${this.loading
|
||||||
? html`<div class="pf-c-backdrop">
|
? html`<div class="pf-c-backdrop">
|
||||||
<div class="pf-l-bullseye">
|
<div class="pf-l-bullseye">
|
||||||
<div class="pf-l-bullseye__item">
|
<div class="pf-l-bullseye__item">
|
||||||
<span
|
<span
|
||||||
|
@ -114,7 +114,7 @@ export class SiteShell extends LitElement {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
: ""}
|
: ""}
|
||||||
<slot name="body"> </slot>`;
|
<slot name="body"> </slot>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import { css, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
import { css, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||||
import { Application } from "../../api/application";
|
import { Application } from "../../api/application";
|
||||||
import { DefaultClient, PBResponse } from "../../api/client";
|
import { DefaultClient, PBResponse } from "../../api/client";
|
||||||
|
import { PolicyBinding } from "../../api/policy_binding";
|
||||||
import { COMMON_STYLES } from "../../common/styles";
|
import { COMMON_STYLES } from "../../common/styles";
|
||||||
import { Table } from "../../elements/table/Table";
|
import { Table } from "../../elements/table/Table";
|
||||||
|
|
||||||
@customElement("pb-bound-policies-list")
|
@customElement("pb-bound-policies-list")
|
||||||
export class BoundPoliciesList extends Table<any> {
|
export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||||
@property()
|
@property()
|
||||||
target?: string;
|
target?: string;
|
||||||
|
|
||||||
apiEndpoint(page: number): Promise<PBResponse<any>> {
|
apiEndpoint(page: number): Promise<PBResponse<PolicyBinding>> {
|
||||||
return DefaultClient.fetch<PBResponse<any>>(["policies", "bindings"], {
|
return DefaultClient.fetch<PBResponse<PolicyBinding>>(["policies", "bindings"], {
|
||||||
target: this.target!,
|
target: this.target!,
|
||||||
ordering: "order",
|
ordering: "order",
|
||||||
page: page,
|
page: page,
|
||||||
|
@ -21,12 +22,12 @@ export class BoundPoliciesList extends Table<any> {
|
||||||
return ["Policy", "Enabled", "Order", "Timeout", ""];
|
return ["Policy", "Enabled", "Order", "Timeout", ""];
|
||||||
}
|
}
|
||||||
|
|
||||||
row(item: any): string[] {
|
row(item: PolicyBinding): string[] {
|
||||||
return [
|
return [
|
||||||
item.policy_obj.name,
|
item.policy_obj.name,
|
||||||
item.enabled,
|
item.enabled ? "Yes" : "No",
|
||||||
item.order,
|
item.order.toString(),
|
||||||
item.timeout,
|
item.timeout.toString(),
|
||||||
`
|
`
|
||||||
<pb-modal-button href="administration/policies/bindings/${item.pk}/update/">
|
<pb-modal-button href="administration/policies/bindings/${item.pk}/update/">
|
||||||
<pb-spinner-button slot="trigger" class="pf-m-secondary">
|
<pb-spinner-button slot="trigger" class="pf-m-secondary">
|
||||||
|
@ -70,7 +71,7 @@ export class ApplicationViewPage extends LitElement {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render(): TemplateResult {
|
||||||
if (!this.application) {
|
if (!this.application) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
@ -84,44 +85,24 @@ export class ApplicationViewPage extends LitElement {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<pb-tabs>
|
<pb-tabs>
|
||||||
<section
|
<section slot="page-1" tab-title="Users" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||||
slot="page-1"
|
|
||||||
tab-title="Users"
|
|
||||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
|
||||||
>
|
|
||||||
<div class="pf-l-gallery pf-m-gutter">
|
<div class="pf-l-gallery pf-m-gutter">
|
||||||
<div
|
<div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-4-col" style="grid-column-end: span 3;grid-row-end: span 2;">
|
||||||
class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-4-col"
|
|
||||||
style="grid-column-end: span 3;grid-row-end: span 2;"
|
|
||||||
>
|
|
||||||
<div class="pf-c-card__header">
|
<div class="pf-c-card__header">
|
||||||
<div class="pf-c-card__header-main">
|
<div class="pf-c-card__header-main">
|
||||||
<i class="pf-icon pf-icon-server"></i> Logins over the last 24
|
<i class="pf-icon pf-icon-server"></i> Logins over the last 24 hours
|
||||||
hours
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
<pb-admin-logins-chart
|
${this.application ?
|
||||||
url="${DefaultClient.makeUrl([
|
html`<pb-admin-logins-chart url="${DefaultClient.makeUrl(["core", "applications", this.application?.slug!, "metrics"])}"></pb-admin-logins-chart>` : ""}
|
||||||
"core",
|
|
||||||
"applications",
|
|
||||||
this.application?.slug!,
|
|
||||||
"metrics",
|
|
||||||
])}"
|
|
||||||
></pb-admin-logins-chart>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<div
|
<div slot="page-2" tab-title="Policy Bindings" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||||
slot="page-2"
|
|
||||||
tab-title="Policy Bindings"
|
|
||||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
|
||||||
>
|
|
||||||
<div class="pf-c-card">
|
<div class="pf-c-card">
|
||||||
<pb-bound-policies-list
|
<pb-bound-policies-list .target=${this.application.pk}></pb-bound-policies-list>
|
||||||
.target=${this.application.pk}
|
|
||||||
></pb-bound-policies-list>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</pb-tabs>`;
|
</pb-tabs>`;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export function getCookie(name: string) {
|
export function getCookie(name: string): string | undefined {
|
||||||
let cookieValue = null;
|
let cookieValue = undefined;
|
||||||
if (document.cookie && document.cookie !== "") {
|
if (document.cookie && document.cookie !== "") {
|
||||||
const cookies = document.cookie.split(";");
|
const cookies = document.cookie.split(";");
|
||||||
for (let i = 0; i < cookies.length; i++) {
|
for (let i = 0; i < cookies.length; i++) {
|
||||||
|
|
Reference in New Issue