diff --git a/web/package-lock.json b/web/package-lock.json index 4efea8bd9..6933a88ac 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -182,6 +182,16 @@ "@polymer/polymer": "^3.0.0" } }, + "@polymer/iron-checked-element-behavior": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@polymer/iron-checked-element-behavior/-/iron-checked-element-behavior-3.0.1.tgz", + "integrity": "sha512-aDr0cbCNVq49q+pOqa6CZutFh+wWpwPMLpEth9swx+GkAj+gCURhuQkaUYhIo5f2egDbEioR1aeHMnPlU9dQZA==", + "requires": { + "@polymer/iron-form-element-behavior": "^3.0.0-pre.26", + "@polymer/iron-validatable-behavior": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" + } + }, "@polymer/iron-flex-layout": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@polymer/iron-flex-layout/-/iron-flex-layout-3.0.1.tgz", @@ -234,6 +244,30 @@ "@polymer/polymer": "^3.0.0" } }, + "@polymer/paper-behaviors": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@polymer/paper-behaviors/-/paper-behaviors-3.0.1.tgz", + "integrity": "sha512-6knhj69fPJejv8qR0kCSUY+Q0XjaUf0OSnkjRjmTJPAwSrRYtgqE+l6P1FfA+py1X/cUjgne9EF5rMZAKJIg1g==", + "requires": { + "@polymer/iron-behaviors": "^3.0.0-pre.26", + "@polymer/iron-checked-element-behavior": "^3.0.0-pre.26", + "@polymer/paper-ripple": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" + } + }, + "@polymer/paper-checkbox": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@polymer/paper-checkbox/-/paper-checkbox-3.1.0.tgz", + "integrity": "sha512-kXm6yDG1tT8if0XuJ2cc9NF+g8Ev4wG+rnf0a+Sx+O7J6fn1jcnBlYn72FlrfjVjDQZDBFmT6nynhD5PvFw8iQ==", + "requires": { + "@polymer/iron-a11y-keys-behavior": "^3.0.0-pre.26", + "@polymer/iron-checked-element-behavior": "^3.0.0-pre.26", + "@polymer/paper-behaviors": "^3.0.0-pre.27", + "@polymer/paper-ripple": "^3.0.0-pre.26", + "@polymer/paper-styles": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" + } + }, "@polymer/paper-input": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@polymer/paper-input/-/paper-input-3.2.1.tgz", @@ -248,6 +282,15 @@ "@polymer/polymer": "^3.0.0" } }, + "@polymer/paper-ripple": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@polymer/paper-ripple/-/paper-ripple-3.0.2.tgz", + "integrity": "sha512-DnLNvYIMsiayeICroYxx6Q6Hg1cUU8HN2sbutXazlemAlGqdq80qz3TIaVdbpbt/pvjcFGX2HtntMlPstCge8Q==", + "requires": { + "@polymer/iron-a11y-keys-behavior": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" + } + }, "@polymer/paper-styles": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@polymer/paper-styles/-/paper-styles-3.0.1.tgz", diff --git a/web/package.json b/web/package.json index 9921a4efa..235074cdd 100644 --- a/web/package.json +++ b/web/package.json @@ -13,6 +13,7 @@ "@fortawesome/fontawesome-free": "^5.15.3", "@patternfly/patternfly": "^4.90.5", "@polymer/iron-form": "^3.0.1", + "@polymer/paper-checkbox": "^3.1.0", "@polymer/paper-input": "^3.2.1", "@sentry/browser": "^6.2.3", "@sentry/tracing": "^6.2.3", diff --git a/web/src/authentik.css b/web/src/authentik.css index 3cf2133ce..29559ee42 100644 --- a/web/src/authentik.css +++ b/web/src/authentik.css @@ -103,8 +103,11 @@ body { } paper-input { - /* --paper-input-container-color: var(--ak-accent); */ - --paper-input-container-input-color: var(--ak-dark-foreground); + /* --paper-input-container-input-color: var(--ak-dark-foreground); */ + --primary-text-color: var(--ak-dark-foreground); + } + paper-checkbox { + --primary-text-color: var(--ak-dark-foreground); } /* Global page background colour */ diff --git a/web/src/elements/forms/Form.ts b/web/src/elements/forms/Form.ts index 6d3b3ba05..81907318f 100644 --- a/web/src/elements/forms/Form.ts +++ b/web/src/elements/forms/Form.ts @@ -2,40 +2,52 @@ import "@polymer/paper-input/paper-input"; import "@polymer/iron-form/iron-form"; import { PaperInputElement } from "@polymer/paper-input/paper-input"; import { showMessage } from "../../elements/messages/MessageContainer"; -import { customElement, html, LitElement, property, TemplateResult } from "lit-element"; +import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; +import PFBase from "@patternfly/patternfly/patternfly-base.css"; +import PFCard from "@patternfly/patternfly/components/Card/card.css"; +import PFButton from "@patternfly/patternfly/components/Button/button.css"; +import AKGlobal from "../../authentik.css"; +import PFForm from "@patternfly/patternfly/components/Form/form.css"; +import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; export interface ErrorResponse { [key: string]: string[]; } @customElement("ak-form") -export class Form extends LitElement { +export class Form extends LitElement { @property() successMessage = ""; @property() - send!: (data: Record) => Promise; + send!: (data: T) => Promise; - submit(ev: Event): Promise | undefined { + static get styles(): CSSResult[] { + return [PFBase, PFCard, PFButton, PFForm, PFFormControl, AKGlobal]; + } + + submit(ev: Event): Promise | undefined { ev.preventDefault(); const ironForm = this.shadowRoot?.querySelector("iron-form"); if (!ironForm) { + console.warn("authentik/forms: failed to find iron-form"); return; } - const data = ironForm.serializeForm(); - return this.send(data).then(() => { + const data = ironForm.serializeForm() as T; + return this.send(data).then((r) => { showMessage({ level_tag: "success", message: this.successMessage }); + return r; }).catch((ex: Response) => { if (ex.status > 399 && ex.status < 500) { return ex.json(); } return ex; - }).then((errorMessage?: ErrorResponse) => { - if (!errorMessage) return; + }).then((errorMessage: ErrorResponse) => { + if (!errorMessage) return errorMessage; const elements: PaperInputElement[] = ironForm._getSubmittableElements(); elements.forEach((element) => { const elementName = element.name; @@ -45,13 +57,18 @@ export class Form extends LitElement { element.invalid = true; } }); + return errorMessage; }); } + renderForm(): TemplateResult { + return html``; + } + render(): TemplateResult { return html` { this.submit(ev); }}> - + ${this.renderForm()} `; } diff --git a/web/src/elements/forms/ModalForm.ts b/web/src/elements/forms/ModalForm.ts index 8538036ea..aeaa38416 100644 --- a/web/src/elements/forms/ModalForm.ts +++ b/web/src/elements/forms/ModalForm.ts @@ -4,10 +4,10 @@ import { ModalButton } from "../buttons/ModalButton"; import { Form } from "./Form"; @customElement("ak-forms-modal") -export class DeleteForm extends ModalButton { +export class ModalForm extends ModalButton { confirm(): void { - this.querySelectorAll
("ak-form").forEach(form => { + this.querySelectorAll>("ak-form").forEach(form => { const formPromise = form.submit(new Event("submit")); if (!formPromise) { return; diff --git a/web/src/pages/groups/GroupForm.ts b/web/src/pages/groups/GroupForm.ts new file mode 100644 index 000000000..6e2cb3678 --- /dev/null +++ b/web/src/pages/groups/GroupForm.ts @@ -0,0 +1,49 @@ +import { CoreApi, Group } from "authentik-api"; +import { gettext } from "django"; +import { customElement, property } from "lit-element"; +import { html, TemplateResult } from "lit-html"; +import { DEFAULT_CONFIG } from "../../api/Config"; +import { Form } from "../../elements/forms/Form"; +import { ifDefined } from "lit-html/directives/if-defined"; +import "@polymer/paper-input/paper-input"; +import "@polymer/iron-form/iron-form"; +import '@polymer/paper-checkbox/paper-checkbox.js'; + +@customElement("ak-group-form") +export class GroupForm extends Form { + + @property({attribute: false}) + group?: Group; + + send = (data: Group): Promise => { + if (this.group) { + return new CoreApi(DEFAULT_CONFIG).coreGroupsUpdate({ + groupUuid: this.group.pk || "", + data: data + }); + } else { + return new CoreApi(DEFAULT_CONFIG).coreGroupsCreate({ + data: data + }); + } + }; + + renderForm(): TemplateResult { + return html` + + + + ${gettext("Is superuser")} + +

${gettext("Users added to this group will be superusers.")}

+ `; + } + +} diff --git a/web/src/pages/groups/GroupListPage.ts b/web/src/pages/groups/GroupListPage.ts index fd4bf00ae..cdaee5797 100644 --- a/web/src/pages/groups/GroupListPage.ts +++ b/web/src/pages/groups/GroupListPage.ts @@ -11,6 +11,8 @@ import { PAGE_SIZE } from "../../constants"; import { CoreApi, Group } from "authentik-api"; import { DEFAULT_CONFIG } from "../../api/Config"; import { AdminURLManager } from "../../api/legacy"; +import "../../elements/forms/ModalForm"; +import "./GroupForm"; @customElement("ak-group-list") export class GroupListPage extends TablePage { @@ -56,12 +58,19 @@ export class GroupListPage extends TablePage { html`${item.users.keys.length}`, html`${item.isSuperuser ? "Yes" : "No"}`, html` - - + + + ${gettext("Update")} + + + ${gettext("Update Group")} + + + + + { renderToolbar(): TemplateResult { return html` - + ${gettext("Create")}