From 1a727b9ea0c596eafb0544106991b4fd2e3fc899 Mon Sep 17 00:00:00 2001 From: Marc 'risson' Schmitt Date: Fri, 28 Mar 2025 13:04:18 +0100 Subject: [PATCH] web/admin: reworked sync status card (cherry-pick #13625) (#13692) web/admin: reworked sync status card (#13625) * reworked sync status * update imports * add story and fix import * format --------- Signed-off-by: Jens Langhammer Co-authored-by: Jens L. --- .../ApplicationEntitlementPage.ts | 2 +- .../GoogleWorkspaceProviderViewPage.ts | 2 +- .../providers/scim/SCIMProviderViewPage.ts | 2 +- .../kerberos/KerberosSourceViewPage.ts | 2 +- .../admin/sources/ldap/LDAPSourceViewPage.ts | 2 +- .../elements/sync/SyncStatusCard.stories.ts | 157 ++++++++++++++++++ web/src/elements/{ => sync}/SyncStatusCard.ts | 109 ++++++++---- 7 files changed, 242 insertions(+), 34 deletions(-) create mode 100644 web/src/elements/sync/SyncStatusCard.stories.ts rename web/src/elements/{ => sync}/SyncStatusCard.ts (56%) diff --git a/web/src/admin/applications/entitlements/ApplicationEntitlementPage.ts b/web/src/admin/applications/entitlements/ApplicationEntitlementPage.ts index bedff89e7c..5dcace54a6 100644 --- a/web/src/admin/applications/entitlements/ApplicationEntitlementPage.ts +++ b/web/src/admin/applications/entitlements/ApplicationEntitlementPage.ts @@ -94,7 +94,7 @@ export class ApplicationEntitlementsPage extends Table { } renderExpanded(item: ApplicationEntitlement): TemplateResult { - return html` + return html`
diff --git a/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderViewPage.ts b/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderViewPage.ts index 138bcad1f9..fc77690eaa 100644 --- a/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderViewPage.ts +++ b/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderViewPage.ts @@ -7,10 +7,10 @@ import { EVENT_REFRESH } from "@goauthentik/common/constants"; import "@goauthentik/components/events/ObjectChangelog"; import { AKElement } from "@goauthentik/elements/Base"; import "@goauthentik/elements/Markdown"; -import "@goauthentik/elements/SyncStatusCard"; import "@goauthentik/elements/Tabs"; import "@goauthentik/elements/buttons/ActionButton"; import "@goauthentik/elements/buttons/ModalButton"; +import "@goauthentik/elements/sync/SyncStatusCard"; import { msg } from "@lit/localize"; import { CSSResult, PropertyValues, TemplateResult, html } from "lit"; diff --git a/web/src/admin/providers/scim/SCIMProviderViewPage.ts b/web/src/admin/providers/scim/SCIMProviderViewPage.ts index d0b6f11210..708b2c1181 100644 --- a/web/src/admin/providers/scim/SCIMProviderViewPage.ts +++ b/web/src/admin/providers/scim/SCIMProviderViewPage.ts @@ -9,10 +9,10 @@ import "@goauthentik/components/events/ObjectChangelog"; import MDSCIMProvider from "@goauthentik/docs/add-secure-apps/providers/scim/index.md"; import { AKElement } from "@goauthentik/elements/Base"; import "@goauthentik/elements/Markdown"; -import "@goauthentik/elements/SyncStatusCard"; import "@goauthentik/elements/Tabs"; import "@goauthentik/elements/buttons/ActionButton"; import "@goauthentik/elements/buttons/ModalButton"; +import "@goauthentik/elements/sync/SyncStatusCard"; import { msg } from "@lit/localize"; import { CSSResult, PropertyValues, TemplateResult, html } from "lit"; diff --git a/web/src/admin/sources/kerberos/KerberosSourceViewPage.ts b/web/src/admin/sources/kerberos/KerberosSourceViewPage.ts index 521866f048..b1b268a616 100644 --- a/web/src/admin/sources/kerberos/KerberosSourceViewPage.ts +++ b/web/src/admin/sources/kerberos/KerberosSourceViewPage.ts @@ -8,11 +8,11 @@ import MDSourceKerberosBrowser from "@goauthentik/docs/users-sources/sources/pro import { AKElement } from "@goauthentik/elements/Base"; import "@goauthentik/elements/CodeMirror"; import "@goauthentik/elements/Markdown"; -import "@goauthentik/elements/SyncStatusCard"; import "@goauthentik/elements/Tabs"; import "@goauthentik/elements/buttons/ActionButton"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/sync/SyncStatusCard"; import { msg } from "@lit/localize"; import { CSSResult, TemplateResult, html } from "lit"; diff --git a/web/src/admin/sources/ldap/LDAPSourceViewPage.ts b/web/src/admin/sources/ldap/LDAPSourceViewPage.ts index 936fa01762..184f4ca821 100644 --- a/web/src/admin/sources/ldap/LDAPSourceViewPage.ts +++ b/web/src/admin/sources/ldap/LDAPSourceViewPage.ts @@ -6,11 +6,11 @@ import { EVENT_REFRESH } from "@goauthentik/common/constants"; import "@goauthentik/components/events/ObjectChangelog"; import { AKElement } from "@goauthentik/elements/Base"; import "@goauthentik/elements/CodeMirror"; -import "@goauthentik/elements/SyncStatusCard"; import "@goauthentik/elements/Tabs"; import "@goauthentik/elements/buttons/ActionButton"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/sync/SyncStatusCard"; import { msg } from "@lit/localize"; import { CSSResult, TemplateResult, html } from "lit"; diff --git a/web/src/elements/sync/SyncStatusCard.stories.ts b/web/src/elements/sync/SyncStatusCard.stories.ts new file mode 100644 index 0000000000..acd00c3800 --- /dev/null +++ b/web/src/elements/sync/SyncStatusCard.stories.ts @@ -0,0 +1,157 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; + +import { html } from "lit"; + +import { LogLevelEnum, SyncStatus, SystemTaskStatusEnum } from "@goauthentik/api"; + +import "./SyncStatusCard"; + +const metadata: Meta = { + title: "Elements/", + component: "ak-sync-status-card", +}; + +export default metadata; + +export const Running: StoryObj = { + args: { + status: { + isRunning: true, + tasks: [], + } as SyncStatus, + }, + // @ts-ignore + render: ({ status }: SyncStatus) => { + return html`
+ { + return status; + }} + > +
`; + }, +}; + +export const SingleTask: StoryObj = { + args: { + status: { + isRunning: false, + tasks: [ + { + uuid: "9ff42169-8249-4b67-ae3d-e455d822de2b", + name: "Single task", + fullName: "foo:bar:baz", + status: SystemTaskStatusEnum.Successful, + messages: [ + { + logger: "foo", + event: "bar", + attributes: { + foo: "bar", + }, + timestamp: new Date(), + logLevel: LogLevelEnum.Info, + }, + ], + description: "foo", + startTimestamp: new Date(), + finishTimestamp: new Date(), + duration: 0, + }, + ], + } as SyncStatus, + }, + // @ts-ignore + render: ({ status }: SyncStatus) => { + return html`
+ { + return status; + }} + > +
`; + }, +}; + +export const MultipleTasks: StoryObj = { + args: { + status: { + isRunning: false, + tasks: [ + { + uuid: "9ff42169-8249-4b67-ae3d-e455d822de2b", + name: "Single task", + fullName: "foo:bar:baz", + status: SystemTaskStatusEnum.Successful, + messages: [ + { + logger: "foo", + event: "bar", + attributes: { + foo: "bar", + }, + timestamp: new Date(), + logLevel: LogLevelEnum.Info, + }, + ], + description: "foo", + startTimestamp: new Date(), + finishTimestamp: new Date(), + duration: 0, + }, + { + uuid: "9ff42169-8249-4b67-ae3d-e455d822de2b", + name: "Single task", + fullName: "foo:bar:baz", + status: SystemTaskStatusEnum.Successful, + messages: [ + { + logger: "foo", + event: "bar", + attributes: { + foo: "bar", + }, + timestamp: new Date(), + logLevel: LogLevelEnum.Info, + }, + ], + description: "foo", + startTimestamp: new Date(), + finishTimestamp: new Date(), + duration: 0, + }, + { + uuid: "9ff42169-8249-4b67-ae3d-e455d822de2b", + name: "Single task", + fullName: "foo:bar:baz", + status: SystemTaskStatusEnum.Successful, + messages: [ + { + logger: "foo", + event: "bar", + attributes: { + foo: "bar", + }, + timestamp: new Date(), + logLevel: LogLevelEnum.Info, + }, + ], + description: "foo", + startTimestamp: new Date(), + finishTimestamp: new Date(), + duration: 0, + }, + ], + } as SyncStatus, + }, + // @ts-ignore + render: ({ status }: SyncStatus) => { + return html`
+ { + return status; + }} + > +
`; + }, +}; diff --git a/web/src/elements/SyncStatusCard.ts b/web/src/elements/sync/SyncStatusCard.ts similarity index 56% rename from web/src/elements/SyncStatusCard.ts rename to web/src/elements/sync/SyncStatusCard.ts index 0c633cd5f5..ce329dd61a 100644 --- a/web/src/elements/SyncStatusCard.ts +++ b/web/src/elements/sync/SyncStatusCard.ts @@ -3,17 +3,92 @@ import { getRelativeTime } from "@goauthentik/common/utils"; import "@goauthentik/components/ak-status-label"; import { AKElement } from "@goauthentik/elements/Base"; import "@goauthentik/elements/EmptyState"; +import "@goauthentik/elements/buttons/ActionButton"; import "@goauthentik/elements/events/LogViewer"; +import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; -import { msg, str } from "@lit/localize"; -import { CSSResult, TemplateResult, html, nothing } from "lit"; +import { msg } from "@lit/localize"; +import { CSSResult, TemplateResult, css, html } from "lit"; import { customElement, property, state } from "lit/decorators.js"; import PFCard from "@patternfly/patternfly/components/Card/card.css"; +import PFTable from "@patternfly/patternfly/components/Table/table.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; import { SyncStatus, SystemTask, SystemTaskStatusEnum } from "@goauthentik/api"; +@customElement("ak-sync-status-table") +export class SyncStatusTable extends Table { + @property({ attribute: false }) + tasks: SystemTask[] = []; + + expandable = true; + + static get styles() { + return super.styles.concat(css` + code:not(:last-of-type)::after { + content: "-"; + margin: 0 0.25rem; + } + `); + } + + async apiEndpoint(): Promise> { + return { + pagination: { + next: 0, + previous: 0, + count: this.tasks.length, + current: 1, + totalPages: 1, + startIndex: 0, + endIndex: this.tasks.length, + }, + results: this.tasks, + }; + } + + columns(): TableColumn[] { + return [ + new TableColumn(msg("Task")), + new TableColumn(msg("Status")), + new TableColumn(msg("Finished")), + ]; + } + + row(item: SystemTask): TemplateResult[] { + const nameParts = item.fullName.split(":"); + nameParts.shift(); + return [ + html`
${item.name}
+ ${nameParts.map((part) => html`${part}`)}`, + html``, + html`
${getRelativeTime(item.finishTimestamp)}
+ ${item.finishTimestamp.toLocaleString()}`, + ]; + } + + renderExpanded(item: SystemTask): TemplateResult { + return html` +
+ +
+ `; + } + + renderToolbarContainer() { + return html``; + } + + renderTablePagination() { + return html``; + } +} + @customElement("ak-sync-status-card") export class SyncStatusCard extends AKElement { @state() @@ -29,7 +104,7 @@ export class SyncStatusCard extends AKElement { triggerSync!: () => Promise; static get styles(): CSSResult[] { - return [PFBase, PFCard]; + return [PFBase, PFCard, PFTable]; } firstUpdated() { @@ -40,25 +115,6 @@ export class SyncStatusCard extends AKElement { }); } - renderSyncTask(task: SystemTask): TemplateResult { - return html`
  • - ${(this.syncState?.tasks || []).length > 1 ? html`${task.name}` : nothing} - - ${msg( - str`Finished ${getRelativeTime(task.finishTimestamp)} (${task.finishTimestamp.toLocaleString()})`, - )} - -
  • `; - } - renderSyncStatus(): TemplateResult { if (this.loading) { return html``; @@ -72,13 +128,7 @@ export class SyncStatusCard extends AKElement { if (this.syncState.tasks.length < 1) { return html`${msg("Not synced yet.")}`; } - return html` -
      - ${this.syncState.tasks.map((task) => { - return this.renderSyncTask(task); - })} -
    - `; + return html``; } render(): TemplateResult { @@ -120,6 +170,7 @@ export class SyncStatusCard extends AKElement { declare global { interface HTMLElementTagNameMap { + "ak-sync-status-table": SyncStatusTable; "ak-sync-status-card": SyncStatusCard; } }