Compare commits
	
		
			42 Commits
		
	
	
		
			website/in
			...
			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/applications/ApplicationForm"; | ||||||
| import "@goauthentik/admin/policies/BoundPoliciesList"; | import "@goauthentik/admin/policies/BoundPoliciesList"; | ||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import { PFSize } from "@goauthentik/common/enums.js"; |  | ||||||
| import "@goauthentik/components/ak-app-icon"; | import "@goauthentik/components/ak-app-icon"; | ||||||
| import "@goauthentik/components/events/ObjectChangelog"; | import "@goauthentik/components/events/ObjectChangelog"; | ||||||
| import { AKElement } from "@goauthentik/elements/Base"; | import { AKElement } from "@goauthentik/elements/Base"; | ||||||
| @ -13,10 +12,8 @@ import "@goauthentik/elements/Tabs"; | |||||||
| import "@goauthentik/elements/buttons/SpinnerButton"; | import "@goauthentik/elements/buttons/SpinnerButton"; | ||||||
| import "@goauthentik/elements/rbac/ObjectPermissionsPage"; | import "@goauthentik/elements/rbac/ObjectPermissionsPage"; | ||||||
|  |  | ||||||
| import { msg } from "@lit/localize"; | import { PropertyValues } from "lit"; | ||||||
| import { CSSResult, PropertyValues, TemplateResult, html } from "lit"; |  | ||||||
| import { customElement, property, state } from "lit/decorators.js"; | 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 PFBanner from "@patternfly/patternfly/components/Banner/banner.css"; | ||||||
| import PFButton from "@patternfly/patternfly/components/Button/button.css"; | import PFButton from "@patternfly/patternfly/components/Button/button.css"; | ||||||
| @ -35,18 +32,14 @@ import { | |||||||
|     RbacPermissionsAssignedByUsersListModelEnum, |     RbacPermissionsAssignedByUsersListModelEnum, | ||||||
| } from "@goauthentik/api"; | } from "@goauthentik/api"; | ||||||
|  |  | ||||||
|  | import { | ||||||
|  |     ApplicationViewPageLoadingRenderer, | ||||||
|  |     ApplicationViewPageRenderer, | ||||||
|  | } from "./ApplicationViewPageRenderers.js"; | ||||||
|  |  | ||||||
| @customElement("ak-application-view") | @customElement("ak-application-view") | ||||||
| export class ApplicationViewPage extends AKElement { | export class ApplicationViewPage extends AKElement { | ||||||
|     @property({ type: String }) |     static get styles() { | ||||||
|     applicationSlug?: string; |  | ||||||
|  |  | ||||||
|     @state() |  | ||||||
|     application?: Application; |  | ||||||
|  |  | ||||||
|     @state() |  | ||||||
|     missingOutpost = false; |  | ||||||
|  |  | ||||||
|     static get styles(): CSSResult[] { |  | ||||||
|         return [ |         return [ | ||||||
|             PFBase, |             PFBase, | ||||||
|             PFList, |             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>) { |     fetchIsMissingOutpost(providersByPk: Array<number>) { | ||||||
|         new OutpostsApi(DEFAULT_CONFIG) |         new OutpostsApi(DEFAULT_CONFIG) | ||||||
|             .outpostsInstancesList({ |             .outpostsInstancesList({ | ||||||
| @ -94,231 +96,15 @@ export class ApplicationViewPage extends AKElement { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     render(): TemplateResult { |     render() { | ||||||
|         return html`<ak-page-header |         const renderer = this.application | ||||||
|                 header=${this.application?.name || msg("Loading")} |             ? new ApplicationViewPageRenderer( | ||||||
|                 description=${ifDefined(this.application?.metaPublisher)} |                   this.application, | ||||||
|                 .iconImage=${true} |                   this.missingOutpost, | ||||||
|             > |                   RbacPermissionsAssignedByUsersListModelEnum.CoreApplication, | ||||||
|                 <ak-app-icon |               ) | ||||||
|                     size=${PFSize.Medium} |             : new ApplicationViewPageLoadingRenderer(); | ||||||
|                     slot="icon" |  | ||||||
|                     .app=${this.application} |  | ||||||
|                 ></ak-app-icon> |  | ||||||
|             </ak-page-header> |  | ||||||
|             ${this.renderApp()}`; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     renderApp(): TemplateResult { |         return renderer.render(); | ||||||
|         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>`; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										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 { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter"; | ||||||
|  |  | ||||||
| import { msg } from "@lit/localize"; | import { msg } from "@lit/localize"; | ||||||
|  | import { PropertyValues } from "@lit/reactive-element/reactive-element"; | ||||||
| import { TemplateResult, css, html } from "lit"; | 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 { map } from "lit/directives/map.js"; | ||||||
|  |  | ||||||
| import PFCheck from "@patternfly/patternfly/components/Check/check.css"; | import PFCheck from "@patternfly/patternfly/components/Check/check.css"; | ||||||
| @ -112,10 +113,14 @@ export class CheckboxGroup extends AkElementWithCustomEvents { | |||||||
|     @queryAll('input[type="checkbox"]') |     @queryAll('input[type="checkbox"]') | ||||||
|     checkboxes!: NodeListOf<HTMLInputElement>; |     checkboxes!: NodeListOf<HTMLInputElement>; | ||||||
|  |  | ||||||
|     internals?: ElementInternals; |     @state() | ||||||
|  |     values: string[] = []; | ||||||
|  |  | ||||||
|     get json() { |     internals?: ElementInternals; | ||||||
|         return this.value; |     doneFirstUpdate = false; | ||||||
|  |  | ||||||
|  |     json() { | ||||||
|  |         return this.values; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private get formValue() { |     private get formValue() { | ||||||
| @ -124,7 +129,7 @@ export class CheckboxGroup extends AkElementWithCustomEvents { | |||||||
|         } |         } | ||||||
|         const name = this.name; |         const name = this.name; | ||||||
|         const entries = new FormData(); |         const entries = new FormData(); | ||||||
|         this.value.forEach((v) => entries.append(name, v)); |         this.values.forEach((v) => entries.append(name, v)); | ||||||
|         return entries; |         return entries; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -136,14 +141,14 @@ export class CheckboxGroup extends AkElementWithCustomEvents { | |||||||
|  |  | ||||||
|     onClick(ev: Event) { |     onClick(ev: Event) { | ||||||
|         ev.stopPropagation(); |         ev.stopPropagation(); | ||||||
|         this.value = Array.from(this.checkboxes) |         this.values = Array.from(this.checkboxes) | ||||||
|             .filter((checkbox) => checkbox.checked) |             .filter((checkbox) => checkbox.checked) | ||||||
|             .map((checkbox) => checkbox.name); |             .map((checkbox) => checkbox.name); | ||||||
|         this.dispatchCustomEvent("change", this.value); |         this.dispatchCustomEvent("change", this.values); | ||||||
|         this.dispatchCustomEvent("input", this.value); |         this.dispatchCustomEvent("input", this.values); | ||||||
|         if (this.internals) { |         if (this.internals) { | ||||||
|             this.internals.setValidity({}); |             this.internals.setValidity({}); | ||||||
|             if (this.required && this.value.length === 0) { |             if (this.required && this.values.length === 0) { | ||||||
|                 this.internals.setValidity( |                 this.internals.setValidity( | ||||||
|                     { |                     { | ||||||
|                         valueMissing: true, |                         valueMissing: true, | ||||||
| @ -154,6 +159,16 @@ export class CheckboxGroup extends AkElementWithCustomEvents { | |||||||
|             } |             } | ||||||
|             this.internals.setFormValue(this.formValue); |             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() { |     connectedCallback() { | ||||||
| @ -183,7 +198,7 @@ export class CheckboxGroup extends AkElementWithCustomEvents { | |||||||
|  |  | ||||||
|     render() { |     render() { | ||||||
|         const renderOne = ([name, label]: CheckboxPr) => { |         const renderOne = ([name, label]: CheckboxPr) => { | ||||||
|             const selected = this.value.includes(name); |             const selected = this.values.includes(name); | ||||||
|             const blockFwd = (e: Event) => { |             const blockFwd = (e: Event) => { | ||||||
|                 e.stopImmediatePropagation(); |                 e.stopImmediatePropagation(); | ||||||
|             }; |             }; | ||||||
|  | |||||||
| @ -53,6 +53,9 @@ export class AkDualSelectProvider extends CustomListenerElement(AKElement) { | |||||||
|  |  | ||||||
|     private isLoading = false; |     private isLoading = false; | ||||||
|  |  | ||||||
|  |     private doneFirstUpdate = false; | ||||||
|  |     private internalSelected: DualSelectPair[] = []; | ||||||
|  |  | ||||||
|     private pagination?: Pagination; |     private pagination?: Pagination; | ||||||
|  |  | ||||||
|     constructor() { |     constructor() { | ||||||
| @ -69,6 +72,11 @@ export class AkDualSelectProvider extends CustomListenerElement(AKElement) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     willUpdate(changedProperties: PropertyValues<this>) { |     willUpdate(changedProperties: PropertyValues<this>) { | ||||||
|  |         if (changedProperties.has("selected") && !this.doneFirstUpdate) { | ||||||
|  |             this.doneFirstUpdate = true; | ||||||
|  |             this.internalSelected = this.selected; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (changedProperties.has("searchDelay")) { |         if (changedProperties.has("searchDelay")) { | ||||||
|             this.doSearch = debounce( |             this.doSearch = debounce( | ||||||
|                 AkDualSelectProvider.prototype.doSearch.bind(this), |                 AkDualSelectProvider.prototype.doSearch.bind(this), | ||||||
| @ -105,7 +113,8 @@ export class AkDualSelectProvider extends CustomListenerElement(AKElement) { | |||||||
|         if (!(event instanceof CustomEvent)) { |         if (!(event instanceof CustomEvent)) { | ||||||
|             throw new Error(`Expecting a CustomEvent for change, received ${event} instead`); |             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) { |     onSearch(event: Event) { | ||||||
| @ -124,12 +133,16 @@ export class AkDualSelectProvider extends CustomListenerElement(AKElement) { | |||||||
|         return this.dualSelector.value!.selected.map(([k, _]) => k); |         return this.dualSelector.value!.selected.map(([k, _]) => k); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     json() { | ||||||
|  |         return this.value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     render() { |     render() { | ||||||
|         return html`<ak-dual-select |         return html`<ak-dual-select | ||||||
|             ${ref(this.dualSelector)} |             ${ref(this.dualSelector)} | ||||||
|             .options=${this.options} |             .options=${this.options} | ||||||
|             .pages=${this.pagination} |             .pages=${this.pagination} | ||||||
|             .selected=${this.selected} |             .selected=${this.internalSelected} | ||||||
|             available-label=${this.availableLabel} |             available-label=${this.availableLabel} | ||||||
|             selected-label=${this.selectedLabel} |             selected-label=${this.selectedLabel} | ||||||
|         ></ak-dual-select>`; |         ></ak-dual-select>`; | ||||||
|  | |||||||
| @ -80,7 +80,7 @@ export function serializeForm<T extends KeyUnknown>( | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ("akControl" in inputElement.dataset) { |         if ("akControl" in inputElement.dataset) { | ||||||
|             assignValue(element, inputElement.value, json); |             assignValue(element, (inputElement as unknown as AkControlElement).json(), json); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	