From 6254efa0849bd6d5a8f1f3b31d2271c421970c24 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Fri, 5 Jan 2024 12:54:35 +0100 Subject: [PATCH] add modal, refine table,refactor, fix lint --- .../admin-notify-dashboard.module.ts | 4 +- .../admin-notify-detail-modal.component.html | 14 +++ .../admin-notify-detail-modal.component.scss | 0 ...dmin-notify-detail-modal.component.spec.ts | 23 ++++ .../admin-notify-detail-modal.component.ts | 32 ++++++ .../admin-notify-search-result.component.html | 19 ++-- .../admin-notify-search-result.component.ts | 54 ++++++++- .../models/admin-notify-message.model.ts | 31 ++++- .../object-collection.component.html | 31 +++-- .../listable-object.decorator.ts | 2 +- .../tabulatable-objects.decorator.ts | 106 ++---------------- ...ulatable-result-list-elements.component.ts | 4 +- .../object-table/object-table.component.html | 3 +- .../object-table/object-table.component.ts | 8 -- 14 files changed, 188 insertions(+), 143 deletions(-) create mode 100644 src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.html create mode 100644 src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.scss create mode 100644 src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.spec.ts create mode 100644 src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.ts diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-dashboard.module.ts b/src/app/admin/admin-notify-dashboard/admin-notify-dashboard.module.ts index 06747dd2d5..23200bb9fc 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-dashboard.module.ts +++ b/src/app/admin/admin-notify-dashboard/admin-notify-dashboard.module.ts @@ -12,6 +12,7 @@ import { AdminNotifySearchResultComponent } from './admin-notify-search-result/a import { AdminNotifyOutgoingComponent } from './admin-notify-logs/admin-notify-outgoing/admin-notify-outgoing.component'; +import { AdminNotifyDetailModalComponent } from './admin-notify-detail-modal/admin-notify-detail-modal.component'; @NgModule({ @@ -28,7 +29,8 @@ import { AdminNotifyMetricsComponent, AdminNotifyIncomingComponent, AdminNotifyOutgoingComponent, - AdminNotifySearchResultComponent + AdminNotifySearchResultComponent, + AdminNotifyDetailModalComponent ] }) export class AdminNotifyDashboardModule { diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.html b/src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.html new file mode 100644 index 0000000000..57e1b40c1d --- /dev/null +++ b/src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.html @@ -0,0 +1,14 @@ + + diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.scss b/src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.spec.ts b/src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.spec.ts new file mode 100644 index 0000000000..7ac979050c --- /dev/null +++ b/src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdminNotifyDetailModalComponent } from './admin-notify-detail-modal.component'; + +describe('AdminNotifyDetailModalComponent', () => { + let component: AdminNotifyDetailModalComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AdminNotifyDetailModalComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AdminNotifyDetailModalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.ts b/src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.ts new file mode 100644 index 0000000000..3227e52420 --- /dev/null +++ b/src/app/admin/admin-notify-dashboard/admin-notify-detail-modal/admin-notify-detail-modal.component.ts @@ -0,0 +1,32 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { AdminNotifyMessage } from '../models/admin-notify-message.model'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + selector: 'ds-admin-notify-detail-modal', + templateUrl: './admin-notify-detail-modal.component.html', + styleUrls: ['./admin-notify-detail-modal.component.scss'] +}) +export class AdminNotifyDetailModalComponent { + @Input() notifyMessage: AdminNotifyMessage; + @Input() notifyMessageKeys: string[]; + + /** + * An event fired when the modal is closed + */ + @Output() + response = new EventEmitter(); + + + constructor(protected activeModal: NgbActiveModal) { + } + + + /** + * Close the modal and set the response to true so RootComponent knows the modal was closed + */ + closeModal() { + this.activeModal.close(); + this.response.emit(true); + } +} diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.html b/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.html index 89b2285422..80c7faa086 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.html +++ b/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.html @@ -1,22 +1,22 @@ -
-
- +
+
- + + +
TimestampSourceOrigin Target Type StatusAction
-
{{message.queueTimeout}}
+
{{message.queueTimeout}}
-
{{message.source}}
+
{{message.origin}}
{{message.target}}
@@ -27,8 +27,13 @@
{{message.queueStatusLabel}}
+
+ + +
+
-
diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.ts b/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.ts index 93eca302c1..f0c9e3f7d8 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.ts +++ b/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { AdminNotifySearchResult } from '../models/admin-notify-message-search-result.model'; import { ViewMode } from '../../../core/shared/view-mode.model'; import { Context } from '../../../core/shared/context.model'; @@ -10,16 +10,60 @@ import { TabulatableResultListElementsComponent } from '../../../shared/object-list/search-result-list-element/tabulatable-search-result/tabulatable-result-list-elements.component'; import { PaginatedList } from '../../../core/data/paginated-list.model'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { TruncatableService } from '../../../shared/truncatable/truncatable.service'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { APP_CONFIG, AppConfig } from '../../../../config/app-config.interface'; +import { AdminNotifyDetailModalComponent } from '../admin-notify-detail-modal/admin-notify-detail-modal.component'; -@tabulatableObjectsComponent(AdminNotifySearchResult, ViewMode.Table, Context.CoarNotify) +@tabulatableObjectsComponent(PaginatedList, ViewMode.Table, Context.CoarNotify) @Component({ selector: 'ds-admin-notify-search-result', templateUrl: './admin-notify-search-result.component.html', styleUrls: ['./admin-notify-search-result.component.scss'] }) -export class AdminNotifySearchResultComponent extends TabulatableResultListElementsComponent, AdminNotifyMessage> implements OnInit{ +export class AdminNotifySearchResultComponent extends TabulatableResultListElementsComponent, AdminNotifySearchResult> implements OnInit{ public notifyMessages: AdminNotifyMessage[]; - ngOnInit() { - this.notifyMessages = this.objects.page.map(object => object.indexableObject); + + private queueStatusMap = { + QUEUE_STATUS_PROCESSED: 'Processed', + QUEUE_STATUS_FAILED: 'Failed', + QUEUE_STATUS_UNMAPPED_ACTION: 'Unmapped action', + }; + + constructor(private modalService: NgbModal, + protected truncatableService: TruncatableService, + public dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig?: AppConfig) { + super(truncatableService, dsoNameService, appConfig); } + + /** + * Map messages on init for readable representation + */ + ngOnInit() { + this.notifyMessages = this.objects.page.map(object => { + const indexableObject = object.indexableObject; + indexableObject.coarNotifyType = indexableObject.coarNotifyType.split(':')[1]; + indexableObject.queueStatusLabel = this.queueStatusMap[indexableObject.queueStatusLabel]; + return indexableObject; + }); + } + + /** + * Open modal for details visualization + * @param message the message to be displayed + */ + openDetailModal(message: AdminNotifyMessage) { + const modalRef = this.modalService.open(AdminNotifyDetailModalComponent); + const messageKeys = Object.keys(message); + const keysToRead = []; + messageKeys.forEach((key) => { + if (typeof message[key] !== 'object') { + keysToRead.push(key); + } + }); + modalRef.componentInstance.notifyMessage = message; + modalRef.componentInstance.notifyMessageKeys = keysToRead; + } } diff --git a/src/app/admin/admin-notify-dashboard/models/admin-notify-message.model.ts b/src/app/admin/admin-notify-dashboard/models/admin-notify-message.model.ts index be861861b4..a463de483a 100644 --- a/src/app/admin/admin-notify-dashboard/models/admin-notify-message.model.ts +++ b/src/app/admin/admin-notify-dashboard/models/admin-notify-message.model.ts @@ -26,11 +26,35 @@ export class AdminNotifyMessage extends DSpaceObject { @autoserialize coarNotifyType: string; + /** + * The type of the activity + */ + @autoserialize + activityStreamType: string; + + /** + * The object the message reply to + */ + @autoserialize + inReplyTo: string; + + /** + * The attempts of the queue + */ + @autoserialize + queueAttempts: number; + + /** + * Timestamp of the last queue attempt + */ + @autoserialize + queueLastStartTime: string; + /** * The type of the activity stream */ @autoserialize - source: number; + origin: number; /** * The type of the activity stream @@ -56,11 +80,6 @@ export class AdminNotifyMessage extends DSpaceObject { @autoserialize queueStatus: number; - /** - * The status of the queue - */ - @autoserialize - indexableObject: AdminNotifyMessage; @deserialize _links: { diff --git a/src/app/shared/object-collection/object-collection.component.html b/src/app/shared/object-collection/object-collection.component.html index 45b82dd859..179a70bfd7 100644 --- a/src/app/shared/object-collection/object-collection.component.html +++ b/src/app/shared/object-collection/object-collection.component.html @@ -58,20 +58,19 @@ - + diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object.decorator.ts b/src/app/shared/object-collection/shared/listable-object/listable-object.decorator.ts index 470bcfcdaf..f56e983dff 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object.decorator.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object.decorator.ts @@ -34,7 +34,7 @@ export const DEFAULT_THEME = '*'; * - { level: 1, relevancy: 1 } is less relevant than { level: 2, relevancy: 0 } * - { level: 1, relevancy: 1 } is more relevant than null */ -class MatchRelevancy { +export class MatchRelevancy { constructor(public match: any, public level: number, public relevancy: number) { diff --git a/src/app/shared/object-collection/shared/tabulatable-objects/tabulatable-objects.decorator.ts b/src/app/shared/object-collection/shared/tabulatable-objects/tabulatable-objects.decorator.ts index 9ab9e7c53d..bfe57149da 100644 --- a/src/app/shared/object-collection/shared/tabulatable-objects/tabulatable-objects.decorator.ts +++ b/src/app/shared/object-collection/shared/tabulatable-objects/tabulatable-objects.decorator.ts @@ -1,70 +1,16 @@ import { ViewMode } from '../../../../core/shared/view-mode.model'; import { Context } from '../../../../core/shared/context.model'; -import { hasNoValue, hasValue, isNotEmpty } from '../../../empty.util'; +import { hasNoValue, hasValue } from '../../../empty.util'; import { GenericConstructor } from '../../../../core/shared/generic-constructor'; import { ListableObject } from '../listable-object.model'; -import { environment } from '../../../../../environments/environment'; -import { ThemeConfig } from '../../../../../config/theme.config'; -import { InjectionToken } from '@angular/core'; +import { + DEFAULT_CONTEXT, + DEFAULT_THEME, + DEFAULT_VIEW_MODE, + MatchRelevancy, resolveTheme +} from '../listable-object/listable-object.decorator'; +import { PaginatedList } from '../../../../core/data/paginated-list.model'; -export const DEFAULT_VIEW_MODE = ViewMode.ListElement; -export const DEFAULT_CONTEXT = Context.Any; -export const DEFAULT_THEME = '*'; - -/** - * A class used to compare two matches and their relevancy to determine which of the two gains priority over the other - * - * "level" represents the index of the first default value that was used to find the match with: - * ViewMode being index 0, Context index 1 and theme index 2. Examples: - * - If a default value was used for context, but not view-mode and theme, the "level" will be 1 - * - If a default value was used for view-mode and context, but not for theme, the "level" will be 0 - * - If no default value was used for any of the fields, the "level" will be 3 - * - * "relevancy" represents the amount of values that didn't require a default value to fall back on. Examples: - * - If a default value was used for theme, but not view-mode and context, the "relevancy" will be 2 - * - If a default value was used for view-mode and context, but not for theme, the "relevancy" will be 1 - * - If a default value was used for all fields, the "relevancy" will be 0 - * - If no default value was used for any of the fields, the "relevancy" will be 3 - * - * To determine which of two MatchRelevancies is the most relevant, we compare "level" and "relevancy" in that order. - * If any of the two is higher than the other, that match is most relevant. Examples: - * - { level: 1, relevancy: 1 } is more relevant than { level: 0, relevancy: 2 } - * - { level: 1, relevancy: 1 } is less relevant than { level: 1, relevancy: 2 } - * - { level: 1, relevancy: 1 } is more relevant than { level: 1, relevancy: 0 } - * - { level: 1, relevancy: 1 } is less relevant than { level: 2, relevancy: 0 } - * - { level: 1, relevancy: 1 } is more relevant than null - */ -class MatchRelevancy { - constructor(public match: any, - public level: number, - public relevancy: number) { - } - - isMoreRelevantThan(otherMatch: MatchRelevancy): boolean { - if (hasNoValue(otherMatch)) { - return true; - } - if (otherMatch.level > this.level) { - return false; - } - if (otherMatch.level === this.level && otherMatch.relevancy > this.relevancy) { - return false; - } - return true; - } - - isLessRelevantThan(otherMatch: MatchRelevancy): boolean { - return !this.isMoreRelevantThan(otherMatch); - } -} - -/** - * Factory to allow us to inject getThemeConfigFor so we can mock it in tests - */ -export const GET_THEME_CONFIG_FOR_FACTORY = new InjectionToken<(str) => ThemeConfig>('getThemeConfigFor', { - providedIn: 'root', - factory: () => getThemeConfigFor -}); const map = new Map(); @@ -75,7 +21,7 @@ const map = new Map(); * @param context The optional context the component represents * @param theme The optional theme for the component */ -export function tabulatableObjectsComponent(objectsType: string | GenericConstructor, viewMode: ViewMode, context: Context = DEFAULT_CONTEXT, theme = DEFAULT_THEME) { +export function tabulatableObjectsComponent(objectsType: string | GenericConstructor>, viewMode: ViewMode, context: Context = DEFAULT_CONTEXT, theme = DEFAULT_THEME) { return function decorator(component: any) { if (hasNoValue(objectsType)) { return; @@ -107,7 +53,7 @@ export function tabulatableObjectsComponent(objectsType: string | GenericConstru export function getTabulatableObjectsComponent(types: (string | GenericConstructor)[], viewMode: ViewMode, context: Context = DEFAULT_CONTEXT, theme: string = DEFAULT_THEME) { let currentBestMatch: MatchRelevancy = null; for (const type of types) { - const typeMap = map.get(type); + const typeMap = map.get(PaginatedList); if (hasValue(typeMap)) { const match = getMatch(typeMap, [viewMode, context, theme], [DEFAULT_VIEW_MODE, DEFAULT_CONTEXT, DEFAULT_THEME]); if (hasNoValue(currentBestMatch) || currentBestMatch.isLessRelevantThan(match)) { @@ -160,35 +106,3 @@ function getMatch(typeMap: Map, keys: any[], defaults: any[]): MatchRe } return null; } - -/** - * Searches for a ThemeConfig by its name; - */ -export const getThemeConfigFor = (themeName: string): ThemeConfig => { - return environment.themes.find(theme => theme.name === themeName); -}; - -/** - * Find a match in the given map for the given theme name, taking theme extension into account - * - * @param contextMap A map of theme names to components - * @param themeName The name of the theme to check - * @param checkedThemeNames The list of theme names that are already checked - */ -export const resolveTheme = (contextMap: Map, themeName: string, checkedThemeNames: string[] = []): any => { - const match = contextMap.get(themeName); - if (hasValue(match)) { - return match; - } else { - const cfg = getThemeConfigFor(themeName); - if (hasValue(cfg) && isNotEmpty(cfg.extends)) { - const nextTheme = cfg.extends; - const nextCheckedThemeNames = [...checkedThemeNames, themeName]; - if (checkedThemeNames.includes(nextTheme)) { - throw new Error('Theme extension cycle detected: ' + [...nextCheckedThemeNames, nextTheme].join(' -> ')); - } else { - return resolveTheme(contextMap, nextTheme, nextCheckedThemeNames); - } - } - } -}; diff --git a/src/app/shared/object-list/search-result-list-element/tabulatable-search-result/tabulatable-result-list-elements.component.ts b/src/app/shared/object-list/search-result-list-element/tabulatable-search-result/tabulatable-result-list-elements.component.ts index ed30e78c73..956f750b9e 100644 --- a/src/app/shared/object-list/search-result-list-element/tabulatable-search-result/tabulatable-result-list-elements.component.ts +++ b/src/app/shared/object-list/search-result-list-element/tabulatable-search-result/tabulatable-result-list-elements.component.ts @@ -2,17 +2,17 @@ import { Component, Inject } from '@angular/core'; import { AbstractTabulatableElementComponent } from '../../../object-collection/shared/objects-collection-tabulatable/objects-collection-tabulatable.component'; -import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { TruncatableService } from '../../../truncatable/truncatable.service'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interface'; import { PaginatedList } from '../../../../core/data/paginated-list.model'; +import { SearchResult } from '../../../search/models/search-result.model'; @Component({ selector: 'ds-search-result-list-element', template: `` }) -export class TabulatableResultListElementsComponent, K extends DSpaceObject> extends AbstractTabulatableElementComponent { +export class TabulatableResultListElementsComponent, K extends SearchResult> extends AbstractTabulatableElementComponent { public constructor(protected truncatableService: TruncatableService, public dsoNameService: DSONameService, @Inject(APP_CONFIG) protected appConfig?: AppConfig) { diff --git a/src/app/shared/object-table/object-table.component.html b/src/app/shared/object-table/object-table.component.html index a4fe8cfc38..fcfa4aa691 100644 --- a/src/app/shared/object-table/object-table.component.html +++ b/src/app/shared/object-table/object-table.component.html @@ -15,9 +15,10 @@ (paginationChange)="onPaginationChange($event)" (prev)="goPrev()" (next)="goNext()" + [retainScrollPosition]="true" >
-
+