From 8417d8508f1a94c9577a8b878d0d751dd8c393fa Mon Sep 17 00:00:00 2001 From: "Jens L." Date: Fri, 21 Mar 2025 22:48:28 +0000 Subject: [PATCH] web/admin: reworked sync status card (#13625) * reworked sync status Signed-off-by: Jens Langhammer * update imports Signed-off-by: Jens Langhammer * add story and fix import Signed-off-by: Jens Langhammer * format Signed-off-by: Jens Langhammer --------- Signed-off-by: Jens Langhammer --- .../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 0f34e6aef3..7d6fc3eaac 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/ak-status-label"; import "@goauthentik/components/events/ObjectChangelog"; import { AKElement } from "@goauthentik/elements/Base"; -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 ebf041813a..9607fe79e3 100644 --- a/web/src/admin/providers/scim/SCIMProviderViewPage.ts +++ b/web/src/admin/providers/scim/SCIMProviderViewPage.ts @@ -9,11 +9,11 @@ import "@goauthentik/components/ak-status-label"; 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/SyncStatusCard"; import "@goauthentik/elements/Tabs"; import "@goauthentik/elements/ak-mdx"; 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 b745eb49d4..b2d8df5cfa 100644 --- a/web/src/admin/sources/kerberos/KerberosSourceViewPage.ts +++ b/web/src/admin/sources/kerberos/KerberosSourceViewPage.ts @@ -7,12 +7,12 @@ import "@goauthentik/components/events/ObjectChangelog"; import MDSourceKerberosBrowser from "@goauthentik/docs/users-sources/sources/protocols/kerberos/browser.md"; import { AKElement } from "@goauthentik/elements/Base"; import "@goauthentik/elements/CodeMirror"; -import "@goauthentik/elements/SyncStatusCard"; import "@goauthentik/elements/Tabs"; import "@goauthentik/elements/ak-mdx"; 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; } }