diff --git a/web/src/admin/admin-overview/cards/RecentEventsCard.ts b/web/src/admin/admin-overview/cards/RecentEventsCard.ts
index b02c992b2..11c197dd8 100644
--- a/web/src/admin/admin-overview/cards/RecentEventsCard.ts
+++ b/web/src/admin/admin-overview/cards/RecentEventsCard.ts
@@ -1,9 +1,9 @@
-import "@goauthentik/admin/events/EventInfo";
import { EventGeo } from "@goauthentik/admin/events/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events";
import { actionToLabel } from "@goauthentik/common/labels";
import { truncate } from "@goauthentik/common/utils";
+import "@goauthentik/components/ak-event-info";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/Dropdown";
import "@goauthentik/elements/buttons/ModalButton";
diff --git a/web/src/admin/events/EventInfo.ts b/web/src/admin/events/EventInfo.ts
deleted file mode 100644
index 0a1b32cc4..000000000
--- a/web/src/admin/events/EventInfo.ts
+++ /dev/null
@@ -1,442 +0,0 @@
-import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
-import { VERSION } from "@goauthentik/common/constants";
-import { EventContext, EventModel, EventWithContext } from "@goauthentik/common/events";
-import { AKElement } from "@goauthentik/elements/Base";
-import "@goauthentik/elements/Expand";
-import "@goauthentik/elements/Spinner";
-import { PFSize } from "@goauthentik/elements/Spinner";
-
-import { msg, str } from "@lit/localize";
-import { CSSResult, TemplateResult, css, html } from "lit";
-import { customElement, property } from "lit/decorators.js";
-import { until } from "lit/directives/until.js";
-
-import PFButton from "@patternfly/patternfly/components/Button/button.css";
-import PFCard from "@patternfly/patternfly/components/Card/card.css";
-import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
-import PFList from "@patternfly/patternfly/components/List/list.css";
-import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";
-import PFBase from "@patternfly/patternfly/patternfly-base.css";
-
-import { EventActions, FlowsApi } from "@goauthentik/api";
-
-@customElement("ak-event-info")
-export class EventInfo extends AKElement {
- @property({ attribute: false })
- event!: EventWithContext;
-
- static get styles(): CSSResult[] {
- return [
- PFBase,
- PFButton,
- PFFlex,
- PFCard,
- PFList,
- PFDescriptionList,
- css`
- code {
- display: block;
- white-space: pre-wrap;
- word-break: break-all;
- }
- .pf-l-flex {
- justify-content: space-between;
- }
- .pf-l-flex__item {
- min-width: 25%;
- }
- iframe {
- width: 100%;
- height: 50rem;
- }
- `,
- ];
- }
-
- getModelInfo(context: EventModel): TemplateResult {
- if (context === null) {
- return html`-`;
- }
- return html`
-
-
-
-
- ${msg("UID")}
-
-
-
-
${context.pk}
-
-
-
-
-
- ${msg("Name")}
-
-
-
-
${context.name}
-
-
-
-
-
- ${msg("App")}
-
-
-
-
${context.app}
-
-
-
-
-
- ${msg("Model Name")}
-
-
-
-
${context.model_name}
-
-
-
-
`;
- }
-
- getEmailInfo(context: EventContext): TemplateResult {
- if (context === null) {
- return html`-`;
- }
- return html`
-
-
-
- ${msg("Message")}
-
-
-
-
${context.message}
-
-
-
-
-
- ${msg("Subject")}
-
-
-
-
${context.subject}
-
-
-
-
-
- ${msg("From")}
-
-
-
-
${context.from_email}
-
-
-
-
-
- ${msg("To")}
-
-
-
-
- ${(context.to_email as string[]).map((to) => {
- return html`
- ${to}
`;
- })}
-
-
-
-
`;
- }
-
- defaultResponse(): TemplateResult {
- return html`
-
-
${msg("Context")}
-
- ${JSON.stringify(this.event?.context, null, 4)}
-
-
-
-
${msg("User")}
-
- ${JSON.stringify(this.event?.user, null, 4)}
-
-
-
`;
- }
-
- buildGitHubIssueUrl(context: EventContext): string {
- const httpRequest = this.event.context.http_request as EventContext;
- let title = "";
- if (httpRequest) {
- title = `${httpRequest?.method} ${httpRequest?.path}`;
- }
- // https://docs.github.com/en/issues/tracking-your-work-with-issues/creating-issues/about-automation-for-issues-and-pull-requests-with-query-parameters
- const fullBody = `
-**Describe the bug**
-A clear and concise description of what the bug is.
-
-**To Reproduce**
-Steps to reproduce the behavior:
-1. Go to '...'
-2. Click on '....'
-3. Scroll down to '....'
-4. See error
-
-**Expected behavior**
-A clear and concise description of what you expected to happen.
-
-**Screenshots**
-If applicable, add screenshots to help explain your problem.
-
-**Logs**
-
- Stacktrace from authentik
-
-\`\`\`
-${context.message as string}
-\`\`\`
-
-
-
-**Version and Deployment (please complete the following information):**
-- authentik version: ${VERSION}
-- Deployment: [e.g. docker-compose, helm]
-
-**Additional context**
-Add any other context about the problem here.
- `;
- return `https://github.com/goauthentik/authentik/issues/
-new?labels=bug,from_authentik&title=${encodeURIComponent(title)}
-&body=${encodeURIComponent(fullBody)}`.trim();
- }
-
- render(): TemplateResult {
- if (!this.event) {
- return html``;
- }
- switch (this.event?.action) {
- case EventActions.ModelCreated:
- case EventActions.ModelUpdated:
- case EventActions.ModelDeleted:
- return html`
- ${msg("Affected model:")}
-
- ${this.getModelInfo(this.event.context?.model as EventModel)}
-
- `;
- case EventActions.AuthorizeApplication:
- return html`
-
-
${msg("Authorized application:")}
-
- ${this.getModelInfo(
- this.event.context.authorized_application as EventModel,
- )}
-
-
-
-
${msg("Using flow")}
-
-
${until(
- new FlowsApi(DEFAULT_CONFIG)
- .flowsInstancesList({
- flowUuid: this.event.context.flow as string,
- })
- .then((resp) => {
- return html`${resp.results[0].name}`;
- }),
- html``,
- )}
-
-
-
-
- ${this.defaultResponse()}`;
- case EventActions.EmailSent:
- return html`${msg("Email info:")}
- ${this.getEmailInfo(this.event.context)}
-
-
- `;
- case EventActions.SecretView:
- return html` ${msg("Secret:")}
- ${this.getModelInfo(this.event.context.secret as EventModel)}`;
- case EventActions.SystemException:
- return html`
-
-
${msg("Exception")}
-
-
-
${this.event.context.message}
-
-
-
- ${this.defaultResponse()}`;
- case EventActions.PropertyMappingException:
- return html`
-
-
${msg("Exception")}
-
-
${this.event.context.message || this.event.context.error}
-
-
-
-
${msg("Expression")}
-
- ${this.event.context.expression}
-
-
-
- ${this.defaultResponse()}`;
- case EventActions.PolicyException:
- return html`
-
-
${msg("Binding")}
- ${this.getModelInfo(this.event.context.binding as EventModel)}
-
-
-
${msg("Request")}
-
-
- -
- ${msg("Object")}:
- ${this.getModelInfo(
- (this.event.context.request as EventContext)
- .obj as EventModel,
- )}
-
- -
- ${msg("Context")}:
-
${JSON.stringify(
- (this.event.context.request as EventContext)
- .context,
- null,
- 4,
- )}
-
-
-
-
-
-
${msg("Exception")}
-
- ${this.event.context.message || this.event.context.error}
-
-
-
- ${this.defaultResponse()}`;
- case EventActions.PolicyExecution:
- return html`
-
-
${msg("Binding")}
- ${this.getModelInfo(this.event.context.binding as EventModel)}
-
-
-
${msg("Request")}
-
-
- -
- ${msg("Object")}:
- ${this.getModelInfo(
- (this.event.context.request as EventContext)
- .obj as EventModel,
- )}
-
- -
- ${msg("Context")}:
-
${JSON.stringify(
- (this.event.context.request as EventContext)
- .context,
- null,
- 4,
- )}
-
-
-
-
-
-
${msg("Result")}
-
-
- -
- ${msg("Passing")}:
- ${(this.event.context.result as EventContext).passing}
-
- -
- ${msg("Messages")}:
-
- ${(
- (this.event.context.result as EventContext)
- .messages as string[]
- ).map((msg) => {
- return html`- ${msg}
`;
- })}
-
-
-
-
-
-
- ${this.defaultResponse()}`;
- case EventActions.ConfigurationError:
- return html`${this.event.context.message}
- ${this.defaultResponse()}`;
- case EventActions.UpdateAvailable:
- return html`${msg("New version available!")}
-
- ${this.event.context.new_version}
- `;
- // Action types which typically don't record any extra context.
- // If context is not empty, we fall to the default response.
- case EventActions.Login:
- if ("using_source" in this.event.context) {
- return html`
-
-
${msg("Using source")}
- ${this.getModelInfo(this.event.context.using_source as EventModel)}
-
-
`;
- }
- return this.defaultResponse();
- case EventActions.LoginFailed:
- return html`
- ${msg(str`Attempted to log in as ${this.event.context.username}`)}
-
- ${this.defaultResponse()}`;
- case EventActions.Logout:
- if (Object.keys(this.event.context).length === 0) {
- return html`${msg("No additional data available.")}`;
- }
- return this.defaultResponse();
- case EventActions.SystemTaskException:
- return html`
-
-
${msg("Exception")}
-
-
${this.event.context.message}
-
-
-
`;
- default:
- return this.defaultResponse();
- }
- }
-}
diff --git a/web/src/admin/events/EventListPage.ts b/web/src/admin/events/EventListPage.ts
index 38b310106..4d42e8c74 100644
--- a/web/src/admin/events/EventListPage.ts
+++ b/web/src/admin/events/EventListPage.ts
@@ -1,9 +1,9 @@
-import "@goauthentik/admin/events/EventInfo";
import { EventGeo } from "@goauthentik/admin/events/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events";
import { actionToLabel } from "@goauthentik/common/labels";
import { uiConfig } from "@goauthentik/common/ui/config";
+import "@goauthentik/components/ak-event-info";
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage";
diff --git a/web/src/admin/events/EventViewPage.ts b/web/src/admin/events/EventViewPage.ts
index ad23514eb..b5351840a 100644
--- a/web/src/admin/events/EventViewPage.ts
+++ b/web/src/admin/events/EventViewPage.ts
@@ -1,8 +1,8 @@
-import "@goauthentik/admin/events/EventInfo";
import { EventGeo } from "@goauthentik/admin/events/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events";
import { actionToLabel } from "@goauthentik/common/labels";
+import "@goauthentik/components/ak-event-info";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/PageHeader";
diff --git a/web/src/components/ak-event-info.ts b/web/src/components/ak-event-info.ts
new file mode 100644
index 000000000..6901f31a8
--- /dev/null
+++ b/web/src/components/ak-event-info.ts
@@ -0,0 +1,491 @@
+import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
+import { VERSION } from "@goauthentik/common/constants";
+import { EventContext, EventModel, EventWithContext } from "@goauthentik/common/events";
+import { AKElement } from "@goauthentik/elements/Base";
+import "@goauthentik/elements/Expand";
+import "@goauthentik/elements/Spinner";
+import { PFSize } from "@goauthentik/elements/Spinner";
+
+import { msg, str } from "@lit/localize";
+import { CSSResult, TemplateResult, css, html } from "lit";
+import { customElement, property } from "lit/decorators.js";
+import { map } from "lit/directives/map.js";
+import { until } from "lit/directives/until.js";
+
+import PFButton from "@patternfly/patternfly/components/Button/button.css";
+import PFCard from "@patternfly/patternfly/components/Card/card.css";
+import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
+import PFList from "@patternfly/patternfly/components/List/list.css";
+import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";
+import PFBase from "@patternfly/patternfly/patternfly-base.css";
+
+import { EventActions, FlowsApi } from "@goauthentik/api";
+
+type Pair = [string, string | number | EventContext | EventModel | string[] | TemplateResult];
+
+// https://docs.github.com/en/issues/tracking-your-work-with-issues/creating-issues/about-automation-for-issues-and-pull-requests-with-query-parameters
+
+// This is the template message body with our stacktrace passed to github via a querystring. It is
+// 702 bytes long in UTF-8. [As of July
+// 2023](https://saturncloud.io/blog/what-is-the-maximum-length-of-a-url-in-different-browsers/),
+// the longest URL (not query string, **URL**) passable via this method is 2048 bytes. This is a bit
+// of a hack, but it will get the top of the context across even if it exceeds the limit of the more
+// restrictive browsers.
+
+const githubIssueMessageBody = (context: EventContext) => `
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Logs**
+
+ Stacktrace from authentik
+
+\`\`\`
+${context.message as string}
+\`\`\`
+
+
+
+**Version and Deployment (please complete the following information):**
+- authentik version: ${VERSION}
+- Deployment: [e.g. docker-compose, helm]
+
+**Additional context**
+Add any other context about the problem here.
+ `;
+
+@customElement("ak-event-info")
+export class EventInfo extends AKElement {
+ @property({ attribute: false })
+ event!: EventWithContext;
+
+ static get styles(): CSSResult[] {
+ return [
+ PFBase,
+ PFButton,
+ PFFlex,
+ PFCard,
+ PFList,
+ PFDescriptionList,
+ css`
+ code {
+ display: block;
+ white-space: pre-wrap;
+ word-break: break-all;
+ }
+ .pf-l-flex {
+ justify-content: space-between;
+ }
+ .pf-l-flex__item {
+ min-width: 25%;
+ }
+ iframe {
+ width: 100%;
+ height: 50rem;
+ }
+ `,
+ ];
+ }
+
+ renderDescriptionGroup([term, description]: Pair) {
+ return html`
+
+ ${term}
+
+
+ ${description}
+
+
`;
+ }
+
+ getModelInfo(context: EventModel): TemplateResult {
+ if (context === null) {
+ return html`-`;
+ }
+
+ const modelFields: Pair[] = [
+ [msg("UID"), context.pk],
+ [msg("Name"), context.name],
+ [msg("App"), context.app],
+ [msg("Model Name"), context.model_name],
+ ];
+
+ return html`
+
+ ${map(modelFields, this.renderDescriptionGroup)}
+
+
`;
+ }
+
+ getEmailInfo(context: EventContext): TemplateResult {
+ if (context === null) {
+ return html`-`;
+ }
+
+ // prettier-ignore
+ const emailFields: Pair[] = [
+ [msg("Message"), context.message],
+ [msg("Subject"), context.subject],
+ [msg("From"), context.from_email],
+ [msg("To"), html`${(context.to_email as string[]).map((to) => {
+ return html`${to}`;
+ })}`],
+ ];
+
+ return html`
+ ${map(emailFields, this.renderDescriptionGroup)}
+
`;
+ }
+
+ renderDefaultResponse(): TemplateResult {
+ return html`
+
+
${msg("Context")}
+
+ ${JSON.stringify(this.event?.context, null, 4)}
+
+
+
+
${msg("User")}
+
+ ${JSON.stringify(this.event?.user, null, 4)}
+
+
+
`;
+ }
+
+ buildGitHubIssueUrl(context: EventContext): string {
+ const httpRequest = this.event.context.http_request as EventContext;
+ const title = httpRequest ? `${httpRequest?.method} ${httpRequest?.path}` : "";
+
+ return [
+ "https://github.com/goauthentik/authentik/issues/new",
+ "?labels=bug,from_authentik",
+ `&title=${encodeURIComponent(title)}`,
+ `&body=${encodeURIComponent(githubIssueMessageBody(context))}`,
+ ]
+ .join("")
+ .trim();
+ }
+
+ // It's commonplace not to put the return type on most functions in Typescript. In this case,
+ // however, putting this return type creates a virtuous check of *all* the subrenderers to
+ // ensure that all of them return what we're expecting.
+
+ render(): TemplateResult {
+ if (!this.event) {
+ return html``;
+ }
+
+ switch (this.event?.action) {
+ case EventActions.ModelCreated:
+ case EventActions.ModelUpdated:
+ case EventActions.ModelDeleted:
+ return this.renderModelChanged();
+
+ case EventActions.AuthorizeApplication:
+ return this.renderAuthorizeApplication();
+
+ case EventActions.EmailSent:
+ return this.renderEmailSent();
+
+ case EventActions.SecretView:
+ return this.renderSecretView();
+
+ case EventActions.SystemException:
+ return this.renderSystemException();
+
+ case EventActions.PropertyMappingException:
+ return this.renderPropertyMappingException();
+
+ case EventActions.PolicyException:
+ return this.renderPolicyException();
+
+ case EventActions.PolicyExecution:
+ return this.renderPolicyExecution();
+
+ case EventActions.ConfigurationError:
+ return this.renderConfigurationError();
+
+ case EventActions.UpdateAvailable:
+ return this.renderUpdateAvailable();
+
+ // Action types which typically don't record any extra context.
+ // If context is not empty, we fall to the default response.
+ case EventActions.Login:
+ return this.renderLogin();
+
+ case EventActions.LoginFailed:
+ return this.renderLoginFailed();
+
+ case EventActions.Logout:
+ return this.renderLogout();
+
+ case EventActions.SystemTaskException:
+ return this.renderSystemTaskException();
+
+ default:
+ return this.renderDefaultResponse();
+ }
+ }
+
+ renderModelChanged() {
+ return html`
+ ${msg("Affected model:")}
+
+ ${this.getModelInfo(this.event.context?.model as EventModel)}
+
+ `;
+ }
+
+ renderAuthorizeApplication() {
+ return html`
+
+
${msg("Authorized application:")}
+
+ ${this.getModelInfo(
+ this.event.context.authorized_application as EventModel,
+ )}
+
+
+
+
${msg("Using flow")}
+
+
${until(
+ new FlowsApi(DEFAULT_CONFIG)
+ .flowsInstancesList({
+ flowUuid: this.event.context.flow as string,
+ })
+ .then((resp) => {
+ return html`${resp.results[0].name}`;
+ }),
+ html``,
+ )}
+
+
+
+
+ ${this.renderDefaultResponse()}`;
+ }
+
+ renderEmailSent() {
+ return html`${msg("Email info:")}
+ ${this.getEmailInfo(this.event.context)}
+
+
+ `;
+ }
+
+ renderSecretView() {
+ return html` ${msg("Secret:")}
+ ${this.getModelInfo(this.event.context.secret as EventModel)}`;
+ }
+
+ renderSystemException() {
+ return html`
+
+
${msg("Exception")}
+
+
+
${this.event.context.message}
+
+
+
+ ${this.renderDefaultResponse()}`;
+ }
+
+ renderPropertyMappingException() {
+ return html`
+
+
${msg("Exception")}
+
+
${this.event.context.message || this.event.context.error}
+
+
+
+
${msg("Expression")}
+
+ ${this.event.context.expression}
+
+
+
+ ${this.renderDefaultResponse()}`;
+ }
+
+ renderPolicyException() {
+ return html`
+
+
${msg("Binding")}
+ ${this.getModelInfo(this.event.context.binding as EventModel)}
+
+
+
${msg("Request")}
+
+
+ -
+ ${msg("Object")}:
+ ${this.getModelInfo(
+ (this.event.context.request as EventContext).obj as EventModel,
+ )}
+
+ -
+ ${msg("Context")}:
+
${JSON.stringify(
+ (this.event.context.request as EventContext).context,
+ null,
+ 4,
+ )}
+
+
+
+
+
+
${msg("Exception")}
+
+ ${this.event.context.message || this.event.context.error}
+
+
+
+ ${this.renderDefaultResponse()}`;
+ }
+
+ renderPolicyExecution() {
+ return html`
+
+
${msg("Binding")}
+ ${this.getModelInfo(this.event.context.binding as EventModel)}
+
+
+
${msg("Request")}
+
+
+ -
+ ${msg("Object")}:
+ ${this.getModelInfo(
+ (this.event.context.request as EventContext).obj as EventModel,
+ )}
+
+ -
+ ${msg("Context")}:
+
${JSON.stringify(
+ (this.event.context.request as EventContext).context,
+ null,
+ 4,
+ )}
+
+
+
+
+
+
${msg("Result")}
+
+
+ -
+ ${msg("Passing")}:
+ ${(this.event.context.result as EventContext).passing}
+
+ -
+ ${msg("Messages")}:
+
+ ${(
+ (this.event.context.result as EventContext)
+ .messages as string[]
+ ).map((msg) => {
+ return html`- ${msg}
`;
+ })}
+
+
+
+
+
+
+ ${this.renderDefaultResponse()}`;
+ }
+
+ renderConfigurationError() {
+ return html`${this.event.context.message}
+ ${this.renderDefaultResponse()}`;
+ }
+
+ renderUpdateAvailable() {
+ return html`${msg("New version available")}
+
+ ${this.event.context.new_version}
+ `;
+ // Action types which typically don't record any extra context.
+ // If context is not empty, we fall to the default response.
+ }
+
+ renderLogin() {
+ if ("using_source" in this.event.context) {
+ return html`
+
+
${msg("Using source")}
+ ${this.getModelInfo(this.event.context.using_source as EventModel)}
+
+
`;
+ }
+ return this.renderDefaultResponse();
+ }
+
+ renderLoginFailed() {
+ return html`
+ ${msg(str`Attempted to log in as ${this.event.context.username}`)}
+
+ ${this.renderDefaultResponse()}`;
+ }
+
+ renderLogout() {
+ if (Object.keys(this.event.context).length === 0) {
+ return html`${msg("No additional data available.")}`;
+ }
+ return this.renderDefaultResponse();
+ }
+
+ renderSystemTaskException() {
+ return html`
+
+
${msg("Exception")}
+
+
${this.event.context.message}
+
+
+
`;
+ }
+}
diff --git a/web/src/components/events/ObjectChangelog.ts b/web/src/components/events/ObjectChangelog.ts
index d9e53000f..160a98d73 100644
--- a/web/src/components/events/ObjectChangelog.ts
+++ b/web/src/components/events/ObjectChangelog.ts
@@ -1,7 +1,7 @@
-import "@goauthentik/admin/events/EventInfo";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events";
import { uiConfig } from "@goauthentik/common/ui/config";
+import "@goauthentik/components/ak-event-info";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/Dropdown";
import "@goauthentik/elements/buttons/ModalButton";
diff --git a/web/src/components/events/UserEvents.ts b/web/src/components/events/UserEvents.ts
index 6820953d2..8b8065792 100644
--- a/web/src/components/events/UserEvents.ts
+++ b/web/src/components/events/UserEvents.ts
@@ -1,15 +1,13 @@
-import "@goauthentik/admin/events/EventInfo";
-import "@goauthentik/admin/events/EventInfo";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events";
import { actionToLabel } from "@goauthentik/common/labels";
import { uiConfig } from "@goauthentik/common/ui/config";
+import "@goauthentik/components/ak-event-info";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/Dropdown";
import "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/buttons/SpinnerButton";
-import { PaginatedResponse } from "@goauthentik/elements/table/Table";
-import { Table, TableColumn } from "@goauthentik/elements/table/Table";
+import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
import { msg, str } from "@lit/localize";
import { TemplateResult, html } from "lit";