web/admin: add toggle to hide deactivated users (#5419)
* web/admin: add toggle to hide deactivated users Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make default user path configurable Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
		| @ -266,11 +266,7 @@ export class AdminInterface extends Interface { | |||||||
|             <ak-sidebar-item> |             <ak-sidebar-item> | ||||||
|                 <span slot="label">${t`Directory`}</span> |                 <span slot="label">${t`Directory`}</span> | ||||||
|                 <ak-sidebar-item |                 <ak-sidebar-item | ||||||
|                     path=${`/identity/users;${encodeURIComponent( |                     path="/identity/users" | ||||||
|                         JSON.stringify({ |  | ||||||
|                             path: "users", |  | ||||||
|                         }), |  | ||||||
|                     )}`} |  | ||||||
|                     .activeWhen=${[`^/identity/users/(?<id>${ID_REGEX})$`]} |                     .activeWhen=${[`^/identity/users/(?<id>${ID_REGEX})$`]} | ||||||
|                 > |                 > | ||||||
|                     <span slot="label">${t`Users`}</span> |                     <span slot="label">${t`Users`}</span> | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ import { TablePage } from "@goauthentik/elements/table/TablePage"; | |||||||
| import { t } from "@lingui/macro"; | import { t } from "@lingui/macro"; | ||||||
|  |  | ||||||
| import { TemplateResult, html } from "lit"; | import { TemplateResult, html } from "lit"; | ||||||
| import { customElement, property } from "lit/decorators.js"; | import { customElement, property, state } from "lit/decorators.js"; | ||||||
| import { ifDefined } from "lit/directives/if-defined.js"; | import { ifDefined } from "lit/directives/if-defined.js"; | ||||||
|  |  | ||||||
| import { PropertyMapping, PropertymappingsApi } from "@goauthentik/api"; | import { PropertyMapping, PropertymappingsApi } from "@goauthentik/api"; | ||||||
| @ -43,7 +43,7 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> { | |||||||
|     @property() |     @property() | ||||||
|     order = "name"; |     order = "name"; | ||||||
|  |  | ||||||
|     @property({ type: Boolean }) |     @state() | ||||||
|     hideManaged = getURLParam<boolean>("hideManaged", true); |     hideManaged = getURLParam<boolean>("hideManaged", true); | ||||||
|  |  | ||||||
|     async apiEndpoint(page: number): Promise<PaginatedResponse<PropertyMapping>> { |     async apiEndpoint(page: number): Promise<PaginatedResponse<PropertyMapping>> { | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ import "@goauthentik/admin/users/UserPasswordForm"; | |||||||
| import "@goauthentik/admin/users/UserResetEmailForm"; | import "@goauthentik/admin/users/UserResetEmailForm"; | ||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import { MessageLevel } from "@goauthentik/common/messages"; | import { MessageLevel } from "@goauthentik/common/messages"; | ||||||
| import { uiConfig } from "@goauthentik/common/ui/config"; | import { DefaultUIConfig, uiConfig } from "@goauthentik/common/ui/config"; | ||||||
| import { first } from "@goauthentik/common/utils"; | import { first } from "@goauthentik/common/utils"; | ||||||
| import { rootInterface } from "@goauthentik/elements/Base"; | import { rootInterface } from "@goauthentik/elements/Base"; | ||||||
| import { PFColor } from "@goauthentik/elements/Label"; | import { PFColor } from "@goauthentik/elements/Label"; | ||||||
| @ -16,7 +16,7 @@ import "@goauthentik/elements/buttons/ActionButton"; | |||||||
| import "@goauthentik/elements/forms/DeleteBulkForm"; | import "@goauthentik/elements/forms/DeleteBulkForm"; | ||||||
| import "@goauthentik/elements/forms/ModalForm"; | import "@goauthentik/elements/forms/ModalForm"; | ||||||
| import { showMessage } from "@goauthentik/elements/messages/MessageContainer"; | import { showMessage } from "@goauthentik/elements/messages/MessageContainer"; | ||||||
| import { getURLParam } from "@goauthentik/elements/router/RouteMatch"; | import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch"; | ||||||
| import { PaginatedResponse } from "@goauthentik/elements/table/Table"; | import { PaginatedResponse } from "@goauthentik/elements/table/Table"; | ||||||
| import { TableColumn } from "@goauthentik/elements/table/Table"; | import { TableColumn } from "@goauthentik/elements/table/Table"; | ||||||
| import { TablePage } from "@goauthentik/elements/table/TablePage"; | import { TablePage } from "@goauthentik/elements/table/TablePage"; | ||||||
| @ -54,7 +54,10 @@ export class UserListPage extends TablePage<User> { | |||||||
|     order = "last_login"; |     order = "last_login"; | ||||||
|  |  | ||||||
|     @property() |     @property() | ||||||
|     activePath = getURLParam<string>("path", "/"); |     activePath; | ||||||
|  |  | ||||||
|  |     @state() | ||||||
|  |     hideDeactivated = getURLParam<boolean>("hideDeactivated", false); | ||||||
|  |  | ||||||
|     @state() |     @state() | ||||||
|     userPaths?: UserPath; |     userPaths?: UserPath; | ||||||
| @ -63,6 +66,16 @@ export class UserListPage extends TablePage<User> { | |||||||
|         return super.styles.concat(PFDescriptionList, PFCard, PFAlert); |         return super.styles.concat(PFDescriptionList, PFCard, PFAlert); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     constructor() { | ||||||
|  |         super(); | ||||||
|  |         this.activePath = getURLParam<string>("path", "/"); | ||||||
|  |         uiConfig().then((c) => { | ||||||
|  |             if (c.defaults.userPath !== new DefaultUIConfig().defaults.userPath) { | ||||||
|  |                 this.activePath = c.defaults.userPath; | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     async apiEndpoint(page: number): Promise<PaginatedResponse<User>> { |     async apiEndpoint(page: number): Promise<PaginatedResponse<User>> { | ||||||
|         const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList({ |         const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList({ | ||||||
|             ordering: this.order, |             ordering: this.order, | ||||||
| @ -70,6 +83,7 @@ export class UserListPage extends TablePage<User> { | |||||||
|             pageSize: (await uiConfig()).pagination.perPage, |             pageSize: (await uiConfig()).pagination.perPage, | ||||||
|             search: this.search || "", |             search: this.search || "", | ||||||
|             pathStartswith: getURLParam("path", ""), |             pathStartswith: getURLParam("path", ""), | ||||||
|  |             isActive: this.hideDeactivated ? true : undefined, | ||||||
|         }); |         }); | ||||||
|         this.userPaths = await new CoreApi(DEFAULT_CONFIG).coreUsersPathsRetrieve({ |         this.userPaths = await new CoreApi(DEFAULT_CONFIG).coreUsersPathsRetrieve({ | ||||||
|             search: this.search, |             search: this.search, | ||||||
| @ -131,6 +145,37 @@ export class UserListPage extends TablePage<User> { | |||||||
|         </ak-forms-delete-bulk>`; |         </ak-forms-delete-bulk>`; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     renderToolbarAfter(): TemplateResult { | ||||||
|  |         return html`  | ||||||
|  |             <div class="pf-c-toolbar__group pf-m-filter-group"> | ||||||
|  |                 <div class="pf-c-toolbar__item pf-m-search-filter"> | ||||||
|  |                     <div class="pf-c-input-group"> | ||||||
|  |                         <label class="pf-c-switch"> | ||||||
|  |                             <input | ||||||
|  |                                 class="pf-c-switch__input" | ||||||
|  |                                 type="checkbox" | ||||||
|  |                                 ?checked=${this.hideDeactivated} | ||||||
|  |                                 @change=${() => { | ||||||
|  |                                     this.hideDeactivated = !this.hideDeactivated; | ||||||
|  |                                     this.page = 1; | ||||||
|  |                                     this.fetch(); | ||||||
|  |                                     updateURLParams({ | ||||||
|  |                                         hideDeactivated: this.hideDeactivated, | ||||||
|  |                                     }); | ||||||
|  |                                 }} | ||||||
|  |                             /> | ||||||
|  |                             <span class="pf-c-switch__toggle"> | ||||||
|  |                                 <span class="pf-c-switch__toggle-icon"> | ||||||
|  |                                     <i class="fas fa-check" aria-hidden="true"></i> | ||||||
|  |                                 </span> | ||||||
|  |                             </span> | ||||||
|  |                             <span class="pf-c-switch__label">${t`Hide deactivated user`}</span> | ||||||
|  |                         </label> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div>`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     row(item: User): TemplateResult[] { |     row(item: User): TemplateResult[] { | ||||||
|         return [ |         return [ | ||||||
|             html`<a href="#/identity/users/${item.pk}"> |             html`<a href="#/identity/users/${item.pk}"> | ||||||
|  | |||||||
| @ -9,6 +9,8 @@ import { | |||||||
|     ResponseContext, |     ResponseContext, | ||||||
| } from "@goauthentik/api"; | } from "@goauthentik/api"; | ||||||
|  |  | ||||||
|  | export const CSRFHeaderName = "X-authentik-CSRF"; | ||||||
|  |  | ||||||
| export interface RequestInfo { | export interface RequestInfo { | ||||||
|     method: string; |     method: string; | ||||||
|     path: string; |     path: string; | ||||||
| @ -32,7 +34,7 @@ export class LoggingMiddleware implements Middleware { | |||||||
| export class CSRFMiddleware implements Middleware { | export class CSRFMiddleware implements Middleware { | ||||||
|     pre?(context: RequestContext): Promise<FetchParams | void> { |     pre?(context: RequestContext): Promise<FetchParams | void> { | ||||||
|         // @ts-ignore |         // @ts-ignore | ||||||
|         context.init.headers["X-authentik-CSRF"] = getCookie("authentik_csrf"); |         context.init.headers[CSRFHeaderName] = getCookie("authentik_csrf"); | ||||||
|         return Promise.resolve(context); |         return Promise.resolve(context); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -43,6 +43,9 @@ export interface UIConfig { | |||||||
|         type: LayoutType; |         type: LayoutType; | ||||||
|     }; |     }; | ||||||
|     locale: string; |     locale: string; | ||||||
|  |     defaults: { | ||||||
|  |         userPath: string, | ||||||
|  |     }, | ||||||
| } | } | ||||||
|  |  | ||||||
| export class DefaultUIConfig implements UIConfig { | export class DefaultUIConfig implements UIConfig { | ||||||
| @ -68,6 +71,9 @@ export class DefaultUIConfig implements UIConfig { | |||||||
|         perPage: 20, |         perPage: 20, | ||||||
|     }; |     }; | ||||||
|     locale = ""; |     locale = ""; | ||||||
|  |     defaults = { | ||||||
|  |         userPath: "users", | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     constructor() { |     constructor() { | ||||||
|         if (currentInterface() === "user") { |         if (currentInterface() === "user") { | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
|  | import { CSRFHeaderName } from "@goauthentik/common/api/middleware"; | ||||||
| import { EVENT_THEME_CHANGE } from "@goauthentik/common/constants"; | import { EVENT_THEME_CHANGE } from "@goauthentik/common/constants"; | ||||||
| import { globalAK } from "@goauthentik/common/global"; | import { globalAK } from "@goauthentik/common/global"; | ||||||
| import { first, getCookie } from "@goauthentik/common/utils"; | import { first, getCookie } from "@goauthentik/common/utils"; | ||||||
| @ -90,10 +91,7 @@ export class APIBrowser extends Interface { | |||||||
|                         }; |                         }; | ||||||
|                     }>, |                     }>, | ||||||
|                 ) => { |                 ) => { | ||||||
|                     e.detail.request.headers.append( |                     e.detail.request.headers.append(CSRFHeaderName, getCookie("authentik_csrf")); | ||||||
|                         "X-authentik-CSRF", |  | ||||||
|                         getCookie("authentik_csrf"), |  | ||||||
|                     ); |  | ||||||
|                 }} |                 }} | ||||||
|             > |             > | ||||||
|                 <div slot="nav-logo"> |                 <div slot="nav-logo"> | ||||||
|  | |||||||
| @ -4,6 +4,10 @@ | |||||||
|  |  | ||||||
| How many items should be retrieved per page. Defaults to 20. | How many items should be retrieved per page. Defaults to 20. | ||||||
|  |  | ||||||
|  | ### `settings.defaults.userPath` | ||||||
|  |  | ||||||
|  | Default user path which is opened when opening the user list. Defaults to `users`. | ||||||
|  |  | ||||||
| ### `settings.theme.base` | ### `settings.theme.base` | ||||||
|  |  | ||||||
| Configure the base color scheme. Defaults to `automatic`, which switches between dark and light mode based on the users' browsers' preference. Choices: `automatic`, `dark`, `light`. | Configure the base color scheme. Defaults to `automatic`, which switches between dark and light mode based on the users' browsers' preference. Choices: `automatic`, `dark`, `light`. | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Jens L
					Jens L