diff --git a/DNN Platform/Modules/BulkInstall/App_LocalResources/BulkInstall.resx b/DNN Platform/Modules/BulkInstall/App_LocalResources/BulkInstall.resx index 036f24d0d9e..52a8d0d486d 100644 --- a/DNN Platform/Modules/BulkInstall/App_LocalResources/BulkInstall.resx +++ b/DNN Platform/Modules/BulkInstall/App_LocalResources/BulkInstall.resx @@ -303,4 +303,7 @@ Platform Version + + All + \ No newline at end of file diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/clients/event-log-client.ts b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/clients/event-log-client.ts index 51faa0a3366..5d12ff97b93 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/clients/event-log-client.ts +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/clients/event-log-client.ts @@ -1,6 +1,6 @@ import { DnnServicesFramework } from '@dnncommunity/dnn-elements'; import type { Event } from '../components/tabs/dnn-bi-logs/dnn-bi-logs.model'; -import { EventLogSeverityInfo, eventLogSeverity } from '../enums/EventLogSeverity'; +import { eventLogSeverity, EventLogSeverityInfo } from '../enums/EventLogSeverity'; export class EventLogClient { private readonly sf: DnnServicesFramework; @@ -11,17 +11,22 @@ export class EventLogClient { this.requestUrl = this.sf.getServiceRoot('BulkInstall') + 'EventLog/'; } - public async browse(): Promise { - const response = await fetch(`${this.requestUrl}Browse`, { + public async browse(pageIndex = 0, severity?: EventLogSeverityInfo, eventType?: string): Promise { + const severityParam = severity ? `&severity=${severity.eventLogSeverityKey}` : ''; + const eventTypeParam = eventType ? `&eventType=${eventType}` : ''; + const response = await fetch(`${this.requestUrl}Browse?pageIndex=${pageIndex}${severityParam}${eventTypeParam}`, { headers: this.sf.getModuleHeaders(), }); const responseBody = (await response.json()) as ResponseBody; - const browseResponse = { - Data: responseBody.Data.map(e => EventLogClient.toEvent(e)), - Pagination: responseBody.Pagination, + return { + data: responseBody.Data.map(e => EventLogClient.toEvent(e)), + pagination: EventLogClient.toPagination(responseBody.Pagination), }; + } - return browseResponse; + public async getEventTypes(): Promise { + const response = await fetch(`${this.requestUrl}EventTypes`, { headers: this.sf.getModuleHeaders() }); + return (await response.json()) as string[]; } private static toSeverity(severity: EventLogSeverityResponse): EventLogSeverityInfo { @@ -47,26 +52,33 @@ export class EventLogClient { severity: EventLogClient.toSeverity(eventLog.Severity), }; } + + private static toPagination(pagination: PaginationResponse): Pagination { + return { + currentPage: pagination.CurrentPage, + pages: pagination.Pages, + }; + } } export interface BrowseResponse { - Data: Event[]; - Pagination: Pagination; + data: Event[]; + pagination: Pagination; } export interface Pagination { - Records: number; - Pages: number; - CurrentPage: number; - Navigation: { - Previous?: string; - Next?: string; - }; + pages: number; + currentPage: number; } interface ResponseBody { Data: EventLogResponse[]; - Pagination: Pagination; + Pagination: PaginationResponse; +} + +interface PaginationResponse { + Pages: number; + CurrentPage: number; } interface EventLogResponse { diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/clients/localization-client.ts b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/clients/localization-client.ts index 052c6d1ed98..c79a9c5e3ff 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/clients/localization-client.ts +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/clients/localization-client.ts @@ -28,6 +28,7 @@ export class LocalizationClient { export interface BulkInstallLocalization { Action: string; Add: string; + All: string; ApiError: string; ApiAuthDisabled: string; ApiKey: string; diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components.d.ts b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components.d.ts index f5fe5408167..cf1afcbec71 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components.d.ts +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components.d.ts @@ -6,7 +6,9 @@ */ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; import { InstallJob, PackageJob, Session, UploadStatus } from "./components/tabs/dnn-bi-install/dnn-bi-install.model"; +import { Pagination } from "./clients/event-log-client"; export { InstallJob, PackageJob, Session, UploadStatus } from "./components/tabs/dnn-bi-install/dnn-bi-install.model"; +export { Pagination } from "./clients/event-log-client"; export namespace Components { interface DnnBiApiUsers { } @@ -28,6 +30,12 @@ export namespace Components { } interface DnnBiIpSafelist { } + interface DnnBiLogPagination { + /** + * The pagination + */ + "pagination": Pagination; + } interface DnnBiLogs { } interface DnnBiPackageJob { @@ -61,6 +69,10 @@ export namespace Components { "moduleId": number; } } +export interface DnnBiLogPaginationCustomEvent extends CustomEvent { + detail: T; + target: HTMLDnnBiLogPaginationElement; +} export interface DnnBiQueuedFileCustomEvent extends CustomEvent { detail: T; target: HTMLDnnBiQueuedFileElement; @@ -114,6 +126,23 @@ declare global { prototype: HTMLDnnBiIpSafelistElement; new (): HTMLDnnBiIpSafelistElement; }; + interface HTMLDnnBiLogPaginationElementEventMap { + "pageSelected": number; + } + interface HTMLDnnBiLogPaginationElement extends Components.DnnBiLogPagination, HTMLStencilElement { + addEventListener(type: K, listener: (this: HTMLDnnBiLogPaginationElement, ev: DnnBiLogPaginationCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLDnnBiLogPaginationElement, ev: DnnBiLogPaginationCustomEvent) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; + } + var HTMLDnnBiLogPaginationElement: { + prototype: HTMLDnnBiLogPaginationElement; + new (): HTMLDnnBiLogPaginationElement; + }; interface HTMLDnnBiLogsElement extends Components.DnnBiLogs, HTMLStencilElement { } var HTMLDnnBiLogsElement: { @@ -158,6 +187,7 @@ declare global { "dnn-bi-install": HTMLDnnBiInstallElement; "dnn-bi-install-job": HTMLDnnBiInstallJobElement; "dnn-bi-ip-safelist": HTMLDnnBiIpSafelistElement; + "dnn-bi-log-pagination": HTMLDnnBiLogPaginationElement; "dnn-bi-logs": HTMLDnnBiLogsElement; "dnn-bi-package-job": HTMLDnnBiPackageJobElement; "dnn-bi-queued-file": HTMLDnnBiQueuedFileElement; @@ -187,6 +217,13 @@ declare namespace LocalJSX { } interface DnnBiIpSafelist { } + interface DnnBiLogPagination { + "onPageSelected"?: (event: DnnBiLogPaginationCustomEvent) => void; + /** + * The pagination + */ + "pagination": Pagination; + } interface DnnBiLogs { } interface DnnBiPackageJob { @@ -240,6 +277,7 @@ declare namespace LocalJSX { "dnn-bi-install": DnnBiInstall; "dnn-bi-install-job": DnnBiInstallJob; "dnn-bi-ip-safelist": DnnBiIpSafelist; + "dnn-bi-log-pagination": DnnBiLogPagination; "dnn-bi-logs": DnnBiLogs; "dnn-bi-package-job": Omit & { [K in keyof DnnBiPackageJob & keyof DnnBiPackageJobAttributes]?: DnnBiPackageJob[K] } & { [K in keyof DnnBiPackageJob & keyof DnnBiPackageJobAttributes as `attr:${K}`]?: DnnBiPackageJobAttributes[K] } & { [K in keyof DnnBiPackageJob & keyof DnnBiPackageJobAttributes as `prop:${K}`]?: DnnBiPackageJob[K] } & OneOf<"attempted", DnnBiPackageJob["attempted"], DnnBiPackageJobAttributes["attempted"]>; "dnn-bi-queued-file": Omit & { [K in keyof DnnBiQueuedFile & keyof DnnBiQueuedFileAttributes]?: DnnBiQueuedFile[K] } & { [K in keyof DnnBiQueuedFile & keyof DnnBiQueuedFileAttributes as `attr:${K}`]?: DnnBiQueuedFileAttributes[K] } & { [K in keyof DnnBiQueuedFile & keyof DnnBiQueuedFileAttributes as `prop:${K}`]?: DnnBiQueuedFile[K] } & OneOf<"maxUploadFileSize", DnnBiQueuedFile["maxUploadFileSize"], DnnBiQueuedFileAttributes["maxUploadFileSize"]>; @@ -258,6 +296,7 @@ declare module "@stencil/core" { "dnn-bi-install": LocalJSX.IntrinsicElements["dnn-bi-install"] & JSXBase.HTMLAttributes; "dnn-bi-install-job": LocalJSX.IntrinsicElements["dnn-bi-install-job"] & JSXBase.HTMLAttributes; "dnn-bi-ip-safelist": LocalJSX.IntrinsicElements["dnn-bi-ip-safelist"] & JSXBase.HTMLAttributes; + "dnn-bi-log-pagination": LocalJSX.IntrinsicElements["dnn-bi-log-pagination"] & JSXBase.HTMLAttributes; "dnn-bi-logs": LocalJSX.IntrinsicElements["dnn-bi-logs"] & JSXBase.HTMLAttributes; "dnn-bi-package-job": LocalJSX.IntrinsicElements["dnn-bi-package-job"] & JSXBase.HTMLAttributes; "dnn-bi-queued-file": LocalJSX.IntrinsicElements["dnn-bi-queued-file"] & JSXBase.HTMLAttributes; diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/dnn-bulk-install/dnn-bulk-install.tsx b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/dnn-bulk-install/dnn-bulk-install.tsx index b392731e9e1..7ca4219830e 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/dnn-bulk-install/dnn-bulk-install.tsx +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/dnn-bulk-install/dnn-bulk-install.tsx @@ -1,6 +1,6 @@ import { Component, Host, h, Prop } from '@stencil/core'; +import store from '../../stores/store'; import { LocalizationClient } from '../../clients/localization-client'; -import state from '../../stores/store'; @Component({ tag: 'dnn-bulk-install', @@ -18,9 +18,9 @@ export class DnnBulkInstall { } async componentWillLoad() { - state.moduleId = this.moduleId; + store.moduleId = this.moduleId; try { - state.resx = await this.localizationClient.getResources(); + store.resx = await this.localizationClient.getResources(); } catch (error) { console.error(error); } @@ -31,22 +31,22 @@ export class DnnBulkInstall {
- +
- +
- +
- +
diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/dnn-bulk-install/readme.md b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/dnn-bulk-install/readme.md index 28691e9f5a8..9e7481160fb 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/dnn-bulk-install/readme.md +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/dnn-bulk-install/readme.md @@ -44,6 +44,9 @@ graph TD; dnn-bi-queued-file --> dnn-bi-checkmark-icon dnn-button --> dnn-modal dnn-button --> dnn-button + dnn-bi-logs --> dnn-select + dnn-bi-logs --> dnn-bi-log-pagination + dnn-select --> dnn-fieldset dnn-bi-api-users --> dnn-button dnn-bi-api-users --> dnn-modal dnn-bi-api-users --> dnn-input diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-api-users/dnn-bi-api-users.scss b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-api-users/dnn-bi-api-users.scss index 0365e77dab2..7800eb93455 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-api-users/dnn-bi-api-users.scss +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-api-users/dnn-bi-api-users.scss @@ -82,13 +82,6 @@ color: var(--dnn-color-neutral, #ededee); } - /* Utility */ - .clearfix::after { - content: ''; - display: table; - clear: both; - } - form.create-user { margin: 1rem; display: flex; diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-api-users/dnn-bi-api-users.tsx b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-api-users/dnn-bi-api-users.tsx index 775e364bb41..89821b8559c 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-api-users/dnn-bi-api-users.tsx +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-api-users/dnn-bi-api-users.tsx @@ -1,7 +1,7 @@ import { Component, Host, h, State } from '@stencil/core'; -import { User } from './dnn-bi-api-users.model'; -import state from '../../../stores/store'; +import store from '../../../stores/store'; import { ApiUserClient } from '../../../clients/api-user-client'; +import { User } from './dnn-bi-api-users.model'; interface NewUser { name: string; @@ -36,7 +36,7 @@ export class DnnBiApiUsers { private apiUserClient: ApiUserClient; constructor() { - this.apiUserClient = new ApiUserClient(state.moduleId); + this.apiUserClient = new ApiUserClient(store.moduleId); } async componentWillLoad() { @@ -72,7 +72,7 @@ export class DnnBiApiUsers { {this.enabled === false && (
-

{state.resx.ApiAuthDisabled}

+

{store.resx.ApiAuthDisabled}

)} @@ -87,22 +87,22 @@ export class DnnBiApiUsers { return; }} > - {state.resx.NewApiUser} + {store.resx.NewApiUser}
-

{state.resx.ApiUsers}

+

{store.resx.ApiUsers}

- - - - - + + + + + @@ -121,7 +121,7 @@ export class DnnBiApiUsers { return; }} > - {state.resx.Delete} + {store.resx.Delete} @@ -141,19 +141,19 @@ export class DnnBiApiUsers { return; }} > -

{state.resx.NewApiUser}

+

{store.resx.NewApiUser}

(this.newUser = { ...this.newUser, name: e.detail as string })} /> (this.newUser = { ...this.newUser, bypassIPWhitelist: e.detail === 'checked' })} /> - {state.resx.BypassIpAllowList} + {store.resx.BypassIpAllowList} - {state.resx.Create} + {store.resx.Create} diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-install/dnn-bi-install-job/dnn-bi-install-job.tsx b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-install/dnn-bi-install-job/dnn-bi-install-job.tsx index 54760bffd22..0a53a6d4ce4 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-install/dnn-bi-install-job/dnn-bi-install-job.tsx +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-install/dnn-bi-install-job/dnn-bi-install-job.tsx @@ -1,5 +1,5 @@ import { Component, h, Host, Prop } from '@stencil/core'; -import state from '../../../../stores/store'; +import store from '../../../../stores/store'; import { InstallJob } from '../dnn-bi-install.model'; @Component({ @@ -31,7 +31,7 @@ export class DnnBiInstallJob { {this.job.packages.length !== 1 && (
- {this.job.packages.length} {state.resx.Packages} + {this.job.packages.length} {store.resx.Packages}
    {this.job.packages.map(p => ( diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-install/dnn-bi-install.tsx b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-install/dnn-bi-install.tsx index 6769ac6ab79..df1fc41588f 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-install/dnn-bi-install.tsx +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-install/dnn-bi-install.tsx @@ -1,8 +1,8 @@ import { Component, Fragment, h, Host, State } from '@stencil/core'; import store from '../../../stores/store'; -import { InstallJob, Session, UploadStatus } from './dnn-bi-install.model'; import { InstallClient } from '../../../clients/install-client'; import { sessionStatus } from '../../../enums/SessionStatus'; +import { InstallJob, Session, UploadStatus } from './dnn-bi-install.model'; type FileViewModel = { type: 'pending'; file: File } | { type: 'error'; file: File } | { type: 'uploaded'; job: InstallJob }; diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-install/dnn-bi-queued-file/dnn-bi-queued-file.tsx b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-install/dnn-bi-queued-file/dnn-bi-queued-file.tsx index 09b73ddc923..1c3efcf5063 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-install/dnn-bi-queued-file/dnn-bi-queued-file.tsx +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-install/dnn-bi-queued-file/dnn-bi-queued-file.tsx @@ -1,8 +1,8 @@ import { Component, Element, Event, EventEmitter, h, Host, Prop, State } from '@stencil/core'; -import state from '../../../../stores/store'; +import store from '../../../../stores/store'; import { getFileSize } from '../../../../utilities/filesize-utilities'; -import { Session, UploadStatus } from '../dnn-bi-install.model'; import { InstallClient } from '../../../../clients/install-client'; +import { Session, UploadStatus } from '../dnn-bi-install.model'; @Component({ tag: 'dnn-bi-queued-file', @@ -32,7 +32,7 @@ export class DnnBiQueuedFile { private abortController: AbortController; constructor() { - this.installClient = new InstallClient(state.moduleId); + this.installClient = new InstallClient(store.moduleId); } async componentDidLoad() { @@ -40,7 +40,7 @@ export class DnnBiQueuedFile { this.abortController = new AbortController(); await this.installClient.addPackage(this.session.sessionGuid, this.file, this.abortController.signal, ev => this.onProgress(ev)); this.uploadCompleted.emit(UploadStatus.Success); - this.successMessage = state.resx.FileUploadedMessage; + this.successMessage = store.resx.FileUploadedMessage; } catch (err) { if (this.dismissed) { this.uploadCompleted.emit(UploadStatus.Cancelled); @@ -101,7 +101,7 @@ export class DnnBiQueuedFile { {this.successMessage === undefined && (
{state.resx.Name}{state.resx.ApiKey}{state.resx.EncryptionKey}{state.resx.BypassIpAllowList}{state.resx.Action}{store.resx.Name}{store.resx.ApiKey}{store.resx.EncryptionKey}{store.resx.BypassIpAllowList}{store.resx.Action}
- - - + + + @@ -122,7 +122,7 @@ export class DnnBiIpSafelist { return; }} > - {state.resx.Delete} + {store.resx.Delete} @@ -135,14 +135,14 @@ export class DnnBiIpSafelist {
-

{state.resx.IPSafeListConfiguration}

+

{store.resx.IPSafeListConfiguration}

{ @@ -150,7 +150,7 @@ export class DnnBiIpSafelist { return; }} > - {state.resx.Save} + {store.resx.Save}
diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-log-pagination/dnn-bi-log-pagination.scss b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-log-pagination/dnn-bi-log-pagination.scss new file mode 100644 index 00000000000..2d94d545015 --- /dev/null +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-log-pagination/dnn-bi-log-pagination.scss @@ -0,0 +1,26 @@ +:host { + .pagination { + display: flex; + margin: 0; + padding: 1em; + gap: 1em; + li { + flex: 0 0 fit-content; + list-style: none; + margin: 0; + padding: 0; + button { + border: 1px solid transparent; + background: none; + } + &.ellipsis { + color: var(--dnn-color-neutral, #ededee); + } + &.active { + button { + border-bottom-color: var(--dnn-color-neutral, #ededee); + } + } + } + } +} diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-log-pagination/dnn-bi-log-pagination.tsx b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-log-pagination/dnn-bi-log-pagination.tsx new file mode 100644 index 00000000000..1de97cb947f --- /dev/null +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-log-pagination/dnn-bi-log-pagination.tsx @@ -0,0 +1,85 @@ +import { Component, Host, h, Prop, Event, EventEmitter, Fragment } from '@stencil/core'; +import { Pagination } from '../../../../clients/event-log-client'; + +type PageItem = { type: 'page'; index: number; isCurrent: boolean } | { type: 'ellipsis' }; + +const ellipsisItem: PageItem = { type: 'ellipsis' }; +function toPageItem(index: number, current: number): PageItem { + return { + type: 'page', + index: index, + isCurrent: index === current, + }; +} + +function* makePagesIterator(current: number, lastIndex: number) { + const start = Math.max(current - 5, 0); + const end = Math.min(start + 10, lastIndex); + if (start !== 0) { + yield toPageItem(0, current); + if (start === 2) { + yield toPageItem(1, current); + } else if (start !== 1) { + yield ellipsisItem; + } + } + + for (let i = start; i <= end; i++) { + yield toPageItem(i, current); + } + + if (end !== lastIndex) { + if (end === lastIndex - 2) { + yield toPageItem(lastIndex - 1, current); + } else if (end !== lastIndex - 1) { + yield ellipsisItem; + } + + yield toPageItem(lastIndex, current); + } +} + +@Component({ + tag: 'dnn-bi-log-pagination', + styleUrl: 'dnn-bi-log-pagination.scss', + shadow: true, +}) +export class DnnBiLogPagination { + /** The pagination */ + @Prop() public pagination!: Pagination; + + @Event() public pageSelected: EventEmitter; + + private getPages(): PageItem[] { + return Array.from(makePagesIterator(this.pagination.currentPage, this.pagination.pages - 1)); + } + + render() { + return ( + + {this.pagination.pages > 1 && ( +
    + {this.getPages().map(item => ( + <> + {item.type === 'page' && ( +
  1. + +
  2. + )} + {item.type === 'ellipsis' &&
  3. } + + ))} +
+ )} +
+ ); + } +} diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-log-pagination/readme.md b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-log-pagination/readme.md new file mode 100644 index 00000000000..9b55a8b2fb3 --- /dev/null +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-log-pagination/readme.md @@ -0,0 +1,37 @@ +# dnn-bi-log-pagination + + + + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| ------------------------- | --------- | -------------- | ------------ | ----------- | +| `pagination` _(required)_ | -- | The pagination | `Pagination` | `undefined` | + + +## Events + +| Event | Description | Type | +| -------------- | ----------- | --------------------- | +| `pageSelected` | | `CustomEvent` | + + +## Dependencies + +### Used by + + - [dnn-bi-logs](..) + +### Graph +```mermaid +graph TD; + dnn-bi-logs --> dnn-bi-log-pagination + style dnn-bi-log-pagination fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-logs.scss b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-logs.scss index 0fe9917e27c..aa9c2608411 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-logs.scss +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-logs.scss @@ -57,6 +57,12 @@ width: fit-content; } + .filters { + display: flex; + justify-content: end; + gap: 1em; + } + /* Table styling */ .table { width: 100%; @@ -74,11 +80,4 @@ background-color: var(--dnn-color-neutral-dark, #999999); color: var(--dnn-color-neutral, #ededee); } - - /* Utility */ - .clearfix::after { - content: ''; - display: table; - clear: both; - } } diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-logs.tsx b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-logs.tsx index 63cbe732175..85b13cbddf3 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-logs.tsx +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/dnn-bi-logs.tsx @@ -1,7 +1,8 @@ -import { Component, Host, h } from '@stencil/core'; +import { Component, Host, h, State } from '@stencil/core'; +import store from '../../../stores/store'; +import { EventLogClient, Pagination } from '../../../clients/event-log-client'; import { Event } from './dnn-bi-logs.model'; -import state from '../../../stores/store'; -import { EventLogClient } from '../../../clients/event-log-client'; +import { eventLogSeverity, EventLogSeverityInfo } from '../../../enums/EventLogSeverity'; @Component({ tag: 'dnn-bi-logs', @@ -9,26 +10,48 @@ import { EventLogClient } from '../../../clients/event-log-client'; shadow: true, }) export class DnnBiLogs { - private events: Event[] = []; + @State() private events: Event[] = []; + @State() private eventTypes: string[] = []; + @State() private pagination: Pagination; + @State() private severityFilter: EventLogSeverityInfo; + @State() private eventTypeFilter: string; private eventLogClient: EventLogClient; constructor() { - this.eventLogClient = new EventLogClient(state.moduleId); + this.eventLogClient = new EventLogClient(store.moduleId); } async componentWillLoad() { try { - const browseResponse = await this.eventLogClient.browse(); - this.events = browseResponse.Data; + const { data, pagination } = await this.eventLogClient.browse(); + this.events = data; + this.pagination = pagination; + this.eventTypes = await this.eventLogClient.getEventTypes(); } catch (error) { console.error(error); } } - private static formatDate(event: Event): string { + private async setSeverityFilter(key: string) { + this.severityFilter = eventLogSeverity.fromKey(key); + await this.loadPage(0); + } + + private async setEventTypeFilter(eventType: string) { + this.eventTypeFilter = eventType; + await this.loadPage(0); + } + + private async loadPage(pageIndex: number) { + const { data, pagination } = await this.eventLogClient.browse(pageIndex, this.severityFilter, this.eventTypeFilter); + this.events = data; + this.pagination = pagination; + } + + private static formatDate(date: Date): string { const formatter = new Intl.DateTimeFormat(undefined, { dateStyle: 'long', timeStyle: 'medium' }); - return formatter.format(event.date); + return formatter.format(date); } render() { @@ -38,22 +61,39 @@ export class DnnBiLogs {
-

{state.resx.Events}

+

{store.resx.Events}

+
+ this.setSeverityFilter(e.detail).catch(console.error)}> + + + + + + + this.setEventTypeFilter(e.detail).catch(console.error)}> + + {this.eventTypes.map(eventType => ( + + ))} + +
{state.resx.Name}{state.resx.IPAddress}{state.resx.Action}{store.resx.Name}{store.resx.IPAddress}{store.resx.Action}
- - - - + + + + {this.events.map(event => ( - + @@ -61,6 +101,7 @@ export class DnnBiLogs { ))}
{state.resx.Date}{state.resx.Severity}{state.resx.Type}{state.resx.Message}{store.resx.Date}{store.resx.Severity}{store.resx.Type}{store.resx.Message}
{DnnBiLogs.formatDate(event)} + + {event.severity.localizedName} {event.type} {event.message}
+ this.loadPage(e.detail).catch(console.error)} />
diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/readme.md b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/readme.md index 78ef6100d15..deed93bf1d8 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/readme.md +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/components/tabs/dnn-bi-logs/readme.md @@ -9,9 +9,17 @@ - [dnn-bulk-install](../../dnn-bulk-install) +### Depends on + +- dnn-select +- [dnn-bi-log-pagination](dnn-bi-log-pagination) + ### Graph ```mermaid graph TD; + dnn-bi-logs --> dnn-select + dnn-bi-logs --> dnn-bi-log-pagination + dnn-select --> dnn-fieldset dnn-bulk-install --> dnn-bi-logs style dnn-bi-logs fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/enums/EventLogSeverity.ts b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/enums/EventLogSeverity.ts index 526b7203345..acd49e37fd4 100644 --- a/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/enums/EventLogSeverity.ts +++ b/DNN Platform/Modules/BulkInstall/BulkInstall.Web/src/enums/EventLogSeverity.ts @@ -12,6 +12,21 @@ export class EventLogSeverity { this.alert = new EventLogSeverityInfo('Alert'); this.critical = new EventLogSeverityInfo('Critical'); } + + public fromKey(eventLogSeverityKey: string) { + switch (eventLogSeverityKey) { + case this.info.eventLogSeverityKey: + return this.info; + case this.warning.eventLogSeverityKey: + return this.warning; + case this.alert.eventLogSeverityKey: + return this.alert; + case this.critical.eventLogSeverityKey: + return this.critical; + default: + throw new Error(`Invalid EventLogSeverity key: ${eventLogSeverityKey}`); + } + } } export class EventLogSeverityInfo { diff --git a/DNN Platform/Modules/BulkInstall/Components/WebAPI/EventLogController.cs b/DNN Platform/Modules/BulkInstall/Components/WebAPI/EventLogController.cs index aa1c8514922..dbc4f2b81fa 100644 --- a/DNN Platform/Modules/BulkInstall/Components/WebAPI/EventLogController.cs +++ b/DNN Platform/Modules/BulkInstall/Components/WebAPI/EventLogController.cs @@ -29,77 +29,19 @@ public class EventLogController(EventLogManager eventLogManager) : DnnApiControl /// The 0-based page index. /// The page size. /// An event type to filter by, or . - /// A to filter by, or -1. + /// A to filter by, or . /// A response with a list of and pagination data. [HttpGet] - public HttpResponseMessage Browse(int pageIndex = 0, int pageSize = 30, string eventType = null, int severity = -1) + public HttpResponseMessage Browse(int pageIndex = 0, int pageSize = 30, string eventType = null, EventLogSeverity? severity = null) { - EventLogSeverity? actualSeverity = null; - - // Is there a severity set? - if (severity >= 0) - { - actualSeverity = (EventLogSeverity)severity; - } - // Get event logs. - IEnumerable eventLogs = this.eventLogManager.Browse(pageIndex, pageSize, eventType, actualSeverity); + IEnumerable eventLogs = this.eventLogManager.Browse(pageIndex, pageSize, eventType, severity); // Work out pagination details. - int rowCount = this.eventLogManager.BrowseCount(pageIndex, pageSize, eventType, actualSeverity); + int rowCount = this.eventLogManager.BrowseCount(pageIndex, pageSize, eventType, severity); int pageCount = (int)Math.Ceiling(rowCount / (double)pageSize); - // Build navigation. - Dictionary navigation = new Dictionary(); - - // Parameters passed in not changed by pagination. - string fixedParams = string.Empty; - - // Page size. - if (pageSize != 30) - { - fixedParams += $"pageSize={pageSize}"; - } - - // Event type. - if (eventType != null) - { - fixedParams += $"eventType={eventType}"; - } - - // Severity. - if (severity != -1) - { - fixedParams += $"eventType={severity}"; - } - - // Is there a next page? - if (pageIndex < pageCount) - { - string nextLink = $"Browse?pageIndex={pageIndex + 1}"; - - if (!string.IsNullOrEmpty(fixedParams)) - { - nextLink = $"{nextLink}&{fixedParams}"; - } - - navigation.Add("Next", nextLink); - } - - // Is there a previous page? - if (pageIndex > 0) - { - string prevLink = $"Browse?pageIndex={pageIndex - 1}"; - - if (!string.IsNullOrEmpty(fixedParams)) - { - prevLink = $"{prevLink}&{fixedParams}"; - } - - navigation.Add("Previous", prevLink); - } - - var pagination = new { Records = rowCount, Pages = pageCount, CurrentPage = pageIndex, Navigation = navigation, }; + var pagination = new { Pages = pageCount, CurrentPage = pageIndex, }; return this.Request.CreateResponse(HttpStatusCode.OK, new { Data = eventLogs, Pagination = pagination, }); }