web: remove policy bindings page (#370)
* admin: accept ?target for PolicyBindingCreateView * core: fix rendering of hidden fields in horizontal form * web: add create button for application's bound policies * admin: fix delete form not working * web: fix ak-refresh event not being dispatched correctly * web: fix linting errors * admin: fix tests not loading * build(deps-dev): bump eslint from 7.14.0 to 7.15.0 in /web (#372) Bumps [eslint](https://github.com/eslint/eslint) from 7.14.0 to 7.15.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v7.14.0...v7.15.0) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump rollup from 2.34.1 to 2.34.2 in /web (#373) Bumps [rollup](https://github.com/rollup/rollup) from 2.34.1 to 2.34.2. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v2.34.1...v2.34.2) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump @types/codemirror from 0.0.100 to 0.0.102 in /web (#374) Bumps [@types/codemirror](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/codemirror) from 0.0.100 to 0.0.102. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/codemirror) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump bandit from 1.6.2 to 1.6.3 (#371) * build(deps-dev): bump bandit from 1.6.2 to 1.6.3 Bumps [bandit](https://github.com/PyCQA/bandit) from 1.6.2 to 1.6.3. - [Release notes](https://github.com/PyCQA/bandit/releases) - [Commits](https://github.com/PyCQA/bandit/compare/1.6.2...1.6.3) Signed-off-by: dependabot[bot] <support@github.com> * root: update for new bandit version Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jens Langhammer <jens.langhammer@beryju.org> * web: add header to bound-policies * web: fix spacing between bulk_select buttons * web: add separate ak-bound-policies-list, add flow view page * web: fix flows' policies not loading * Squashed commit of the following: commite535cb0ec8
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu Dec 10 09:58:07 2020 +0100 build(deps): bump boto3 from 1.16.32 to 1.16.33 (#383) Bumps [boto3](https://github.com/boto/boto3) from 1.16.32 to 1.16.33. - [Release notes](https://github.com/boto/boto3/releases) - [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst) - [Commits](https://github.com/boto/boto3/compare/1.16.32...1.16.33) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit8c1f55d3e3
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed Dec 9 09:06:45 2020 +0100 build(deps): bump boto3 from 1.16.31 to 1.16.32 (#382) Bumps [boto3](https://github.com/boto/boto3) from 1.16.31 to 1.16.32. - [Release notes](https://github.com/boto/boto3/releases) - [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst) - [Commits](https://github.com/boto/boto3/compare/1.16.31...1.16.32) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commitc3a2cb44cd
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed Dec 9 09:06:29 2020 +0100 build(deps): bump celery from 5.0.3 to 5.0.4 (#380) Bumps [celery](https://github.com/celery/celery) from 5.0.3 to 5.0.4. - [Release notes](https://github.com/celery/celery/releases) - [Changelog](https://github.com/celery/celery/blob/master/Changelog.rst) - [Commits](https://github.com/celery/celery/compare/v5.0.3...v5.0.4) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit682401bbf2
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed Dec 9 07:20:45 2020 +0100 build(deps): bump uvicorn from 0.12.3 to 0.13.0 (#381) Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.12.3 to 0.13.0. - [Release notes](https://github.com/encode/uvicorn/releases) - [Changelog](https://github.com/encode/uvicorn/blob/master/CHANGELOG.md) - [Commits](https://github.com/encode/uvicorn/compare/0.12.3...0.13.0) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit3e6e167348
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue Dec 8 10:32:00 2020 +0100 build(deps-dev): bump @typescript-eslint/parser in /web (#377) Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.9.0 to 4.9.1. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.9.1/packages/parser) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commitd08c1b7b02
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue Dec 8 10:31:47 2020 +0100 build(deps): bump @sentry/browser from 5.28.0 to 5.29.0 in /web (#378) Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 5.28.0 to 5.29.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/5.28.0...5.29.0) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit94d70d252c
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue Dec 8 09:02:37 2020 +0100 build(deps): bump boto3 from 1.16.30 to 1.16.31 (#375) Bumps [boto3](https://github.com/boto/boto3) from 1.16.30 to 1.16.31. - [Release notes](https://github.com/boto/boto3/releases) - [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst) - [Commits](https://github.com/boto/boto3/compare/1.16.30...1.16.31) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commitccfe746dd5
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue Dec 8 09:02:28 2020 +0100 build(deps-dev): bump @typescript-eslint/eslint-plugin in /web (#376) Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.9.0 to 4.9.1. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.9.1/packages/eslint-plugin) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commitef5dffa96a
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue Dec 8 09:02:16 2020 +0100 build(deps): bump @sentry/tracing from 5.28.0 to 5.29.0 in /web (#379) Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 5.28.0 to 5.29.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/5.28.0...5.29.0) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit2caa1e7650
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Dec 7 11:21:07 2020 +0100 build(deps-dev): bump bandit from 1.6.2 to 1.6.3 (#371) * build(deps-dev): bump bandit from 1.6.2 to 1.6.3 Bumps [bandit](https://github.com/PyCQA/bandit) from 1.6.2 to 1.6.3. - [Release notes](https://github.com/PyCQA/bandit/releases) - [Commits](https://github.com/PyCQA/bandit/compare/1.6.2...1.6.3) Signed-off-by: dependabot[bot] <support@github.com> * root: update for new bandit version Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jens Langhammer <jens.langhammer@beryju.org> commit2246f3a534
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Dec 7 10:26:01 2020 +0100 build(deps): bump @types/codemirror from 0.0.100 to 0.0.102 in /web (#374) Bumps [@types/codemirror](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/codemirror) from 0.0.100 to 0.0.102. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/codemirror) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit95ba00cb79
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Dec 7 09:09:49 2020 +0100 build(deps): bump rollup from 2.34.1 to 2.34.2 in /web (#373) Bumps [rollup](https://github.com/rollup/rollup) from 2.34.1 to 2.34.2. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v2.34.1...v2.34.2) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit2ab4d6620f
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Dec 7 09:09:24 2020 +0100 build(deps-dev): bump eslint from 7.14.0 to 7.15.0 in /web (#372) Bumps [eslint](https://github.com/eslint/eslint) from 7.14.0 to 7.15.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v7.14.0...v7.15.0) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * web: fix linting error * web: simplify sidebar logic * web: add support for multiple active matchers per sidebar item * web: move router to elements * flows: add stage_obj to flows api * sources/*: make all sources implement SerializerModel * web: improve listing of stages * web: implement expandable table * web/table: use TemplateResult as return value for row() * web: add empty state, fix link for BoundStageList * admin: make stage binding form accept ?target like policy binding * web: fix styles in dark mode for expanding tables * flows: add policybindingmodel_ptr_id to FlowStageBinding API * web: improve wording for policies * web: fix dark theme for tertiary buttons and static modals * web: implement SourceViewPage * web: add empty state for BoundPoliciesList * web: cleanup URLs for FlowStageBindings * root: remove url attribute from ak-messages Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
76
web/src/api/flow.ts
Normal file
76
web/src/api/flow.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { DefaultClient, PBResponse, QueryArguments } from "./client";
|
||||
|
||||
export enum FlowDesignation {
|
||||
Authentication = "authentication",
|
||||
Authorization = "authorization",
|
||||
Invalidation = "invalidation",
|
||||
Enrollment = "enrollment",
|
||||
Unrenollment = "unenrollment",
|
||||
Recovery = "recovery",
|
||||
StageConfiguration = "stage_configuration",
|
||||
}
|
||||
|
||||
export class Flow {
|
||||
pk: string;
|
||||
policybindingmodel_ptr_id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
title: string;
|
||||
designation: FlowDesignation;
|
||||
background: string;
|
||||
stages: string[];
|
||||
policies: string[];
|
||||
cache_count: number;
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(slug: string): Promise<Flow> {
|
||||
return DefaultClient.fetch<Flow>(["flows", "instances", slug]);
|
||||
}
|
||||
|
||||
static list(filter?: QueryArguments): Promise<PBResponse<Flow>> {
|
||||
return DefaultClient.fetch<PBResponse<Flow>>(["flows", "instances"], filter);
|
||||
}
|
||||
}
|
||||
|
||||
export class Stage {
|
||||
pk: string;
|
||||
name: string;
|
||||
__type__: string;
|
||||
verbose_name: string;
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
}
|
||||
|
||||
export class FlowStageBinding {
|
||||
|
||||
pk: string;
|
||||
policybindingmodel_ptr_id: string;
|
||||
target: string;
|
||||
stage: string;
|
||||
stage_obj: Stage;
|
||||
evaluate_on_plan: boolean;
|
||||
re_evaluate_policies: boolean;
|
||||
order: number;
|
||||
policies: string[];
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(slug: string): Promise<FlowStageBinding> {
|
||||
return DefaultClient.fetch<FlowStageBinding>(["flows", "bindings", slug]);
|
||||
}
|
||||
|
||||
static list(filter?: QueryArguments): Promise<PBResponse<FlowStageBinding>> {
|
||||
return DefaultClient.fetch<PBResponse<FlowStageBinding>>(["flows", "bindings"], filter);
|
||||
}
|
||||
|
||||
static adminUrl(rest: string): string {
|
||||
return `/administration/stages/bindings/${rest}`;
|
||||
}
|
||||
}
|
@ -1,15 +1,33 @@
|
||||
import { DefaultClient, PBResponse, QueryArguments } from "./client";
|
||||
|
||||
export interface Policy {
|
||||
pk: string;
|
||||
name: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface PolicyBinding {
|
||||
export class PolicyBinding {
|
||||
pk: string;
|
||||
policy: string,
|
||||
policy: string;
|
||||
policy_obj: Policy;
|
||||
target: string;
|
||||
enabled: boolean;
|
||||
order: number;
|
||||
timeout: number;
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(pk: string): Promise<PolicyBinding> {
|
||||
return DefaultClient.fetch<PolicyBinding>(["policies", "bindings", pk]);
|
||||
}
|
||||
|
||||
static list(filter?: QueryArguments): Promise<PBResponse<PolicyBinding>> {
|
||||
return DefaultClient.fetch<PBResponse<PolicyBinding>>(["policies", "bindings"], filter);
|
||||
}
|
||||
|
||||
static adminUrl(rest: string): string {
|
||||
return `/administration/policies/bindings/${rest}`;
|
||||
}
|
||||
}
|
||||
|
22
web/src/api/source.ts
Normal file
22
web/src/api/source.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { DefaultClient, PBResponse, QueryArguments } from "./client";
|
||||
|
||||
export class Source {
|
||||
pk: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
enabled: boolean;
|
||||
authentication_flow: string;
|
||||
enrollment_flow: string;
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(slug: string): Promise<Source> {
|
||||
return DefaultClient.fetch<Source>(["sources", "all", slug]);
|
||||
}
|
||||
|
||||
static list(filter?: QueryArguments): Promise<PBResponse<Source>> {
|
||||
return DefaultClient.fetch<PBResponse<Source>>(["sources", "all"], filter);
|
||||
}
|
||||
}
|
@ -137,6 +137,14 @@ select[multiple] {
|
||||
--pf-c-table--BorderColor: var(--ak-dark-background-lighter);
|
||||
--pf-c-table--cell--Color: var(--ak-dark-foreground);
|
||||
}
|
||||
/* class for pagination text */
|
||||
.pf-c-options-menu__toggle {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
/* table icon used for expanding rows */
|
||||
.pf-c-table__toggle-icon {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
/* inputs */
|
||||
.pf-c-form-control {
|
||||
--pf-c-form-control--BorderTopColor: var(--ak-dark-background-lighter);
|
||||
@ -151,6 +159,13 @@ select[multiple] {
|
||||
background-color: var(--ak-dark-background-light);
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
.pf-c-button.pf-m-tertiary {
|
||||
--pf-c-button--after--BorderColor: var(--ak-dark-foreground-darker);
|
||||
color: var(--ak-dark-foreground-darker);
|
||||
}
|
||||
.pf-c-button.pf-m-tertiary:hover {
|
||||
--pf-c-button--after--BorderColor: var(--ak-dark-background-lighter);
|
||||
}
|
||||
.pf-c-form__label-text {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
@ -162,6 +177,12 @@ select[multiple] {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
/* modal */
|
||||
.pf-c-modal-box__header {
|
||||
background-color: var(--ak-dark-background-light);
|
||||
}
|
||||
.pf-c-modal-box__body {
|
||||
background-color: var(--ak-dark-background-light);
|
||||
}
|
||||
.pf-c-modal-box__footer {
|
||||
background-color: var(--ak-dark-background-light);
|
||||
}
|
||||
|
34
web/src/elements/EmptyState.ts
Normal file
34
web/src/elements/EmptyState.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import { COMMON_STYLES } from "../common/styles";
|
||||
|
||||
@customElement("ak-empty-state")
|
||||
export class EmptyState extends LitElement {
|
||||
|
||||
@property({type: String})
|
||||
icon = "";
|
||||
|
||||
@property()
|
||||
header?: string;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return COMMON_STYLES;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="pf-icon ${this.icon} pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
${this.header}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
<slot name="body"></slot>
|
||||
</div>
|
||||
<div class="pf-c-empty-state__primary">
|
||||
<slot name="primary"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
}
|
@ -104,6 +104,7 @@ export class ModalButton extends LitElement {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("ak-refresh", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
79
web/src/elements/policies/BoundPoliciesList.ts
Normal file
79
web/src/elements/policies/BoundPoliciesList.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { PBResponse } from "../../api/client";
|
||||
import { PolicyBinding } from "../../api/policy_binding";
|
||||
import { Table } from "../../elements/table/Table";
|
||||
|
||||
import "../../elements/Tabs";
|
||||
import "../../elements/AdminLoginsChart";
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
|
||||
@customElement("ak-bound-policies-list")
|
||||
export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
@property()
|
||||
target?: string;
|
||||
|
||||
apiEndpoint(page: number): Promise<PBResponse<PolicyBinding>> {
|
||||
return PolicyBinding.list({
|
||||
target: this.target || "",
|
||||
ordering: "order",
|
||||
page: page,
|
||||
});
|
||||
}
|
||||
|
||||
columns(): string[] {
|
||||
return ["Policy", "Enabled", "Order", "Timeout", ""];
|
||||
}
|
||||
|
||||
row(item: PolicyBinding): TemplateResult[] {
|
||||
return [
|
||||
html`${item.policy_obj.name}`,
|
||||
html`${item.enabled ? "Yes" : "No"}`,
|
||||
html`${item.order}`,
|
||||
html`${item.timeout}`,
|
||||
html`
|
||||
<ak-modal-button href="${PolicyBinding.adminUrl(`${item.pk}/update/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
Edit
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="${PolicyBinding.adminUrl(`${item.pk}/delete/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
Delete
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(html`<ak-empty-state header=${gettext("No Policies bound.")} icon="pf-icon-module">
|
||||
<div slot="body">
|
||||
${gettext("No policies are currently bound to this object.")}
|
||||
</div>
|
||||
<div slot="primary">
|
||||
<ak-modal-button href=${PolicyBinding.adminUrl(`create/?target=${this.target}`)}>
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
${gettext("Bind Policy")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</div>
|
||||
</ak-empty-state>`);
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-modal-button href=${PolicyBinding.adminUrl(`create/?target=${this.target}`)}>
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
${gettext("Bind Policy")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ import { Route } from "./Route";
|
||||
import { ROUTES } from "../../routes";
|
||||
import { RouteMatch } from "./RouteMatch";
|
||||
|
||||
import "../generic/SiteShell";
|
||||
import "../../pages/generic/SiteShell";
|
||||
|
||||
@customElement("ak-router-outlet")
|
||||
export class RouterOutlet extends LitElement {
|
@ -13,11 +13,58 @@ import { until } from "lit-html/directives/until";
|
||||
import "./SidebarBrand";
|
||||
import "./SidebarUser";
|
||||
|
||||
export interface SidebarItem {
|
||||
export class SidebarItem {
|
||||
name: string;
|
||||
path?: string[];
|
||||
children?: SidebarItem[];
|
||||
condition?: () => Promise<boolean>;
|
||||
path?: string;
|
||||
|
||||
_children: SidebarItem[];
|
||||
condition: () => Promise<boolean>;
|
||||
|
||||
activeMatchers: RegExp[];
|
||||
|
||||
constructor(name: string, path?: string) {
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
this._children = [];
|
||||
this.condition = async () => true;
|
||||
this.activeMatchers = [];
|
||||
if (this.path) {
|
||||
this.activeMatchers.push(new RegExp(`^${this.path}$`));
|
||||
}
|
||||
}
|
||||
|
||||
children(...children: SidebarItem[]): SidebarItem {
|
||||
this._children = children;
|
||||
return this;
|
||||
}
|
||||
|
||||
activeWhen(...regexp: string[]): SidebarItem {
|
||||
regexp.forEach(r => {
|
||||
this.activeMatchers.push(new RegExp(r));
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
when(condition: () => Promise<boolean>): SidebarItem {
|
||||
this.condition = condition;
|
||||
return this;
|
||||
}
|
||||
|
||||
hasChildren(): boolean {
|
||||
return this._children.length > 0;
|
||||
}
|
||||
|
||||
isActive(activePath: string): boolean {
|
||||
if (!this.path) {
|
||||
return false;
|
||||
}
|
||||
return this.activeMatchers.some(v => {
|
||||
const match = v.exec(activePath);
|
||||
if (match !== null) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("ak-sidebar")
|
||||
@ -78,9 +125,9 @@ export class Sidebar extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
}
|
||||
return html` <li class="pf-c-nav__item ${item.children ? "pf-m-expandable pf-m-expanded" : ""}">
|
||||
return html` <li class="pf-c-nav__item ${item.hasChildren() ? "pf-m-expandable pf-m-expanded" : ""}">
|
||||
${item.path ?
|
||||
html`<a href="#${item.path}" class="pf-c-nav__link ${item.path.some((v) => v === this.activePath) ? "pf-m-current": ""}">
|
||||
html`<a href="#${item.path}" class="pf-c-nav__link ${item.isActive(this.activePath) ? "pf-m-current": ""}">
|
||||
${item.name}
|
||||
</a>` :
|
||||
html`<a class="pf-c-nav__link" aria-expanded="true">
|
||||
@ -91,7 +138,7 @@ export class Sidebar extends LitElement {
|
||||
</a>
|
||||
<section class="pf-c-nav__subnav">
|
||||
<ul class="pf-c-nav__simple-list">
|
||||
${item.children?.map((i) => until(this.renderItem(i), html``))}
|
||||
${item._children.map((i) => until(this.renderItem(i), html``))}
|
||||
</ul>
|
||||
</section>`}
|
||||
</li>`;
|
||||
|
@ -2,14 +2,22 @@ import { gettext } from "django";
|
||||
import { CSSResult, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import { PBResponse } from "../../api/client";
|
||||
import { COMMON_STYLES } from "../../common/styles";
|
||||
import { htmlFromString } from "../../utils";
|
||||
|
||||
import "./TablePagination";
|
||||
import "../EmptyState";
|
||||
|
||||
export abstract class Table<T> extends LitElement {
|
||||
abstract apiEndpoint(page: number): Promise<PBResponse<T>>;
|
||||
abstract columns(): Array<string>;
|
||||
abstract row(item: T): Array<string>;
|
||||
abstract row(item: T): Array<TemplateResult>;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
renderExpanded(item: T): TemplateResult {
|
||||
if (this.expandable) {
|
||||
throw new Error("Expandable is enabled but renderExpanded is not overridden!");
|
||||
}
|
||||
return html``;
|
||||
}
|
||||
|
||||
@property({attribute: false})
|
||||
data?: PBResponse<T>;
|
||||
@ -17,6 +25,12 @@ export abstract class Table<T> extends LitElement {
|
||||
@property({type: Number})
|
||||
page = 1;
|
||||
|
||||
@property({type: Boolean})
|
||||
expandable = false;
|
||||
|
||||
@property({attribute: false})
|
||||
expandedRows: boolean[] = [];
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return COMMON_STYLES;
|
||||
}
|
||||
@ -48,7 +62,7 @@ export abstract class Table<T> extends LitElement {
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
</div>
|
||||
<h2 class="pf-c-title pf-m-lg">Loading</h2>
|
||||
<h2 class="pf-c-title pf-m-lg">${gettext("Loading")}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -56,21 +70,59 @@ export abstract class Table<T> extends LitElement {
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
renderEmpty(inner?: TemplateResult): TemplateResult {
|
||||
return html`<tbody role="rowgroup">
|
||||
<tr role="row">
|
||||
<td role="cell" colspan="8">
|
||||
<div class="pf-l-bullseye">
|
||||
${inner ? inner : html`<ak-empty-state header="none"></ak-empty-state>`}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>`;
|
||||
}
|
||||
|
||||
private renderRows(): TemplateResult[] | undefined {
|
||||
if (!this.data) {
|
||||
return;
|
||||
}
|
||||
return this.data.results.map((item) => {
|
||||
const fullRow = ["<tr role=\"row\">"].concat(
|
||||
this.row(item).map((col) => {
|
||||
return `<td role="cell">${col}</td>`;
|
||||
})
|
||||
);
|
||||
fullRow.push("</tr>");
|
||||
return htmlFromString(...fullRow);
|
||||
if (this.data.pagination.count === 0) {
|
||||
return [this.renderEmpty()];
|
||||
}
|
||||
return this.data.results.map((item: T, idx: number) => {
|
||||
if ((this.expandedRows.length - 1) < idx) {
|
||||
this.expandedRows[idx] = false;
|
||||
}
|
||||
return html`<tbody role="rowgroup" class="${this.expandedRows[idx] ? "pf-m-expanded" : ""}">
|
||||
<tr role="row">
|
||||
${this.expandable ? html`<td class="pf-c-table__toggle" role="cell">
|
||||
<button class="pf-c-button pf-m-plain ${this.expandedRows[idx] ? "pf-m-expanded" : ""}" @click=${() => {
|
||||
this.expandedRows[idx] = !this.expandedRows[idx];
|
||||
this.requestUpdate();
|
||||
}}>
|
||||
<div class="pf-c-table__toggle-icon"> <i class="fas fa-angle-down" aria-hidden="true"></i> </div>
|
||||
</button>
|
||||
</td>` : html``}
|
||||
${this.row(item).map((col) => {
|
||||
return html`<td role="cell">${col}</td>`;
|
||||
})}
|
||||
</tr>
|
||||
<tr class="pf-c-table__expandable-row ${this.expandedRows[idx] ? "pf-m-expanded" : ""}" role="row">
|
||||
<td></td>
|
||||
${this.renderExpanded(item)}
|
||||
</tr>
|
||||
</tbody>`;
|
||||
});
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html` <button
|
||||
@click=${() => { this.fetch(); }}
|
||||
class="pf-c-button pf-m-primary">
|
||||
${gettext("Refresh")}
|
||||
</button>`;
|
||||
}
|
||||
|
||||
renderTable(): TemplateResult {
|
||||
if (!this.data) {
|
||||
this.fetch();
|
||||
@ -78,12 +130,7 @@ export abstract class Table<T> extends LitElement {
|
||||
return html`<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<slot name="create-button"></slot>
|
||||
<button
|
||||
@click=${() => {this.fetch();}}
|
||||
class="pf-c-button pf-m-primary">
|
||||
${gettext("Refresh")}
|
||||
</button>
|
||||
${this.renderToolbar()}
|
||||
</div>
|
||||
<ak-table-pagination
|
||||
class="pf-c-toolbar__item pf-m-pagination"
|
||||
@ -92,15 +139,14 @@ export abstract class Table<T> extends LitElement {
|
||||
</ak-table-pagination>
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-md">
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-md pf-m-expandable">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
${this.expandable ? html`<td role="cell">` : html``}
|
||||
${this.columns().map((col) => html`<th role="columnheader" scope="col">${gettext(col)}</th>`)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
${this.data ? this.renderRows() : this.renderLoading()}
|
||||
</tbody>
|
||||
${this.data ? this.renderRows() : this.renderLoading()}
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
<ak-table-pagination
|
||||
|
@ -39,7 +39,7 @@
|
||||
<script src="/static/dist/main.js" type="module"></script>
|
||||
</head>
|
||||
<body>
|
||||
<ak-messages url="/api/v2beta/root/messages/"></ak-messages>
|
||||
<ak-messages></ak-messages>
|
||||
<div class="pf-c-page">
|
||||
<a class="pf-c-skip-to-content pf-c-button pf-m-primary" href="#main-content"
|
||||
>Skip to content</a
|
||||
|
@ -1,120 +1,45 @@
|
||||
import { customElement } from "lit-element";
|
||||
import { User } from "../api/user";
|
||||
import { SidebarItem } from "../elements/sidebar/Sidebar";
|
||||
import { SLUG_REGEX } from "../elements/router/Route";
|
||||
import { Interface } from "./Interface";
|
||||
|
||||
export const SIDEBAR_ITEMS: SidebarItem[] = [
|
||||
{
|
||||
name: "Library",
|
||||
path: ["/library/"],
|
||||
},
|
||||
{
|
||||
name: "Monitor",
|
||||
path: ["/audit/audit/"],
|
||||
condition: (): Promise<boolean> => {
|
||||
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<boolean> => {
|
||||
return User.me().then(u => u.is_superuser);
|
||||
},
|
||||
},
|
||||
new SidebarItem("Library", "/library/"),
|
||||
new SidebarItem("Monitor", "/audit/audit").when((): Promise<boolean> => {
|
||||
return User.me().then(u => u.is_superuser);
|
||||
}),
|
||||
new SidebarItem("Administration").children(
|
||||
new SidebarItem("Overview", "/administration/overview-ng/"),
|
||||
new SidebarItem("System Tasks", "/administration/tasks/"),
|
||||
new SidebarItem("Applications", "/administration/applications/").activeWhen(
|
||||
`^/applications/(?<slug>${SLUG_REGEX})/$`
|
||||
),
|
||||
new SidebarItem("Sources", "/administration/sources/").activeWhen(
|
||||
`^/sources/(?<slug>${SLUG_REGEX})/$`,
|
||||
),
|
||||
new SidebarItem("Providers", "/administration/providers/"),
|
||||
new SidebarItem("Flows").children(
|
||||
new SidebarItem("Flows", "/administration/flows/").activeWhen(`^/flows/(?<slug>${SLUG_REGEX})/$`),
|
||||
new SidebarItem("Stages", "/administration/stages/"),
|
||||
new SidebarItem("Prompts", "/administration/stages/prompts/"),
|
||||
new SidebarItem("Invitations", "/administration/stages/invitations/"),
|
||||
),
|
||||
new SidebarItem("User Management").children(
|
||||
new SidebarItem("User", "/administration/users/"),
|
||||
new SidebarItem("Groups", "/administration/groups/")
|
||||
),
|
||||
new SidebarItem("Outposts").children(
|
||||
new SidebarItem("Outposts", "/administration/outposts/"),
|
||||
new SidebarItem("Service Connections", "/administration/outposts/service_connections/")
|
||||
),
|
||||
new SidebarItem("Policies", "/administration/policies/"),
|
||||
new SidebarItem("Property Mappings", "/administration/property-mappings"),
|
||||
new SidebarItem("Certificates", "/administration/crypto/certificates"),
|
||||
new SidebarItem("Tokens", "/administration/tokens/"),
|
||||
).when((): Promise<boolean> => {
|
||||
return User.me().then(u => u.is_superuser);
|
||||
})
|
||||
];
|
||||
|
||||
@customElement("ak-interface-admin")
|
||||
|
@ -3,7 +3,7 @@ import { html, LitElement, TemplateResult } from "lit-element";
|
||||
import { SidebarItem } from "../elements/sidebar/Sidebar";
|
||||
|
||||
import "../elements/Messages";
|
||||
import "../pages/router/RouterOutlet";
|
||||
import "../elements/router/RouterOutlet";
|
||||
|
||||
export abstract class Interface extends LitElement {
|
||||
|
||||
|
@ -13,18 +13,18 @@ import "./elements/sidebar/SidebarUser";
|
||||
import "./elements/table/TablePagination";
|
||||
|
||||
import "./elements/AdminLoginsChart";
|
||||
import "./elements/EmptyState";
|
||||
import "./elements/cards/AggregateCard";
|
||||
import "./elements/cards/AggregatePromiseCard";
|
||||
import "./elements/CodeMirror";
|
||||
import "./elements/Messages";
|
||||
import "./elements/Spinner";
|
||||
import "./elements/Tabs";
|
||||
import "./elements/router/RouterOutlet";
|
||||
|
||||
import "./pages/generic/FlowShellCard";
|
||||
import "./pages/generic/SiteShell";
|
||||
|
||||
import "./pages/router/RouterOutlet";
|
||||
|
||||
import "./pages/admin-overview/AdminOverviewPage";
|
||||
import "./pages/admin-overview/TopApplicationsTable";
|
||||
import "./pages/applications/ApplicationListPage";
|
||||
|
@ -1,9 +1,12 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement } from "lit-element";
|
||||
import { customElement, html, TemplateResult } from "lit-element";
|
||||
import { Application } from "../../api/application";
|
||||
import { PBResponse } from "../../api/client";
|
||||
import { TablePage } from "../../elements/table/TablePage";
|
||||
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
|
||||
@customElement("ak-application-list")
|
||||
export class ApplicationList extends TablePage<Application> {
|
||||
pageTitle(): string {
|
||||
@ -27,13 +30,13 @@ export class ApplicationList extends TablePage<Application> {
|
||||
return ["Name", "Slug", "Provider", "Provider Type", ""];
|
||||
}
|
||||
|
||||
row(item: Application): string[] {
|
||||
row(item: Application): TemplateResult[] {
|
||||
return [
|
||||
item.name,
|
||||
item.slug,
|
||||
item.provider.toString(),
|
||||
item.provider.toString(),
|
||||
`
|
||||
html`${item.name}`,
|
||||
html`${item.slug}`,
|
||||
html`${item.provider}`,
|
||||
html`${item.provider}`,
|
||||
html`
|
||||
<ak-modal-button href="administration/policies/bindings/${item.pk}/update/">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
Edit
|
||||
|
@ -1,54 +1,14 @@
|
||||
import { gettext } from "django";
|
||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import { Application } from "../../api/application";
|
||||
import { DefaultClient, PBResponse } from "../../api/client";
|
||||
import { PolicyBinding } from "../../api/policy_binding";
|
||||
import { DefaultClient } from "../../api/client";
|
||||
import { COMMON_STYLES } from "../../common/styles";
|
||||
import { Table } from "../../elements/table/Table";
|
||||
|
||||
import "../../elements/Tabs";
|
||||
import "../../elements/AdminLoginsChart";
|
||||
|
||||
@customElement("ak-bound-policies-list")
|
||||
export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
@property()
|
||||
target?: string;
|
||||
|
||||
apiEndpoint(page: number): Promise<PBResponse<PolicyBinding>> {
|
||||
return DefaultClient.fetch<PBResponse<PolicyBinding>>(["policies", "bindings"], {
|
||||
target: this.target || "",
|
||||
ordering: "order",
|
||||
page: page,
|
||||
});
|
||||
}
|
||||
|
||||
columns(): string[] {
|
||||
return ["Policy", "Enabled", "Order", "Timeout", ""];
|
||||
}
|
||||
|
||||
row(item: PolicyBinding): string[] {
|
||||
return [
|
||||
item.policy_obj.name,
|
||||
item.enabled ? "Yes" : "No",
|
||||
item.order.toString(),
|
||||
item.timeout.toString(),
|
||||
`
|
||||
<ak-modal-button href="administration/policies/bindings/${item.pk}/update/">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
Edit
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="administration/policies/bindings/${item.pk}/delete/">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
Delete
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import "../../elements/policies/BoundPoliciesList";
|
||||
|
||||
@customElement("ak-application-view")
|
||||
export class ApplicationViewPage extends LitElement {
|
||||
@ -108,7 +68,13 @@ export class ApplicationViewPage extends LitElement {
|
||||
</section>
|
||||
<div slot="page-2" data-tab-title="Policy Bindings" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<ak-bound-policies-list .target=${this.application.pk}></ak-bound-policies-list>
|
||||
<div class="pf-c-card__header">
|
||||
<div class="pf-c-card__header-main">
|
||||
${gettext("These policies control which users can access this application.")}
|
||||
</div>
|
||||
</div>
|
||||
<ak-bound-policies-list .target=${this.application.pk}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</ak-tabs>`;
|
||||
|
97
web/src/pages/flows/BoundStagesList.ts
Normal file
97
web/src/pages/flows/BoundStagesList.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { PBResponse } from "../../api/client";
|
||||
import { Table } from "../../elements/table/Table";
|
||||
|
||||
import "../../elements/Tabs";
|
||||
import "../../elements/AdminLoginsChart";
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import "../../elements/policies/BoundPoliciesList";
|
||||
import { FlowStageBinding } from "../../api/flow";
|
||||
|
||||
@customElement("ak-bound-stages-list")
|
||||
export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
expandable = true;
|
||||
|
||||
@property()
|
||||
target?: string;
|
||||
|
||||
apiEndpoint(page: number): Promise<PBResponse<FlowStageBinding>> {
|
||||
return FlowStageBinding.list({
|
||||
target: this.target || "",
|
||||
ordering: "order",
|
||||
page: page,
|
||||
});
|
||||
}
|
||||
|
||||
columns(): string[] {
|
||||
return ["Order", "Name", "Type", ""];
|
||||
}
|
||||
|
||||
row(item: FlowStageBinding): TemplateResult[] {
|
||||
return [
|
||||
html`${item.order}`,
|
||||
html`${item.stage_obj.name}`,
|
||||
html`${item.stage_obj.verbose_name}`,
|
||||
html`
|
||||
<ak-modal-button href="${FlowStageBinding.adminUrl(`${item.pk}/update/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
Edit
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="${FlowStageBinding.adminUrl(`${item.pk}/delete/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
Delete
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
renderExpanded(item: FlowStageBinding): TemplateResult {
|
||||
return html`
|
||||
<td></td>
|
||||
<td role="cell" colspan="3">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<div class="pf-c-content">
|
||||
<p>${gettext("These policies control when this stage will be applied to the flow.")}</p>
|
||||
<ak-bound-policies-list .target=${item.policybindingmodel_ptr_id}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>`;
|
||||
}
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(html`<ak-empty-state header=${gettext("No Stages bound")} icon="pf-icon-module">
|
||||
<div slot="body">
|
||||
${gettext("No stages are currently bound to this flow.")}
|
||||
</div>
|
||||
<div slot="primary">
|
||||
<ak-modal-button href="${FlowStageBinding.adminUrl(`create/?target=${this.target}`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
${gettext("Bind Stage")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</div>
|
||||
</ak-empty-state>`);
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-modal-button href="${FlowStageBinding.adminUrl(`create/?target=${this.target}`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
${gettext("Bind Stage")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
}
|
71
web/src/pages/flows/FlowViewPage.ts
Normal file
71
web/src/pages/flows/FlowViewPage.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { gettext } from "django";
|
||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import { COMMON_STYLES } from "../../common/styles";
|
||||
import { Flow } from "../../api/flow";
|
||||
|
||||
import "../../elements/Tabs";
|
||||
import "../../elements/AdminLoginsChart";
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import "../../elements/policies/BoundPoliciesList";
|
||||
import "./BoundStagesList";
|
||||
|
||||
@customElement("ak-flow-view")
|
||||
export class FlowViewPage extends LitElement {
|
||||
@property()
|
||||
set args(value: { [key: string]: string }) {
|
||||
this.flowSlug = value.slug;
|
||||
}
|
||||
|
||||
@property()
|
||||
set flowSlug(value: string) {
|
||||
Flow.get(value).then((flow) => (this.flow = flow));
|
||||
}
|
||||
|
||||
@property({attribute: false})
|
||||
flow?: Flow;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return COMMON_STYLES.concat(
|
||||
css`
|
||||
img.pf-icon {
|
||||
max-height: 24px;
|
||||
}
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
if (!this.flow) {
|
||||
return html``;
|
||||
}
|
||||
return html`<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-process-automation"></i>
|
||||
${this.flow?.name}
|
||||
</h1>
|
||||
<p>${this.flow?.title}</p>
|
||||
</div>
|
||||
</section>
|
||||
<ak-tabs>
|
||||
<div slot="page-2" data-tab-title="${gettext("Stage Bindings")}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<ak-bound-stages-list .target=${this.flow.pk}>
|
||||
</ak-bound-stages-list>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="page-3" data-tab-title="${gettext("Policy Bindings")}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__header">
|
||||
<div class="pf-c-card__header-main">
|
||||
${gettext("These policies control which users can access this flow.")}
|
||||
</div>
|
||||
</div>
|
||||
<ak-bound-policies-list .target=${this.flow.policybindingmodel_ptr_id}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</ak-tabs>`;
|
||||
}
|
||||
}
|
63
web/src/pages/sources/SourceViewPage.ts
Normal file
63
web/src/pages/sources/SourceViewPage.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { gettext } from "django";
|
||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import { COMMON_STYLES } from "../../common/styles";
|
||||
|
||||
import "../../elements/Tabs";
|
||||
import "../../elements/AdminLoginsChart";
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import "../../elements/policies/BoundPoliciesList";
|
||||
import { Source } from "../../api/source";
|
||||
|
||||
@customElement("ak-source-view")
|
||||
export class SourceViewPage extends LitElement {
|
||||
@property()
|
||||
set args(value: { [key: string]: string }) {
|
||||
this.sourceSlug = value.slug;
|
||||
}
|
||||
|
||||
@property()
|
||||
set sourceSlug(value: string) {
|
||||
Source.get(value).then((source) => (this.source = source));
|
||||
}
|
||||
|
||||
@property({attribute: false})
|
||||
source?: Source;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return COMMON_STYLES.concat(
|
||||
css`
|
||||
img.pf-icon {
|
||||
max-height: 24px;
|
||||
}
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
if (!this.source) {
|
||||
return html``;
|
||||
}
|
||||
return html`<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-middleware"></i>
|
||||
${this.source?.name}
|
||||
</h1>
|
||||
</div>
|
||||
</section>
|
||||
<ak-tabs>
|
||||
<div slot="page-2" data-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__header">
|
||||
<div class="pf-c-card__header-main">
|
||||
${gettext("These policies control which users can access this application.")}
|
||||
</div>
|
||||
</div>
|
||||
<ak-bound-policies-list .target=${this.source.pk}>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</ak-tabs>`;
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
import { html } from "lit-html";
|
||||
import { Route, SLUG_REGEX } from "./pages/router/Route";
|
||||
import { Route, SLUG_REGEX } from "./elements/router/Route";
|
||||
|
||||
import "./pages/LibraryPage";
|
||||
import "./pages/admin-overview/AdminOverviewPage";
|
||||
import "./pages/applications/ApplicationListPage";
|
||||
import "./pages/applications/ApplicationViewPage";
|
||||
import "./pages/sources/SourceViewPage";
|
||||
import "./pages/flows/FlowViewPage";
|
||||
|
||||
export const ROUTES: Route[] = [
|
||||
// Prevent infinite Shell loops
|
||||
@ -16,4 +18,10 @@ export const ROUTES: Route[] = [
|
||||
new Route(new RegExp(`^/applications/(?<slug>${SLUG_REGEX})/$`)).then((args) => {
|
||||
return html`<ak-application-view .args=${args}></ak-application-view>`;
|
||||
}),
|
||||
new Route(new RegExp(`^/sources/(?<slug>${SLUG_REGEX})/$`)).then((args) => {
|
||||
return html`<ak-source-view .args=${args}></ak-source-view>`;
|
||||
}),
|
||||
new Route(new RegExp(`^/flows/(?<slug>${SLUG_REGEX})/$`)).then((args) => {
|
||||
return html`<ak-flow-view .args=${args}></ak-flow-view>`;
|
||||
}),
|
||||
];
|
||||
|
Reference in New Issue
Block a user