Compare commits
	
		
			42 Commits
		
	
	
		
			version/20
			...
			web/legibi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 8f995aab62 | |||
| 2846e49657 | |||
| 0e60e755d4 | |||
| 6cf2433e2b | |||
| e1d565d40e | |||
| ee37e9235b | |||
| 8248163958 | |||
| 9acebec1f6 | |||
| 2a96900dc7 | |||
| ca42506fa0 | |||
| 34de6bfd3a | |||
| 2d94b16411 | |||
| 98503f6009 | |||
| ac4ba5d9e2 | |||
| f19ed14bf8 | |||
| 085debf170 | |||
| cacdf64408 | |||
| 23665d173f | |||
| 272fdc516b | |||
| b08dcc2289 | |||
| c84be1d961 | |||
| 875fc5c735 | |||
| 66cefcc918 | |||
| 5d4c38032f | |||
| 7123b2c57b | |||
| fc00bdee63 | |||
| a056703da0 | |||
| 3f9502072d | |||
| 2d254d6a7e | |||
| a7e3dca917 | |||
| 5d8408287f | |||
| 30beca9118 | |||
| 8946b81dbd | |||
| db96e1a901 | |||
| 8b4e0361c4 | |||
| 22cb5b7379 | |||
| 2d0117d096 | |||
| 035bda4eac | |||
| 50906214e5 | |||
| e505f274b6 | |||
| fe52f44dca | |||
| 3146e5a50f | 
| @ -3,7 +3,6 @@ import "@goauthentik/admin/applications/ApplicationCheckAccessForm"; | ||||
| import "@goauthentik/admin/applications/ApplicationForm"; | ||||
| import "@goauthentik/admin/policies/BoundPoliciesList"; | ||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||
| import { PFSize } from "@goauthentik/common/enums.js"; | ||||
| import "@goauthentik/components/ak-app-icon"; | ||||
| import "@goauthentik/components/events/ObjectChangelog"; | ||||
| import { AKElement } from "@goauthentik/elements/Base"; | ||||
| @ -13,10 +12,8 @@ import "@goauthentik/elements/Tabs"; | ||||
| import "@goauthentik/elements/buttons/SpinnerButton"; | ||||
| import "@goauthentik/elements/rbac/ObjectPermissionsPage"; | ||||
|  | ||||
| import { msg } from "@lit/localize"; | ||||
| import { CSSResult, PropertyValues, TemplateResult, html } from "lit"; | ||||
| import { PropertyValues } from "lit"; | ||||
| import { customElement, property, state } from "lit/decorators.js"; | ||||
| import { ifDefined } from "lit/directives/if-defined.js"; | ||||
|  | ||||
| import PFBanner from "@patternfly/patternfly/components/Banner/banner.css"; | ||||
| import PFButton from "@patternfly/patternfly/components/Button/button.css"; | ||||
| @ -35,18 +32,14 @@ import { | ||||
|     RbacPermissionsAssignedByUsersListModelEnum, | ||||
| } from "@goauthentik/api"; | ||||
|  | ||||
| import { | ||||
|     ApplicationViewPageLoadingRenderer, | ||||
|     ApplicationViewPageRenderer, | ||||
| } from "./ApplicationViewPageRenderers.js"; | ||||
|  | ||||
| @customElement("ak-application-view") | ||||
| export class ApplicationViewPage extends AKElement { | ||||
|     @property({ type: String }) | ||||
|     applicationSlug?: string; | ||||
|  | ||||
|     @state() | ||||
|     application?: Application; | ||||
|  | ||||
|     @state() | ||||
|     missingOutpost = false; | ||||
|  | ||||
|     static get styles(): CSSResult[] { | ||||
|     static get styles() { | ||||
|         return [ | ||||
|             PFBase, | ||||
|             PFList, | ||||
| @ -60,6 +53,15 @@ export class ApplicationViewPage extends AKElement { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     @property({ type: String }) | ||||
|     applicationSlug?: string; | ||||
|  | ||||
|     @state() | ||||
|     application?: Application; | ||||
|  | ||||
|     @state() | ||||
|     missingOutpost = false; | ||||
|  | ||||
|     fetchIsMissingOutpost(providersByPk: Array<number>) { | ||||
|         new OutpostsApi(DEFAULT_CONFIG) | ||||
|             .outpostsInstancesList({ | ||||
| @ -94,231 +96,15 @@ export class ApplicationViewPage extends AKElement { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     render(): TemplateResult { | ||||
|         return html`<ak-page-header | ||||
|                 header=${this.application?.name || msg("Loading")} | ||||
|                 description=${ifDefined(this.application?.metaPublisher)} | ||||
|                 .iconImage=${true} | ||||
|             > | ||||
|                 <ak-app-icon | ||||
|                     size=${PFSize.Medium} | ||||
|                     slot="icon" | ||||
|                     .app=${this.application} | ||||
|                 ></ak-app-icon> | ||||
|             </ak-page-header> | ||||
|             ${this.renderApp()}`; | ||||
|     } | ||||
|     render() { | ||||
|         const renderer = this.application | ||||
|             ? new ApplicationViewPageRenderer( | ||||
|                   this.application, | ||||
|                   this.missingOutpost, | ||||
|                   RbacPermissionsAssignedByUsersListModelEnum.CoreApplication, | ||||
|               ) | ||||
|             : new ApplicationViewPageLoadingRenderer(); | ||||
|  | ||||
|     renderApp(): TemplateResult { | ||||
|         if (!this.application) { | ||||
|             return html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}> | ||||
|             </ak-empty-state>`; | ||||
|         } | ||||
|         return html`<ak-tabs> | ||||
|             ${this.missingOutpost | ||||
|                 ? html`<div slot="header" class="pf-c-banner pf-m-warning"> | ||||
|                       ${msg("Warning: Application is not used by any Outpost.")} | ||||
|                   </div>` | ||||
|                 : html``} | ||||
|             <section | ||||
|                 slot="page-overview" | ||||
|                 data-tab-title="${msg("Overview")}" | ||||
|                 class="pf-c-page__main-section pf-m-no-padding-mobile" | ||||
|             > | ||||
|                 <div class="pf-l-grid pf-m-gutter"> | ||||
|                     <div | ||||
|                         class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-2-col-on-xl pf-m-2-col-on-2xl" | ||||
|                     > | ||||
|                         <div class="pf-c-card__title">${msg("Related")}</div> | ||||
|                         <div class="pf-c-card__body"> | ||||
|                             <dl class="pf-c-description-list"> | ||||
|                                 ${this.application.providerObj | ||||
|                                     ? html`<div class="pf-c-description-list__group"> | ||||
|                                           <dt class="pf-c-description-list__term"> | ||||
|                                               <span class="pf-c-description-list__text" | ||||
|                                                   >${msg("Provider")}</span | ||||
|                                               > | ||||
|                                           </dt> | ||||
|                                           <dd class="pf-c-description-list__description"> | ||||
|                                               <div class="pf-c-description-list__text"> | ||||
|                                                   <a | ||||
|                                                       href="#/core/providers/${this.application | ||||
|                                                           .providerObj?.pk}" | ||||
|                                                   > | ||||
|                                                       ${this.application.providerObj?.name} | ||||
|                                                       (${this.application.providerObj?.verboseName}) | ||||
|                                                   </a> | ||||
|                                               </div> | ||||
|                                           </dd> | ||||
|                                       </div>` | ||||
|                                     : html``} | ||||
|                                 ${(this.application.backchannelProvidersObj || []).length > 0 | ||||
|                                     ? html`<div class="pf-c-description-list__group"> | ||||
|                                           <dt class="pf-c-description-list__term"> | ||||
|                                               <span class="pf-c-description-list__text" | ||||
|                                                   >${msg("Backchannel Providers")}</span | ||||
|                                               > | ||||
|                                           </dt> | ||||
|                                           <dd class="pf-c-description-list__description"> | ||||
|                                               <div class="pf-c-description-list__text"> | ||||
|                                                   <ul class="pf-c-list"> | ||||
|                                                       ${this.application.backchannelProvidersObj.map( | ||||
|                                                           (provider) => { | ||||
|                                                               return html` | ||||
|                                                                   <li> | ||||
|                                                                       <a | ||||
|                                                                           href="#/core/providers/${provider.pk}" | ||||
|                                                                       > | ||||
|                                                                           ${provider.name} | ||||
|                                                                           (${provider.verboseName}) | ||||
|                                                                       </a> | ||||
|                                                                   </li> | ||||
|                                                               `; | ||||
|                                                           }, | ||||
|                                                       )} | ||||
|                                                   </ul> | ||||
|                                               </div> | ||||
|                                           </dd> | ||||
|                                       </div>` | ||||
|                                     : html``} | ||||
|                                 <div class="pf-c-description-list__group"> | ||||
|                                     <dt class="pf-c-description-list__term"> | ||||
|                                         <span class="pf-c-description-list__text" | ||||
|                                             >${msg("Policy engine mode")}</span | ||||
|                                         > | ||||
|                                     </dt> | ||||
|                                     <dd class="pf-c-description-list__description"> | ||||
|                                         <div class="pf-c-description-list__text"> | ||||
|                                             ${this.application.policyEngineMode?.toUpperCase()} | ||||
|                                         </div> | ||||
|                                     </dd> | ||||
|                                 </div> | ||||
|                                 <div class="pf-c-description-list__group"> | ||||
|                                     <dt class="pf-c-description-list__term"> | ||||
|                                         <span class="pf-c-description-list__text" | ||||
|                                             >${msg("Edit")}</span | ||||
|                                         > | ||||
|                                     </dt> | ||||
|                                     <dd class="pf-c-description-list__description"> | ||||
|                                         <div class="pf-c-description-list__text"> | ||||
|                                             <ak-forms-modal> | ||||
|                                                 <span slot="submit"> ${msg("Update")} </span> | ||||
|                                                 <span slot="header"> | ||||
|                                                     ${msg("Update Application")} | ||||
|                                                 </span> | ||||
|                                                 <ak-application-form | ||||
|                                                     slot="form" | ||||
|                                                     .instancePk=${this.application.slug} | ||||
|                                                 > | ||||
|                                                 </ak-application-form> | ||||
|                                                 <button | ||||
|                                                     slot="trigger" | ||||
|                                                     class="pf-c-button pf-m-secondary" | ||||
|                                                 > | ||||
|                                                     ${msg("Edit")} | ||||
|                                                 </button> | ||||
|                                             </ak-forms-modal> | ||||
|                                         </div> | ||||
|                                     </dd> | ||||
|                                 </div> | ||||
|                                 <div class="pf-c-description-list__group"> | ||||
|                                     <dt class="pf-c-description-list__term"> | ||||
|                                         <span class="pf-c-description-list__text" | ||||
|                                             >${msg("Check access")}</span | ||||
|                                         > | ||||
|                                     </dt> | ||||
|                                     <dd class="pf-c-description-list__description"> | ||||
|                                         <div class="pf-c-description-list__text"> | ||||
|                                             <ak-forms-modal .closeAfterSuccessfulSubmit=${false}> | ||||
|                                                 <span slot="submit"> ${msg("Check")} </span> | ||||
|                                                 <span slot="header"> | ||||
|                                                     ${msg("Check Application access")} | ||||
|                                                 </span> | ||||
|                                                 <ak-application-check-access-form | ||||
|                                                     slot="form" | ||||
|                                                     .application=${this.application} | ||||
|                                                 > | ||||
|                                                 </ak-application-check-access-form> | ||||
|                                                 <button | ||||
|                                                     slot="trigger" | ||||
|                                                     class="pf-c-button pf-m-secondary" | ||||
|                                                 > | ||||
|                                                     ${msg("Test")} | ||||
|                                                 </button> | ||||
|                                             </ak-forms-modal> | ||||
|                                         </div> | ||||
|                                     </dd> | ||||
|                                 </div> | ||||
|                                 ${this.application.launchUrl | ||||
|                                     ? html`<div class="pf-c-description-list__group"> | ||||
|                                           <dt class="pf-c-description-list__term"> | ||||
|                                               <span class="pf-c-description-list__text" | ||||
|                                                   >${msg("Launch")}</span | ||||
|                                               > | ||||
|                                           </dt> | ||||
|                                           <dd class="pf-c-description-list__description"> | ||||
|                                               <div class="pf-c-description-list__text"> | ||||
|                                                   <a | ||||
|                                                       target="_blank" | ||||
|                                                       href=${this.application.launchUrl} | ||||
|                                                       slot="trigger" | ||||
|                                                       class="pf-c-button pf-m-secondary" | ||||
|                                                   > | ||||
|                                                       ${msg("Launch")} | ||||
|                                                   </a> | ||||
|                                               </div> | ||||
|                                           </dd> | ||||
|                                       </div>` | ||||
|                                     : html``} | ||||
|                             </dl> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     <div | ||||
|                         class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-10-col-on-xl pf-m-10-col-on-2xl" | ||||
|                     > | ||||
|                         <div class="pf-c-card__title"> | ||||
|                             ${msg("Logins over the last week (per 8 hours)")} | ||||
|                         </div> | ||||
|                         <div class="pf-c-card__body"> | ||||
|                             ${this.application && | ||||
|                             html` <ak-charts-application-authorize | ||||
|                                 applicationSlug=${this.application.slug} | ||||
|                             > | ||||
|                             </ak-charts-application-authorize>`} | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     <div class="pf-c-card pf-l-grid__item pf-m-12-col"> | ||||
|                         <div class="pf-c-card__title">${msg("Changelog")}</div> | ||||
|                         <div class="pf-c-card__body"> | ||||
|                             <ak-object-changelog | ||||
|                                 targetModelPk=${this.application.pk || ""} | ||||
|                                 targetModelApp="authentik_core" | ||||
|                                 targetModelName="application" | ||||
|                             > | ||||
|                             </ak-object-changelog> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </section> | ||||
|             <section | ||||
|                 slot="page-policy-bindings" | ||||
|                 data-tab-title="${msg("Policy / Group / User Bindings")}" | ||||
|                 class="pf-c-page__main-section pf-m-no-padding-mobile" | ||||
|             > | ||||
|                 <div class="pf-c-card"> | ||||
|                     <div class="pf-c-card__title"> | ||||
|                         ${msg("These policies control which users can access this application.")} | ||||
|                     </div> | ||||
|                     <ak-bound-policies-list .target=${this.application.pk}> | ||||
|                     </ak-bound-policies-list> | ||||
|                 </div> | ||||
|             </section> | ||||
|             <ak-rbac-object-permission-page | ||||
|                 slot="page-permissions" | ||||
|                 data-tab-title="${msg("Permissions")}" | ||||
|                 model=${RbacPermissionsAssignedByUsersListModelEnum.CoreApplication} | ||||
|                 objectPk=${this.application.pk} | ||||
|             ></ak-rbac-object-permission-page> | ||||
|         </ak-tabs>`; | ||||
|         return renderer.render(); | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										214
									
								
								web/src/admin/applications/ApplicationViewPageRenderers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								web/src/admin/applications/ApplicationViewPageRenderers.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,214 @@ | ||||
| import { PFSize } from "@goauthentik/common/enums.js"; | ||||
| import { DescriptionPair, renderDescriptionList } from "@goauthentik/components/DescriptionList.js"; | ||||
|  | ||||
| import { msg } from "@lit/localize"; | ||||
| import { html, nothing } from "lit"; | ||||
| import { ifDefined } from "lit/directives/if-defined.js"; | ||||
|  | ||||
| import type { Application, RbacPermissionsAssignedByUsersListModelEnum } from "@goauthentik/api"; | ||||
|  | ||||
| export class ApplicationViewPageLoadingRenderer { | ||||
|     constructor() {} | ||||
|  | ||||
|     render() { | ||||
|         return html`<ak-page-header header=${msg("Loading")} | ||||
|             ><ak-empty-state ?loading="${true}" header=${msg("Loading")}> </ak-empty-state | ||||
|         ></ak-page-header>`; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export class ApplicationViewPageRenderer { | ||||
|     constructor( | ||||
|         private app: Application, | ||||
|         private noOutpost: boolean, | ||||
|         private rbacModel: RbacPermissionsAssignedByUsersListModelEnum, | ||||
|     ) {} | ||||
|  | ||||
|     missingOutpostMessage() { | ||||
|         return this.noOutpost | ||||
|             ? html`<div slot="header" class="pf-c-banner pf-m-warning"> | ||||
|                   ${msg("Warning: Application is not used by any Outpost.")} | ||||
|               </div>` | ||||
|             : nothing; | ||||
|     } | ||||
|  | ||||
|     controlCardContents(app: Application): DescriptionPair[] { | ||||
|         // prettier-ignore | ||||
|         const rows: (DescriptionPair | null)[] = [ | ||||
|             app.providerObj | ||||
|                 ? [ | ||||
|                       msg("Provider"), | ||||
|                       html` | ||||
|                           <a href="#/core/providers/${app.providerObj?.pk}"> | ||||
|                               ${app.providerObj?.name} (${app.providerObj?.verboseName}) | ||||
|                           </a> | ||||
|                       `, | ||||
|                   ] | ||||
|                 : null, | ||||
|  | ||||
|             (app.backchannelProvidersObj || []).length > 0 | ||||
|                 ? [ | ||||
|                       msg("Backchannel Providers"), | ||||
|                       html` | ||||
|                           <ul class="pf-c-list"> | ||||
|                               ${app.backchannelProvidersObj.map((provider) => { | ||||
|                                   return html` | ||||
|                                       <li> | ||||
|                                           <a href="#/core/providers/${provider.pk}"> | ||||
|                                               ${provider.name} (${provider.verboseName}) | ||||
|                                           </a> | ||||
|                                       </li> | ||||
|                                   `; | ||||
|                               })} | ||||
|                           </ul> | ||||
|                       `, | ||||
|                   ] | ||||
|                 : null, | ||||
|  | ||||
|             [ | ||||
|                 msg("Policy engine mode"),  | ||||
|                 app.policyEngineMode?.toUpperCase() | ||||
|             ], | ||||
|  | ||||
|             [ | ||||
|                 msg("Edit"), | ||||
|                 html` | ||||
|                     <ak-forms-modal> | ||||
|                         <span slot="submit"> ${msg("Update")} </span> | ||||
|                         <span slot="header"> ${msg("Update Application")} </span> | ||||
|                         <ak-application-form slot="form" .instancePk=${app.slug}> | ||||
|                         </ak-application-form> | ||||
|                         <button slot="trigger" class="pf-c-button pf-m-secondary"> | ||||
|                             ${msg("Edit")} | ||||
|                         </button> | ||||
|                     </ak-forms-modal> | ||||
|                 `, | ||||
|             ], | ||||
|  | ||||
|             [ | ||||
|                 msg("Check access"), | ||||
|                 html` | ||||
|                     <ak-forms-modal .closeAfterSuccessfulSubmit=${false}> | ||||
|                         <span slot="submit"> ${msg("Check")} </span> | ||||
|                         <span slot="header"> ${msg("Check Application access")} </span> | ||||
|                         <ak-application-check-access-form slot="form" .application=${app}> | ||||
|                         </ak-application-check-access-form> | ||||
|                         <button slot="trigger" class="pf-c-button pf-m-secondary"> | ||||
|                             ${msg("Test")} | ||||
|                         </button> | ||||
|                     </ak-forms-modal> | ||||
|                 `, | ||||
|             ], | ||||
|  | ||||
|             app.launchUrl | ||||
|                 ? [ | ||||
|                       msg("Launch"), | ||||
|                       html` | ||||
|                           <a | ||||
|                               target="_blank" | ||||
|                               href=${app.launchUrl} | ||||
|                               slot="trigger" | ||||
|                               class="pf-c-button pf-m-secondary" | ||||
|                           > | ||||
|                               ${msg("Launch")} | ||||
|                           </a> | ||||
|                       `, | ||||
|                   ] | ||||
|                 : null, | ||||
|         ]; | ||||
|  | ||||
|         return rows.filter((row) => row !== null) as DescriptionPair[]; | ||||
|     } | ||||
|  | ||||
|     controlCard(app: Application) { | ||||
|         return html` | ||||
|             <div class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-2-col-on-xl pf-m-2-col-on-2xl"> | ||||
|                 <div class="pf-c-card__title">${msg("Related")}</div> | ||||
|                 <div class="pf-c-card__body"> | ||||
|                     ${renderDescriptionList(this.controlCardContents(app))} | ||||
|                 </div> | ||||
|             </div> | ||||
|         `; | ||||
|     } | ||||
|  | ||||
|     loginsChart(app: Application) { | ||||
|         return html`<div | ||||
|             class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-10-col-on-xl pf-m-10-col-on-2xl" | ||||
|         > | ||||
|             <div class="pf-c-card__title">${msg("Logins over the last week (per 8 hours)")}</div> | ||||
|             <div class="pf-c-card__body"> | ||||
|                 ${app && | ||||
|                 html` <ak-charts-application-authorize applicationSlug=${app.slug}> | ||||
|                 </ak-charts-application-authorize>`} | ||||
|             </div> | ||||
|         </div>`; | ||||
|     } | ||||
|  | ||||
|     changelog(app: Application) { | ||||
|         return html` | ||||
|             <div class="pf-c-card pf-l-grid__item pf-m-12-col"> | ||||
|                 <div class="pf-c-card__title">${msg("Changelog")}</div> | ||||
|                 <div class="pf-c-card__body"> | ||||
|                     <ak-object-changelog | ||||
|                         targetModelPk=${app.pk || ""} | ||||
|                         targetModelApp="authentik_core" | ||||
|                         targetModelName="application" | ||||
|                     > | ||||
|                     </ak-object-changelog> | ||||
|                 </div> | ||||
|             </div> | ||||
|         `; | ||||
|     } | ||||
|  | ||||
|     overview(app: Application) { | ||||
|         return html`  | ||||
|             <div class="pf-l-grid pf-m-gutter"> | ||||
|                 ${this.controlCard(app)} ${this.loginsChart(app)} ${this.changelog(app)} | ||||
|             </div> | ||||
|         </section>`; | ||||
|     } | ||||
|  | ||||
|     policiesList(app: Application) { | ||||
|         return html` | ||||
|             <div class="pf-c-card"> | ||||
|                 <div class="pf-c-card__title"> | ||||
|                     ${msg("These policies control which users can access this application.")} | ||||
|                 </div> | ||||
|                 <ak-bound-policies-list .target=${app.pk}> </ak-bound-policies-list> | ||||
|             </div> | ||||
|         `; | ||||
|     } | ||||
|  | ||||
|     render() { | ||||
|         return html` <ak-page-header | ||||
|                 header=${this.app.name} | ||||
|                 description=${ifDefined(this.app.metaPublisher)} | ||||
|                 .iconImage=${true} | ||||
|             > | ||||
|                 <ak-app-icon size=${PFSize.Medium} slot="icon" .app=${this.app}></ak-app-icon> | ||||
|             </ak-page-header> | ||||
|             <ak-tabs> | ||||
|                 ${this.missingOutpostMessage()} | ||||
|                 <section | ||||
|                     slot="page-overview" | ||||
|                     data-tab-title="${msg("Overview")}" | ||||
|                     class="pf-c-page__main-section pf-m-no-padding-mobile" | ||||
|                 > | ||||
|                     ${this.overview(this.app)} | ||||
|                 </section> | ||||
|                 <section | ||||
|                     slot="page-policy-bindings" | ||||
|                     data-tab-title="${msg("Policy / Group / User Bindings")}" | ||||
|                     class="pf-c-page__main-section pf-m-no-padding-mobile" | ||||
|                 > | ||||
|                     ${this.policiesList(this.app)} | ||||
|                 </section> | ||||
|                 <ak-rbac-object-permission-page | ||||
|                     slot="page-permissions" | ||||
|                     data-tab-title="${msg("Permissions")}" | ||||
|                     model=${this.rbacModel} | ||||
|                     objectPk=${this.app.pk} | ||||
|                 ></ak-rbac-object-permission-page> | ||||
|             </ak-tabs>`; | ||||
|     } | ||||
| } | ||||
| @ -2,8 +2,9 @@ import { AKElement } from "@goauthentik/elements/Base"; | ||||
| import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter"; | ||||
|  | ||||
| import { msg } from "@lit/localize"; | ||||
| import { PropertyValues } from "@lit/reactive-element/reactive-element"; | ||||
| import { TemplateResult, css, html } from "lit"; | ||||
| import { customElement, property, queryAll } from "lit/decorators.js"; | ||||
| import { customElement, property, queryAll, state } from "lit/decorators.js"; | ||||
| import { map } from "lit/directives/map.js"; | ||||
|  | ||||
| import PFCheck from "@patternfly/patternfly/components/Check/check.css"; | ||||
| @ -112,10 +113,14 @@ export class CheckboxGroup extends AkElementWithCustomEvents { | ||||
|     @queryAll('input[type="checkbox"]') | ||||
|     checkboxes!: NodeListOf<HTMLInputElement>; | ||||
|  | ||||
|     internals?: ElementInternals; | ||||
|     @state() | ||||
|     values: string[] = []; | ||||
|  | ||||
|     get json() { | ||||
|         return this.value; | ||||
|     internals?: ElementInternals; | ||||
|     doneFirstUpdate = false; | ||||
|  | ||||
|     json() { | ||||
|         return this.values; | ||||
|     } | ||||
|  | ||||
|     private get formValue() { | ||||
| @ -124,7 +129,7 @@ export class CheckboxGroup extends AkElementWithCustomEvents { | ||||
|         } | ||||
|         const name = this.name; | ||||
|         const entries = new FormData(); | ||||
|         this.value.forEach((v) => entries.append(name, v)); | ||||
|         this.values.forEach((v) => entries.append(name, v)); | ||||
|         return entries; | ||||
|     } | ||||
|  | ||||
| @ -136,14 +141,14 @@ export class CheckboxGroup extends AkElementWithCustomEvents { | ||||
|  | ||||
|     onClick(ev: Event) { | ||||
|         ev.stopPropagation(); | ||||
|         this.value = Array.from(this.checkboxes) | ||||
|         this.values = Array.from(this.checkboxes) | ||||
|             .filter((checkbox) => checkbox.checked) | ||||
|             .map((checkbox) => checkbox.name); | ||||
|         this.dispatchCustomEvent("change", this.value); | ||||
|         this.dispatchCustomEvent("input", this.value); | ||||
|         this.dispatchCustomEvent("change", this.values); | ||||
|         this.dispatchCustomEvent("input", this.values); | ||||
|         if (this.internals) { | ||||
|             this.internals.setValidity({}); | ||||
|             if (this.required && this.value.length === 0) { | ||||
|             if (this.required && this.values.length === 0) { | ||||
|                 this.internals.setValidity( | ||||
|                     { | ||||
|                         valueMissing: true, | ||||
| @ -154,6 +159,16 @@ export class CheckboxGroup extends AkElementWithCustomEvents { | ||||
|             } | ||||
|             this.internals.setFormValue(this.formValue); | ||||
|         } | ||||
|         // Doing a write-back so anyone examining the checkbox.value field will get something | ||||
|         // meaningful. Doesn't do anything for anyone, usually, but it's nice to have. | ||||
|         this.value = this.values; | ||||
|     } | ||||
|  | ||||
|     willUpdate(changed: PropertyValues<this>) { | ||||
|         if (changed.has("value") && !this.doneFirstUpdate) { | ||||
|             this.doneFirstUpdate = true; | ||||
|             this.values = this.value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     connectedCallback() { | ||||
| @ -183,7 +198,7 @@ export class CheckboxGroup extends AkElementWithCustomEvents { | ||||
|  | ||||
|     render() { | ||||
|         const renderOne = ([name, label]: CheckboxPr) => { | ||||
|             const selected = this.value.includes(name); | ||||
|             const selected = this.values.includes(name); | ||||
|             const blockFwd = (e: Event) => { | ||||
|                 e.stopImmediatePropagation(); | ||||
|             }; | ||||
|  | ||||
| @ -53,6 +53,9 @@ export class AkDualSelectProvider extends CustomListenerElement(AKElement) { | ||||
|  | ||||
|     private isLoading = false; | ||||
|  | ||||
|     private doneFirstUpdate = false; | ||||
|     private internalSelected: DualSelectPair[] = []; | ||||
|  | ||||
|     private pagination?: Pagination; | ||||
|  | ||||
|     constructor() { | ||||
| @ -69,6 +72,11 @@ export class AkDualSelectProvider extends CustomListenerElement(AKElement) { | ||||
|     } | ||||
|  | ||||
|     willUpdate(changedProperties: PropertyValues<this>) { | ||||
|         if (changedProperties.has("selected") && !this.doneFirstUpdate) { | ||||
|             this.doneFirstUpdate = true; | ||||
|             this.internalSelected = this.selected; | ||||
|         } | ||||
|  | ||||
|         if (changedProperties.has("searchDelay")) { | ||||
|             this.doSearch = debounce( | ||||
|                 AkDualSelectProvider.prototype.doSearch.bind(this), | ||||
| @ -105,7 +113,8 @@ export class AkDualSelectProvider extends CustomListenerElement(AKElement) { | ||||
|         if (!(event instanceof CustomEvent)) { | ||||
|             throw new Error(`Expecting a CustomEvent for change, received ${event} instead`); | ||||
|         } | ||||
|         this.selected = event.detail.value; | ||||
|         this.internalSelected = event.detail.value; | ||||
|         this.selected = this.internalSelected; | ||||
|     } | ||||
|  | ||||
|     onSearch(event: Event) { | ||||
| @ -124,12 +133,16 @@ export class AkDualSelectProvider extends CustomListenerElement(AKElement) { | ||||
|         return this.dualSelector.value!.selected.map(([k, _]) => k); | ||||
|     } | ||||
|  | ||||
|     json() { | ||||
|         return this.value; | ||||
|     } | ||||
|  | ||||
|     render() { | ||||
|         return html`<ak-dual-select | ||||
|             ${ref(this.dualSelector)} | ||||
|             .options=${this.options} | ||||
|             .pages=${this.pagination} | ||||
|             .selected=${this.selected} | ||||
|             .selected=${this.internalSelected} | ||||
|             available-label=${this.availableLabel} | ||||
|             selected-label=${this.selectedLabel} | ||||
|         ></ak-dual-select>`; | ||||
|  | ||||
| @ -80,7 +80,7 @@ export function serializeForm<T extends KeyUnknown>( | ||||
|         } | ||||
|  | ||||
|         if ("akControl" in inputElement.dataset) { | ||||
|             assignValue(element, inputElement.value, json); | ||||
|             assignValue(element, (inputElement as unknown as AkControlElement).json(), json); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	