From 752cb4d4b04b0ee89cd4e4b74b8b98f449e17907 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Mon, 30 Oct 2023 12:19:44 +0100 Subject: [PATCH 01/38] [CST-11178] show allowed correction types --- src/app/app-routing-paths.ts | 10 ++ src/app/core/core.module.ts | 4 + .../submission/correctiontype-data.service.ts | 89 ++++++++++++++ .../models/correction-type-mode.model.ts | 35 ++++++ .../correction-type-menu.component.html | 7 ++ .../correction-type-menu.component.spec.ts | 116 ++++++++++++++++++ .../correction-type-menu.component.ts | 105 ++++++++++++++++ 7 files changed, 366 insertions(+) create mode 100644 src/app/core/submission/correctiontype-data.service.ts create mode 100644 src/app/core/submission/models/correction-type-mode.model.ts create mode 100644 src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.html create mode 100644 src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.spec.ts create mode 100644 src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts diff --git a/src/app/app-routing-paths.ts b/src/app/app-routing-paths.ts index fe2837c6e3f..8430454d630 100644 --- a/src/app/app-routing-paths.ts +++ b/src/app/app-routing-paths.ts @@ -132,3 +132,13 @@ export const SUBSCRIPTIONS_MODULE_PATH = 'subscriptions'; export function getSubscriptionsModuleRoute() { return `/${SUBSCRIPTIONS_MODULE_PATH}`; } + +export const EDIT_ITEM_PATH = 'edit-items'; +export function getEditItemPageRoute() { + return `/${EDIT_ITEM_PATH}`; +} +export const CORRECTION_TYPE_PATH = 'corrections'; +export function getCorrectionTypePageRoute(itemUuid: string, typeId: string) { + return `/items/${itemUuid}/${CORRECTION_TYPE_PATH}/${typeId}`; +} + diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index e176af7d550..0eeb476d69d 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -185,6 +185,8 @@ import { FlatBrowseDefinition } from './shared/flat-browse-definition.model'; import { ValueListBrowseDefinition } from './shared/value-list-browse-definition.model'; import { NonHierarchicalBrowseDefinition } from './shared/non-hierarchical-browse-definition'; import { BulkAccessConditionOptions } from './config/models/bulk-access-condition-options.model'; +import { CorrectionTypeMode } from './submission/models/correction-type-mode.model'; +import { CorrectionTypeDataService } from './submission/correctiontype-data.service'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -308,6 +310,7 @@ const PROVIDERS = [ OrcidQueueDataService, OrcidHistoryDataService, SupervisionOrderDataService + CorrectionTypeDataService, ]; /** @@ -387,6 +390,7 @@ export const models = Subscription, ItemRequest, BulkAccessConditionOptions + CorrectionTypeMode ]; @NgModule({ diff --git a/src/app/core/submission/correctiontype-data.service.ts b/src/app/core/submission/correctiontype-data.service.ts new file mode 100644 index 00000000000..4d293558f7f --- /dev/null +++ b/src/app/core/submission/correctiontype-data.service.ts @@ -0,0 +1,89 @@ +import { Injectable } from '@angular/core'; + +import { dataService } from '../data/base/data-service.decorator'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { RequestService } from '../data/request.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { IdentifiableDataService } from '../data/base/identifiable-data.service'; +import { SearchDataImpl } from '../data/base/search-data'; +import { CorrectionTypeMode } from './models/correction-type-mode.model'; +import { Observable, map } from 'rxjs'; +import { RemoteData } from '../data/remote-data'; +import { PaginatedList } from '../data/paginated-list.model'; +import { FindListOptions } from '../data/find-list-options.model'; +import { RequestParam } from '../cache/models/request-param.model'; +import { getAllSucceededRemoteDataPayload, getPaginatedListPayload } from '../shared/operators'; + +/** + * A service that provides methods to make REST requests with correctiontypes endpoint. + */ +@Injectable() +@dataService(CorrectionTypeMode.type) +export class CorrectionTypeDataService extends IdentifiableDataService { + protected linkPath = 'correctiontypes'; + protected searchByTopic = 'findByTopic'; + protected searchFindByItem = 'findByItem'; + private searchData: SearchDataImpl; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + ) { + super('correctiontypes', requestService, rdbService, objectCache, halService); + + this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); + } + + /** + * Get the correction type by id + * @param id the id of the correction type + * @param useCachedVersionIfAvailable use the cached version if available + * @param reRequestOnStale re-request on stale + * @returns {Observable>} the correction type + */ + getCorrectionTypeById(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true): Observable> { + return this.findById(id, useCachedVersionIfAvailable, reRequestOnStale); + } + + /** + * Search for the correction types for the item + * @param itemUuid the uuid of the item + * @param useCachedVersionIfAvailable use the cached version if available + * @returns the list of correction types for the item + */ + findByItem(itemUuid: string, useCachedVersionIfAvailable): Observable>> { + const options = new FindListOptions(); + options.searchParams = [new RequestParam('uuid', itemUuid)]; + return this.searchData.searchBy(this.searchFindByItem, options, useCachedVersionIfAvailable); + } + + /** + * Find the correction type for the topic + * @param topic the topic of the correction type to search for + * @param useCachedVersionIfAvailable use the cached version if available + * @param reRequestOnStale re-request on stale + * @returns the correction type for the topic + */ + findByTopic(topic: string, useCachedVersionIfAvailable = true, reRequestOnStale = true): Observable { + const options = new FindListOptions(); + options.searchParams = [ + { + fieldName: 'topic', + fieldValue: topic, + }, + ]; + + return this.searchData.searchBy(this.searchByTopic, options, useCachedVersionIfAvailable, reRequestOnStale).pipe( + getAllSucceededRemoteDataPayload(), + getPaginatedListPayload(), + map((list: CorrectionTypeMode[]) => { + return list[0]; + }) + ); + } +} diff --git a/src/app/core/submission/models/correction-type-mode.model.ts b/src/app/core/submission/models/correction-type-mode.model.ts new file mode 100644 index 00000000000..b9bb033d867 --- /dev/null +++ b/src/app/core/submission/models/correction-type-mode.model.ts @@ -0,0 +1,35 @@ +import { autoserialize, deserialize } from 'cerialize'; +import { typedObject } from '../../cache/builders/build-decorators'; +import { CacheableObject } from '../../cache/cacheable-object.model'; +import { ResourceType } from '../../shared/resource-type'; +import { excludeFromEquals } from '../../utilities/equals.decorators'; +import { HALLink } from '../../shared/hal-link.model'; + +@typedObject +export class CorrectionTypeMode extends CacheableObject { + static type = new ResourceType('correctiontype'); + + /** + * The object type + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + + @autoserialize + id: string; + + @autoserialize + topic: string; + + @autoserialize + discoveryConfiguration: string; + + @autoserialize + creationForm: string; + + @deserialize + _links: { + self: HALLink; + }; +} diff --git a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.html b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.html new file mode 100644 index 00000000000..6504d6ecaf4 --- /dev/null +++ b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.html @@ -0,0 +1,7 @@ + + + diff --git a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.spec.ts b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.spec.ts new file mode 100644 index 00000000000..0f2eb02f601 --- /dev/null +++ b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.spec.ts @@ -0,0 +1,116 @@ +import { Item } from './../../../core/shared/item.model'; +import { DSpaceObject } from './../../../core/shared/dspace-object.model'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CorrectionTypeMenuComponent } from './correction-type-menu.component'; +import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; +import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; +import { createPaginatedList } from '../../testing/utils.test'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateLoaderMock } from '../../mocks/translate-loader.mock'; +import { RouterTestingModule } from '@angular/router/testing'; +import { CorrectionTypeDataService } from '../../../core/submission/correctiontype-data.service'; +import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { By } from '@angular/platform-browser'; +import { CorrectionTypeMode } from '../../../core/submission/models/correction-type-mode.model'; + +describe('CorrectionTypeMenuComponent', () => { + let component: CorrectionTypeMenuComponent; + let fixture: ComponentFixture; + let componentAsAny: any; + + let correctionTypeService: any; + let dso: DSpaceObject; + const notificationService = new NotificationsServiceStub(); + const correctionType: CorrectionTypeMode = Object.assign(new CorrectionTypeMode(), { + id: 'addpersonalpath', + creationForm:'manageRelation', + discoveryConfiguration: 'RELATION.PersonPath.Items', + topic: '/DSPACEUSERS/RELATIONADD/PERSONALPATH' + }); + + const correctionTypeObjRDList$ = createSuccessfulRemoteDataObject$(createPaginatedList([correctionType])); + + beforeEach(async () => { + dso = Object.assign(new Item(), { + id: 'test-item', + _links: { + self: { href: 'test-item-selflink' } + } + }); + + correctionTypeService = jasmine.createSpyObj('CorrectionTypeDataService', { + findByItem: jasmine.createSpy('findByItem') + }); + + await TestBed.configureTestingModule({ + declarations: [ CorrectionTypeMenuComponent ], + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + RouterTestingModule.withRoutes([])], + providers: [ + { provide: CorrectionTypeDataService, useValue: correctionTypeService }, + { provide: 'contextMenuObjectProvider', useValue: dso }, + { provide: 'contextMenuObjectTypeProvider', useValue: DSpaceObjectType.ITEM }, + { provide: NotificationsService, useValue: notificationService } + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(CorrectionTypeMenuComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('when correction types are available', () => { + beforeEach(() => { + correctionTypeService.findByItem.and.returnValue(correctionTypeObjRDList$); + fixture = TestBed.createComponent(CorrectionTypeMenuComponent); + component = fixture.componentInstance; + componentAsAny = fixture.componentInstance; + component.contextMenuObject = dso; + fixture.detectChanges(); + }); + + it('should init properly', () => { + expect(componentAsAny.correctionTypes$.value).toEqual([correctionType]); + }); + + it('should render a button', () => { + const link = fixture.debugElement.query(By.css('button')); + expect(link).not.toBeNull(); + }); + }); + + describe('when is no data are available', () => { + beforeEach(() => { + correctionTypeService.findByItem.and.returnValue(createSuccessfulRemoteDataObject$(createPaginatedList([]))); + fixture = TestBed.createComponent(CorrectionTypeMenuComponent); + component = fixture.componentInstance; + componentAsAny = fixture.componentInstance; + component.contextMenuObject = dso; + fixture.detectChanges(); + }); + + it('should init edit mode properly', () => { + expect(componentAsAny.correctionTypes$.value).toEqual([]); + }); + + it('should render a button', () => { + const link = fixture.debugElement.query(By.css('button')); + expect(link).toBeNull(); + }); + }); +}); diff --git a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts new file mode 100644 index 00000000000..2f1b341b9fe --- /dev/null +++ b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts @@ -0,0 +1,105 @@ +import { getAllSucceededRemoteDataPayload, getPaginatedListPayload } from './../../../core/shared/operators'; +import { CorrectionTypeDataService } from './../../../core/submission/correctiontype-data.service'; +import { DSpaceObject } from './../../../core/shared/dspace-object.model'; +import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; +import { ContextMenuEntryComponent } from '../context-menu-entry.component'; +import { ContextMenuEntryType } from '../context-menu-entry-type'; +import { BehaviorSubject, Observable, Subscription, map, startWith, tap } from 'rxjs'; +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { CorrectionTypeMode } from '../../../core/submission/models/correction-type-mode.model'; +import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { hasValue, isNotEmpty } from '../../empty.util'; +import { rendersContextMenuEntriesForType } from '../context-menu.decorator'; +import { getCorrectionTypePageRoute } from '../../../app-routing-paths'; + +@Component({ + selector: 'ds-correction-type-menu', + templateUrl: './correction-type-menu.component.html', +}) +@rendersContextMenuEntriesForType(DSpaceObjectType.ITEM) +export class CorrectionTypeMenuComponent extends ContextMenuEntryComponent implements OnInit, OnDestroy { + + /** + * The menu entry type + */ + public static menuEntryType: ContextMenuEntryType = ContextMenuEntryType.CorrectionType; + + /** + * A boolean representing if a request operation is pending + * @type {BehaviorSubject} + */ + public processing$ = new BehaviorSubject(false); + + /** + * Reference to NgbModal + */ + public modalRef: NgbModalRef; + + /** + * List of Edit Modes available on this item + * for the current user + */ + private correctionTypes$: BehaviorSubject = new BehaviorSubject([]); + + /** + * Variable to track subscription and unsubscribe it onDestroy + */ + private sub: Subscription; + + constructor( + @Inject('contextMenuObjectProvider') protected injectedContextMenuObject: DSpaceObject, + @Inject('contextMenuObjectTypeProvider') protected injectedContextMenuObjectType: DSpaceObjectType, + private correctionTypeService: CorrectionTypeDataService, + public notificationService: NotificationsService + ) { + super(injectedContextMenuObject, injectedContextMenuObjectType, ContextMenuEntryType.EditSubmission); + } + + ngOnInit(): void { + this.notificationService.claimedProfile.subscribe(() => { + this.getData(); + }); + } + + /** + * Check if edit mode is available + */ + getCorrectionTypes(): Observable { + return this.correctionTypes$; + } + + /** + * Check if edit mode is available + */ + isAvailable(): Observable { + return this.correctionTypes$.asObservable().pipe( + map((type) => isNotEmpty(type) && type.length > 0) + ); + } + + getData(): void { + this.sub = this.correctionTypeService.findByItem(this.contextMenuObject.id, true).pipe( + tap((types) => console.log(types)), + getAllSucceededRemoteDataPayload(), + getPaginatedListPayload(), + startWith([]) + ).subscribe((types: CorrectionTypeMode[]) => { + console.log(types); + this.correctionTypes$.next(types); + }); + } + + getTypeRoute(id: string) { + return getCorrectionTypePageRoute(this.contextMenuObject.id, id); + } + + /** + * Make sure the subscription is unsubscribed from when this component is destroyed + */ + ngOnDestroy(): void { + if (hasValue(this.sub)) { + this.sub.unsubscribe(); + } + } +} From bf67fee89b1d72f65d1f80f50f0cf8dca728dd52 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Mon, 30 Oct 2023 12:21:38 +0100 Subject: [PATCH 02/38] [CST-11178][CST-11179] page to select the target item for "relation" correction suggestion --- src/app/app-routing.module.ts | 5 + .../quality-assurance-event-data.service.ts | 23 +- .../correction-type-menu.component.ts | 33 +-- .../correction-suggestion-page.decorator.ts | 15 ++ .../correction-suggestion-routing.module.ts | 23 ++ .../correction-suggestion.component.html | 3 + .../correction-suggestion.component.scss | 0 .../correction-suggestion.component.spec.ts | 25 ++ .../correction-suggestion.component.ts | 94 +++++++ .../correction-suggestion.module.ts | 39 +++ .../correction-types/correction-type-forms.ts | 3 + ...ge-relation-correction-type.component.html | 54 ++++ ...ge-relation-correction-type.component.scss | 0 ...relation-correction-type.component.spec.ts | 25 ++ ...nage-relation-correction-type.component.ts | 252 ++++++++++++++++++ src/themes/custom/lazy-theme.module.ts | 2 + 16 files changed, 576 insertions(+), 20 deletions(-) create mode 100644 src/app/shared/correction-suggestion/correction-suggestion-page.decorator.ts create mode 100644 src/app/shared/correction-suggestion/correction-suggestion-routing.module.ts create mode 100644 src/app/shared/correction-suggestion/correction-suggestion.component.html create mode 100644 src/app/shared/correction-suggestion/correction-suggestion.component.scss create mode 100644 src/app/shared/correction-suggestion/correction-suggestion.component.spec.ts create mode 100644 src/app/shared/correction-suggestion/correction-suggestion.component.ts create mode 100644 src/app/shared/correction-suggestion/correction-suggestion.module.ts create mode 100644 src/app/shared/correction-suggestion/correction-types/correction-type-forms.ts create mode 100644 src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html create mode 100644 src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.scss create mode 100644 src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.spec.ts create mode 100644 src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index deb68f1ea92..28623e3ce66 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -238,6 +238,11 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone canActivate: [AuthenticatedGuard] }, { path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent }, + { + path: 'items', + loadChildren: () => import('./shared/correction-suggestion/correction-suggestion.module') + .then((m) => m.CorrectionSuggestionModule) + }, ] } ], { diff --git a/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts b/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts index 7f7e68afaab..c2acc7490a3 100644 --- a/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts +++ b/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { find, take } from 'rxjs/operators'; +import { find, switchMap, take } from 'rxjs/operators'; import { ReplaceOperation } from 'fast-json-patch'; import { HALEndpointService } from '../../../shared/hal-endpoint.service'; @@ -200,4 +200,25 @@ export class QualityAssuranceEventDataService extends IdentifiableDataService(requestId); } + + /** + * Perform a post on an endpoint related to correction type + * @param data the data to post + * @returns the RestResponse as an Observable + */ + postData(data: string): Observable> { + const requestId = this.requestService.generateRequestId(); + const href$ = this.getBrowseEndpoint(); + + return href$.pipe( + switchMap((href: string) => { + const request = new PostRequest(requestId, href, data); + if (hasValue(this.responseMsToLive)) { + request.responseMsToLive = this.responseMsToLive; + } + this.requestService.send(request); + return this.rdbService.buildFromRequestUUID(requestId); + }) + ); + } } diff --git a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts index 2f1b341b9fe..51aaafb7e42 100644 --- a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts +++ b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts @@ -4,8 +4,7 @@ import { DSpaceObject } from './../../../core/shared/dspace-object.model'; import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { ContextMenuEntryComponent } from '../context-menu-entry.component'; import { ContextMenuEntryType } from '../context-menu-entry-type'; -import { BehaviorSubject, Observable, Subscription, map, startWith, tap } from 'rxjs'; -import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { BehaviorSubject, Observable, Subscription, map, startWith} from 'rxjs'; import { CorrectionTypeMode } from '../../../core/submission/models/correction-type-mode.model'; import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; import { NotificationsService } from '../../notifications/notifications.service'; @@ -25,17 +24,6 @@ export class CorrectionTypeMenuComponent extends ContextMenuEntryComponent imple */ public static menuEntryType: ContextMenuEntryType = ContextMenuEntryType.CorrectionType; - /** - * A boolean representing if a request operation is pending - * @type {BehaviorSubject} - */ - public processing$ = new BehaviorSubject(false); - - /** - * Reference to NgbModal - */ - public modalRef: NgbModalRef; - /** * List of Edit Modes available on this item * for the current user @@ -51,9 +39,9 @@ export class CorrectionTypeMenuComponent extends ContextMenuEntryComponent imple @Inject('contextMenuObjectProvider') protected injectedContextMenuObject: DSpaceObject, @Inject('contextMenuObjectTypeProvider') protected injectedContextMenuObjectType: DSpaceObjectType, private correctionTypeService: CorrectionTypeDataService, - public notificationService: NotificationsService + public notificationService: NotificationsService, ) { - super(injectedContextMenuObject, injectedContextMenuObjectType, ContextMenuEntryType.EditSubmission); + super(injectedContextMenuObject, injectedContextMenuObjectType, ContextMenuEntryType.CorrectionType); } ngOnInit(): void { @@ -78,19 +66,26 @@ export class CorrectionTypeMenuComponent extends ContextMenuEntryComponent imple ); } + /** + * Get correction types + * useCachedVersionIfAvailable = false to force refreshing the list + */ getData(): void { - this.sub = this.correctionTypeService.findByItem(this.contextMenuObject.id, true).pipe( - tap((types) => console.log(types)), + this.sub = this.correctionTypeService.findByItem(this.contextMenuObject.id, false).pipe( getAllSucceededRemoteDataPayload(), getPaginatedListPayload(), startWith([]) ).subscribe((types: CorrectionTypeMode[]) => { - console.log(types); this.correctionTypes$.next(types); }); } - getTypeRoute(id: string) { + /** + * Get the route to the correction type page + * @param id correction type id + * @returns the route to the correction type page + */ + getTypeRoute(id: string): string { return getCorrectionTypePageRoute(this.contextMenuObject.id, id); } diff --git a/src/app/shared/correction-suggestion/correction-suggestion-page.decorator.ts b/src/app/shared/correction-suggestion/correction-suggestion-page.decorator.ts new file mode 100644 index 00000000000..88f1c9f8025 --- /dev/null +++ b/src/app/shared/correction-suggestion/correction-suggestion-page.decorator.ts @@ -0,0 +1,15 @@ +const pageMap = new Map(); + +export function renderCorrectionFor(creationMode: string) { + return function decorator(component: any) { + if (!component) { + return; + } + pageMap.set(creationMode, component); + }; +} + +export function getCorrectionComponent(creationMode: string) { + return pageMap.get(creationMode); +} + diff --git a/src/app/shared/correction-suggestion/correction-suggestion-routing.module.ts b/src/app/shared/correction-suggestion/correction-suggestion-routing.module.ts new file mode 100644 index 00000000000..e07e4749bbc --- /dev/null +++ b/src/app/shared/correction-suggestion/correction-suggestion-routing.module.ts @@ -0,0 +1,23 @@ +import { ItemBreadcrumbResolver } from './../../core/breadcrumbs/item-breadcrumb.resolver'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { CorrectionSuggestionComponent } from './correction-suggestion.component'; + +const routes: Routes = [ + { + path: ':uuid/corrections/:correctionType', + component: CorrectionSuggestionComponent, + resolve: { + breadcrumb: ItemBreadcrumbResolver, + }, + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + providers: [ + ItemBreadcrumbResolver + ] +}) +export class CorrectionSuggestionRoutingModule { } diff --git a/src/app/shared/correction-suggestion/correction-suggestion.component.html b/src/app/shared/correction-suggestion/correction-suggestion.component.html new file mode 100644 index 00000000000..7cc498504ee --- /dev/null +++ b/src/app/shared/correction-suggestion/correction-suggestion.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/app/shared/correction-suggestion/correction-suggestion.component.scss b/src/app/shared/correction-suggestion/correction-suggestion.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/shared/correction-suggestion/correction-suggestion.component.spec.ts b/src/app/shared/correction-suggestion/correction-suggestion.component.spec.ts new file mode 100644 index 00000000000..c5876712a90 --- /dev/null +++ b/src/app/shared/correction-suggestion/correction-suggestion.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CorrectionSuggestionComponent } from './correction-suggestion.component'; + +describe('CorrectionSuggestionComponent', () => { + let component: CorrectionSuggestionComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ CorrectionSuggestionComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(CorrectionSuggestionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/correction-suggestion/correction-suggestion.component.ts b/src/app/shared/correction-suggestion/correction-suggestion.component.ts new file mode 100644 index 00000000000..2a2772a21dd --- /dev/null +++ b/src/app/shared/correction-suggestion/correction-suggestion.component.ts @@ -0,0 +1,94 @@ +import { getRemoteDataPayload } from './../../core/shared/operators'; +import { CorrectionTypeDataService } from './../../core/submission/correctiontype-data.service'; +import { Component, ComponentFactoryResolver, Injector, OnInit } from '@angular/core'; +import { CorrectionTypeMode } from '../../core/submission/models/correction-type-mode.model'; +import { GenericConstructor } from '../../core/shared/generic-constructor'; +import { getCorrectionComponent } from './correction-suggestion-page.decorator'; +import { ActivatedRoute, Params } from '@angular/router'; +import { hasValue } from '../empty.util'; +import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators'; + +@Component({ + selector: 'ds-correction-suggestion', + templateUrl: './correction-suggestion.component.html', + styleUrls: ['./correction-suggestion.component.scss'] +}) +export class CorrectionSuggestionComponent implements OnInit { + + /** + * The correction type object + */ + public correctionTypeObject: CorrectionTypeMode; + + /** + * The correction type id + */ + private correctionTypeId: string; + + /** + * The creation form + */ + private creationForm: string; + + /** + * The injector for the component + */ + public objectInjector: Injector; + + constructor( + private componentFactoryResolver: ComponentFactoryResolver, + private aroute: ActivatedRoute, + private correctionTypeDataService: CorrectionTypeDataService, + private injector: Injector, + ) { + this.aroute.params.subscribe((params: Params) => { + this.correctionTypeId = params.correctionType; + }); + } + + ngOnInit(): void { + this.initComponent(); + } + + /** + * Initialize the component by fetching the correction type object + * and rendering the correct component based on the creation form + */ + initComponent(): void { + if (hasValue(this.correctionTypeId)) { + this.correctionTypeDataService.getCorrectionTypeById(this.correctionTypeId) + .pipe( + getFirstCompletedRemoteData(), + getRemoteDataPayload(), + ) + .subscribe((correctionType: CorrectionTypeMode) => { + if (hasValue(correctionType)) { + this.correctionTypeObject = correctionType; + this.creationForm = correctionType.creationForm; + this.componentFactoryResolver.resolveComponentFactory(this.getComponent()); + this.injectData(); + } + }); + } + } + + /** + * Inject the data into the component + */ + private injectData(): void { + this.objectInjector = Injector.create({ + providers: [ + { provide: 'correctionTypeObjectProvider', useValue: this.correctionTypeObject, deps: [] }, + ], + parent: this.injector, + }); + } + + /** + * Fetch the component depending on the creation form + * @returns {GenericConstructor} + */ + getComponent(): GenericConstructor { + return getCorrectionComponent(this.creationForm); + } +} diff --git a/src/app/shared/correction-suggestion/correction-suggestion.module.ts b/src/app/shared/correction-suggestion/correction-suggestion.module.ts new file mode 100644 index 00000000000..37204b441e6 --- /dev/null +++ b/src/app/shared/correction-suggestion/correction-suggestion.module.ts @@ -0,0 +1,39 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CorrectionSuggestionComponent } from './correction-suggestion.component'; +import { SharedModule } from '../shared.module'; +import { CorrectionSuggestionRoutingModule } from './correction-suggestion-routing.module'; +import { ManageRelationCorrectionTypeComponent } from './correction-types/manage-relation-correction-type/manage-relation-correction-type.component'; +import { SearchModule } from '../search/search.module'; + +const COMPONENTS = [ + CorrectionSuggestionComponent, + ManageRelationCorrectionTypeComponent, +]; + +const ENTRY_COMPONENTS = [ + ManageRelationCorrectionTypeComponent, +]; + +@NgModule({ + declarations: [ + COMPONENTS, + ], + imports: [ + CommonModule, + CorrectionSuggestionRoutingModule, + SharedModule, + SearchModule + ], + exports: [ + COMPONENTS, + ] +}) +export class CorrectionSuggestionModule { + static withEntryComponents() { + return { + ngModule: CorrectionSuggestionModule, + providers: ENTRY_COMPONENTS.map((component) => ({ provide: component })) + }; + } + } diff --git a/src/app/shared/correction-suggestion/correction-types/correction-type-forms.ts b/src/app/shared/correction-suggestion/correction-types/correction-type-forms.ts new file mode 100644 index 00000000000..9faf8ca18be --- /dev/null +++ b/src/app/shared/correction-suggestion/correction-types/correction-type-forms.ts @@ -0,0 +1,3 @@ +export enum CorrectionTypeForms { + MANAGE_RELATION = 'manageRelation', +} diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html new file mode 100644 index 00000000000..fe3f64e8db7 --- /dev/null +++ b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html @@ -0,0 +1,54 @@ +
+

+ {{ ( 'correction-type.manage-relation.' + (correctionType.topic | lowercase) + '.searchHeader' | translate) }} +

+
+
+
+ + + + + + + +
+ +

{{( 'correction-type.manage-relation.search.notFound' | translate)}}

+
+
+ +
+
+
+ +
+
+ +
+
+
diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.scss b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.spec.ts b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.spec.ts new file mode 100644 index 00000000000..d012163f7b3 --- /dev/null +++ b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ManageRelationCorrectionTypeComponent } from './manage-relation-correction-type.component'; + +describe('ManageRelationCorrectionTypeComponent', () => { + let component: ManageRelationCorrectionTypeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ManageRelationCorrectionTypeComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ManageRelationCorrectionTypeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts new file mode 100644 index 00000000000..5f5d5b9c45e --- /dev/null +++ b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts @@ -0,0 +1,252 @@ +import { NotificationsService } from './../../../notifications/notifications.service'; +import { OpenaireBrokerEventObject } from './../../../../core/openaire/broker/models/openaire-broker-event.model'; +import { getFirstSucceededRemoteDataPayload } from './../../../../core/shared/operators'; +import { ItemDataService } from './../../../../core/data/item-data.service'; +import { OpenaireBrokerEventRestService } from './../../../../core/openaire/broker/events/openaire-broker-event-rest.service'; +import { Context } from './../../../../core/shared/context.model'; +import { CollectionElementLinkType } from './../../../object-collection/collection-element-link.type'; +import { DSpaceObject } from './../../../../core/shared/dspace-object.model'; +import { SearchResult } from './../../../search/models/search-result.model'; +import { RemoteData } from './../../../../core/data/remote-data'; +import { PaginatedList } from './../../../../core/data/paginated-list.model'; +import { PaginatedSearchOptions } from './../../../search/models/paginated-search-options.model'; +import { SelectableListService } from './../../../object-list/selectable-list/selectable-list.service'; +import { SearchService } from './../../../../core/shared/search/search.service'; +import { PaginationComponentOptions } from './../../../pagination/pagination-component-options.model'; +import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; +import { renderCorrectionFor } from '../../correction-suggestion-page.decorator'; +import { CorrectionTypeMode } from '../../../../core/submission/models/correction-type-mode.model'; +import { CorrectionTypeForms } from './../correction-type-forms'; +import { ActivatedRoute, Params, Router } from '@angular/router'; +import { Observable, Subscription, of as observableOf, switchMap } from 'rxjs'; +import { hasValue, isNotEmpty } from '../../../../shared/empty.util'; +import { ListableObject } from '../../../../shared/object-collection/shared/listable-object.model'; +import { Item } from '../../../../core/shared/item.model'; +import { ImportType } from '../../../../openaire/broker/project-entry-import-modal/project-entry-import-modal.component'; + +@Component({ + selector: 'ds-manage-relation-correction-type', + templateUrl: './manage-relation-correction-type.component.html', + styleUrls: ['./manage-relation-correction-type.component.scss'] +}) +@renderCorrectionFor(CorrectionTypeForms.MANAGE_RELATION) +export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy { + + /** + * The correction type object + */ + correctionType: CorrectionTypeMode; + + /** + * The item uuid from the parent object + */ + itemUuid: string; + + /** + * The project title from the parent object + */ + projectTitle = ''; + + /** + * Pagination options + */ + pagination: PaginationComponentOptions; + + /** + * The number of results per page + */ + pageSize = 3; + + /** + * Entities to show in the list + */ + localEntitiesRD$: Observable>>>; + + /** + * Search options to use for fetching projects + */ + searchOptions: PaginatedSearchOptions; + + /** + * The list of subscriptions + */ + protected subs: Subscription[] = []; + + /** + * Information about the data loading status + */ + isLoading$ = observableOf(true); + + /** + * The selected local entity + */ + selectedEntity: ListableObject; + + /** + * The type of link to render in listable elements + */ + linkTypes = CollectionElementLinkType; + + /** + * The context we're currently in (submission) + */ + context = Context.Search; + + /** + * List ID for selecting local entities + */ + entityListId = 'correction-suggestion-manage-relation'; + /** + * List ID for selecting local authorities + */ + authorityListId = 'correction-suggestion-manage-relation-authority'; + + /** + * ImportType enum + */ + importType = ImportType; + + /** + * The type of import the user currently has selected + */ + selectedImportType = ImportType.None; + + constructor( + @Inject('correctionTypeObjectProvider') private correctionTypeObject: CorrectionTypeMode, + public searchService: SearchService, + private selectService: SelectableListService, + private aroute: ActivatedRoute, + private openaireBrokerEventRestService: OpenaireBrokerEventRestService, + private itemService: ItemDataService, + private notificationsService: NotificationsService, + private router: Router + ) { + this.correctionType = correctionTypeObject; + this.aroute.params.subscribe((params: Params) => { + this.itemUuid = params.uuid; + }); + } + + /** + * Get the search results + */ + ngOnInit(): void { + this.pagination = Object.assign(new PaginationComponentOptions(), { id: 'correction-suggestion-manage-relation', pageSize: this.pageSize }); + this.searchOptions = Object.assign(new PaginatedSearchOptions( + { + configuration: this.correctionType.discoveryConfiguration, + scope: this.itemUuid, + pagination: this.pagination + } + )); + + this.localEntitiesRD$ = this.searchService.search(this.searchOptions); + this.subs.push( + this.localEntitiesRD$.subscribe( + () => this.isLoading$ = observableOf(false) + ) + ); + } + + + /** + * Perform a project search by title. + */ + public search(searchTitle): void { + if (isNotEmpty(searchTitle)) { + const filterRegEx = /[:]/g; + this.isLoading$ = observableOf(true); + this.searchOptions = Object.assign(new PaginatedSearchOptions( + { + configuration: this.correctionType.discoveryConfiguration, + query: (searchTitle) ? searchTitle.replace(filterRegEx, '') : searchTitle, + scope: this.itemUuid, + pagination: this.pagination + } + )); + this.localEntitiesRD$ = this.searchService.search(this.searchOptions); + this.subs.push( + this.localEntitiesRD$.subscribe( + () => this.isLoading$ = observableOf(false) + ) + ); + } + } + + /** + * Deselected a local entity + */ + public deselectEntity(): void { + this.selectedEntity = undefined; + if (this.selectedImportType === ImportType.LocalEntity) { + this.selectedImportType = ImportType.None; + } + } + + /** + * Selected a local entity + * @param entity + */ + public selectEntity(entity): void { + this.selectedEntity = entity; + this.selectedImportType = ImportType.LocalEntity; + } + + /** + * Deselect every element from both entity and authority lists + */ + public deselectAllLists(): void { + this.selectService.deselectAll(this.entityListId); + this.selectService.deselectAll(this.authorityListId); + } + + /** + * Perform the action based on the correction type + * by posting the data to the OpenAIRE Broker Event API. + * Data is formatted as follows, in the exact order: + * + * + * + */ + performAction() { + if (hasValue(this.selectedEntity)) { + const selectedItemLink = (this.selectedEntity as SearchResult).indexableObject._links.self.href; + + this.itemService.findById(this.itemUuid).pipe( + getFirstSucceededRemoteDataPayload(), + switchMap((item: Item) => { + console.log(item); + const data: string = item._links.self.href + '\n' + selectedItemLink + '\n' + this.correctionTypeObject._links.self.href; + return this.openaireBrokerEventRestService.postData(data); + }) + ).subscribe((res: RemoteData) => { + if (res.hasSucceeded) { + this.selectedImportType = ImportType.None; + // TODO: show success message based on the type of correction + this.notificationsService.success('Correction suggestion submitted', 'The correction suggestion has been submitted'); + this.deselectAllLists(); + this.back(); + } else { + this.notificationsService.error('Error submitting correction suggestion', 'The correction suggestion could not be submitted'); + } + }); + } + } + + /** + * Navigate back to the previous page + */ + back() { + this.router.navigate(['../'], { relativeTo: this.aroute }); + } + + /** + * Unsubscribe from all subscriptions. + */ + ngOnDestroy(): void { + this.deselectAllLists(); + this.subs + .filter((sub) => hasValue(sub)) + .forEach((sub) => sub.unsubscribe()); + } +} diff --git a/src/themes/custom/lazy-theme.module.ts b/src/themes/custom/lazy-theme.module.ts index edb3f5478c9..bff10cdd5a3 100644 --- a/src/themes/custom/lazy-theme.module.ts +++ b/src/themes/custom/lazy-theme.module.ts @@ -1,3 +1,4 @@ +import { CorrectionSuggestionModule } from './../../app/shared/correction-suggestion/correction-suggestion.module'; import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AdminRegistriesModule } from '../../app/admin/admin-registries/admin-registries.module'; @@ -299,6 +300,7 @@ const DECLARATIONS = [ NgxGalleryModule, FormModule, RequestCopyModule, + CorrectionSuggestionModule, ], declarations: DECLARATIONS, exports: [ From d49185e449faeacf32723330a488d4300737873a Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Mon, 30 Oct 2023 12:23:01 +0100 Subject: [PATCH 03/38] [CST-11178][CST-11179] POST action of correction suggestion --- .../quality-assurance-event-data.service.ts | 3 ++- .../correction-suggestion.component.ts | 2 +- ...nage-relation-correction-type.component.ts | 23 ++++++++++--------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts b/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts index c2acc7490a3..ae1538f7bb2 100644 --- a/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts +++ b/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts @@ -25,6 +25,7 @@ import { SearchData, SearchDataImpl } from '../../../data/base/search-data'; import { DefaultChangeAnalyzer } from '../../../data/default-change-analyzer.service'; import { hasValue } from '../../../../shared/empty.util'; import { DeleteByIDRequest, PostRequest } from '../../../data/request.models'; +import { HttpHeaders } from '@angular/common/http'; /** * The service handling all Quality Assurance topic REST requests. @@ -212,7 +213,7 @@ export class QualityAssuranceEventDataService extends IdentifiableDataService { - const request = new PostRequest(requestId, href, data); + const request = new PostRequest(requestId, href, data, { headers: new HttpHeaders().set('Content-Type', 'text/uri-list') }); if (hasValue(this.responseMsToLive)) { request.responseMsToLive = this.responseMsToLive; } diff --git a/src/app/shared/correction-suggestion/correction-suggestion.component.ts b/src/app/shared/correction-suggestion/correction-suggestion.component.ts index 2a2772a21dd..dcf379bd771 100644 --- a/src/app/shared/correction-suggestion/correction-suggestion.component.ts +++ b/src/app/shared/correction-suggestion/correction-suggestion.component.ts @@ -6,7 +6,7 @@ import { GenericConstructor } from '../../core/shared/generic-constructor'; import { getCorrectionComponent } from './correction-suggestion-page.decorator'; import { ActivatedRoute, Params } from '@angular/router'; import { hasValue } from '../empty.util'; -import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators'; +import { getFirstCompletedRemoteData } from '../../core/shared/operators'; @Component({ selector: 'ds-correction-suggestion', diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts index 5f5d5b9c45e..3ad300a2a65 100644 --- a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts +++ b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts @@ -1,6 +1,6 @@ import { NotificationsService } from './../../../notifications/notifications.service'; import { OpenaireBrokerEventObject } from './../../../../core/openaire/broker/models/openaire-broker-event.model'; -import { getFirstSucceededRemoteDataPayload } from './../../../../core/shared/operators'; +import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from './../../../../core/shared/operators'; import { ItemDataService } from './../../../../core/data/item-data.service'; import { OpenaireBrokerEventRestService } from './../../../../core/openaire/broker/events/openaire-broker-event-rest.service'; import { Context } from './../../../../core/shared/context.model'; @@ -23,6 +23,7 @@ import { hasValue, isNotEmpty } from '../../../../shared/empty.util'; import { ListableObject } from '../../../../shared/object-collection/shared/listable-object.model'; import { Item } from '../../../../core/shared/item.model'; import { ImportType } from '../../../../openaire/broker/project-entry-import-modal/project-entry-import-modal.component'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'ds-manage-relation-correction-type', @@ -119,7 +120,8 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy private openaireBrokerEventRestService: OpenaireBrokerEventRestService, private itemService: ItemDataService, private notificationsService: NotificationsService, - private router: Router + private router: Router, + private translate: TranslateService ) { this.correctionType = correctionTypeObject; this.aroute.params.subscribe((params: Params) => { @@ -134,8 +136,8 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy this.pagination = Object.assign(new PaginationComponentOptions(), { id: 'correction-suggestion-manage-relation', pageSize: this.pageSize }); this.searchOptions = Object.assign(new PaginatedSearchOptions( { - configuration: this.correctionType.discoveryConfiguration, - scope: this.itemUuid, + configuration: 'default', //this.correctionType.discoveryConfiguration, + scope: null, pagination: this.pagination } )); @@ -148,7 +150,6 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy ); } - /** * Perform a project search by title. */ @@ -215,19 +216,19 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy this.itemService.findById(this.itemUuid).pipe( getFirstSucceededRemoteDataPayload(), switchMap((item: Item) => { - console.log(item); - const data: string = item._links.self.href + '\n' + selectedItemLink + '\n' + this.correctionTypeObject._links.self.href; + const data: string = this.correctionTypeObject._links.self.href + '\n' + item._links.self.href + '\n' + selectedItemLink; return this.openaireBrokerEventRestService.postData(data); - }) + }), + getFirstCompletedRemoteData() ).subscribe((res: RemoteData) => { if (res.hasSucceeded) { this.selectedImportType = ImportType.None; - // TODO: show success message based on the type of correction - this.notificationsService.success('Correction suggestion submitted', 'The correction suggestion has been submitted'); + const message = 'correction-type.manage-relation.' + this.correctionTypeObject.id + '.notification.success'; + this.notificationsService.success(this.translate.instant(message)); this.deselectAllLists(); this.back(); } else { - this.notificationsService.error('Error submitting correction suggestion', 'The correction suggestion could not be submitted'); + this.notificationsService.error(this.translate.instant('correction-type.manage-relation.action.notification.error')); } }); } From eeefd867d471e1b1d7750695e208c4ec4eb0a1b9 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Mon, 30 Oct 2023 12:24:10 +0100 Subject: [PATCH 04/38] [CST-11179][CST-11178] Fixes --- .../submission/correctiontype-data.service.ts | 18 +- .../models/correction-type-mode.model.ts | 2 +- .../correction-type-menu.component.spec.ts | 4 +- .../correction-type-menu.component.ts | 8 +- .../correction-suggestion.component.spec.ts | 59 ++++++- .../correction-suggestion.component.ts | 12 +- ...ge-relation-correction-type.component.html | 4 +- ...relation-correction-type.component.spec.ts | 154 +++++++++++++++++- ...nage-relation-correction-type.component.ts | 20 +-- 9 files changed, 240 insertions(+), 41 deletions(-) diff --git a/src/app/core/submission/correctiontype-data.service.ts b/src/app/core/submission/correctiontype-data.service.ts index 4d293558f7f..d9675894d32 100644 --- a/src/app/core/submission/correctiontype-data.service.ts +++ b/src/app/core/submission/correctiontype-data.service.ts @@ -8,7 +8,7 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv import { ObjectCacheService } from '../cache/object-cache.service'; import { IdentifiableDataService } from '../data/base/identifiable-data.service'; import { SearchDataImpl } from '../data/base/search-data'; -import { CorrectionTypeMode } from './models/correction-type-mode.model'; +import { CorrectionType } from './models/correction-type-mode.model'; import { Observable, map } from 'rxjs'; import { RemoteData } from '../data/remote-data'; import { PaginatedList } from '../data/paginated-list.model'; @@ -20,12 +20,12 @@ import { getAllSucceededRemoteDataPayload, getPaginatedListPayload } from '../sh * A service that provides methods to make REST requests with correctiontypes endpoint. */ @Injectable() -@dataService(CorrectionTypeMode.type) -export class CorrectionTypeDataService extends IdentifiableDataService { +@dataService(CorrectionType.type) +export class CorrectionTypeDataService extends IdentifiableDataService { protected linkPath = 'correctiontypes'; protected searchByTopic = 'findByTopic'; protected searchFindByItem = 'findByItem'; - private searchData: SearchDataImpl; + private searchData: SearchDataImpl; constructor( protected requestService: RequestService, @@ -44,9 +44,9 @@ export class CorrectionTypeDataService extends IdentifiableDataService>} the correction type + * @returns {Observable>} the correction type */ - getCorrectionTypeById(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true): Observable> { + getCorrectionTypeById(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true): Observable> { return this.findById(id, useCachedVersionIfAvailable, reRequestOnStale); } @@ -56,7 +56,7 @@ export class CorrectionTypeDataService extends IdentifiableDataService>> { + findByItem(itemUuid: string, useCachedVersionIfAvailable): Observable>> { const options = new FindListOptions(); options.searchParams = [new RequestParam('uuid', itemUuid)]; return this.searchData.searchBy(this.searchFindByItem, options, useCachedVersionIfAvailable); @@ -69,7 +69,7 @@ export class CorrectionTypeDataService extends IdentifiableDataService { + findByTopic(topic: string, useCachedVersionIfAvailable = true, reRequestOnStale = true): Observable { const options = new FindListOptions(); options.searchParams = [ { @@ -81,7 +81,7 @@ export class CorrectionTypeDataService extends IdentifiableDataService { + map((list: CorrectionType[]) => { return list[0]; }) ); diff --git a/src/app/core/submission/models/correction-type-mode.model.ts b/src/app/core/submission/models/correction-type-mode.model.ts index b9bb033d867..4daac538933 100644 --- a/src/app/core/submission/models/correction-type-mode.model.ts +++ b/src/app/core/submission/models/correction-type-mode.model.ts @@ -6,7 +6,7 @@ import { excludeFromEquals } from '../../utilities/equals.decorators'; import { HALLink } from '../../shared/hal-link.model'; @typedObject -export class CorrectionTypeMode extends CacheableObject { +export class CorrectionType extends CacheableObject { static type = new ResourceType('correctiontype'); /** diff --git a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.spec.ts b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.spec.ts index 0f2eb02f601..a6713a12a8c 100644 --- a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.spec.ts +++ b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.spec.ts @@ -13,7 +13,7 @@ import { CorrectionTypeDataService } from '../../../core/submission/correctionty import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; import { NotificationsService } from '../../notifications/notifications.service'; import { By } from '@angular/platform-browser'; -import { CorrectionTypeMode } from '../../../core/submission/models/correction-type-mode.model'; +import { CorrectionType } from '../../../core/submission/models/correction-type-mode.model'; describe('CorrectionTypeMenuComponent', () => { let component: CorrectionTypeMenuComponent; @@ -23,7 +23,7 @@ describe('CorrectionTypeMenuComponent', () => { let correctionTypeService: any; let dso: DSpaceObject; const notificationService = new NotificationsServiceStub(); - const correctionType: CorrectionTypeMode = Object.assign(new CorrectionTypeMode(), { + const correctionType: CorrectionType = Object.assign(new CorrectionType(), { id: 'addpersonalpath', creationForm:'manageRelation', discoveryConfiguration: 'RELATION.PersonPath.Items', diff --git a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts index 51aaafb7e42..ac209757b7a 100644 --- a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts +++ b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts @@ -5,7 +5,7 @@ import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { ContextMenuEntryComponent } from '../context-menu-entry.component'; import { ContextMenuEntryType } from '../context-menu-entry-type'; import { BehaviorSubject, Observable, Subscription, map, startWith} from 'rxjs'; -import { CorrectionTypeMode } from '../../../core/submission/models/correction-type-mode.model'; +import { CorrectionType } from '../../../core/submission/models/correction-type-mode.model'; import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; import { NotificationsService } from '../../notifications/notifications.service'; import { hasValue, isNotEmpty } from '../../empty.util'; @@ -28,7 +28,7 @@ export class CorrectionTypeMenuComponent extends ContextMenuEntryComponent imple * List of Edit Modes available on this item * for the current user */ - private correctionTypes$: BehaviorSubject = new BehaviorSubject([]); + private correctionTypes$: BehaviorSubject = new BehaviorSubject([]); /** * Variable to track subscription and unsubscribe it onDestroy @@ -53,7 +53,7 @@ export class CorrectionTypeMenuComponent extends ContextMenuEntryComponent imple /** * Check if edit mode is available */ - getCorrectionTypes(): Observable { + getCorrectionTypes(): Observable { return this.correctionTypes$; } @@ -75,7 +75,7 @@ export class CorrectionTypeMenuComponent extends ContextMenuEntryComponent imple getAllSucceededRemoteDataPayload(), getPaginatedListPayload(), startWith([]) - ).subscribe((types: CorrectionTypeMode[]) => { + ).subscribe((types: CorrectionType[]) => { this.correctionTypes$.next(types); }); } diff --git a/src/app/shared/correction-suggestion/correction-suggestion.component.spec.ts b/src/app/shared/correction-suggestion/correction-suggestion.component.spec.ts index c5876712a90..cdb793f7ea8 100644 --- a/src/app/shared/correction-suggestion/correction-suggestion.component.spec.ts +++ b/src/app/shared/correction-suggestion/correction-suggestion.component.spec.ts @@ -1,14 +1,61 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { CorrectionSuggestionComponent } from './correction-suggestion.component'; +import { ActivatedRoute } from '@angular/router'; +import { CorrectionTypeDataService } from 'src/app/core/submission/correctiontype-data.service'; +import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; +import { CommonModule } from '@angular/common'; +import { BrowserModule } from '@angular/platform-browser'; +import { TranslateModule } from '@ngx-translate/core'; +import { RouterTestingModule } from '@angular/router/testing'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('CorrectionSuggestionComponent', () => { let component: CorrectionSuggestionComponent; let fixture: ComponentFixture; + const correctionTypeMock = { + id: 'addpersonalpath', + topic: '/DSPACEUSERS/RELATIONADD/PERSONALPATH', + discoveryConfiguration: 'RELATION.PersonPath.Items', + creationForm: 'manageRelation', + type: 'correctiontype', + _links: { + self: { + href: 'https://rest.api/discover/configurations/addpersonalpath', + }, + }, + }; + + const correctionTypeMockRD$ = createSuccessfulRemoteDataObject$(correctionTypeMock); + + const mockActivatedRoute = { + snapshot:{ + params: { + correctionType: 'addpersonalpath' + } + } + }; + + let correctionTypeDataService: any; + beforeEach(async () => { + correctionTypeDataService = jasmine.createSpyObj('correctionTypeDataService', { + getCorrectionTypeById: jasmine.createSpy('getCorrectionTypeById') + }); + await TestBed.configureTestingModule({ - declarations: [ CorrectionSuggestionComponent ] + declarations: [ CorrectionSuggestionComponent ], + imports: [ + CommonModule, + BrowserModule, + TranslateModule.forRoot(), + RouterTestingModule.withRoutes([]), + ], + providers: [ + { provide: ActivatedRoute, useValue: mockActivatedRoute }, + { provide: CorrectionTypeDataService, useValue: correctionTypeDataService }, + ], + schemas: [NO_ERRORS_SCHEMA] }) .compileComponents(); }); @@ -16,10 +63,18 @@ describe('CorrectionSuggestionComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(CorrectionSuggestionComponent); component = fixture.componentInstance; + correctionTypeDataService.getCorrectionTypeById.and.returnValue(correctionTypeMockRD$); + spyOn(component, 'initComponent'); + fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should subscribe to route params and set correctionTypeId & initialize component', () => { + expect((component as any).correctionTypeId).toEqual('addpersonalpath'); + expect(component.initComponent).toHaveBeenCalled(); + }); }); diff --git a/src/app/shared/correction-suggestion/correction-suggestion.component.ts b/src/app/shared/correction-suggestion/correction-suggestion.component.ts index dcf379bd771..5cd23e9b32f 100644 --- a/src/app/shared/correction-suggestion/correction-suggestion.component.ts +++ b/src/app/shared/correction-suggestion/correction-suggestion.component.ts @@ -1,10 +1,10 @@ import { getRemoteDataPayload } from './../../core/shared/operators'; import { CorrectionTypeDataService } from './../../core/submission/correctiontype-data.service'; import { Component, ComponentFactoryResolver, Injector, OnInit } from '@angular/core'; -import { CorrectionTypeMode } from '../../core/submission/models/correction-type-mode.model'; +import { CorrectionType } from '../../core/submission/models/correction-type-mode.model'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { getCorrectionComponent } from './correction-suggestion-page.decorator'; -import { ActivatedRoute, Params } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { hasValue } from '../empty.util'; import { getFirstCompletedRemoteData } from '../../core/shared/operators'; @@ -18,7 +18,7 @@ export class CorrectionSuggestionComponent implements OnInit { /** * The correction type object */ - public correctionTypeObject: CorrectionTypeMode; + public correctionTypeObject: CorrectionType; /** * The correction type id @@ -41,9 +41,7 @@ export class CorrectionSuggestionComponent implements OnInit { private correctionTypeDataService: CorrectionTypeDataService, private injector: Injector, ) { - this.aroute.params.subscribe((params: Params) => { - this.correctionTypeId = params.correctionType; - }); + this.correctionTypeId = this.aroute.snapshot.params.correctionType; } ngOnInit(): void { @@ -61,7 +59,7 @@ export class CorrectionSuggestionComponent implements OnInit { getFirstCompletedRemoteData(), getRemoteDataPayload(), ) - .subscribe((correctionType: CorrectionTypeMode) => { + .subscribe((correctionType: CorrectionType) => { if (hasValue(correctionType)) { this.correctionTypeObject = correctionType; this.creationForm = correctionType.creationForm; diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html index fe3f64e8db7..f7e62857dfc 100644 --- a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html +++ b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html @@ -11,9 +11,9 @@

[placeholder]="'correction-type.manage-relation.search.placeholder' | translate" aria-label="" aria-describedby="">
+ (click)="projectTitle = ''">{{('correction-type.manage-relation.search.btn.clear' | translate)}} + (click)="search(projectTitle)">{{('correction-type.manage-relation.search.btn.search' | translate)}}
diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.spec.ts b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.spec.ts index d012163f7b3..a329138c06b 100644 --- a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.spec.ts +++ b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.spec.ts @@ -1,25 +1,173 @@ +import { ThemeService } from './../../../theme-support/theme.service'; + +import { SearchModule } from './../../../search/search.module'; +import { RouterMock } from './../../../mocks/router.mock'; +import { NotificationsService } from './../../../notifications/notifications.service'; +import { ItemDataService } from './../../../../core/data/item-data.service'; +import { OpenaireBrokerEventRestService } from './../../../../core/openaire/broker/events/openaire-broker-event-rest.service'; +import { Item } from './../../../../core/shared/item.model'; +import { SelectableListService } from './../../../object-list/selectable-list/selectable-list.service'; +import { OpenaireMockDspaceObject, getMockOpenaireBrokerEventRestService } from './../../../mocks/openaire.mock'; +import { PaginatedSearchOptions } from './../../../search/models/paginated-search-options.model'; +import { PaginationComponentOptions } from './../../../pagination/pagination-component-options.model'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ManageRelationCorrectionTypeComponent } from './manage-relation-correction-type.component'; +import { PageInfo } from './../../../../core/shared/page-info.model'; +import { buildPaginatedList } from './../../../../core/data/paginated-list.model'; +import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from './../../../../shared/remote-data.utils'; +import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { CommonModule } from '@angular/common'; +import { RouterTestingModule } from '@angular/router/testing'; +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; +import { SearchService } from './../../../../core/shared/search/search.service'; +import { of } from 'rxjs'; +import { ActivatedRoute, Router } from '@angular/router'; +import { MockActivatedRoute } from './../../../../shared/mocks/active-router.mock'; +import { NotificationsServiceStub } from './../../../../shared/testing/notifications-service.stub'; +import { getMockTranslateService } from './../../../../shared/mocks/translate.service.mock'; +import { TranslateLoaderMock } from './../../../../shared/mocks/translate-loader.mock'; +import { SearchServiceStub } from './../../../../shared/testing/search-service.stub'; +import { BrowserModule } from '@angular/platform-browser'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { getMockThemeService } from './../../../../shared/mocks/theme-service.mock'; +import { ImportType } from './../../../../openaire/broker/project-entry-import-modal/project-entry-import-modal.component'; describe('ManageRelationCorrectionTypeComponent', () => { let component: ManageRelationCorrectionTypeComponent; + let compAsAny: any; let fixture: ComponentFixture; + const correctionTypeMock = { + id: 'addpersonalpath', + topic: '/DSPACEUSERS/RELATIONADD/PERSONALPATH', + discoveryConfiguration: 'RELATION.PersonPath.Items', + creationForm: 'manageRelation', + type: 'correctiontype', + _links: { + self: { + href: 'https://rest.api/discover/configurations/addpersonalpath', + }, + }, + }; + + const pagination = Object.assign( + new PaginationComponentOptions(), { + id: 'correction-suggestion-manage-relation', + pageSize: 3 + } + ); + + const uuid = '123e4567-e89b-12d3-a456-426614174003'; + + const searchOptions = Object.assign(new PaginatedSearchOptions( + { + configuration: 'RELATION.PersonPath.Items', + scope: '123e4567-e89b-12d3-a456-426614174013', + pagination: pagination + } + )); + + const pageInfo = new PageInfo({ + elementsPerPage: 3, + totalElements: 1, + totalPages: 1, + currentPage: 1 + }); + + const array = [ + OpenaireMockDspaceObject, + ]; + const paginatedList = buildPaginatedList(pageInfo, array); + const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList); + + const searchServiceStub = Object.assign(new SearchServiceStub(), { + search: () => of(paginatedListRD), + }); + + const openaireBrokerEventRestServiceStub: any = getMockOpenaireBrokerEventRestService(); + + const itemDataService = { + findById: (id: string) => createSuccessfulRemoteDataObject$(new Item()) + }; + beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ ManageRelationCorrectionTypeComponent ] - }) - .compileComponents(); + declarations: [ManageRelationCorrectionTypeComponent, TestComponent ], + imports: [ + CommonModule, + BrowserModule, + NoopAnimationsModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + RouterTestingModule.withRoutes([]), + SearchModule + ], + providers: [ + { provide: 'correctionTypeObjectProvider', useValue: correctionTypeMock }, + { provide: SearchService, useValue: searchServiceStub }, + { provide: OpenaireBrokerEventRestService, useValue: openaireBrokerEventRestServiceStub }, + { provide: SelectableListService, useValue: jasmine.createSpyObj('selectableListService', ['deselect', 'select', 'deselectAll']) }, + { provide: ActivatedRoute, useValue: new MockActivatedRoute() }, + { provide: ItemDataService, useValue: itemDataService }, + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: Router, useValue: new RouterMock() }, + { provide: TranslateService, useValue: getMockTranslateService() }, + { provide: ThemeService, useValue: getMockThemeService() }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(ManageRelationCorrectionTypeComponent); component = fixture.componentInstance; + compAsAny = component as any; + component.localEntitiesRD$ = createSuccessfulRemoteDataObject$(paginatedList); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + describe('search', () => { + it('should call SearchService.search', () => { + (searchServiceStub as any).search.and.returnValue(of(paginatedListRD)); + component.pagination = pagination; + component.search(''); + expect(compAsAny.searchService.search).toHaveBeenCalledWith(searchOptions); + }); + }); + + describe('selectEntity', () => { + const entity = Object.assign(new Item(), { uuid: uuid }); + beforeEach(() => { + component.selectEntity(entity); + }); + it('should set selected entity', () => { + expect(component.selectedEntity).toBe(entity); + }); + it('should set the import type to local entity', () => { + expect(component.selectedImportType).toEqual(ImportType.LocalEntity); + }); + }); + + afterEach(() => { + fixture.destroy(); + component = null; + compAsAny = null; + }); }); + +@Component({ + selector: 'ds-test-cmp', + template: `` +}) +class TestComponent { + +} diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts index 3ad300a2a65..9275101125e 100644 --- a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts +++ b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts @@ -15,9 +15,9 @@ import { SearchService } from './../../../../core/shared/search/search.service'; import { PaginationComponentOptions } from './../../../pagination/pagination-component-options.model'; import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { renderCorrectionFor } from '../../correction-suggestion-page.decorator'; -import { CorrectionTypeMode } from '../../../../core/submission/models/correction-type-mode.model'; +import { CorrectionType } from '../../../../core/submission/models/correction-type-mode.model'; import { CorrectionTypeForms } from './../correction-type-forms'; -import { ActivatedRoute, Params, Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { Observable, Subscription, of as observableOf, switchMap } from 'rxjs'; import { hasValue, isNotEmpty } from '../../../../shared/empty.util'; import { ListableObject } from '../../../../shared/object-collection/shared/listable-object.model'; @@ -36,7 +36,7 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy /** * The correction type object */ - correctionType: CorrectionTypeMode; + correctionType: CorrectionType; /** * The item uuid from the parent object @@ -113,20 +113,18 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy selectedImportType = ImportType.None; constructor( - @Inject('correctionTypeObjectProvider') private correctionTypeObject: CorrectionTypeMode, - public searchService: SearchService, + @Inject('correctionTypeObjectProvider') private correctionTypeObject: CorrectionType, + private searchService: SearchService, private selectService: SelectableListService, private aroute: ActivatedRoute, private openaireBrokerEventRestService: OpenaireBrokerEventRestService, private itemService: ItemDataService, private notificationsService: NotificationsService, private router: Router, - private translate: TranslateService + private translateService: TranslateService ) { this.correctionType = correctionTypeObject; - this.aroute.params.subscribe((params: Params) => { - this.itemUuid = params.uuid; - }); + this.itemUuid = this.aroute.snapshot.params.uuid; } /** @@ -224,11 +222,11 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy if (res.hasSucceeded) { this.selectedImportType = ImportType.None; const message = 'correction-type.manage-relation.' + this.correctionTypeObject.id + '.notification.success'; - this.notificationsService.success(this.translate.instant(message)); + this.notificationsService.success(this.translateService.get(message)); this.deselectAllLists(); this.back(); } else { - this.notificationsService.error(this.translate.instant('correction-type.manage-relation.action.notification.error')); + this.notificationsService.error(this.translateService.get('correction-type.manage-relation.action.notification.error')); } }); } From d7857a6036c9bf08df3e1b3c7d7c3a17b87b82b5 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Wed, 9 Aug 2023 18:28:54 +0200 Subject: [PATCH 05/38] [CST-11179][CST-11178] fix --- .../manage-relation-correction-type.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts index 9275101125e..eee2053e8d3 100644 --- a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts +++ b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts @@ -134,8 +134,8 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy this.pagination = Object.assign(new PaginationComponentOptions(), { id: 'correction-suggestion-manage-relation', pageSize: this.pageSize }); this.searchOptions = Object.assign(new PaginatedSearchOptions( { - configuration: 'default', //this.correctionType.discoveryConfiguration, - scope: null, + configuration: this.correctionType.discoveryConfiguration, + scope: this.itemUuid, pagination: this.pagination } )); From c50dd72d9228fbfcc6d3deee9babf8d1fa876dab Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Thu, 10 Aug 2023 09:29:25 +0200 Subject: [PATCH 06/38] [CST-11178][CST-11179] allow access for authorized user only --- .../correction-type-menu.component.html | 2 +- .../correction-type-menu.component.ts | 17 +++++++++++++---- .../correction-suggestion-routing.module.ts | 2 ++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.html b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.html index 6504d6ecaf4..2f3b46279db 100644 --- a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.html +++ b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.html @@ -1,4 +1,4 @@ - + From ac7cf5b4bac18baf236b3b3517702d9d3037f392 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 29 Aug 2023 11:30:29 +0200 Subject: [PATCH 11/38] [CST-11178] Fix issue while retrieving the id --- .../manage-relation-correction-type.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts index 4db0aff5283..3f8ebec197b 100644 --- a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts +++ b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts @@ -128,7 +128,7 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy private translateService: TranslateService ) { this.correctionType = correctionTypeObject; - this.itemUuid = this.aroute.snapshot.params.uuid; + this.itemUuid = this.aroute.snapshot.params.id; } /** From 1a525dfbe7bde36567a65e374d39202b2c23ff98 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Mon, 30 Oct 2023 12:28:40 +0100 Subject: [PATCH 12/38] [CST-11179] Fixed breadcrumb & refresh & labels & redirection from /full page --- src/app/item-page/item-page-routing.module.ts | 6 +++++ .../correction-suggestion-routing.module.ts | 9 ++++--- .../correction-suggestion.component.ts | 4 ++- ...nage-relation-correction-type.component.ts | 26 +++++++++++-------- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/app/item-page/item-page-routing.module.ts b/src/app/item-page/item-page-routing.module.ts index 1d683201f0b..04e4ba21797 100644 --- a/src/app/item-page/item-page-routing.module.ts +++ b/src/app/item-page/item-page-routing.module.ts @@ -48,6 +48,12 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; .then((m) => m.CorrectionSuggestionModule), canActivate: [AuthenticatedGuard] }, + { + path: `full/${CORRECTION_TYPE_PATH}` , + loadChildren: () => import('../shared/correction-suggestion/correction-suggestion.module') + .then((m) => m.CorrectionSuggestionModule), + canActivate: [AuthenticatedGuard] + }, { path: ITEM_EDIT_PATH, loadChildren: () => import('./edit-item-page/edit-item-page.module') diff --git a/src/app/shared/correction-suggestion/correction-suggestion-routing.module.ts b/src/app/shared/correction-suggestion/correction-suggestion-routing.module.ts index 07b1066e6be..b562785f860 100644 --- a/src/app/shared/correction-suggestion/correction-suggestion-routing.module.ts +++ b/src/app/shared/correction-suggestion/correction-suggestion-routing.module.ts @@ -1,15 +1,16 @@ -import { ItemBreadcrumbResolver } from './../../core/breadcrumbs/item-breadcrumb.resolver'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CorrectionSuggestionComponent } from './correction-suggestion.component'; +import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; const routes: Routes = [ { path: ':correctionType', component: CorrectionSuggestionComponent, resolve: { - breadcrumb: ItemBreadcrumbResolver, - } + breadcrumb: I18nBreadcrumbResolver + }, + data: { title: 'item-correction-suggestion.correction-type', breadcrumbKey: 'item-correction-suggestion.correction-type' }, } ]; @@ -17,7 +18,7 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule], providers: [ - ItemBreadcrumbResolver + I18nBreadcrumbResolver ] }) export class CorrectionSuggestionRoutingModule { } diff --git a/src/app/shared/correction-suggestion/correction-suggestion.component.ts b/src/app/shared/correction-suggestion/correction-suggestion.component.ts index 5cd23e9b32f..df70a1756fb 100644 --- a/src/app/shared/correction-suggestion/correction-suggestion.component.ts +++ b/src/app/shared/correction-suggestion/correction-suggestion.component.ts @@ -1,6 +1,6 @@ import { getRemoteDataPayload } from './../../core/shared/operators'; import { CorrectionTypeDataService } from './../../core/submission/correctiontype-data.service'; -import { Component, ComponentFactoryResolver, Injector, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, ComponentFactoryResolver, Injector, OnInit } from '@angular/core'; import { CorrectionType } from '../../core/submission/models/correction-type-mode.model'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { getCorrectionComponent } from './correction-suggestion-page.decorator'; @@ -40,6 +40,7 @@ export class CorrectionSuggestionComponent implements OnInit { private aroute: ActivatedRoute, private correctionTypeDataService: CorrectionTypeDataService, private injector: Injector, + private chd: ChangeDetectorRef, ) { this.correctionTypeId = this.aroute.snapshot.params.correctionType; } @@ -65,6 +66,7 @@ export class CorrectionSuggestionComponent implements OnInit { this.creationForm = correctionType.creationForm; this.componentFactoryResolver.resolveComponentFactory(this.getComponent()); this.injectData(); + this.chd.detectChanges(); } }); } diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts index 3f8ebec197b..63e388bf7fc 100644 --- a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts +++ b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts @@ -55,12 +55,11 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy /** * Pagination options */ - pagination: PaginationComponentOptions; - - /** - * The number of results per page - */ - pageSize = 3; + pagination: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { + id: 'csmr', + pageSize: 3, + currentPage: 1 + }); /** * Entities to show in the list @@ -125,7 +124,7 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy private itemService: ItemDataService, private notificationsService: NotificationsService, private router: Router, - private translateService: TranslateService + private translateService: TranslateService, ) { this.correctionType = correctionTypeObject; this.itemUuid = this.aroute.snapshot.params.id; @@ -135,7 +134,6 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy * Get the search results */ ngOnInit(): void { - this.pagination = Object.assign(new PaginationComponentOptions(), { id: 'correction-suggestion-manage-relation', pageSize: this.pageSize }); this.searchOptions = Object.assign(new PaginatedSearchOptions( { configuration: this.correctionType.discoveryConfiguration, @@ -146,8 +144,12 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy this.localEntitiesRD$ = this.searchService.search(this.searchOptions); this.subs.push( - this.localEntitiesRD$.subscribe( - () => this.isLoading$ = observableOf(false) + this.localEntitiesRD$.pipe( + getFirstCompletedRemoteData(), + ).subscribe( + () => { + this.isLoading$ = observableOf(false); + } ) ); } @@ -169,7 +171,9 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy )); this.localEntitiesRD$ = this.searchService.search(this.searchOptions); this.subs.push( - this.localEntitiesRD$.subscribe( + this.localEntitiesRD$.pipe( + getFirstCompletedRemoteData(), + ).subscribe( () => this.isLoading$ = observableOf(false) ) ); From 143916c6049b1a13caa7bccc8edc06bb2263e3ed Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Wed, 6 Sep 2023 15:54:39 +0200 Subject: [PATCH 13/38] [CST-11178][CST-11179] fixed pagination for relationship management --- ...ge-relation-correction-type.component.html | 30 ++++---- ...nage-relation-correction-type.component.ts | 75 ++++++++++++------- 2 files changed, 63 insertions(+), 42 deletions(-) diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html index f7e62857dfc..32824cf6350 100644 --- a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html +++ b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html @@ -11,26 +11,28 @@

[placeholder]="'correction-type.manage-relation.search.placeholder' | translate" aria-label="" aria-describedby="">
+ (click)="projectTitle = ''; search('')">{{('correction-type.manage-relation.search.btn.clear' | translate)}}
- - + + +
diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts index 63e388bf7fc..6e285c2987f 100644 --- a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts +++ b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts @@ -20,7 +20,7 @@ import { renderCorrectionFor } from '../../correction-suggestion-page.decorator' import { CorrectionType } from '../../../../core/submission/models/correction-type-mode.model'; import { CorrectionTypeForms } from '../correction-type-forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { Observable, of as observableOf, Subscription, switchMap } from 'rxjs'; +import { finalize, Observable, of as observableOf, Subscription, switchMap } from 'rxjs'; import { hasValue, isNotEmpty } from '../../../empty.util'; import { ListableObject } from '../../../object-collection/shared/listable-object.model'; import { Item } from '../../../../core/shared/item.model'; @@ -55,9 +55,9 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy /** * Pagination options */ - pagination: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { + pagination: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { id: 'csmr', - pageSize: 3, + pageSize: 10, currentPage: 1 }); @@ -99,11 +99,11 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy /** * List ID for selecting local entities */ - entityListId = 'correction-suggestion-manage-relation'; + entityListId = 'csmr'; /** * List ID for selecting local authorities */ - authorityListId = 'correction-suggestion-manage-relation-authority'; + authorityListId = 'csmr-authority'; /** * ImportType enum @@ -134,23 +134,33 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy * Get the search results */ ngOnInit(): void { - this.searchOptions = Object.assign(new PaginatedSearchOptions( - { - configuration: this.correctionType.discoveryConfiguration, - scope: this.itemUuid, - pagination: this.pagination - } - )); + this.getData(); + } - this.localEntitiesRD$ = this.searchService.search(this.searchOptions); - this.subs.push( - this.localEntitiesRD$.pipe( - getFirstCompletedRemoteData(), - ).subscribe( - () => { - this.isLoading$ = observableOf(false); + private getData(){ + this.localEntitiesRD$ = this.aroute.queryParams + .pipe( + switchMap((params) => { + if (hasValue(params)) { + this.pagination = Object.assign(new PaginationComponentOptions(),{ + ...this.pagination, + currentPage: params[`${this.pagination.id}.page`] || 1 + }); } - ) + + this.searchOptions = Object.assign(new PaginatedSearchOptions( + { + configuration: this.correctionType.discoveryConfiguration, + scope: this.itemUuid, + pagination: this.pagination, + } + )); + + return this.searchService.search(this.searchOptions).pipe( + getFirstCompletedRemoteData(), + finalize(() => this.isLoading$ = observableOf(false)) + ); + }) ); } @@ -169,15 +179,24 @@ export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy pagination: this.pagination } )); - this.localEntitiesRD$ = this.searchService.search(this.searchOptions); - this.subs.push( - this.localEntitiesRD$.pipe( - getFirstCompletedRemoteData(), - ).subscribe( - () => this.isLoading$ = observableOf(false) - ) - ); + } else { + this.searchOptions = Object.assign(new PaginatedSearchOptions( + { + configuration: this.correctionType.discoveryConfiguration, + scope: this.itemUuid, + pagination: this.pagination + } + )); } + + this.localEntitiesRD$ = this.searchService.search(this.searchOptions); + this.subs.push( + this.localEntitiesRD$.pipe( + getFirstCompletedRemoteData(), + ).subscribe( + () => this.isLoading$ = observableOf(false) + ) + ); } /** From 9094d8233ffe349b51b53178c6cc119272e8b09f Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Wed, 1 Nov 2023 14:29:26 +0100 Subject: [PATCH 14/38] [CST-12109] implemented withdrawn-reinstate requests --- src/app/app-routing.module.ts | 8 +- src/app/core/core.module.ts | 6 +- .../quality-assurance-event-data.service.ts | 4 +- src/app/item-page/item-page-routing.module.ts | 13 - src/app/item-page/item-shared.module.ts | 4 + .../correction-type-menu.component.html | 7 - .../correction-type-menu.component.spec.ts | 116 -------- .../correction-type-menu.component.ts | 98 ------ .../correction-suggestion-page.decorator.ts | 15 - .../correction-suggestion-routing.module.ts | 24 -- .../correction-suggestion.component.html | 3 - .../correction-suggestion.component.spec.ts | 80 ----- .../correction-suggestion.component.ts | 94 ------ .../correction-suggestion.module.ts | 39 --- .../correction-types/correction-type-forms.ts | 3 - ...ge-relation-correction-type.component.html | 56 ---- ...ge-relation-correction-type.component.scss | 0 ...relation-correction-type.component.spec.ts | 173 ----------- ...nage-relation-correction-type.component.ts | 278 ------------------ ...m-withdrawn-reinstate-modal.component.html | 48 +++ ...-withdrawn-reinstate-modal.component.scss} | 0 .../withdrawn-reinstate-modal.component.ts | 47 +++ .../shared/dso-page/dso-edit-menu.resolver.ts | 34 ++- .../dso-reinstate-modal.service.ts | 65 ++++ .../dso-withdrawn-modal.service.ts | 64 ++++ src/app/shared/shared.module.ts | 6 +- src/assets/i18n/en.json5 | 20 ++ src/themes/custom/lazy-theme.module.ts | 4 +- 28 files changed, 292 insertions(+), 1017 deletions(-) delete mode 100644 src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.html delete mode 100644 src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.spec.ts delete mode 100644 src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts delete mode 100644 src/app/shared/correction-suggestion/correction-suggestion-page.decorator.ts delete mode 100644 src/app/shared/correction-suggestion/correction-suggestion-routing.module.ts delete mode 100644 src/app/shared/correction-suggestion/correction-suggestion.component.html delete mode 100644 src/app/shared/correction-suggestion/correction-suggestion.component.spec.ts delete mode 100644 src/app/shared/correction-suggestion/correction-suggestion.component.ts delete mode 100644 src/app/shared/correction-suggestion/correction-suggestion.module.ts delete mode 100644 src/app/shared/correction-suggestion/correction-types/correction-type-forms.ts delete mode 100644 src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html delete mode 100644 src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.scss delete mode 100644 src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.spec.ts delete mode 100644 src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts create mode 100644 src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.html rename src/app/shared/correction-suggestion/{correction-suggestion.component.scss => item-withdrawn-reinstate-modal.component.scss} (100%) create mode 100644 src/app/shared/correction-suggestion/withdrawn-reinstate-modal.component.ts create mode 100644 src/app/shared/dso-page/dso-reinstate-service/dso-reinstate-modal.service.ts create mode 100644 src/app/shared/dso-page/dso-withdrawn-service/dso-withdrawn-modal.service.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index b1c04c40da1..0d7a0bd0f05 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -237,13 +237,7 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone .then((m) => m.SubscriptionsPageRoutingModule), canActivate: [AuthenticatedGuard] }, - { path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent }, - { - path: 'items', - loadChildren: () => import('./shared/correction-suggestion/correction-suggestion.module') - .then((m) => m.CorrectionSuggestionModule), - canActivate: [AuthenticatedGuard] - }, + { path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent } ] } ], { diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 0eeb476d69d..8d8d2e93e6e 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -185,7 +185,6 @@ import { FlatBrowseDefinition } from './shared/flat-browse-definition.model'; import { ValueListBrowseDefinition } from './shared/value-list-browse-definition.model'; import { NonHierarchicalBrowseDefinition } from './shared/non-hierarchical-browse-definition'; import { BulkAccessConditionOptions } from './config/models/bulk-access-condition-options.model'; -import { CorrectionTypeMode } from './submission/models/correction-type-mode.model'; import { CorrectionTypeDataService } from './submission/correctiontype-data.service'; /** @@ -309,8 +308,8 @@ const PROVIDERS = [ OrcidAuthService, OrcidQueueDataService, OrcidHistoryDataService, - SupervisionOrderDataService - CorrectionTypeDataService, + SupervisionOrderDataService, + CorrectionTypeDataService ]; /** @@ -390,7 +389,6 @@ export const models = Subscription, ItemRequest, BulkAccessConditionOptions - CorrectionTypeMode ]; @NgModule({ diff --git a/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts b/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts index ae1538f7bb2..23e7d6a6a4c 100644 --- a/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts +++ b/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts @@ -207,7 +207,7 @@ export class QualityAssuranceEventDataService extends IdentifiableDataService> { + postData(data: string): Observable> { const requestId = this.requestService.generateRequestId(); const href$ = this.getBrowseEndpoint(); @@ -218,7 +218,7 @@ export class QualityAssuranceEventDataService extends IdentifiableDataService(requestId); + return this.rdbService.buildFromRequestUUID(requestId); }) ); } diff --git a/src/app/item-page/item-page-routing.module.ts b/src/app/item-page/item-page-routing.module.ts index 04e4ba21797..0c855ab34dd 100644 --- a/src/app/item-page/item-page-routing.module.ts +++ b/src/app/item-page/item-page-routing.module.ts @@ -16,7 +16,6 @@ import { MenuItemType } from '../shared/menu/menu-item-type.model'; import { VersionPageComponent } from './version-page/version-page/version-page.component'; import { BitstreamRequestACopyPageComponent } from './bitstreams/request-a-copy/bitstream-request-a-copy-page.component'; import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths'; -import { CORRECTION_TYPE_PATH, REQUEST_COPY_MODULE_PATH } from '../app-routing-paths'; import { OrcidPageComponent } from './orcid-page/orcid-page.component'; import { OrcidPageGuard } from './orcid-page/orcid-page.guard'; import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; @@ -42,18 +41,6 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; path: 'full', component: ThemedFullItemPageComponent, }, - { - path: CORRECTION_TYPE_PATH, - loadChildren: () => import('../shared/correction-suggestion/correction-suggestion.module') - .then((m) => m.CorrectionSuggestionModule), - canActivate: [AuthenticatedGuard] - }, - { - path: `full/${CORRECTION_TYPE_PATH}` , - loadChildren: () => import('../shared/correction-suggestion/correction-suggestion.module') - .then((m) => m.CorrectionSuggestionModule), - canActivate: [AuthenticatedGuard] - }, { path: ITEM_EDIT_PATH, loadChildren: () => import('./edit-item-page/edit-item-page.module') diff --git a/src/app/item-page/item-shared.module.ts b/src/app/item-page/item-shared.module.ts index 9c2bbba6194..9a7477e6aea 100644 --- a/src/app/item-page/item-shared.module.ts +++ b/src/app/item-page/item-shared.module.ts @@ -16,10 +16,14 @@ import { RelatedItemsComponent } from './simple/related-items/related-items-comp import { ThemedMetadataRepresentationListComponent } from './simple/metadata-representation-list/themed-metadata-representation-list.component'; +import { + ItemWithdrawnReinstateModalComponent +} from '../shared/correction-suggestion/withdrawn-reinstate-modal.component'; const ENTRY_COMPONENTS = [ ItemVersionsDeleteModalComponent, ItemVersionsSummaryModalComponent, + ItemWithdrawnReinstateModalComponent ]; diff --git a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.html b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.html deleted file mode 100644 index 09a41614b7c..00000000000 --- a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.html +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.spec.ts b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.spec.ts deleted file mode 100644 index a6713a12a8c..00000000000 --- a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { Item } from './../../../core/shared/item.model'; -import { DSpaceObject } from './../../../core/shared/dspace-object.model'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { CorrectionTypeMenuComponent } from './correction-type-menu.component'; -import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; -import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; -import { createPaginatedList } from '../../testing/utils.test'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { TranslateLoaderMock } from '../../mocks/translate-loader.mock'; -import { RouterTestingModule } from '@angular/router/testing'; -import { CorrectionTypeDataService } from '../../../core/submission/correctiontype-data.service'; -import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; -import { NotificationsService } from '../../notifications/notifications.service'; -import { By } from '@angular/platform-browser'; -import { CorrectionType } from '../../../core/submission/models/correction-type-mode.model'; - -describe('CorrectionTypeMenuComponent', () => { - let component: CorrectionTypeMenuComponent; - let fixture: ComponentFixture; - let componentAsAny: any; - - let correctionTypeService: any; - let dso: DSpaceObject; - const notificationService = new NotificationsServiceStub(); - const correctionType: CorrectionType = Object.assign(new CorrectionType(), { - id: 'addpersonalpath', - creationForm:'manageRelation', - discoveryConfiguration: 'RELATION.PersonPath.Items', - topic: '/DSPACEUSERS/RELATIONADD/PERSONALPATH' - }); - - const correctionTypeObjRDList$ = createSuccessfulRemoteDataObject$(createPaginatedList([correctionType])); - - beforeEach(async () => { - dso = Object.assign(new Item(), { - id: 'test-item', - _links: { - self: { href: 'test-item-selflink' } - } - }); - - correctionTypeService = jasmine.createSpyObj('CorrectionTypeDataService', { - findByItem: jasmine.createSpy('findByItem') - }); - - await TestBed.configureTestingModule({ - declarations: [ CorrectionTypeMenuComponent ], - imports: [ - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock - } - }), - RouterTestingModule.withRoutes([])], - providers: [ - { provide: CorrectionTypeDataService, useValue: correctionTypeService }, - { provide: 'contextMenuObjectProvider', useValue: dso }, - { provide: 'contextMenuObjectTypeProvider', useValue: DSpaceObjectType.ITEM }, - { provide: NotificationsService, useValue: notificationService } - ] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(CorrectionTypeMenuComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('when correction types are available', () => { - beforeEach(() => { - correctionTypeService.findByItem.and.returnValue(correctionTypeObjRDList$); - fixture = TestBed.createComponent(CorrectionTypeMenuComponent); - component = fixture.componentInstance; - componentAsAny = fixture.componentInstance; - component.contextMenuObject = dso; - fixture.detectChanges(); - }); - - it('should init properly', () => { - expect(componentAsAny.correctionTypes$.value).toEqual([correctionType]); - }); - - it('should render a button', () => { - const link = fixture.debugElement.query(By.css('button')); - expect(link).not.toBeNull(); - }); - }); - - describe('when is no data are available', () => { - beforeEach(() => { - correctionTypeService.findByItem.and.returnValue(createSuccessfulRemoteDataObject$(createPaginatedList([]))); - fixture = TestBed.createComponent(CorrectionTypeMenuComponent); - component = fixture.componentInstance; - componentAsAny = fixture.componentInstance; - component.contextMenuObject = dso; - fixture.detectChanges(); - }); - - it('should init edit mode properly', () => { - expect(componentAsAny.correctionTypes$.value).toEqual([]); - }); - - it('should render a button', () => { - const link = fixture.debugElement.query(By.css('button')); - expect(link).toBeNull(); - }); - }); -}); diff --git a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts b/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts deleted file mode 100644 index 2fb195ccf75..00000000000 --- a/src/app/shared/context-menu/correction-type-menu/correction-type-menu.component.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { getAllSucceededRemoteDataPayload, getPaginatedListPayload } from '../../../core/shared/operators'; -import { CorrectionTypeDataService } from '../../../core/submission/correctiontype-data.service'; -import { DSpaceObject } from '../../../core/shared/dspace-object.model'; -import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; -import { ContextMenuEntryComponent } from '../context-menu-entry.component'; -import { ContextMenuEntryType } from '../context-menu-entry-type'; -import { BehaviorSubject, map, Observable, startWith, Subscription } from 'rxjs'; -import { CorrectionType } from '../../../core/submission/models/correction-type-mode.model'; -import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; -import { hasValue, isNotEmpty } from '../../empty.util'; -import { rendersContextMenuEntriesForType } from '../context-menu.decorator'; -import { CORRECTION_TYPE_PATH } from '../../../app-routing-paths'; - -@Component({ - selector: 'ds-correction-type-menu', - templateUrl: './correction-type-menu.component.html', -}) -@rendersContextMenuEntriesForType(DSpaceObjectType.ITEM) -export class CorrectionTypeMenuComponent extends ContextMenuEntryComponent implements OnInit, OnDestroy { - - /** - * The menu entry type - */ - public static menuEntryType: ContextMenuEntryType = ContextMenuEntryType.CorrectionType; - - /** - * List of Edit Modes available on this item - * for the current user - */ - private correctionTypes$: BehaviorSubject = new BehaviorSubject([]); - - /** - * Variable to track subscription and unsubscribe it onDestroy - */ - private sub: Subscription; - - isAuthorized$: Observable; - - constructor( - @Inject('contextMenuObjectProvider') protected injectedContextMenuObject: DSpaceObject, - @Inject('contextMenuObjectTypeProvider') protected injectedContextMenuObjectType: DSpaceObjectType, - private correctionTypeService: CorrectionTypeDataService, - ) { - super(injectedContextMenuObject, injectedContextMenuObjectType, ContextMenuEntryType.CorrectionType); - } - - ngOnInit(): void { - this.getData(); - } - - /** - * Check if edit mode is available - */ - getCorrectionTypes(): Observable { - return this.correctionTypes$.asObservable(); - } - - /** - * Check if any correction type are available - */ - hasCorrectionTypeAvailable(): Observable { - return this.correctionTypes$.asObservable().pipe( - map((type) => isNotEmpty(type) && type.length > 0) - ); - } - - /** - * Get correction types - * useCachedVersionIfAvailable = false to force refreshing the list - */ - getData(): void { - this.sub = this.correctionTypeService.findByItem(this.contextMenuObject.id, false).pipe( - getAllSucceededRemoteDataPayload(), - getPaginatedListPayload(), - startWith([]) - ).subscribe((types: CorrectionType[]) => { - this.correctionTypes$.next(types); - }); - } - - /** - * Get the route to the correction type page - * @param typeId correction type id - * @returns the route to the correction type page - */ - getTypeRoute(typeId: string): string[] { - return ['./', CORRECTION_TYPE_PATH, typeId]; - } - - /** - * Make sure the subscription is unsubscribed from when this component is destroyed - */ - ngOnDestroy(): void { - if (hasValue(this.sub)) { - this.sub.unsubscribe(); - } - } -} diff --git a/src/app/shared/correction-suggestion/correction-suggestion-page.decorator.ts b/src/app/shared/correction-suggestion/correction-suggestion-page.decorator.ts deleted file mode 100644 index 88f1c9f8025..00000000000 --- a/src/app/shared/correction-suggestion/correction-suggestion-page.decorator.ts +++ /dev/null @@ -1,15 +0,0 @@ -const pageMap = new Map(); - -export function renderCorrectionFor(creationMode: string) { - return function decorator(component: any) { - if (!component) { - return; - } - pageMap.set(creationMode, component); - }; -} - -export function getCorrectionComponent(creationMode: string) { - return pageMap.get(creationMode); -} - diff --git a/src/app/shared/correction-suggestion/correction-suggestion-routing.module.ts b/src/app/shared/correction-suggestion/correction-suggestion-routing.module.ts deleted file mode 100644 index b562785f860..00000000000 --- a/src/app/shared/correction-suggestion/correction-suggestion-routing.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { CorrectionSuggestionComponent } from './correction-suggestion.component'; -import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; - -const routes: Routes = [ - { - path: ':correctionType', - component: CorrectionSuggestionComponent, - resolve: { - breadcrumb: I18nBreadcrumbResolver - }, - data: { title: 'item-correction-suggestion.correction-type', breadcrumbKey: 'item-correction-suggestion.correction-type' }, - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], - providers: [ - I18nBreadcrumbResolver - ] -}) -export class CorrectionSuggestionRoutingModule { } diff --git a/src/app/shared/correction-suggestion/correction-suggestion.component.html b/src/app/shared/correction-suggestion/correction-suggestion.component.html deleted file mode 100644 index 7cc498504ee..00000000000 --- a/src/app/shared/correction-suggestion/correction-suggestion.component.html +++ /dev/null @@ -1,3 +0,0 @@ -
- -
diff --git a/src/app/shared/correction-suggestion/correction-suggestion.component.spec.ts b/src/app/shared/correction-suggestion/correction-suggestion.component.spec.ts deleted file mode 100644 index cdb793f7ea8..00000000000 --- a/src/app/shared/correction-suggestion/correction-suggestion.component.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { CorrectionSuggestionComponent } from './correction-suggestion.component'; -import { ActivatedRoute } from '@angular/router'; -import { CorrectionTypeDataService } from 'src/app/core/submission/correctiontype-data.service'; -import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; -import { CommonModule } from '@angular/common'; -import { BrowserModule } from '@angular/platform-browser'; -import { TranslateModule } from '@ngx-translate/core'; -import { RouterTestingModule } from '@angular/router/testing'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; - -describe('CorrectionSuggestionComponent', () => { - let component: CorrectionSuggestionComponent; - let fixture: ComponentFixture; - - const correctionTypeMock = { - id: 'addpersonalpath', - topic: '/DSPACEUSERS/RELATIONADD/PERSONALPATH', - discoveryConfiguration: 'RELATION.PersonPath.Items', - creationForm: 'manageRelation', - type: 'correctiontype', - _links: { - self: { - href: 'https://rest.api/discover/configurations/addpersonalpath', - }, - }, - }; - - const correctionTypeMockRD$ = createSuccessfulRemoteDataObject$(correctionTypeMock); - - const mockActivatedRoute = { - snapshot:{ - params: { - correctionType: 'addpersonalpath' - } - } - }; - - let correctionTypeDataService: any; - - beforeEach(async () => { - correctionTypeDataService = jasmine.createSpyObj('correctionTypeDataService', { - getCorrectionTypeById: jasmine.createSpy('getCorrectionTypeById') - }); - - await TestBed.configureTestingModule({ - declarations: [ CorrectionSuggestionComponent ], - imports: [ - CommonModule, - BrowserModule, - TranslateModule.forRoot(), - RouterTestingModule.withRoutes([]), - ], - providers: [ - { provide: ActivatedRoute, useValue: mockActivatedRoute }, - { provide: CorrectionTypeDataService, useValue: correctionTypeDataService }, - ], - schemas: [NO_ERRORS_SCHEMA] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(CorrectionSuggestionComponent); - component = fixture.componentInstance; - correctionTypeDataService.getCorrectionTypeById.and.returnValue(correctionTypeMockRD$); - spyOn(component, 'initComponent'); - - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should subscribe to route params and set correctionTypeId & initialize component', () => { - expect((component as any).correctionTypeId).toEqual('addpersonalpath'); - expect(component.initComponent).toHaveBeenCalled(); - }); -}); diff --git a/src/app/shared/correction-suggestion/correction-suggestion.component.ts b/src/app/shared/correction-suggestion/correction-suggestion.component.ts deleted file mode 100644 index df70a1756fb..00000000000 --- a/src/app/shared/correction-suggestion/correction-suggestion.component.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { getRemoteDataPayload } from './../../core/shared/operators'; -import { CorrectionTypeDataService } from './../../core/submission/correctiontype-data.service'; -import { ChangeDetectorRef, Component, ComponentFactoryResolver, Injector, OnInit } from '@angular/core'; -import { CorrectionType } from '../../core/submission/models/correction-type-mode.model'; -import { GenericConstructor } from '../../core/shared/generic-constructor'; -import { getCorrectionComponent } from './correction-suggestion-page.decorator'; -import { ActivatedRoute } from '@angular/router'; -import { hasValue } from '../empty.util'; -import { getFirstCompletedRemoteData } from '../../core/shared/operators'; - -@Component({ - selector: 'ds-correction-suggestion', - templateUrl: './correction-suggestion.component.html', - styleUrls: ['./correction-suggestion.component.scss'] -}) -export class CorrectionSuggestionComponent implements OnInit { - - /** - * The correction type object - */ - public correctionTypeObject: CorrectionType; - - /** - * The correction type id - */ - private correctionTypeId: string; - - /** - * The creation form - */ - private creationForm: string; - - /** - * The injector for the component - */ - public objectInjector: Injector; - - constructor( - private componentFactoryResolver: ComponentFactoryResolver, - private aroute: ActivatedRoute, - private correctionTypeDataService: CorrectionTypeDataService, - private injector: Injector, - private chd: ChangeDetectorRef, - ) { - this.correctionTypeId = this.aroute.snapshot.params.correctionType; - } - - ngOnInit(): void { - this.initComponent(); - } - - /** - * Initialize the component by fetching the correction type object - * and rendering the correct component based on the creation form - */ - initComponent(): void { - if (hasValue(this.correctionTypeId)) { - this.correctionTypeDataService.getCorrectionTypeById(this.correctionTypeId) - .pipe( - getFirstCompletedRemoteData(), - getRemoteDataPayload(), - ) - .subscribe((correctionType: CorrectionType) => { - if (hasValue(correctionType)) { - this.correctionTypeObject = correctionType; - this.creationForm = correctionType.creationForm; - this.componentFactoryResolver.resolveComponentFactory(this.getComponent()); - this.injectData(); - this.chd.detectChanges(); - } - }); - } - } - - /** - * Inject the data into the component - */ - private injectData(): void { - this.objectInjector = Injector.create({ - providers: [ - { provide: 'correctionTypeObjectProvider', useValue: this.correctionTypeObject, deps: [] }, - ], - parent: this.injector, - }); - } - - /** - * Fetch the component depending on the creation form - * @returns {GenericConstructor} - */ - getComponent(): GenericConstructor { - return getCorrectionComponent(this.creationForm); - } -} diff --git a/src/app/shared/correction-suggestion/correction-suggestion.module.ts b/src/app/shared/correction-suggestion/correction-suggestion.module.ts deleted file mode 100644 index 37204b441e6..00000000000 --- a/src/app/shared/correction-suggestion/correction-suggestion.module.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { CorrectionSuggestionComponent } from './correction-suggestion.component'; -import { SharedModule } from '../shared.module'; -import { CorrectionSuggestionRoutingModule } from './correction-suggestion-routing.module'; -import { ManageRelationCorrectionTypeComponent } from './correction-types/manage-relation-correction-type/manage-relation-correction-type.component'; -import { SearchModule } from '../search/search.module'; - -const COMPONENTS = [ - CorrectionSuggestionComponent, - ManageRelationCorrectionTypeComponent, -]; - -const ENTRY_COMPONENTS = [ - ManageRelationCorrectionTypeComponent, -]; - -@NgModule({ - declarations: [ - COMPONENTS, - ], - imports: [ - CommonModule, - CorrectionSuggestionRoutingModule, - SharedModule, - SearchModule - ], - exports: [ - COMPONENTS, - ] -}) -export class CorrectionSuggestionModule { - static withEntryComponents() { - return { - ngModule: CorrectionSuggestionModule, - providers: ENTRY_COMPONENTS.map((component) => ({ provide: component })) - }; - } - } diff --git a/src/app/shared/correction-suggestion/correction-types/correction-type-forms.ts b/src/app/shared/correction-suggestion/correction-types/correction-type-forms.ts deleted file mode 100644 index 9faf8ca18be..00000000000 --- a/src/app/shared/correction-suggestion/correction-types/correction-type-forms.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum CorrectionTypeForms { - MANAGE_RELATION = 'manageRelation', -} diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html deleted file mode 100644 index 32824cf6350..00000000000 --- a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.html +++ /dev/null @@ -1,56 +0,0 @@ -
-

- {{ ( 'correction-type.manage-relation.' + (correctionType.topic | lowercase) + '.searchHeader' | translate) }} -

-
-
-
- - - - - - - - -
- -

{{( 'correction-type.manage-relation.search.notFound' | translate)}}

-
-
- -
-
-
- -
-
- -
-
-
diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.scss b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.spec.ts b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.spec.ts deleted file mode 100644 index a329138c06b..00000000000 --- a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.spec.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { ThemeService } from './../../../theme-support/theme.service'; - -import { SearchModule } from './../../../search/search.module'; -import { RouterMock } from './../../../mocks/router.mock'; -import { NotificationsService } from './../../../notifications/notifications.service'; -import { ItemDataService } from './../../../../core/data/item-data.service'; -import { OpenaireBrokerEventRestService } from './../../../../core/openaire/broker/events/openaire-broker-event-rest.service'; -import { Item } from './../../../../core/shared/item.model'; -import { SelectableListService } from './../../../object-list/selectable-list/selectable-list.service'; -import { OpenaireMockDspaceObject, getMockOpenaireBrokerEventRestService } from './../../../mocks/openaire.mock'; -import { PaginatedSearchOptions } from './../../../search/models/paginated-search-options.model'; -import { PaginationComponentOptions } from './../../../pagination/pagination-component-options.model'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ManageRelationCorrectionTypeComponent } from './manage-relation-correction-type.component'; -import { PageInfo } from './../../../../core/shared/page-info.model'; -import { buildPaginatedList } from './../../../../core/data/paginated-list.model'; -import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from './../../../../shared/remote-data.utils'; -import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; -import { CommonModule } from '@angular/common'; -import { RouterTestingModule } from '@angular/router/testing'; -import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; -import { SearchService } from './../../../../core/shared/search/search.service'; -import { of } from 'rxjs'; -import { ActivatedRoute, Router } from '@angular/router'; -import { MockActivatedRoute } from './../../../../shared/mocks/active-router.mock'; -import { NotificationsServiceStub } from './../../../../shared/testing/notifications-service.stub'; -import { getMockTranslateService } from './../../../../shared/mocks/translate.service.mock'; -import { TranslateLoaderMock } from './../../../../shared/mocks/translate-loader.mock'; -import { SearchServiceStub } from './../../../../shared/testing/search-service.stub'; -import { BrowserModule } from '@angular/platform-browser'; -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { getMockThemeService } from './../../../../shared/mocks/theme-service.mock'; -import { ImportType } from './../../../../openaire/broker/project-entry-import-modal/project-entry-import-modal.component'; - -describe('ManageRelationCorrectionTypeComponent', () => { - let component: ManageRelationCorrectionTypeComponent; - let compAsAny: any; - let fixture: ComponentFixture; - - const correctionTypeMock = { - id: 'addpersonalpath', - topic: '/DSPACEUSERS/RELATIONADD/PERSONALPATH', - discoveryConfiguration: 'RELATION.PersonPath.Items', - creationForm: 'manageRelation', - type: 'correctiontype', - _links: { - self: { - href: 'https://rest.api/discover/configurations/addpersonalpath', - }, - }, - }; - - const pagination = Object.assign( - new PaginationComponentOptions(), { - id: 'correction-suggestion-manage-relation', - pageSize: 3 - } - ); - - const uuid = '123e4567-e89b-12d3-a456-426614174003'; - - const searchOptions = Object.assign(new PaginatedSearchOptions( - { - configuration: 'RELATION.PersonPath.Items', - scope: '123e4567-e89b-12d3-a456-426614174013', - pagination: pagination - } - )); - - const pageInfo = new PageInfo({ - elementsPerPage: 3, - totalElements: 1, - totalPages: 1, - currentPage: 1 - }); - - const array = [ - OpenaireMockDspaceObject, - ]; - const paginatedList = buildPaginatedList(pageInfo, array); - const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList); - - const searchServiceStub = Object.assign(new SearchServiceStub(), { - search: () => of(paginatedListRD), - }); - - const openaireBrokerEventRestServiceStub: any = getMockOpenaireBrokerEventRestService(); - - const itemDataService = { - findById: (id: string) => createSuccessfulRemoteDataObject$(new Item()) - }; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ManageRelationCorrectionTypeComponent, TestComponent ], - imports: [ - CommonModule, - BrowserModule, - NoopAnimationsModule, - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock - } - }), - RouterTestingModule.withRoutes([]), - SearchModule - ], - providers: [ - { provide: 'correctionTypeObjectProvider', useValue: correctionTypeMock }, - { provide: SearchService, useValue: searchServiceStub }, - { provide: OpenaireBrokerEventRestService, useValue: openaireBrokerEventRestServiceStub }, - { provide: SelectableListService, useValue: jasmine.createSpyObj('selectableListService', ['deselect', 'select', 'deselectAll']) }, - { provide: ActivatedRoute, useValue: new MockActivatedRoute() }, - { provide: ItemDataService, useValue: itemDataService }, - { provide: NotificationsService, useValue: new NotificationsServiceStub() }, - { provide: Router, useValue: new RouterMock() }, - { provide: TranslateService, useValue: getMockTranslateService() }, - { provide: ThemeService, useValue: getMockThemeService() }, - ], - schemas: [NO_ERRORS_SCHEMA] - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(ManageRelationCorrectionTypeComponent); - component = fixture.componentInstance; - compAsAny = component as any; - component.localEntitiesRD$ = createSuccessfulRemoteDataObject$(paginatedList); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('search', () => { - it('should call SearchService.search', () => { - (searchServiceStub as any).search.and.returnValue(of(paginatedListRD)); - component.pagination = pagination; - component.search(''); - expect(compAsAny.searchService.search).toHaveBeenCalledWith(searchOptions); - }); - }); - - describe('selectEntity', () => { - const entity = Object.assign(new Item(), { uuid: uuid }); - beforeEach(() => { - component.selectEntity(entity); - }); - it('should set selected entity', () => { - expect(component.selectedEntity).toBe(entity); - }); - it('should set the import type to local entity', () => { - expect(component.selectedImportType).toEqual(ImportType.LocalEntity); - }); - }); - - afterEach(() => { - fixture.destroy(); - component = null; - compAsAny = null; - }); -}); - -@Component({ - selector: 'ds-test-cmp', - template: `` -}) -class TestComponent { - -} diff --git a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts b/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts deleted file mode 100644 index 6e285c2987f..00000000000 --- a/src/app/shared/correction-suggestion/correction-types/manage-relation-correction-type/manage-relation-correction-type.component.ts +++ /dev/null @@ -1,278 +0,0 @@ -import { NotificationsService } from '../../../notifications/notifications.service'; -import { OpenaireBrokerEventObject } from '../../../../core/openaire/broker/models/openaire-broker-event.model'; -import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; -import { ItemDataService } from '../../../../core/data/item-data.service'; -import { - OpenaireBrokerEventRestService -} from '../../../../core/openaire/broker/events/openaire-broker-event-rest.service'; -import { Context } from '../../../../core/shared/context.model'; -import { CollectionElementLinkType } from '../../../object-collection/collection-element-link.type'; -import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; -import { SearchResult } from '../../../search/models/search-result.model'; -import { RemoteData } from '../../../../core/data/remote-data'; -import { PaginatedList } from '../../../../core/data/paginated-list.model'; -import { PaginatedSearchOptions } from '../../../search/models/paginated-search-options.model'; -import { SelectableListService } from '../../../object-list/selectable-list/selectable-list.service'; -import { SearchService } from '../../../../core/shared/search/search.service'; -import { PaginationComponentOptions } from '../../../pagination/pagination-component-options.model'; -import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; -import { renderCorrectionFor } from '../../correction-suggestion-page.decorator'; -import { CorrectionType } from '../../../../core/submission/models/correction-type-mode.model'; -import { CorrectionTypeForms } from '../correction-type-forms'; -import { ActivatedRoute, Router } from '@angular/router'; -import { finalize, Observable, of as observableOf, Subscription, switchMap } from 'rxjs'; -import { hasValue, isNotEmpty } from '../../../empty.util'; -import { ListableObject } from '../../../object-collection/shared/listable-object.model'; -import { Item } from '../../../../core/shared/item.model'; -import { - ImportType -} from '../../../../openaire/broker/project-entry-import-modal/project-entry-import-modal.component'; -import { TranslateService } from '@ngx-translate/core'; - -@Component({ - selector: 'ds-manage-relation-correction-type', - templateUrl: './manage-relation-correction-type.component.html', - styleUrls: ['./manage-relation-correction-type.component.scss'] -}) -@renderCorrectionFor(CorrectionTypeForms.MANAGE_RELATION) -export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy { - - /** - * The correction type object - */ - correctionType: CorrectionType; - - /** - * The item uuid from the parent object - */ - itemUuid: string; - - /** - * The project title from the parent object - */ - projectTitle = ''; - - /** - * Pagination options - */ - pagination: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'csmr', - pageSize: 10, - currentPage: 1 - }); - - /** - * Entities to show in the list - */ - localEntitiesRD$: Observable>>>; - - /** - * Search options to use for fetching projects - */ - searchOptions: PaginatedSearchOptions; - - /** - * The list of subscriptions - */ - protected subs: Subscription[] = []; - - /** - * Information about the data loading status - */ - isLoading$ = observableOf(true); - - /** - * The selected local entity - */ - selectedEntity: ListableObject; - - /** - * The type of link to render in listable elements - */ - linkTypes = CollectionElementLinkType; - - /** - * The context we're currently in (submission) - */ - context = Context.Search; - - /** - * List ID for selecting local entities - */ - entityListId = 'csmr'; - /** - * List ID for selecting local authorities - */ - authorityListId = 'csmr-authority'; - - /** - * ImportType enum - */ - importType = ImportType; - - /** - * The type of import the user currently has selected - */ - selectedImportType = ImportType.None; - - constructor( - @Inject('correctionTypeObjectProvider') private correctionTypeObject: CorrectionType, - private searchService: SearchService, - private selectService: SelectableListService, - private aroute: ActivatedRoute, - private openaireBrokerEventRestService: OpenaireBrokerEventRestService, - private itemService: ItemDataService, - private notificationsService: NotificationsService, - private router: Router, - private translateService: TranslateService, - ) { - this.correctionType = correctionTypeObject; - this.itemUuid = this.aroute.snapshot.params.id; - } - - /** - * Get the search results - */ - ngOnInit(): void { - this.getData(); - } - - private getData(){ - this.localEntitiesRD$ = this.aroute.queryParams - .pipe( - switchMap((params) => { - if (hasValue(params)) { - this.pagination = Object.assign(new PaginationComponentOptions(),{ - ...this.pagination, - currentPage: params[`${this.pagination.id}.page`] || 1 - }); - } - - this.searchOptions = Object.assign(new PaginatedSearchOptions( - { - configuration: this.correctionType.discoveryConfiguration, - scope: this.itemUuid, - pagination: this.pagination, - } - )); - - return this.searchService.search(this.searchOptions).pipe( - getFirstCompletedRemoteData(), - finalize(() => this.isLoading$ = observableOf(false)) - ); - }) - ); - } - - /** - * Perform a project search by title. - */ - public search(searchTitle): void { - if (isNotEmpty(searchTitle)) { - const filterRegEx = /[:]/g; - this.isLoading$ = observableOf(true); - this.searchOptions = Object.assign(new PaginatedSearchOptions( - { - configuration: this.correctionType.discoveryConfiguration, - query: (searchTitle) ? searchTitle.replace(filterRegEx, '') : searchTitle, - scope: this.itemUuid, - pagination: this.pagination - } - )); - } else { - this.searchOptions = Object.assign(new PaginatedSearchOptions( - { - configuration: this.correctionType.discoveryConfiguration, - scope: this.itemUuid, - pagination: this.pagination - } - )); - } - - this.localEntitiesRD$ = this.searchService.search(this.searchOptions); - this.subs.push( - this.localEntitiesRD$.pipe( - getFirstCompletedRemoteData(), - ).subscribe( - () => this.isLoading$ = observableOf(false) - ) - ); - } - - /** - * Deselected a local entity - */ - public deselectEntity(): void { - this.selectedEntity = undefined; - if (this.selectedImportType === ImportType.LocalEntity) { - this.selectedImportType = ImportType.None; - } - } - - /** - * Selected a local entity - * @param entity - */ - public selectEntity(entity): void { - this.selectedEntity = entity; - this.selectedImportType = ImportType.LocalEntity; - } - - /** - * Deselect every element from both entity and authority lists - */ - public deselectAllLists(): void { - this.selectService.deselectAll(this.entityListId); - this.selectService.deselectAll(this.authorityListId); - } - - /** - * Perform the action based on the correction type - * by posting the data to the OpenAIRE Broker Event API. - * Data is formatted as follows, in the exact order: - * - * - * - */ - performAction() { - if (hasValue(this.selectedEntity)) { - const selectedItemLink = (this.selectedEntity as SearchResult).indexableObject._links.self.href; - - this.itemService.findById(this.itemUuid).pipe( - getFirstSucceededRemoteDataPayload(), - switchMap((item: Item) => { - const data: string = this.correctionTypeObject._links.self.href + '\n' + item._links.self.href + '\n' + selectedItemLink; - return this.openaireBrokerEventRestService.postData(data); - }), - getFirstCompletedRemoteData() - ).subscribe((res: RemoteData) => { - if (res.hasSucceeded) { - this.selectedImportType = ImportType.None; - const message = 'correction-type.manage-relation.' + this.correctionTypeObject.id + '.notification.success'; - this.notificationsService.success(this.translateService.get(message)); - this.deselectAllLists(); - this.back(); - } else { - this.notificationsService.error(this.translateService.get('correction-type.manage-relation.action.notification.error')); - } - }); - } - } - - /** - * Navigate back to the previous page - */ - back() { - this.router.navigate(['../../'], { relativeTo: this.aroute }); - } - - /** - * Unsubscribe from all subscriptions. - */ - ngOnDestroy(): void { - this.deselectAllLists(); - this.subs - .filter((sub) => hasValue(sub)) - .forEach((sub) => sub.unsubscribe()); - } -} diff --git a/src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.html b/src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.html new file mode 100644 index 00000000000..0c17373c407 --- /dev/null +++ b/src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.html @@ -0,0 +1,48 @@ +
+ + + +
+ + + + + + + + + diff --git a/src/app/shared/correction-suggestion/correction-suggestion.component.scss b/src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.scss similarity index 100% rename from src/app/shared/correction-suggestion/correction-suggestion.component.scss rename to src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.scss diff --git a/src/app/shared/correction-suggestion/withdrawn-reinstate-modal.component.ts b/src/app/shared/correction-suggestion/withdrawn-reinstate-modal.component.ts new file mode 100644 index 00000000000..8bce9460e4e --- /dev/null +++ b/src/app/shared/correction-suggestion/withdrawn-reinstate-modal.component.ts @@ -0,0 +1,47 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { ModalBeforeDismiss } from '../interfaces/modal-before-dismiss.interface'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { BehaviorSubject, Observable, of } from 'rxjs'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { DSpaceObject } from '../../core/shared/dspace-object.model'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; + +@Component({ + selector: 'ds-item-withdrawn-reinstate-modal', + templateUrl: './item-withdrawn-reinstate-modal.component.html', + styleUrls: ['./item-withdrawn-reinstate-modal.component.scss'] +}) +export class ItemWithdrawnReinstateModalComponent implements ModalBeforeDismiss { + + summary: string; + + canWithdraw$: Observable = of(false); + canReinstate$: Observable = of(false); + submitted$: BehaviorSubject = new BehaviorSubject(false); + + @Output() createQAEvent: EventEmitter = new EventEmitter(); + + constructor( + protected activeModal: NgbActiveModal, + protected authorizationService: AuthorizationDataService + ) {} + + onModalClose() { + this.activeModal.close(); + } + + beforeDismiss(): boolean { + // prevent the modal from being dismissed after version creation is initiated + return !this.submitted$.getValue(); + } + + onModalSubmit() { + this.submitted$.next(true); + this.createQAEvent.emit(this.summary); + } + + public setDso(dso: DSpaceObject) { + this.canWithdraw$ = this.authorizationService.isAuthorized(FeatureID.WithdrawItem, dso.self); + this.canReinstate$ = this.authorizationService.isAuthorized(FeatureID.ReinstateItem, dso.self); + } +} diff --git a/src/app/shared/dso-page/dso-edit-menu.resolver.ts b/src/app/shared/dso-page/dso-edit-menu.resolver.ts index 80a69c2830d..52ae6551945 100644 --- a/src/app/shared/dso-page/dso-edit-menu.resolver.ts +++ b/src/app/shared/dso-page/dso-edit-menu.resolver.ts @@ -21,6 +21,8 @@ import { getDSORoute } from '../../app-routing-paths'; import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service'; import { NotificationsService } from '../notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; +import { DsoWithdrawnModalService } from './dso-withdrawn-service/dso-withdrawn-modal.service'; +import { DsoReinstateModalService } from './dso-reinstate-service/dso-reinstate-modal.service'; /** * Creates the menus for the dspace object pages @@ -39,6 +41,8 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection protected researcherProfileService: ResearcherProfileDataService, protected notificationsService: NotificationsService, protected translate: TranslateService, + protected dsoWithdrawnModalService: DsoWithdrawnModalService, + protected dsoReinstateModalService: DsoReinstateModalService ) { } @@ -125,8 +129,10 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection this.dsoVersioningModalService.getVersioningTooltipMessage(dso, 'item.page.version.hasDraft', 'item.page.version.create'), this.authorizationService.isAuthorized(FeatureID.CanSynchronizeWithORCID, dso.self), this.authorizationService.isAuthorized(FeatureID.CanClaimItem, dso.self), + this.authorizationService.isAuthorized(FeatureID.WithdrawItem, dso.self), + this.authorizationService.isAuthorized(FeatureID.ReinstateItem, dso.self), ]).pipe( - map(([canCreateVersion, disableVersioning, versionTooltip, canSynchronizeWithOrcid, canClaimItem]) => { + map(([canCreateVersion, disableVersioning, versionTooltip, canSynchronizeWithOrcid, canClaimItem, canWithdrawItem, canReinstateItem]) => { const isPerson = this.getDsoType(dso) === 'person'; return [ { @@ -170,6 +176,32 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection icon: 'hand-paper', index: 3 }, + { + id: 'withdrawn-item', + active: false, + visible: canWithdrawItem, + model: { + type: MenuItemType.ONCLICK, + function: () => { + this.dsoWithdrawnModalService.openCreateWithdrawnModal(dso); + } + } as OnClickMenuItemModel, + icon: 'lock', + index: 4 + }, + { + id: 'reinstate-item', + active: false, + visible: canReinstateItem, + model: { + type: MenuItemType.ONCLICK, + function: () => { + this.dsoReinstateModalService.openCreateReinstateModal(dso); + } + } as OnClickMenuItemModel, + icon: 'unlock-keyhole', + index: 5 + } ]; }), ); diff --git a/src/app/shared/dso-page/dso-reinstate-service/dso-reinstate-modal.service.ts b/src/app/shared/dso-page/dso-reinstate-service/dso-reinstate-modal.service.ts new file mode 100644 index 00000000000..60714a9788c --- /dev/null +++ b/src/app/shared/dso-page/dso-reinstate-service/dso-reinstate-modal.service.ts @@ -0,0 +1,65 @@ +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { ItemDataService } from '../../../core/data/item-data.service'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; +import { + QualityAssuranceEventDataService +} from '../../../core/suggestion-notifications/qa/events/quality-assurance-event-data.service'; +import { ItemWithdrawnReinstateModalComponent } from '../../correction-suggestion/withdrawn-reinstate-modal.component'; +import { filter } from 'rxjs/operators'; +import { RemoteData } from '../../../core/data/remote-data'; +import { + QualityAssuranceEventObject +} from '../../../core/suggestion-notifications/qa/models/quality-assurance-event.model'; + + +@Injectable({ + providedIn: 'root' +}) +export class DsoReinstateModalService { + + constructor( + protected router: Router, + protected modalService: NgbModal, + protected itemService: ItemDataService, + private notificationsService: NotificationsService, + private translateService: TranslateService, + protected qaEventDataService: QualityAssuranceEventDataService + ) {} + + /** + * Open the reinstate modal for the provided dso + */ + openCreateReinstateModal(dso): void { + const selfHref = dso._links.self.href; + + const data = 'https://localhost:8080/server/api/config/correctiontypes/reinstateRequest' + '\n' + selfHref; + // Open modal + const activeModal = this.modalService.open(ItemWithdrawnReinstateModalComponent); + (activeModal.componentInstance as ItemWithdrawnReinstateModalComponent).setDso(dso); + (activeModal.componentInstance as ItemWithdrawnReinstateModalComponent).submitted$ + .pipe( + filter((val) => val) + ).subscribe( + () => { + this.sendQARequest(data); + activeModal.close(); + } + ); + } + + sendQARequest(data: string): void { + this.qaEventDataService.postData(data) + .subscribe((res: RemoteData) => { + if (res.hasSucceeded) { + const message = 'reinstate'; + this.notificationsService.success(this.translateService.get(message)); + } else { + this.notificationsService.error(this.translateService.get('correction-type.manage-relation.action.notification.error')); + } + }); + } + +} diff --git a/src/app/shared/dso-page/dso-withdrawn-service/dso-withdrawn-modal.service.ts b/src/app/shared/dso-page/dso-withdrawn-service/dso-withdrawn-modal.service.ts new file mode 100644 index 00000000000..8986fa9555c --- /dev/null +++ b/src/app/shared/dso-page/dso-withdrawn-service/dso-withdrawn-modal.service.ts @@ -0,0 +1,64 @@ +import { Injectable } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { Router } from '@angular/router'; +import { ItemDataService } from '../../../core/data/item-data.service'; +import { ItemWithdrawnReinstateModalComponent } from '../../correction-suggestion/withdrawn-reinstate-modal.component'; +import { + QualityAssuranceEventDataService +} from '../../../core/suggestion-notifications/qa/events/quality-assurance-event-data.service'; +import { + QualityAssuranceEventObject +} from '../../../core/suggestion-notifications/qa/models/quality-assurance-event.model'; +import { RemoteData } from '../../../core/data/remote-data'; +import { TranslateService } from '@ngx-translate/core'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { filter } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class DsoWithdrawnModalService { + + constructor( + protected router: Router, + protected modalService: NgbModal, + protected itemService: ItemDataService, + private notificationsService: NotificationsService, + private translateService: TranslateService, + protected qaEventDataService: QualityAssuranceEventDataService + ) {} + + /** + * Open the create withdrawn modal for the provided dso + */ + openCreateWithdrawnModal(dso): void { + const selfHref = dso._links.self.href; + + const data = 'https://localhost:8080/server/api/config/correctiontypes/withdrawnRequest' + '\n' + selfHref; + // Open modal + const activeModal = this.modalService.open(ItemWithdrawnReinstateModalComponent); + (activeModal.componentInstance as ItemWithdrawnReinstateModalComponent).setDso(dso); + (activeModal.componentInstance as ItemWithdrawnReinstateModalComponent).submitted$ + .pipe( + filter((val) => val) + ).subscribe( + () => { + this.sendQARequest(data); + activeModal.close(); + } + ); + } + + sendQARequest(data: string): void { + this.qaEventDataService.postData(data) + .subscribe((res: RemoteData) => { + if (res.hasSucceeded) { + const message = 'withdrawn'; + this.notificationsService.success(this.translateService.get(message)); + } else { + this.notificationsService.error(this.translateService.get('correction-type.manage-relation.action.notification.error')); + } + }); + } +} + diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 0f7871f7f9b..90a853168cd 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -284,6 +284,9 @@ import { } from '../item-page/simple/field-components/specific-field/title/themed-item-page-field.component'; import { BitstreamListItemComponent } from './object-list/bitstream-list-item/bitstream-list-item.component'; import { NgxPaginationModule } from 'ngx-pagination'; +import { + QualityAssuranceEventDataService +} from '../core/suggestion-notifications/qa/events/quality-assurance-event-data.service'; const MODULES = [ CommonModule, @@ -471,7 +474,8 @@ const ENTRY_COMPONENTS = [ const PROVIDERS = [ TruncatableService, MockAdminGuard, - AbstractTrackableComponent + AbstractTrackableComponent, + QualityAssuranceEventDataService ]; const DIRECTIVES = [ diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 4c13ec73d14..c0407c8bf88 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2562,6 +2562,12 @@ "item.version.create.modal.header": "New version", + "item.qa.withdrawn.modal.header": "Send Withdrawn Item request", + + "item.qa.reinstate.modal.header": "Send Reinstate Item request", + + "item.qa.reinstate.create.modal.header": "New version", + "item.version.create.modal.text": "Create a new version for this item", "item.version.create.modal.text.startingFrom": "starting from version {{version}}", @@ -2570,16 +2576,30 @@ "item.version.create.modal.button.confirm.tooltip": "Create new version", + "item.qa.withdrawn-reinstate.modal.button.confirm.tooltip": "Send request", + + "item.qa.withdrown-reinstate.create.modal.button.confirm": "Send Request", + "item.version.create.modal.button.cancel": "Cancel", + "item.qa.withdrawn-reinstate.create.modal.button.cancel": "Cancel", + "item.version.create.modal.button.cancel.tooltip": "Do not create new version", + "item.qa.withdrawn-reinstate.create.modal.button.cancel.tooltip": "Do not send request", + "item.version.create.modal.form.summary.label": "Summary", + "item.qa-withdrawn-reinstate.create.modal.form.summary.label": "Summary", + "item.version.create.modal.form.summary.placeholder": "Insert the summary for the new version", + "item.qa.withdrown-reinstate.modal.form.summary.placeholder": "Indicates the reasons (optionaly)", + "item.version.create.modal.submitted.header": "Creating new version...", + "item.qa.withdrawn.modal.submitted.header": "Sending withdrawn request...", + "item.version.create.modal.submitted.text": "The new version is being created. This may take some time if the item has a lot of relationships.", "item.version.create.notification.success": "New version has been created with version number {{version}}", diff --git a/src/themes/custom/lazy-theme.module.ts b/src/themes/custom/lazy-theme.module.ts index bff10cdd5a3..f2ef797eac1 100644 --- a/src/themes/custom/lazy-theme.module.ts +++ b/src/themes/custom/lazy-theme.module.ts @@ -1,4 +1,3 @@ -import { CorrectionSuggestionModule } from './../../app/shared/correction-suggestion/correction-suggestion.module'; import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AdminRegistriesModule } from '../../app/admin/admin-registries/admin-registries.module'; @@ -299,8 +298,7 @@ const DECLARATIONS = [ SystemWideAlertModule, NgxGalleryModule, FormModule, - RequestCopyModule, - CorrectionSuggestionModule, + RequestCopyModule ], declarations: DECLARATIONS, exports: [ From 0b391970e0975f7b0cc0839eb9c55c949cd08991 Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Wed, 1 Nov 2023 14:40:46 +0100 Subject: [PATCH 15/38] [CST-12109] added new labels --- src/app/shared/dso-page/dso-edit-menu.resolver.ts | 2 ++ src/assets/i18n/en.json5 | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/app/shared/dso-page/dso-edit-menu.resolver.ts b/src/app/shared/dso-page/dso-edit-menu.resolver.ts index 52ae6551945..1dcae4de1fc 100644 --- a/src/app/shared/dso-page/dso-edit-menu.resolver.ts +++ b/src/app/shared/dso-page/dso-edit-menu.resolver.ts @@ -182,6 +182,7 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection visible: canWithdrawItem, model: { type: MenuItemType.ONCLICK, + text:'item.page.withdrawn', function: () => { this.dsoWithdrawnModalService.openCreateWithdrawnModal(dso); } @@ -195,6 +196,7 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection visible: canReinstateItem, model: { type: MenuItemType.ONCLICK, + text:'item.page.reinstate', function: () => { this.dsoReinstateModalService.openCreateReinstateModal(dso); } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index c0407c8bf88..4b03ce208f9 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2450,6 +2450,10 @@ "item.page.version.create": "Create new version", + "item.page.withdrawn": "Withdrawn this Item", + + "item.page.reinstate": "Reinstate this Item", + "item.page.version.hasDraft": "A new version cannot be created because there is an inprogress submission in the version history", "item.page.claim.button": "Claim", From 6dc3528ae8cd607973e451a6ed616d9305c690da Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Fri, 3 Nov 2023 00:36:40 +0100 Subject: [PATCH 16/38] [CTS-12109] refactoring --- .../quality-assurance-event-data.service.ts | 15 ++++++++-- ...m-withdrawn-reinstate-modal.component.html | 6 ++-- .../withdrawn-reinstate-modal.component.ts | 28 +++++++++++-------- .../shared/dso-page/dso-edit-menu.resolver.ts | 4 +-- .../dso-reinstate-modal.service.ts | 28 +++++++++++-------- .../dso-withdrawn-modal.service.ts | 26 +++++++++-------- .../quality-assurance-events.component.html | 3 +- 7 files changed, 65 insertions(+), 45 deletions(-) diff --git a/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts b/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts index 23e7d6a6a4c..284cd8791a7 100644 --- a/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts +++ b/src/app/core/suggestion-notifications/qa/events/quality-assurance-event-data.service.ts @@ -25,7 +25,8 @@ import { SearchData, SearchDataImpl } from '../../../data/base/search-data'; import { DefaultChangeAnalyzer } from '../../../data/default-change-analyzer.service'; import { hasValue } from '../../../../shared/empty.util'; import { DeleteByIDRequest, PostRequest } from '../../../data/request.models'; -import { HttpHeaders } from '@angular/common/http'; +import { HttpHeaders, HttpParams } from '@angular/common/http'; +import { HttpOptions } from '../../../dspace-rest/dspace-rest.service'; /** * The service handling all Quality Assurance topic REST requests. @@ -207,13 +208,21 @@ export class QualityAssuranceEventDataService extends IdentifiableDataService> { + postData(target: string, correctionType: string, related: string, reason: string): Observable> { const requestId = this.requestService.generateRequestId(); const href$ = this.getBrowseEndpoint(); return href$.pipe( switchMap((href: string) => { - const request = new PostRequest(requestId, href, data, { headers: new HttpHeaders().set('Content-Type', 'text/uri-list') }); + const options: HttpOptions = Object.create({}); + let headers = new HttpHeaders(); + headers = headers.append('Content-Type', 'application/json'); + options.headers = headers; + let params = new HttpParams(); + params = params.append('target', target) + .append('correctionType', correctionType); + options.params = params; + const request = new PostRequest(requestId, href, {'reason': reason} , options); if (hasValue(this.responseMsToLive)) { request.responseMsToLive = this.responseMsToLive; } diff --git a/src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.html b/src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.html index 0c17373c407..1201fbee6f2 100644 --- a/src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.html +++ b/src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.html @@ -1,5 +1,5 @@
- diff --git a/src/app/notifications/qa/topics/quality-assurance-topics.component.ts b/src/app/notifications/qa/topics/quality-assurance-topics.component.ts index ecc7e668985..96a613d26ca 100644 --- a/src/app/notifications/qa/topics/quality-assurance-topics.component.ts +++ b/src/app/notifications/qa/topics/quality-assurance-topics.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'; import { Observable, Subscription } from 'rxjs'; import { distinctUntilChanged, map, take, tap } from 'rxjs/operators'; @@ -20,7 +20,6 @@ import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../core import { Item } from '../../../core/shared/item.model'; import { getItemPageRoute } from '../../../item-page/item-page-routing-paths'; import { getNotificatioQualityAssuranceRoute } from '../../../admin/admin-routing-paths'; -import { format } from 'date-fns'; /** * Component to display the Quality Assurance topic list. @@ -30,7 +29,7 @@ import { format } from 'date-fns'; templateUrl: './quality-assurance-topics.component.html', styleUrls: ['./quality-assurance-topics.component.scss'], }) -export class QualityAssuranceTopicsComponent implements OnInit { +export class QualityAssuranceTopicsComponent implements OnInit, OnDestroy, AfterViewInit { /** * The pagination system configuration for HTML listing. * @type {PaginationComponentOptions} @@ -138,7 +137,7 @@ export class QualityAssuranceTopicsComponent implements OnInit { * Dispatch the Quality Assurance topics retrival. */ public getQualityAssuranceTopics(source: string, target?: string): void { - this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig).pipe( + this.subs.push(this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig).pipe( distinctUntilChanged(), ).subscribe((options: PaginationComponentOptions) => { this.notificationsStateService.dispatchRetrieveQualityAssuranceTopics( @@ -147,7 +146,7 @@ export class QualityAssuranceTopicsComponent implements OnInit { source, target ); - }); + })); } /** @@ -202,20 +201,6 @@ export class QualityAssuranceTopicsComponent implements OnInit { return getNotificatioQualityAssuranceRoute(); } - /** - * Formats the given date string into the format 'yyyy-MM-dd HH:mm:ss'. - * If the date is falsy, an empty string is returned. - * - * @param date - The date string to format. - * @returns The formatted date string. - */ - formatDate(date: string): string { - if (!date) { - return ''; - } - return format(new Date(date), 'yyyy-MM-dd HH:mm:ss'); - } - /** * Unsubscribe from all subscriptions. */ diff --git a/src/app/shared/correction-suggestion/withdrawn-reinstate-modal.component.ts b/src/app/shared/correction-suggestion/withdrawn-reinstate-modal.component.ts index f843bec75d7..de842eb1e5b 100644 --- a/src/app/shared/correction-suggestion/withdrawn-reinstate-modal.component.ts +++ b/src/app/shared/correction-suggestion/withdrawn-reinstate-modal.component.ts @@ -9,13 +9,29 @@ import { AuthorizationDataService } from '../../core/data/feature-authorization/ templateUrl: './item-withdrawn-reinstate-modal.component.html', styleUrls: ['./item-withdrawn-reinstate-modal.component.scss'] }) +/** + * Represents a modal component for withdrawing or reinstating an item. + * Implements the ModalBeforeDismiss interface. + */ export class ItemWithdrawnReinstateModalComponent implements ModalBeforeDismiss { + /** + * The reason for withdrawing or reinstating a suggestion. + */ reason: string; - + /** + * Indicates whether the item can be withdrawn. + */ canWithdraw: boolean; + /** + * BehaviorSubject that represents the submitted state. + * Emits a boolean value indicating whether the form has been submitted or not. + */ submitted$: BehaviorSubject = new BehaviorSubject(false); - + /** + * Event emitter for creating a QA event. + * @event createQAEvent + */ @Output() createQAEvent: EventEmitter = new EventEmitter(); constructor( @@ -23,22 +39,36 @@ export class ItemWithdrawnReinstateModalComponent implements ModalBeforeDismiss protected authorizationService: AuthorizationDataService, ) {} + /** + * Closes the modal. + */ onModalClose() { this.activeModal.close(); } + /** + * Determines whether the modal can be dismissed. + * @returns {boolean} True if the modal can be dismissed, false otherwise. + */ beforeDismiss(): boolean { // prevent the modal from being dismissed after version creation is initiated return !this.submitted$.getValue(); } + /** + * Handles the submission of the modal form. + * Emits the reason for withdrawal or reinstatement through the createQAEvent output. + */ onModalSubmit() { this.submitted$.next(true); this.createQAEvent.emit(this.reason); } + /** + * Sets the withdrawal state of the component. + * @param state The new withdrawal state. + */ public setWithdraw(state: boolean) { this.canWithdraw = state; } - } diff --git a/src/app/shared/dso-page/dso-withdrawn-reinstate-service/dso-withdrawn-reinstate-modal.service.ts b/src/app/shared/dso-page/dso-withdrawn-reinstate-service/dso-withdrawn-reinstate-modal.service.ts index bb96c693aa0..d5f47ed57bf 100644 --- a/src/app/shared/dso-page/dso-withdrawn-reinstate-service/dso-withdrawn-reinstate-modal.service.ts +++ b/src/app/shared/dso-page/dso-withdrawn-reinstate-service/dso-withdrawn-reinstate-modal.service.ts @@ -23,6 +23,9 @@ export const REQUEST_REINSTATE = 'REQUEST/REINSTATE'; @Injectable({ providedIn: 'root' }) +/** + * Service for managing the withdrawn/reinstate modal for a DSO. + */ export class DsoWithdrawnReinstateModalService { constructor( diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index bbfc4f6e4fd..9b03d04d795 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -518,7 +518,7 @@ "admin.quality-assurance.page.title": "Quality Assurance", - "admin.notifications.source.breadcrumbs": "Quality Assurance Source", + "admin.notifications.source.breadcrumbs": "Quality Assurance", "admin.access-control.groups.form.tooltip.editGroupPage": "On this page, you can modify the properties and members of a group. In the top section, you can edit the group name and description, unless this is an admin group for a collection or community, in which case the group name and description are auto-generated and cannot be edited. In the following sections, you can edit group membership. See [the wiki](https://wiki.lyrasis.org/display/DSDOC7x/Create+or+manage+a+user+group) for more details.", @@ -2480,9 +2480,9 @@ "item.page.version.create": "Create new version", - "item.page.withdrawn": "Withdraw this Item", + "item.page.withdrawn": "Request a withdrawal for this item", - "item.page.reinstate": "Reinstate this Item", + "item.page.reinstate": "Request reinstatement", "item.page.version.hasDraft": "A new version cannot be created because there is an inprogress submission in the version history", @@ -3190,7 +3190,9 @@ "quality-assurance.table.actions": "Actions", - "quality-assurance.button.detail": "Show details", + "quality-assurance.source-list.button.detail": "Show topics for {{param}}", + + "quality-assurance.topics-list.button.detail": "Show suggestions for {{param}}", "quality-assurance.noTopics": "No topics found.", @@ -3256,6 +3258,8 @@ "quality-assurance.events.back": "Back to topics", + "quality-assurance.events.back-to-sources": "Back to sources", + "quality-assurance.event.table.less": "Show less", "quality-assurance.event.table.more": "Show more", From 7350fdd1254250debe4e3cefd5fef28251a0edf6 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Wed, 21 Feb 2024 18:06:45 +0100 Subject: [PATCH 32/38] [CST-12109] qa-event notification box fixes --- .../admin-notifications-routing.module.ts | 15 ++++ .../qa-event-notification.component.html | 18 ++++- .../qa-event-notification.component.spec.ts | 72 +++++++++++++------ .../qa-event-notification.component.ts | 16 +++-- .../quality-assurance-events.component.html | 8 --- .../quality-assurance-events.component.ts | 36 ++++++---- .../quality-assurance-topics.component.html | 10 +-- .../quality-assurance-topics.component.ts | 13 +++- src/app/shared/utils/split.pipe.ts | 6 ++ .../suggestions.service.ts | 15 ++-- src/assets/i18n/en.json5 | 12 ++-- 11 files changed, 149 insertions(+), 72 deletions(-) diff --git a/src/app/admin/admin-notifications/admin-notifications-routing.module.ts b/src/app/admin/admin-notifications/admin-notifications-routing.module.ts index 761c819b1b7..e00a88cbe22 100644 --- a/src/app/admin/admin-notifications/admin-notifications-routing.module.ts +++ b/src/app/admin/admin-notifications/admin-notifications-routing.module.ts @@ -56,6 +56,21 @@ import { showBreadcrumbsFluid: false } }, + { + canActivate: [ AuthenticatedGuard ], + path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId/target/:targetId`, + component: AdminQualityAssuranceTopicsPageComponent, + pathMatch: 'full', + resolve: { + breadcrumb: I18nBreadcrumbResolver, + openaireQualityAssuranceTopicsParams: AdminQualityAssuranceTopicsPageResolver + }, + data: { + title: 'admin.quality-assurance.page.title', + breadcrumbKey: 'admin.quality-assurance', + showBreadcrumbsFluid: false + } + }, { canActivate: [ SiteAdministratorGuard ], path: `${QUALITY_ASSURANCE_EDIT_PATH}`, diff --git a/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.html b/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.html index e38ecd82394..169fe00950f 100644 --- a/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.html +++ b/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.html @@ -2,9 +2,21 @@
-
{{ this.item.isArchived ? ('qa-event-notification.check.notification-withdrawn' | translate) - : ('qa-event-notification.check.notification-reinstate' | translate) }}
-
+
diff --git a/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.spec.ts b/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.spec.ts index 2a1ccb28aec..a9d16440a80 100644 --- a/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.spec.ts +++ b/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.spec.ts @@ -1,47 +1,79 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { QaEventNotificationComponent } from './qa-event-notification.component'; -import { createSuccessfulRemoteDataObject$ } from 'src/app/shared/remote-data.utils'; -import { createPaginatedList } from 'src/app/shared/testing/utils.test'; -import { QualityAssuranceSourceObject } from 'src/app/core/notifications/qa/models/quality-assurance-source.model'; +import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; +import { createPaginatedList } from '../../../shared/testing/utils.test'; +import { QualityAssuranceSourceObject } from '../../../core/notifications/qa/models/quality-assurance-source.model'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; -import { QualityAssuranceSourceDataService } from 'src/app/core/notifications/qa/source/quality-assurance-source-data.service'; -import { RequestService } from 'src/app/core/data/request.service'; -import { NotificationsService } from 'src/app/shared/notifications/notifications.service'; -import { ObjectCacheService } from 'src/app/core/cache/object-cache.service'; -import { RemoteDataBuildService } from 'src/app/core/cache/builders/remote-data-build.service'; +import { QualityAssuranceSourceDataService } from '../../../core/notifications/qa/source/quality-assurance-source-data.service'; +import { RequestService } from '../../../core/data/request.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { ObjectCacheService } from '../../../core/cache/object-cache.service'; +import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service'; import { provideMockStore } from '@ngrx/store/testing'; -import { HALEndpointService } from 'src/app/core/shared/hal-endpoint.service'; -import { HALEndpointServiceStub } from 'src/app/shared/testing/hal-endpoint-service.stub'; +import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; +import { HALEndpointServiceStub } from '../../../shared/testing/hal-endpoint-service.stub'; +import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { of } from 'rxjs'; +import { By } from '@angular/platform-browser'; +import { SplitPipe } from 'src/app/shared/utils/split.pipe'; describe('QaEventNotificationComponent', () => { let component: QaEventNotificationComponent; let fixture: ComponentFixture; let qualityAssuranceSourceDataServiceStub: any; + let authorizationService: AuthorizationDataService; - const obj = createSuccessfulRemoteDataObject$(createPaginatedList([new QualityAssuranceSourceObject()])); + const obj = Object.assign(new QualityAssuranceSourceObject(), { + id: 'sourceName:target', + source: 'sourceName', + target: 'target', + totalEvents: 1 + }); + + const objPL = createSuccessfulRemoteDataObject$(createPaginatedList([obj])); const item = Object.assign({ uuid: '1234' }); beforeEach(async () => { + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: of(true) + }); + + qualityAssuranceSourceDataServiceStub = { + getSourcesByTarget: () => objPL + }; await TestBed.configureTestingModule({ imports: [CommonModule, TranslateModule.forRoot()], - declarations: [ QaEventNotificationComponent ], + declarations: [QaEventNotificationComponent, SplitPipe], providers: [ - { provide: QualityAssuranceSourceDataService, useValue: qualityAssuranceSourceDataServiceStub }, - { provide: RequestService, useValue: {} }, - { provide: NotificationsService, useValue: {} }, - { provide: HALEndpointService, useValue: new HALEndpointServiceStub('test')}, - ObjectCacheService, - RemoteDataBuildService, - provideMockStore({}) - ], + { provide: QualityAssuranceSourceDataService, useValue: qualityAssuranceSourceDataServiceStub }, + { provide: RequestService, useValue: {} }, + { provide: NotificationsService, useValue: {} }, + { provide: HALEndpointService, useValue: new HALEndpointServiceStub('test') }, + { provide: AuthorizationDataService, useValue: authorizationService }, + ObjectCacheService, + RemoteDataBuildService, + provideMockStore({}) + ], }) .compileComponents(); fixture = TestBed.createComponent(QaEventNotificationComponent); component = fixture.componentInstance; component.item = item; + component.sources$ = of([obj]); fixture.detectChanges(); }); + it('should create', () => { expect(component).toBeTruthy(); }); + + it('should display sources if present', () => { + const alertElements = fixture.debugElement.queryAll(By.css('.alert')); + expect(alertElements.length).toBe(1); + }); + + it('should return the quality assurance route when getQualityAssuranceRoute is called', () => { + const route = component.getQualityAssuranceRoute(); + expect(route).toBe('/notifications/quality-assurance'); + }); }); diff --git a/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.ts b/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.ts index e5f509c405e..6f8e55c9d2b 100644 --- a/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.ts +++ b/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.ts @@ -6,10 +6,12 @@ import { FindListOptions } from '../../../core/data/find-list-options.model'; import { RequestParam } from '../../../core/cache/models/request-param.model'; import { QualityAssuranceSourceDataService } from '../../../core/notifications/qa/source/quality-assurance-source-data.service'; import { QualityAssuranceSourceObject } from '../../../core/notifications/qa/models/quality-assurance-source.model'; -import { map } from 'rxjs/operators'; +import { catchError, map } from 'rxjs/operators'; import { RemoteData } from '../../../core/data/remote-data'; import { getNotificatioQualityAssuranceRoute } from '../../../admin/admin-routing-paths'; import { PaginatedList } from 'src/app/core/data/paginated-list.model'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; @Component({ selector: 'ds-qa-event-notification', @@ -32,11 +34,16 @@ export class QaEventNotificationComponent implements OnChanges { */ sources$: Observable; /** - * The type of alert to display for the notification. + * An observable that emits a boolean representing whether the current user is an admin. */ + isAdmin$: Observable; + constructor( private qualityAssuranceSourceDataService: QualityAssuranceSourceDataService, - ) { } + private authService: AuthorizationDataService, + ) { + this.isAdmin$ = this.authService.isAuthorized(FeatureID.AdministratorOf); + } /** * Detect changes to the item input and update the sources$ observable. @@ -63,7 +70,8 @@ export class QaEventNotificationComponent implements OnChanges { return data.payload.page; } return []; - }) + }), + catchError(() => []) ); } diff --git a/src/app/notifications/qa/events/quality-assurance-events.component.html b/src/app/notifications/qa/events/quality-assurance-events.component.html index 9d9cd2b58c1..286552a00c6 100644 --- a/src/app/notifications/qa/events/quality-assurance-events.component.html +++ b/src/app/notifications/qa/events/quality-assurance-events.component.html @@ -196,14 +196,6 @@

- diff --git a/src/app/notifications/qa/events/quality-assurance-events.component.ts b/src/app/notifications/qa/events/quality-assurance-events.component.ts index 7dcccae2de2..60550a8bafe 100644 --- a/src/app/notifications/qa/events/quality-assurance-events.component.ts +++ b/src/app/notifications/qa/events/quality-assurance-events.component.ts @@ -33,7 +33,7 @@ import { FindListOptions } from '../../../core/data/find-list-options.model'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; import { NoContent } from '../../../core/shared/NoContent.model'; -import {environment} from '../../../../environments/environment'; +import { environment } from '../../../../environments/environment'; /** * Component to display the Quality Assurance event list. @@ -119,6 +119,9 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy { */ protected subs: Subscription[] = []; + /** + * Observable that emits a boolean value indicating whether the user is an admin. + */ isAdmin$: Observable; /** @@ -146,15 +149,13 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy { */ ngOnInit(): void { this.isEventPageLoading.next(true); - this.isAdmin$ = this.authorizationService.isAuthorized(FeatureID.AdministratorOf); - // this.sourceId = this.activatedRoute.snapshot.params.sourceId; this.activatedRoute.paramMap.pipe( - tap((params) => { - this.sourceUrlForProjectSearch = environment.qualityAssuranceConfig.sourceUrlMapForProjectSearch[params.get('sourceId')]; - this.sourceId = params.get('sourceId'); - }), - map((params) => params.get('topicId')), + tap((params) => { + this.sourceUrlForProjectSearch = environment.qualityAssuranceConfig.sourceUrlMapForProjectSearch[params.get('sourceId')]; + this.sourceId = params.get('sourceId'); + }), + map((params) => params.get('topicId')), take(1), switchMap((id: string) => { const regEx = /!/g; @@ -180,11 +181,11 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy { */ public hasDetailColumn(): boolean { return (this.showTopic.indexOf('/PROJECT') !== -1 || - this.showTopic.indexOf('/PID') !== -1 || - this.showTopic.indexOf('/SUBJECT') !== -1 || - this.showTopic.indexOf('/WITHDRAWN') !== -1 || - this.showTopic.indexOf('/REINSTATE') !== -1 || - this.showTopic.indexOf('/ABSTRACT') !== -1 + this.showTopic.indexOf('/PID') !== -1 || + this.showTopic.indexOf('/SUBJECT') !== -1 || + this.showTopic.indexOf('/WITHDRAWN') !== -1 || + this.showTopic.indexOf('/REINSTATE') !== -1 || + this.showTopic.indexOf('/ABSTRACT') !== -1 ); } @@ -270,9 +271,9 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy { eventData.isRunning = true; let operation; if (action === 'UNDO') { - operation = this.delete(eventData); + operation = this.delete(eventData); } else { - operation = this.qualityAssuranceEventRestService.patchEvent(action, eventData.event, eventData.reason); + operation = this.qualityAssuranceEventRestService.patchEvent(action, eventData.event, eventData.reason); } this.subs.push( operation.pipe( @@ -462,6 +463,11 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy { ); } + /** + * Deletes a quality assurance event. + * @param qaEvent The quality assurance event to delete. + * @returns An Observable of RemoteData containing NoContent. + */ delete(qaEvent: QualityAssuranceEventData): Observable> { return this.qualityAssuranceEventRestService.deleteQAEvent(qaEvent); } diff --git a/src/app/notifications/qa/topics/quality-assurance-topics.component.html b/src/app/notifications/qa/topics/quality-assurance-topics.component.html index e2148a611e9..631296ea1c4 100644 --- a/src/app/notifications/qa/topics/quality-assurance-topics.component.html +++ b/src/app/notifications/qa/topics/quality-assurance-topics.component.html @@ -44,7 +44,7 @@

{{'quality-assurance.topics'| translate}}

@@ -58,12 +58,4 @@

{{'quality-assurance.topics'| translate}}

- diff --git a/src/app/notifications/qa/topics/quality-assurance-topics.component.ts b/src/app/notifications/qa/topics/quality-assurance-topics.component.ts index 96a613d26ca..bda45c71c87 100644 --- a/src/app/notifications/qa/topics/quality-assurance-topics.component.ts +++ b/src/app/notifications/qa/topics/quality-assurance-topics.component.ts @@ -14,7 +14,7 @@ import { AdminQualityAssuranceTopicsPageParams } from '../../../admin/admin-notifications/admin-quality-assurance-topics-page/admin-quality-assurance-topics-page-resolver.service'; import { PaginationService } from '../../../core/pagination/pagination.service'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { ItemDataService } from '../../../core/data/item-data.service'; import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../core/shared/operators'; import { Item } from '../../../core/shared/item.model'; @@ -87,6 +87,7 @@ export class QualityAssuranceTopicsComponent implements OnInit, OnDestroy, After private activatedRoute: ActivatedRoute, private itemService: ItemDataService, private notificationsStateService: NotificationsStateService, + private router: Router, ) { this.sourceId = this.activatedRoute.snapshot.params.sourceId; this.targetId = this.activatedRoute.snapshot.params.targetId; @@ -96,7 +97,15 @@ export class QualityAssuranceTopicsComponent implements OnInit, OnDestroy, After * Component initialization. */ ngOnInit(): void { - this.topics$ = this.notificationsStateService.getQualityAssuranceTopics(); + this.topics$ = this.notificationsStateService.getQualityAssuranceTopics().pipe( + tap((topics: QualityAssuranceTopicObject[]) => { + const forward = this.activatedRoute.snapshot.queryParams?.forward === 'true'; + if (topics.length === 1 && forward) { + // If there is only one topic, navigate to the first topic automatically + this.router.navigate([this.getQualityAssuranceRoute(), this.sourceId, topics[0].id]); + } + }) + ); this.totalElements$ = this.notificationsStateService.getQualityAssuranceTopicsTotals(); } diff --git a/src/app/shared/utils/split.pipe.ts b/src/app/shared/utils/split.pipe.ts index cccd285922a..e4d0f2cc49c 100644 --- a/src/app/shared/utils/split.pipe.ts +++ b/src/app/shared/utils/split.pipe.ts @@ -1,4 +1,10 @@ import { Pipe, PipeTransform } from '@angular/core'; +/** + * Custom pipe to split a string into an array of substrings based on a specified separator. + * @param value - The string to be split. + * @param separator - The separator used to split the string. + * @returns An array of substrings. + */ @Pipe({ name: 'dsSplit' }) diff --git a/src/app/suggestion-notifications/suggestions.service.ts b/src/app/suggestion-notifications/suggestions.service.ts index 874659eab5f..8a99403586b 100644 --- a/src/app/suggestion-notifications/suggestions.service.ts +++ b/src/app/suggestion-notifications/suggestions.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { of, forkJoin, Observable } from 'rxjs'; -import { catchError, map, mergeMap, take } from 'rxjs/operators'; +import { catchError, map, mergeMap, take, tap } from 'rxjs/operators'; import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; import { RemoteData } from '../core/data/remote-data'; @@ -12,8 +12,9 @@ import { ResearcherProfile } from '../core/profile/model/researcher-profile.mode import { getAllSucceededRemoteDataPayload, getFinishedRemoteData, + getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload, - getFirstSucceededRemoteListPayload + getFirstSucceededRemoteListPayload, } from '../core/shared/operators'; import { Suggestion } from '../core/suggestion-notifications/models/suggestion.model'; import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service'; @@ -155,10 +156,10 @@ export class SuggestionsService { */ public retrieveCurrentUserSuggestions(userUuid: string): Observable { return this.researcherProfileService.findById(userUuid, true).pipe( - getFirstSucceededRemoteDataPayload(), - mergeMap((profile: ResearcherProfile) => { - if (isNotEmpty(profile)) { - return this.researcherProfileService.findRelatedItemId(profile).pipe( + getFirstCompletedRemoteData(), + mergeMap((profile: RemoteData ) => { + if (isNotEmpty(profile) && profile.hasSucceeded && isNotEmpty(profile.payload)) { + return this.researcherProfileService.findRelatedItemId(profile.payload).pipe( mergeMap((itemId: string) => { return this.suggestionsDataService.getTargetsByUser(itemId).pipe( getFirstSucceededRemoteListPayload() @@ -169,7 +170,7 @@ export class SuggestionsService { return of([]); } }), - take(1) + catchError(() => of([])) ); } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 27e0fee7446..27cc62033fa 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2708,13 +2708,15 @@ "correction-type.manage-relation.action.notification.withdrawn": "Withdraw request sent.", - "qa-event-notification.check.notification-withdrawn": "You have requested to withdraw this item.", + "qa-event-notification.check.notification-withdrawn.user": "There are {{num}} items pending review(s) to check.", - "qa-event-notification.check.notification-reinstate": "You have requested to reinstate this item.", + "qa-event-notification.check.notification-withdrawn.admin": "There are {{num}} {{source}} feedback(s) pending.", - "qa-event-notification-undo-withdrawn.check.button": "Undo request withdrawal", + "qa-event-notification.check.notification-reinstate": "There are {{num}} pending reinstatement(s) pending for this item.", - "qa-event-notification-undo-reinstate.check.button": "Undo request reinstatment", + "qa-event-notification-undo-withdrawn.check.button": "Check", + + "qa-event-notification-undo-reinstate.check.button": "Check", "item.version.create.modal.submitted.text": "The new version is being created. This may take some time if the item has a lot of relationships.", @@ -3246,6 +3248,8 @@ "quality-assurance.topics.description": "Below you can see all the topics received from the subscriptions to {{source}}.", + "quality-assurance.topics.description-with-target": "Below you can see all the topics received from the subscriptions to {{source}} in regards to the", + "quality-assurance.source.description": "Below you can see all the notification's sources.", "quality-assurance.topics": "Current Topics", From c34a49e55a252bc83df109f1a0cd582f23b54299 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Wed, 21 Feb 2024 18:11:30 +0100 Subject: [PATCH 33/38] [CST-12109] lint fix --- src/app/suggestion-notifications/suggestions.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/suggestion-notifications/suggestions.service.ts b/src/app/suggestion-notifications/suggestions.service.ts index 8a99403586b..8d70bb9d5f3 100644 --- a/src/app/suggestion-notifications/suggestions.service.ts +++ b/src/app/suggestion-notifications/suggestions.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { of, forkJoin, Observable } from 'rxjs'; -import { catchError, map, mergeMap, take, tap } from 'rxjs/operators'; +import { catchError, map, mergeMap, take } from 'rxjs/operators'; import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; import { RemoteData } from '../core/data/remote-data'; From e7579b7bf57e9a7444f317a5f7c009532b8e30fc Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Fri, 23 Feb 2024 15:03:52 +0100 Subject: [PATCH 34/38] [CST-12109] fix of text messages / source image --- .../alerts/item-alerts.component.spec.ts | 3 +- .../qa-event-notification.component.html | 23 +++--- .../qa-event-notification.component.spec.ts | 6 -- .../qa-event-notification.component.ts | 13 +-- src/app/menu.resolver.ts | 78 +++++++++--------- .../my-dspace-page.component.html | 2 +- ...ace-qa-events-notifications.component.html | 1 + ...ace-qa-events-notifications.component.scss | 1 + ...space-qa-events-notifications.component.ts | 3 +- src/assets/i18n/en.json5 | 12 +-- src/assets/images/qa-DSpaceUsers-logo.png | Bin 0 -> 8609 bytes 11 files changed, 58 insertions(+), 84 deletions(-) create mode 100644 src/assets/images/qa-DSpaceUsers-logo.png diff --git a/src/app/item-page/alerts/item-alerts.component.spec.ts b/src/app/item-page/alerts/item-alerts.component.spec.ts index 3ba88017848..474621f14ff 100644 --- a/src/app/item-page/alerts/item-alerts.component.spec.ts +++ b/src/app/item-page/alerts/item-alerts.component.spec.ts @@ -109,7 +109,7 @@ describe('ItemAlertsComponent', () => { }); }); - it('should return true when user is not an admin and there is at least one correction with topic REQUEST_REINSTATE', fakeAsync(() => { + it('should return true when user is not an admin and there is at least one correction with topic REQUEST_REINSTATE', fakeAsync((done) => { const isAdmin = false; const correction = [{ topic: 'REQUEST_REINSTATE' }]; authorizationService.isAuthorized.and.returnValue(of(isAdmin)); @@ -119,6 +119,7 @@ describe('ItemAlertsComponent', () => { tick(); result$.subscribe((result) => { expect(result).toBeTrue(); + done(); }); })); }); diff --git a/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.html b/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.html index 169fe00950f..5b3bb021f22 100644 --- a/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.html +++ b/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.html @@ -1,24 +1,21 @@
+
+ +
- - - {{ - (isAdmin$ | async) ? ('qa-event-notification.check.notification-withdrawn.admin' | translate : { source: (source.id | dsSplit: ':')[0], num: source.totalEvents }) - : ('qa-event-notification.check.notification-withdrawn.user' | translate : { num: source.totalEvents }) - }} - - - - {{ 'qa-event-notification.check.notification-reinstate' | translate: { num: source.totalEvents } }} - + {{'item.qa-event-notification.check.notification-info' | translate : {num: source.totalEvents } }}
+ class="btn btn-primary align-self-center"> + {{'item.qa-event-notification-info.check.button' | translate}} +
diff --git a/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.spec.ts b/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.spec.ts index a9d16440a80..ce231affeea 100644 --- a/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.spec.ts +++ b/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.spec.ts @@ -13,7 +13,6 @@ import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data import { provideMockStore } from '@ngrx/store/testing'; import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; import { HALEndpointServiceStub } from '../../../shared/testing/hal-endpoint-service.stub'; -import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { of } from 'rxjs'; import { By } from '@angular/platform-browser'; import { SplitPipe } from 'src/app/shared/utils/split.pipe'; @@ -22,7 +21,6 @@ describe('QaEventNotificationComponent', () => { let component: QaEventNotificationComponent; let fixture: ComponentFixture; let qualityAssuranceSourceDataServiceStub: any; - let authorizationService: AuthorizationDataService; const obj = Object.assign(new QualityAssuranceSourceObject(), { id: 'sourceName:target', @@ -34,9 +32,6 @@ describe('QaEventNotificationComponent', () => { const objPL = createSuccessfulRemoteDataObject$(createPaginatedList([obj])); const item = Object.assign({ uuid: '1234' }); beforeEach(async () => { - authorizationService = jasmine.createSpyObj('authorizationService', { - isAuthorized: of(true) - }); qualityAssuranceSourceDataServiceStub = { getSourcesByTarget: () => objPL @@ -49,7 +44,6 @@ describe('QaEventNotificationComponent', () => { { provide: RequestService, useValue: {} }, { provide: NotificationsService, useValue: {} }, { provide: HALEndpointService, useValue: new HALEndpointServiceStub('test') }, - { provide: AuthorizationDataService, useValue: authorizationService }, ObjectCacheService, RemoteDataBuildService, provideMockStore({}) diff --git a/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.ts b/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.ts index 6f8e55c9d2b..1557a65a0e6 100644 --- a/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.ts +++ b/src/app/item-page/simple/qa-event-notification/qa-event-notification.component.ts @@ -9,9 +9,7 @@ import { QualityAssuranceSourceObject } from '../../../core/notifications/qa/mod import { catchError, map } from 'rxjs/operators'; import { RemoteData } from '../../../core/data/remote-data'; import { getNotificatioQualityAssuranceRoute } from '../../../admin/admin-routing-paths'; -import { PaginatedList } from 'src/app/core/data/paginated-list.model'; -import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; -import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; +import { PaginatedList } from '../../../core/data/paginated-list.model'; @Component({ selector: 'ds-qa-event-notification', @@ -33,17 +31,10 @@ export class QaEventNotificationComponent implements OnChanges { * An observable that emits an array of QualityAssuranceSourceObject. */ sources$: Observable; - /** - * An observable that emits a boolean representing whether the current user is an admin. - */ - isAdmin$: Observable; constructor( private qualityAssuranceSourceDataService: QualityAssuranceSourceDataService, - private authService: AuthorizationDataService, - ) { - this.isAdmin$ = this.authService.isAuthorized(FeatureID.AdministratorOf); - } + ) {} /** * Detect changes to the item input and update the sources$ observable. diff --git a/src/app/menu.resolver.ts b/src/app/menu.resolver.ts index 94b56ba99d6..9272ed06ea3 100644 --- a/src/app/menu.resolver.ts +++ b/src/app/menu.resolver.ts @@ -171,7 +171,8 @@ export class MenuResolver implements Resolve { this.authorizationService.isAuthorized(FeatureID.AdministratorOf), this.authorizationService.isAuthorized(FeatureID.CanSubmit), this.authorizationService.isAuthorized(FeatureID.CanEditItem), - ]).subscribe(([isCollectionAdmin, isCommunityAdmin, isSiteAdmin, canSubmit, canEditItem]) => { + this.authorizationService.isAuthorized(FeatureID.CanSeeQA) + ]).subscribe(([isCollectionAdmin, isCommunityAdmin, isSiteAdmin, canSubmit, canEditItem, canSeeQa]) => { const newSubMenuList = [ { id: 'new_community', @@ -362,6 +363,40 @@ export class MenuResolver implements Resolve { icon: 'heartbeat', index: 11 }, + /* Notifications */ + { + id: 'notifications', + active: false, + visible: canSeeQa || isSiteAdmin, + model: { + type: MenuItemType.TEXT, + text: 'menu.section.notifications' + } as TextMenuItemModel, + icon: 'bell', + index: 4 + }, + { + id: 'notifications_quality-assurance', + parentID: 'notifications', + active: false, + visible: canSeeQa, + model: { + type: MenuItemType.LINK, + text: 'menu.section.quality-assurance', + link: '/notifications/quality-assurance' + } as LinkMenuItemModel, + }, + { + id: 'notifications_publication-claim', + parentID: 'notifications', + active: false, + visible: isSiteAdmin, + model: { + type: MenuItemType.LINK, + text: 'menu.section.notifications_publication-claim', + link: '/notifications/' + PUBLICATION_CLAIMS_PATH + } as LinkMenuItemModel, + }, ]; menuList.forEach((menuSection) => this.menuService.addSection(MenuID.ADMIN, Object.assign(menuSection, { shouldPersistOnRouteChange: true @@ -531,46 +566,9 @@ export class MenuResolver implements Resolve { * Create menu sections dependent on whether or not the current user is a site administrator */ createSiteAdministratorMenuSections() { - combineLatest([ - this.authorizationService.isAuthorized(FeatureID.AdministratorOf), - this.authorizationService.isAuthorized(FeatureID.CanSeeQA) - ]) - .subscribe(([authorized, canSeeQA]) => { + this.authorizationService.isAuthorized(FeatureID.AdministratorOf) + .subscribe((authorized) => { const menuList = [ - /* Notifications */ - { - id: 'notifications', - active: false, - visible: authorized && canSeeQA, - model: { - type: MenuItemType.TEXT, - text: 'menu.section.notifications' - } as TextMenuItemModel, - icon: 'bell', - index: 4 - }, - { - id: 'notifications_quality-assurance', - parentID: 'notifications', - active: false, - visible: authorized, - model: { - type: MenuItemType.LINK, - text: 'menu.section.quality-assurance', - link: '/notifications/quality-assurance' - } as LinkMenuItemModel, - }, - { - id: 'notifications_publication-claim', - parentID: 'notifications', - active: false, - visible: authorized, - model: { - type: MenuItemType.LINK, - text: 'menu.section.notifications_publication-claim', - link: '/admin/notifications/' + PUBLICATION_CLAIMS_PATH - } as LinkMenuItemModel, - }, /* Admin Search */ { id: 'admin_search', diff --git a/src/app/my-dspace-page/my-dspace-page.component.html b/src/app/my-dspace-page/my-dspace-page.component.html index 70bcf1b7bc4..cfae8e07a86 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.html +++ b/src/app/my-dspace-page/my-dspace-page.component.html @@ -1,7 +1,7 @@
+ -
From 447b275387877c073ebfbc1f06c70a5e453d1960 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Tue, 27 Feb 2024 10:33:03 +0100 Subject: [PATCH 38/38] [CST-12109] Updated notification text messages --- src/assets/i18n/en.json5 | 8 ++++---- src/assets/i18n/it.json5 | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index b5873f1551e..80c7b0e8ff5 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2428,13 +2428,13 @@ "item.truncatable-part.show-less": "Collapse", - "item.qa-event-notification.check.notification-info": "There are {{num}} pending review to check", + "item.qa-event-notification.check.notification-info": "There are {{num}} pending suggestions related to your account", - "item.qa-event-notification-info.check.button": "Check", + "item.qa-event-notification-info.check.button": "View", - "mydspace.qa-event-notification.check.notification-info": "There are {{num}} pending review to check", + "mydspace.qa-event-notification.check.notification-info": "There are {{num}} pending suggestions related to your account", - "mydspace.qa-event-notification-info.check.button": "Check", + "mydspace.qa-event-notification-info.check.button": "View", "workflow-item.search.result.delete-supervision.modal.header": "Delete Supervision Order", diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 6191cc2ac1c..0960b9fae15 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -3535,21 +3535,21 @@ // "item.truncatable-part.show-less": "Collapse", "item.truncatable-part.show-less": "Riduci", - // "item.qa-event-notification.check.notification-info": "There are {{num}} pending review to check", + // "item.qa-event-notification.check.notification-info": "There are {{num}} pending suggestions related to your account", // TODO New key - Add a translation - "item.qa-event-notification.check.notification-info": "There are {{num}} pending review to check", + "item.qa-event-notification.check.notification-info": "There are {{num}} pending suggestions related to your account", - // "item.qa-event-notification-info.check.button": "Check", + // "item.qa-event-notification-info.check.button": "View", // TODO New key - Add a translation - "item.qa-event-notification-info.check.button": "Check", + "item.qa-event-notification-info.check.button": "View", - // "mydspace.qa-event-notification.check.notification-info": "There are {{num}} pending review to check", + // "mydspace.qa-event-notification.check.notification-info": "There are {{num}} pending suggestions related to your account", // TODO New key - Add a translation - "mydspace.qa-event-notification.check.notification-info": "There are {{num}} pending review to check", + "mydspace.qa-event-notification.check.notification-info": "There are {{num}} pending suggestions related to your account", - // "mydspace.qa-event-notification-info.check.button": "Check", + // "mydspace.qa-event-notification-info.check.button": "View", // TODO New key - Add a translation - "mydspace.qa-event-notification-info.check.button": "Check", + "mydspace.qa-event-notification-info.check.button": "View", // "workflow-item.search.result.delete-supervision.modal.header": "Delete Supervision Order", "workflow-item.search.result.delete-supervision.modal.header": "Elimina l'ordine di supervisione",