mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge pull request #4017 from 4Science/task/main/CST-18016
OpenAlex Integration
This commit is contained in:
@@ -1 +1 @@
|
|||||||
<ds-publication-claim [source]="'openaire'"></ds-publication-claim>
|
<ds-suggestion-sources></ds-suggestion-sources>
|
||||||
|
@@ -6,8 +6,9 @@ import {
|
|||||||
waitForAsync,
|
waitForAsync,
|
||||||
} from '@angular/core/testing';
|
} from '@angular/core/testing';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MockComponent } from 'ng-mocks';
|
||||||
|
|
||||||
import { PublicationClaimComponent } from '../../../notifications/suggestion-targets/publication-claim/publication-claim.component';
|
import { SuggestionSourcesComponent } from '../../../notifications/suggestions/sources/suggestion-sources.component';
|
||||||
import { AdminNotificationsPublicationClaimPageComponent } from './admin-notifications-publication-claim-page.component';
|
import { AdminNotificationsPublicationClaimPageComponent } from './admin-notifications-publication-claim-page.component';
|
||||||
|
|
||||||
describe('AdminNotificationsPublicationClaimPageComponent', () => {
|
describe('AdminNotificationsPublicationClaimPageComponent', () => {
|
||||||
@@ -20,17 +21,10 @@ describe('AdminNotificationsPublicationClaimPageComponent', () => {
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
TranslateModule.forRoot(),
|
TranslateModule.forRoot(),
|
||||||
AdminNotificationsPublicationClaimPageComponent,
|
AdminNotificationsPublicationClaimPageComponent,
|
||||||
],
|
MockComponent(SuggestionSourcesComponent),
|
||||||
providers: [
|
|
||||||
AdminNotificationsPublicationClaimPageComponent,
|
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA],
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
}).overrideComponent(AdminNotificationsPublicationClaimPageComponent, {
|
}).compileComponents();
|
||||||
remove: {
|
|
||||||
imports: [PublicationClaimComponent],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@@ -1,14 +1,12 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { PublicationClaimComponent } from '../../../notifications/suggestion-targets/publication-claim/publication-claim.component';
|
import { SuggestionSourcesComponent } from '../../../notifications/suggestions/sources/suggestion-sources.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-admin-notifications-publication-claim-page',
|
selector: 'ds-admin-notifications-publication-claim-page',
|
||||||
templateUrl: './admin-notifications-publication-claim-page.component.html',
|
templateUrl: './admin-notifications-publication-claim-page.component.html',
|
||||||
styleUrls: ['./admin-notifications-publication-claim-page.component.scss'],
|
styleUrls: ['./admin-notifications-publication-claim-page.component.scss'],
|
||||||
imports: [
|
imports: [ SuggestionSourcesComponent ],
|
||||||
PublicationClaimComponent,
|
|
||||||
],
|
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class AdminNotificationsPublicationClaimPageComponent {
|
export class AdminNotificationsPublicationClaimPageComponent {
|
||||||
|
@@ -2,7 +2,8 @@ import { Route } from '@angular/router';
|
|||||||
|
|
||||||
import { authenticatedGuard } from '../../core/auth/authenticated.guard';
|
import { authenticatedGuard } from '../../core/auth/authenticated.guard';
|
||||||
import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { qualityAssuranceBreadcrumbResolver } from '../../core/breadcrumbs/quality-assurance-breadcrumb.resolver';
|
import { sourcesBreadcrumbResolver } from '../../core/breadcrumbs/sources-breadcrumb.resolver';
|
||||||
|
import { PublicationClaimComponent } from '../../notifications/suggestions/targets/publication-claim/publication-claim.component';
|
||||||
import { AdminNotificationsPublicationClaimPageResolver } from '../../quality-assurance-notifications-pages/notifications-suggestion-targets-page/notifications-suggestion-targets-page-resolver.service';
|
import { AdminNotificationsPublicationClaimPageResolver } from '../../quality-assurance-notifications-pages/notifications-suggestion-targets-page/notifications-suggestion-targets-page-resolver.service';
|
||||||
import { QualityAssuranceEventsPageComponent } from '../../quality-assurance-notifications-pages/quality-assurance-events-page/quality-assurance-events-page.component';
|
import { QualityAssuranceEventsPageComponent } from '../../quality-assurance-notifications-pages/quality-assurance-events-page/quality-assurance-events-page.component';
|
||||||
import { qualityAssuranceEventsPageResolver } from '../../quality-assurance-notifications-pages/quality-assurance-events-page/quality-assurance-events-page.resolver';
|
import { qualityAssuranceEventsPageResolver } from '../../quality-assurance-notifications-pages/quality-assurance-events-page/quality-assurance-events-page.resolver';
|
||||||
@@ -33,13 +34,28 @@ export const ROUTES: Route[] = [
|
|||||||
showBreadcrumbsFluid: false,
|
showBreadcrumbsFluid: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
canActivate: [ authenticatedGuard ],
|
||||||
|
path: `${PUBLICATION_CLAIMS_PATH}/:sourceId`,
|
||||||
|
pathMatch: 'full',
|
||||||
|
component: PublicationClaimComponent,
|
||||||
|
resolve: {
|
||||||
|
breadcrumb: sourcesBreadcrumbResolver,
|
||||||
|
openaireQualityAssuranceEventsParams: AdminNotificationsPublicationClaimPageResolver,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'admin.notifications.publicationclaim.page.title',
|
||||||
|
breadcrumbKey: 'admin.notifications.publicationclaim',
|
||||||
|
showBreadcrumbsFluid: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
canActivate: [authenticatedGuard],
|
canActivate: [authenticatedGuard],
|
||||||
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId`,
|
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId`,
|
||||||
component: QualityAssuranceTopicsPageComponent,
|
component: QualityAssuranceTopicsPageComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: qualityAssuranceBreadcrumbResolver,
|
breadcrumb: sourcesBreadcrumbResolver,
|
||||||
openaireQualityAssuranceTopicsParams: QualityAssuranceTopicsPageResolver,
|
openaireQualityAssuranceTopicsParams: QualityAssuranceTopicsPageResolver,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
@@ -85,7 +101,7 @@ export const ROUTES: Route[] = [
|
|||||||
component: QualityAssuranceEventsPageComponent,
|
component: QualityAssuranceEventsPageComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: qualityAssuranceBreadcrumbResolver,
|
breadcrumb: sourcesBreadcrumbResolver,
|
||||||
openaireQualityAssuranceEventsParams: qualityAssuranceEventsPageResolver,
|
openaireQualityAssuranceEventsParams: qualityAssuranceEventsPageResolver,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
|
@@ -10,6 +10,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
NoPreloading,
|
NoPreloading,
|
||||||
provideRouter,
|
provideRouter,
|
||||||
|
withComponentInputBinding,
|
||||||
withEnabledBlockingInitialNavigation,
|
withEnabledBlockingInitialNavigation,
|
||||||
withInMemoryScrolling,
|
withInMemoryScrolling,
|
||||||
withPreloading,
|
withPreloading,
|
||||||
@@ -109,6 +110,7 @@ export const commonAppConfig: ApplicationConfig = {
|
|||||||
withInMemoryScrolling(APP_ROUTING_SCROLL_CONF),
|
withInMemoryScrolling(APP_ROUTING_SCROLL_CONF),
|
||||||
withEnabledBlockingInitialNavigation(),
|
withEnabledBlockingInitialNavigation(),
|
||||||
withPreloading(NoPreloading),
|
withPreloading(NoPreloading),
|
||||||
|
withComponentInputBinding(),
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
provide: APP_BASE_HREF,
|
provide: APP_BASE_HREF,
|
||||||
|
@@ -1,43 +0,0 @@
|
|||||||
import {
|
|
||||||
TestBed,
|
|
||||||
waitForAsync,
|
|
||||||
} from '@angular/core/testing';
|
|
||||||
import { getTestScheduler } from 'jasmine-marbles';
|
|
||||||
|
|
||||||
import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model';
|
|
||||||
import { QualityAssuranceBreadcrumbService } from './quality-assurance-breadcrumb.service';
|
|
||||||
|
|
||||||
describe('QualityAssuranceBreadcrumbService', () => {
|
|
||||||
let service: QualityAssuranceBreadcrumbService;
|
|
||||||
let translateService: any = {
|
|
||||||
instant: (str) => str,
|
|
||||||
};
|
|
||||||
|
|
||||||
let exampleString;
|
|
||||||
let exampleURL;
|
|
||||||
let exampleQaKey;
|
|
||||||
|
|
||||||
function init() {
|
|
||||||
exampleString = 'sourceId';
|
|
||||||
exampleURL = '/test/quality-assurance/';
|
|
||||||
exampleQaKey = 'admin.quality-assurance.breadcrumbs';
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
|
||||||
init();
|
|
||||||
TestBed.configureTestingModule({}).compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
service = new QualityAssuranceBreadcrumbService(translateService);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getBreadcrumbs', () => {
|
|
||||||
it('should return a breadcrumb based on a string', () => {
|
|
||||||
const breadcrumbs = service.getBreadcrumbs(exampleString, exampleURL);
|
|
||||||
getTestScheduler().expectObservable(breadcrumbs).toBe('(a|)', { a: [new Breadcrumb(exampleQaKey, exampleURL),
|
|
||||||
new Breadcrumb(exampleString, exampleURL + exampleString)],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@@ -1,15 +1,17 @@
|
|||||||
import { qualityAssuranceBreadcrumbResolver } from './quality-assurance-breadcrumb.resolver';
|
import { sourcesBreadcrumbResolver } from './sources-breadcrumb.resolver';
|
||||||
|
|
||||||
describe('qualityAssuranceBreadcrumbResolver', () => {
|
describe('sourcesBreadcrumbResolver', () => {
|
||||||
describe('resolve', () => {
|
describe('resolve', () => {
|
||||||
let resolver: any;
|
let resolver: any;
|
||||||
let qualityAssuranceBreadcrumbService: any;
|
let sourcesBreadcrumbService: any;
|
||||||
let route: any;
|
let route: any;
|
||||||
|
const i18nKey = 'breadcrumbKey';
|
||||||
const fullPath = '/test/quality-assurance/';
|
const fullPath = '/test/quality-assurance/';
|
||||||
const expectedKey = 'testSourceId:testTopicId';
|
const expectedKey = 'breadcrumbKey:testSourceId:testTopicId';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
route = {
|
route = {
|
||||||
|
data: { breadcrumbKey: i18nKey },
|
||||||
paramMap: {
|
paramMap: {
|
||||||
get: function (param) {
|
get: function (param) {
|
||||||
return this[param];
|
return this[param];
|
||||||
@@ -18,13 +20,13 @@ describe('qualityAssuranceBreadcrumbResolver', () => {
|
|||||||
topicId: 'testTopicId',
|
topicId: 'testTopicId',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
qualityAssuranceBreadcrumbService = {};
|
sourcesBreadcrumbService = {};
|
||||||
resolver = qualityAssuranceBreadcrumbResolver;
|
resolver = sourcesBreadcrumbResolver;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve the breadcrumb config', () => {
|
it('should resolve the breadcrumb config', () => {
|
||||||
const resolvedConfig = resolver(route as any, { url: fullPath + 'testSourceId' } as any, qualityAssuranceBreadcrumbService);
|
const resolvedConfig = resolver(route as any, { url: fullPath + 'testSourceId' } as any, sourcesBreadcrumbService);
|
||||||
const expectedConfig = { provider: qualityAssuranceBreadcrumbService, key: expectedKey, url: fullPath };
|
const expectedConfig = { provider: sourcesBreadcrumbService, key: expectedKey, url: fullPath };
|
||||||
expect(resolvedConfig).toEqual(expectedConfig);
|
expect(resolvedConfig).toEqual(expectedConfig);
|
||||||
});
|
});
|
||||||
});
|
});
|
@@ -6,16 +6,17 @@ import {
|
|||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
|
|
||||||
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
||||||
import { QualityAssuranceBreadcrumbService } from './quality-assurance-breadcrumb.service';
|
import { SourcesBreadcrumbService } from './sources-breadcrumb.service';
|
||||||
|
|
||||||
export const qualityAssuranceBreadcrumbResolver: ResolveFn<BreadcrumbConfig<string>> = (
|
export const sourcesBreadcrumbResolver: ResolveFn<BreadcrumbConfig<string>> = (
|
||||||
route: ActivatedRouteSnapshot,
|
route: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot,
|
state: RouterStateSnapshot,
|
||||||
breadcrumbService: QualityAssuranceBreadcrumbService = inject(QualityAssuranceBreadcrumbService),
|
breadcrumbService: SourcesBreadcrumbService = inject(SourcesBreadcrumbService),
|
||||||
): BreadcrumbConfig<string> => {
|
): BreadcrumbConfig<string> => {
|
||||||
|
const breadcrumbKey = route.data.breadcrumbKey;
|
||||||
const sourceId = route.paramMap.get('sourceId');
|
const sourceId = route.paramMap.get('sourceId');
|
||||||
const topicId = route.paramMap.get('topicId');
|
const topicId = route.paramMap.get('topicId');
|
||||||
let key = sourceId;
|
let key = `${breadcrumbKey}:${sourceId}`;
|
||||||
|
|
||||||
if (topicId) {
|
if (topicId) {
|
||||||
key += `:${topicId}`;
|
key += `:${topicId}`;
|
60
src/app/core/breadcrumbs/sources-breadcrumb.service.spec.ts
Normal file
60
src/app/core/breadcrumbs/sources-breadcrumb.service.spec.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import {
|
||||||
|
TestBed,
|
||||||
|
waitForAsync,
|
||||||
|
} from '@angular/core/testing';
|
||||||
|
import { getTestScheduler } from 'jasmine-marbles';
|
||||||
|
|
||||||
|
import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model';
|
||||||
|
import { SourcesBreadcrumbService } from './sources-breadcrumb.service';
|
||||||
|
|
||||||
|
describe('SourcesBreadcrumbService', () => {
|
||||||
|
let service: SourcesBreadcrumbService;
|
||||||
|
let translateService: any = {
|
||||||
|
instant: (str) => str,
|
||||||
|
};
|
||||||
|
|
||||||
|
let exampleString;
|
||||||
|
let exampleSource;
|
||||||
|
let exampleTopic;
|
||||||
|
let exampleArg;
|
||||||
|
let exampleArgTopic;
|
||||||
|
let exampleURL;
|
||||||
|
let exampleQaKey;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
exampleString = 'admin.quality-assurance';
|
||||||
|
exampleSource = 'sourceId';
|
||||||
|
exampleTopic = 'topic';
|
||||||
|
exampleArg = `${exampleString}:${exampleSource}`;
|
||||||
|
exampleArgTopic = `${exampleString}:${exampleSource}:${exampleTopic}`;
|
||||||
|
exampleURL = '/test/quality-assurance/';
|
||||||
|
exampleQaKey = 'admin.quality-assurance.breadcrumbs';
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
init();
|
||||||
|
TestBed.configureTestingModule({}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
service = new SourcesBreadcrumbService(translateService);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getBreadcrumbs', () => {
|
||||||
|
|
||||||
|
it('should return a breadcrumb based on source only', () => {
|
||||||
|
const breadcrumbs = service.getBreadcrumbs(exampleArg, exampleURL);
|
||||||
|
getTestScheduler().expectObservable(breadcrumbs).toBe('(a|)', { a: [new Breadcrumb(exampleQaKey, exampleURL),
|
||||||
|
new Breadcrumb(exampleSource, exampleURL + exampleSource)],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a breadcrumb based also on topic', () => {
|
||||||
|
const breadcrumbs = service.getBreadcrumbs(exampleArgTopic, exampleURL);
|
||||||
|
getTestScheduler().expectObservable(breadcrumbs).toBe('(a|)', { a: [new Breadcrumb(exampleQaKey, exampleURL),
|
||||||
|
new Breadcrumb(exampleSource, exampleURL + exampleSource),
|
||||||
|
new Breadcrumb(exampleTopic, undefined)],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -14,9 +14,9 @@ import { BreadcrumbsProviderService } from './breadcrumbsProviderService';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class QualityAssuranceBreadcrumbService implements BreadcrumbsProviderService<string> {
|
export class SourcesBreadcrumbService implements BreadcrumbsProviderService<string> {
|
||||||
|
|
||||||
private QUALITY_ASSURANCE_BREADCRUMB_KEY = 'admin.quality-assurance.breadcrumbs';
|
private BREADCRUMB_SUFFIX = '.breadcrumbs';
|
||||||
constructor(
|
constructor(
|
||||||
private translationService: TranslateService,
|
private translationService: TranslateService,
|
||||||
) {
|
) {
|
||||||
@@ -31,15 +31,16 @@ export class QualityAssuranceBreadcrumbService implements BreadcrumbsProviderSer
|
|||||||
*/
|
*/
|
||||||
getBreadcrumbs(key: string, url: string): Observable<Breadcrumb[]> {
|
getBreadcrumbs(key: string, url: string): Observable<Breadcrumb[]> {
|
||||||
const args = key.split(':');
|
const args = key.split(':');
|
||||||
const sourceId = args[0];
|
const breadcrumbKey = args[0] + this.BREADCRUMB_SUFFIX;
|
||||||
const topicId = args.length > 2 ? args[args.length - 1] : args[1];
|
const sourceId = args[1];
|
||||||
|
const topicId = args.length > 3 ? args[args.length - 1] : args[2];
|
||||||
|
|
||||||
if (topicId) {
|
if (topicId) {
|
||||||
return observableOf( [new Breadcrumb(this.translationService.instant(this.QUALITY_ASSURANCE_BREADCRUMB_KEY), url),
|
return observableOf( [new Breadcrumb(this.translationService.instant(breadcrumbKey), url),
|
||||||
new Breadcrumb(sourceId, `${url}${sourceId}`),
|
new Breadcrumb(sourceId, `${url}${sourceId}`),
|
||||||
new Breadcrumb(topicId, undefined)]);
|
new Breadcrumb(topicId, undefined)]);
|
||||||
} else {
|
} else {
|
||||||
return observableOf([new Breadcrumb(this.translationService.instant(this.QUALITY_ASSURANCE_BREADCRUMB_KEY), url),
|
return observableOf([new Breadcrumb(this.translationService.instant(breadcrumbKey), url),
|
||||||
new Breadcrumb(sourceId, `${url}${sourceId}`)]);
|
new Breadcrumb(sourceId, `${url}${sourceId}`)]);
|
||||||
}
|
}
|
||||||
|
|
@@ -18,7 +18,7 @@ import {
|
|||||||
} from 'src/config/app-config.interface';
|
} from 'src/config/app-config.interface';
|
||||||
|
|
||||||
import { Site } from '../core/shared/site.model';
|
import { Site } from '../core/shared/site.model';
|
||||||
import { SuggestionsPopupComponent } from '../notifications/suggestions-popup/suggestions-popup.component';
|
import { SuggestionsPopupComponent } from '../notifications/suggestions/popup/suggestions-popup.component';
|
||||||
import { ThemedConfigurationSearchPageComponent } from '../search-page/themed-configuration-search-page.component';
|
import { ThemedConfigurationSearchPageComponent } from '../search-page/themed-configuration-search-page.component';
|
||||||
import { ThemedSearchFormComponent } from '../shared/search-form/themed-search-form.component';
|
import { ThemedSearchFormComponent } from '../shared/search-form/themed-search-form.component';
|
||||||
import { PageWithSidebarComponent } from '../shared/sidebar/page-with-sidebar.component';
|
import { PageWithSidebarComponent } from '../shared/sidebar/page-with-sidebar.component';
|
||||||
|
@@ -28,7 +28,7 @@ import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-
|
|||||||
import { MyDSpaceNewSubmissionComponent } from './my-dspace-new-submission/my-dspace-new-submission.component';
|
import { MyDSpaceNewSubmissionComponent } from './my-dspace-new-submission/my-dspace-new-submission.component';
|
||||||
import { MyDSpacePageComponent } from './my-dspace-page.component';
|
import { MyDSpacePageComponent } from './my-dspace-page.component';
|
||||||
import SpyObj = jasmine.SpyObj;
|
import SpyObj = jasmine.SpyObj;
|
||||||
import { SuggestionsNotificationComponent } from '../notifications/suggestions-notification/suggestions-notification.component';
|
import { SuggestionsNotificationComponent } from '../notifications/suggestions/notification/suggestions-notification.component';
|
||||||
import { MyDspaceQaEventsNotificationsComponent } from './my-dspace-qa-events-notifications/my-dspace-qa-events-notifications.component';
|
import { MyDspaceQaEventsNotificationsComponent } from './my-dspace-qa-events-notifications/my-dspace-qa-events-notifications.component';
|
||||||
|
|
||||||
describe('MyDSpacePageComponent', () => {
|
describe('MyDSpacePageComponent', () => {
|
||||||
|
@@ -14,7 +14,7 @@ import { RoleType } from '../core/roles/role-types';
|
|||||||
import { Context } from '../core/shared/context.model';
|
import { Context } from '../core/shared/context.model';
|
||||||
import { SearchService } from '../core/shared/search/search.service';
|
import { SearchService } from '../core/shared/search/search.service';
|
||||||
import { ViewMode } from '../core/shared/view-mode.model';
|
import { ViewMode } from '../core/shared/view-mode.model';
|
||||||
import { SuggestionsNotificationComponent } from '../notifications/suggestions-notification/suggestions-notification.component';
|
import { SuggestionsNotificationComponent } from '../notifications/suggestions/notification/suggestions-notification.component';
|
||||||
import { RoleDirective } from '../shared/roles/role.directive';
|
import { RoleDirective } from '../shared/roles/role.directive';
|
||||||
import { SearchConfigurationOption } from '../shared/search/search-switch-configuration/search-configuration-option.model';
|
import { SearchConfigurationOption } from '../shared/search/search-switch-configuration/search-configuration-option.model';
|
||||||
import { ThemedSearchComponent } from '../shared/search/themed-search.component';
|
import { ThemedSearchComponent } from '../shared/search/themed-search.component';
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { QualityAssuranceSourceEffects } from './qa/source/quality-assurance-source.effects';
|
import { QualityAssuranceSourceEffects } from './qa/source/quality-assurance-source.effects';
|
||||||
import { QualityAssuranceTopicsEffects } from './qa/topics/quality-assurance-topics.effects';
|
import { QualityAssuranceTopicsEffects } from './qa/topics/quality-assurance-topics.effects';
|
||||||
import { SuggestionTargetsEffects } from './suggestion-targets/suggestion-targets.effects';
|
import { SuggestionTargetsEffects } from './suggestions/targets/suggestion-targets.effects';
|
||||||
|
|
||||||
export const notificationsEffects = [
|
export const notificationsEffects = [
|
||||||
QualityAssuranceTopicsEffects,
|
QualityAssuranceTopicsEffects,
|
||||||
|
@@ -14,7 +14,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
SuggestionTargetsReducer,
|
SuggestionTargetsReducer,
|
||||||
SuggestionTargetState,
|
SuggestionTargetState,
|
||||||
} from './suggestion-targets/suggestion-targets.reducer';
|
} from './suggestions/targets/suggestion-targets.reducer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The OpenAIRE State
|
* The OpenAIRE State
|
||||||
|
@@ -5,65 +5,12 @@
|
|||||||
<ds-alert [type]="'alert-info'" [content]="'quality-assurance.source.description'"></ds-alert>
|
<ds-alert [type]="'alert-info'" [content]="'quality-assurance.source.description'"></ds-alert>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<ds-source-list [loading]="(isSourceLoading() | async) === true || (isSourceProcessing() | async) === true"
|
||||||
<div class="col-12">
|
[paginationConfig]="paginationConfig"
|
||||||
<h2 class="h4 border-bottom pb-2">{{'quality-assurance.source'| translate}}</h2>
|
[showLastEvent]="true"
|
||||||
|
[sources]="sources$ | async"
|
||||||
@if ((isSourceLoading() | async)) {
|
[totalElements]="totalElements$ | async"
|
||||||
<ds-loading class="container" message="{{'quality-assurance.loading' | translate}}"></ds-loading>
|
(paginationChange)="getQualityAssuranceSource()"
|
||||||
}
|
(sourceSelected)="onSelect($event)"></ds-source-list>
|
||||||
@if ((isSourceLoading() | async) !== true) {
|
|
||||||
<ds-pagination
|
|
||||||
[paginationOptions]="paginationConfig"
|
|
||||||
[collectionSize]="(totalElements$ | async)"
|
|
||||||
[hideGear]="false"
|
|
||||||
[hideSortOptions]="true"
|
|
||||||
(paginationChange)="getQualityAssuranceSource()">
|
|
||||||
@if ((isSourceProcessing() | async)) {
|
|
||||||
<ds-loading class="container" message="'quality-assurance.loading' | translate"></ds-loading>
|
|
||||||
}
|
|
||||||
@if ((isSourceProcessing() | async) !== true) {
|
|
||||||
@if ((sources$ | async)?.length === 0) {
|
|
||||||
<div class="alert alert-info w-100 mb-2 mt-2" role="alert">
|
|
||||||
{{'quality-assurance.noSource' | translate}}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@if ((sources$ | async)?.length !== 0) {
|
|
||||||
<div class="table-responsive mt-2">
|
|
||||||
<table id="epeople" class="table table-striped table-hover table-bordered">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">{{'quality-assurance.table.source' | translate}}</th>
|
|
||||||
<th scope="col">{{'quality-assurance.table.last-event' | translate}}</th>
|
|
||||||
<th scope="col">{{'quality-assurance.table.actions' | translate}}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@for (sourceElement of (sources$ | async); track sourceElement; let i = $index) {
|
|
||||||
<tr>
|
|
||||||
<td>{{sourceElement.id}}</td>
|
|
||||||
<td>{{sourceElement.lastEvent | date: 'dd/MM/yyyy hh:mm' }}</td>
|
|
||||||
<td>
|
|
||||||
<div class="btn-group edit-field">
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-primary btn-sm"
|
|
||||||
title="{{'quality-assurance.source-list.button.detail' | translate : { param: sourceElement.id } }}"
|
|
||||||
[routerLink]="[sourceElement.id]">
|
|
||||||
<span class="badge bg-info">{{sourceElement.totalEvents}}</span>
|
|
||||||
<i class="fas fa-info fa-fw"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</ds-pagination>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -16,16 +16,18 @@ import { of as observableOf } from 'rxjs';
|
|||||||
|
|
||||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||||
import { AlertComponent } from '../../../shared/alert/alert.component';
|
import { AlertComponent } from '../../../shared/alert/alert.component';
|
||||||
import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component';
|
|
||||||
import {
|
import {
|
||||||
getMockNotificationsStateService,
|
getMockNotificationsStateService,
|
||||||
qualityAssuranceSourceObjectMoreAbstract,
|
qualityAssuranceSourceObjectMoreAbstract,
|
||||||
qualityAssuranceSourceObjectMorePid,
|
qualityAssuranceSourceObjectMorePid,
|
||||||
} from '../../../shared/mocks/notifications.mock';
|
} from '../../../shared/mocks/notifications.mock';
|
||||||
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
|
|
||||||
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
||||||
import { createTestComponent } from '../../../shared/testing/utils.test';
|
import { createTestComponent } from '../../../shared/testing/utils.test';
|
||||||
import { NotificationsStateService } from '../../notifications-state.service';
|
import { NotificationsStateService } from '../../notifications-state.service';
|
||||||
|
import {
|
||||||
|
SourceListComponent,
|
||||||
|
SourceObject,
|
||||||
|
} from '../../shared/source-list.component';
|
||||||
import { QualityAssuranceSourceComponent } from './quality-assurance-source.component';
|
import { QualityAssuranceSourceComponent } from './quality-assurance-source.component';
|
||||||
|
|
||||||
describe('QualityAssuranceSourceComponent test suite', () => {
|
describe('QualityAssuranceSourceComponent test suite', () => {
|
||||||
@@ -61,8 +63,7 @@ describe('QualityAssuranceSourceComponent test suite', () => {
|
|||||||
remove: {
|
remove: {
|
||||||
imports: [
|
imports: [
|
||||||
AlertComponent,
|
AlertComponent,
|
||||||
ThemedLoadingComponent,
|
SourceListComponent,
|
||||||
PaginationComponent,
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -119,12 +120,19 @@ describe('QualityAssuranceSourceComponent test suite', () => {
|
|||||||
it(('Should init component properly'), () => {
|
it(('Should init component properly'), () => {
|
||||||
comp.ngOnInit();
|
comp.ngOnInit();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
const expected: SourceObject[] = [
|
||||||
|
{ id: qualityAssuranceSourceObjectMorePid.id,
|
||||||
|
lastEvent: qualityAssuranceSourceObjectMorePid.lastEvent,
|
||||||
|
total: qualityAssuranceSourceObjectMorePid.totalEvents,
|
||||||
|
},
|
||||||
|
{ id: qualityAssuranceSourceObjectMoreAbstract.id,
|
||||||
|
lastEvent: qualityAssuranceSourceObjectMoreAbstract.lastEvent,
|
||||||
|
total: qualityAssuranceSourceObjectMoreAbstract.totalEvents,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
expect(comp.sources$).toBeObservable(cold('(a|)', {
|
expect(comp.sources$).toBeObservable(cold('(a|)', {
|
||||||
a: [
|
a: expected,
|
||||||
qualityAssuranceSourceObjectMorePid,
|
|
||||||
qualityAssuranceSourceObjectMoreAbstract,
|
|
||||||
],
|
|
||||||
}));
|
}));
|
||||||
expect(comp.totalElements$).toBeObservable(cold('(a|)', {
|
expect(comp.totalElements$).toBeObservable(cold('(a|)', {
|
||||||
a: 2,
|
a: 2,
|
||||||
|
@@ -8,7 +8,11 @@ import {
|
|||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { RouterLink } from '@angular/router';
|
import {
|
||||||
|
ActivatedRoute,
|
||||||
|
Router,
|
||||||
|
RouterLink,
|
||||||
|
} from '@angular/router';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import {
|
import {
|
||||||
Observable,
|
Observable,
|
||||||
@@ -16,6 +20,7 @@ import {
|
|||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
import {
|
import {
|
||||||
distinctUntilChanged,
|
distinctUntilChanged,
|
||||||
|
map,
|
||||||
take,
|
take,
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
@@ -29,6 +34,10 @@ import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.c
|
|||||||
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
|
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
|
||||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||||
import { NotificationsStateService } from '../../notifications-state.service';
|
import { NotificationsStateService } from '../../notifications-state.service';
|
||||||
|
import {
|
||||||
|
SourceListComponent,
|
||||||
|
SourceObject,
|
||||||
|
} from '../../shared/source-list.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to display the Quality Assurance source list.
|
* Component to display the Quality Assurance source list.
|
||||||
@@ -38,7 +47,7 @@ import { NotificationsStateService } from '../../notifications-state.service';
|
|||||||
templateUrl: './quality-assurance-source.component.html',
|
templateUrl: './quality-assurance-source.component.html',
|
||||||
styleUrls: ['./quality-assurance-source.component.scss'],
|
styleUrls: ['./quality-assurance-source.component.scss'],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [AlertComponent, ThemedLoadingComponent, PaginationComponent, RouterLink, AsyncPipe, TranslateModule, DatePipe],
|
imports: [AlertComponent, ThemedLoadingComponent, PaginationComponent, RouterLink, AsyncPipe, TranslateModule, DatePipe, SourceListComponent],
|
||||||
})
|
})
|
||||||
export class QualityAssuranceSourceComponent implements OnDestroy, OnInit, AfterViewInit {
|
export class QualityAssuranceSourceComponent implements OnDestroy, OnInit, AfterViewInit {
|
||||||
|
|
||||||
@@ -59,7 +68,7 @@ export class QualityAssuranceSourceComponent implements OnDestroy, OnInit, After
|
|||||||
/**
|
/**
|
||||||
* The Quality Assurance source list.
|
* The Quality Assurance source list.
|
||||||
*/
|
*/
|
||||||
public sources$: Observable<QualityAssuranceSourceObject[]>;
|
public sources$: Observable<SourceObject[]>;
|
||||||
/**
|
/**
|
||||||
* The total number of Quality Assurance sources.
|
* The total number of Quality Assurance sources.
|
||||||
*/
|
*/
|
||||||
@@ -74,10 +83,14 @@ export class QualityAssuranceSourceComponent implements OnDestroy, OnInit, After
|
|||||||
* Initialize the component variables.
|
* Initialize the component variables.
|
||||||
* @param {PaginationService} paginationService
|
* @param {PaginationService} paginationService
|
||||||
* @param {NotificationsStateService} notificationsStateService
|
* @param {NotificationsStateService} notificationsStateService
|
||||||
|
* @param {Router} router
|
||||||
|
* @param {ActivatedRoute} route
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private paginationService: PaginationService,
|
private paginationService: PaginationService,
|
||||||
private notificationsStateService: NotificationsStateService,
|
private notificationsStateService: NotificationsStateService,
|
||||||
|
private router: Router,
|
||||||
|
private route: ActivatedRoute,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +98,15 @@ export class QualityAssuranceSourceComponent implements OnDestroy, OnInit, After
|
|||||||
* Component initialization.
|
* Component initialization.
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.sources$ = this.notificationsStateService.getQualityAssuranceSource();
|
this.sources$ = this.notificationsStateService.getQualityAssuranceSource().pipe(
|
||||||
|
map((sources: QualityAssuranceSourceObject[])=> {
|
||||||
|
return sources.map((source: QualityAssuranceSourceObject) => ({
|
||||||
|
id: source.id,
|
||||||
|
lastEvent: source.lastEvent,
|
||||||
|
total: source.totalEvents,
|
||||||
|
}));
|
||||||
|
}),
|
||||||
|
);
|
||||||
this.totalElements$ = this.notificationsStateService.getQualityAssuranceSourceTotals();
|
this.totalElements$ = this.notificationsStateService.getQualityAssuranceSourceTotals();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +123,14 @@ export class QualityAssuranceSourceComponent implements OnDestroy, OnInit, After
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the specified source
|
||||||
|
* @param sourceId
|
||||||
|
*/
|
||||||
|
onSelect(sourceId: string) {
|
||||||
|
this.router.navigate([sourceId], { relativeTo: this.route });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the information about the loading status of the Quality Assurance source (if it's running or not).
|
* Returns the information about the loading status of the Quality Assurance source (if it's running or not).
|
||||||
*
|
*
|
||||||
|
59
src/app/notifications/shared/source-list.component.html
Normal file
59
src/app/notifications/shared/source-list.component.html
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<div class="col-12">
|
||||||
|
<h2 class="h4 border-bottom pb-2">{{'quality-assurance.source'| translate}}</h2>
|
||||||
|
|
||||||
|
@if (loading()) {
|
||||||
|
<ds-loading class="container" message="{{'quality-assurance.loading' | translate}}"></ds-loading>
|
||||||
|
} @else {
|
||||||
|
<ds-pagination
|
||||||
|
[paginationOptions]="paginationConfig()"
|
||||||
|
[collectionSize]="totalElements()"
|
||||||
|
[hideGear]="false"
|
||||||
|
[hideSortOptions]="true"
|
||||||
|
(paginationChange)="paginationChange.emit($event)">
|
||||||
|
@if (sources()?.length === 0) {
|
||||||
|
<div class="alert alert-info w-100 mb-2 mt-2" role="alert">
|
||||||
|
{{'quality-assurance.noSource' | translate}}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (sources()?.length !== 0) {
|
||||||
|
<div class="table-responsive mt-2">
|
||||||
|
<table id="epeople" class="table table-striped table-hover table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">{{'quality-assurance.table.source' | translate}}</th>
|
||||||
|
@if (showLastEvent()) {
|
||||||
|
<th scope="col">{{'quality-assurance.table.last-event' | translate}}</th>
|
||||||
|
}
|
||||||
|
<th scope="col">{{'quality-assurance.table.actions' | translate}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (sourceElement of sources(); track sourceElement; let i = $index) {
|
||||||
|
<tr>
|
||||||
|
<td>{{sourceElement.id}}</td>
|
||||||
|
@if (showLastEvent()) {
|
||||||
|
<td>{{sourceElement.lastEvent | date: 'dd/MM/yyyy hh:mm' }}</td>
|
||||||
|
}
|
||||||
|
<td>
|
||||||
|
<div class="btn-group edit-field">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-primary btn-sm"
|
||||||
|
title="{{'quality-assurance.source-list.button.detail' | translate : { param: sourceElement.id } }}"
|
||||||
|
(click)="sourceSelected.emit(sourceElement.id)">
|
||||||
|
<span class="badge bg-info">{{sourceElement.total}}</span>
|
||||||
|
<i class="fas fa-info fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
</ds-pagination>
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
114
src/app/notifications/shared/source-list.component.spec.ts
Normal file
114
src/app/notifications/shared/source-list.component.spec.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import {
|
||||||
|
AsyncPipe,
|
||||||
|
DatePipe,
|
||||||
|
} from '@angular/common';
|
||||||
|
import {
|
||||||
|
ComponentFixture,
|
||||||
|
TestBed,
|
||||||
|
} from '@angular/core/testing';
|
||||||
|
import { provideRouter } from '@angular/router';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MockComponent } from 'ng-mocks';
|
||||||
|
|
||||||
|
import { AlertComponent } from '../../shared/alert/alert.component';
|
||||||
|
import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component';
|
||||||
|
import { PaginationComponent } from '../../shared/pagination/pagination.component';
|
||||||
|
import {
|
||||||
|
SourceListComponent,
|
||||||
|
SourceObject,
|
||||||
|
} from './source-list.component';
|
||||||
|
|
||||||
|
describe('SourceListComponent', () => {
|
||||||
|
let component: SourceListComponent;
|
||||||
|
let fixture: ComponentFixture<SourceListComponent>;
|
||||||
|
const paginationConfig = {
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
};
|
||||||
|
const sources: SourceObject[] = [
|
||||||
|
{ id: 'source1', lastEvent: '2025-03-12T12:00:00', total: 5 },
|
||||||
|
{ id: 'source1', lastEvent: '2025-03-13T12:00:00', total: 10 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const sourcesWithoutEvent: SourceObject[] = [
|
||||||
|
{ id: 'source1', total: 5 },
|
||||||
|
{ id: 'source1', total: 10 },
|
||||||
|
];
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [SourceListComponent],
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
AlertComponent,
|
||||||
|
AsyncPipe,
|
||||||
|
DatePipe,
|
||||||
|
MockComponent(PaginationComponent),
|
||||||
|
MockComponent(ThemedLoadingComponent),
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
provideRouter([]),
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SourceListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display loading message when loading is true', () => {
|
||||||
|
fixture.componentRef.setInput('loading', true);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement.querySelector('ds-loading')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display sources when loading is false and sources are available', () => {
|
||||||
|
fixture.componentRef.setInput('loading', false);
|
||||||
|
fixture.componentRef.setInput('showLastEvent', true);
|
||||||
|
fixture.componentRef.setInput('sources', sources);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement.querySelector('table')).toBeTruthy();
|
||||||
|
expect(fixture.nativeElement.querySelector('thead tr').children.length).toBe(3);
|
||||||
|
expect(fixture.nativeElement.querySelector('tbody').children.length).toBe(2);
|
||||||
|
expect(fixture.nativeElement.querySelector('tbody tr td').textContent).toContain('source1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not display last event column', () => {
|
||||||
|
fixture.componentRef.setInput('loading', false);
|
||||||
|
fixture.componentRef.setInput('showLastEvent', false);
|
||||||
|
fixture.componentRef.setInput('sources', sourcesWithoutEvent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement.querySelector('table')).toBeTruthy();
|
||||||
|
expect(fixture.nativeElement.querySelector('thead tr').children.length).toBe(2);
|
||||||
|
expect(fixture.nativeElement.querySelector('tbody').children.length).toBe(2);
|
||||||
|
expect(fixture.nativeElement.querySelector('table tbody tr td').textContent).toContain('source1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit sourceSelected event when a source is clicked', () => {
|
||||||
|
spyOn(component.sourceSelected, 'emit');
|
||||||
|
fixture.componentRef.setInput('loading', false);
|
||||||
|
fixture.componentRef.setInput('paginationConfig', paginationConfig);
|
||||||
|
fixture.componentRef.setInput('sources', sources);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const button = fixture.nativeElement.querySelector('.btn-outline-primary');
|
||||||
|
button.click();
|
||||||
|
expect(component.sourceSelected.emit).toHaveBeenCalledWith('source1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit paginationChange event when pagination changes', () => {
|
||||||
|
spyOn(component.paginationChange, 'emit');
|
||||||
|
fixture.componentRef.setInput('loading', false);
|
||||||
|
fixture.componentRef.setInput('paginationConfig', paginationConfig);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
const paginationComponent = fixture.nativeElement.querySelector('ds-pagination');
|
||||||
|
paginationComponent.dispatchEvent(new Event('paginationChange'));
|
||||||
|
expect(component.paginationChange.emit).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
75
src/app/notifications/shared/source-list.component.ts
Normal file
75
src/app/notifications/shared/source-list.component.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import {
|
||||||
|
AsyncPipe,
|
||||||
|
DatePipe,
|
||||||
|
} from '@angular/common';
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
input,
|
||||||
|
InputSignal,
|
||||||
|
output,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { RouterLink } from '@angular/router';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { AlertComponent } from '../../shared/alert/alert.component';
|
||||||
|
import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component';
|
||||||
|
import { PaginationComponent } from '../../shared/pagination/pagination.component';
|
||||||
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
|
|
||||||
|
export interface SourceObject {
|
||||||
|
id: string;
|
||||||
|
lastEvent?: string;
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to display the Quality Assurance source list.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-source-list',
|
||||||
|
templateUrl: './source-list.component.html',
|
||||||
|
styleUrls: ['./source-list.component.scss'],
|
||||||
|
standalone: true,
|
||||||
|
imports: [AlertComponent, ThemedLoadingComponent, PaginationComponent, RouterLink, AsyncPipe, TranslateModule, DatePipe],
|
||||||
|
})
|
||||||
|
export class SourceListComponent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean indicating whether the sources are in a loading state.
|
||||||
|
*/
|
||||||
|
loading: InputSignal<boolean> = input<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pagination system configuration for HTML listing.
|
||||||
|
* @type {PaginationComponentOptions}
|
||||||
|
*/
|
||||||
|
paginationConfig: InputSignal<PaginationComponentOptions> = input<PaginationComponentOptions>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean indicating whether to show the last event column.
|
||||||
|
*/
|
||||||
|
showLastEvent: InputSignal<boolean> = input<boolean>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The source list.
|
||||||
|
*/
|
||||||
|
sources: InputSignal<SourceObject[]|null> = input<SourceObject[]|null>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total number of Quality Assurance sources.
|
||||||
|
*/
|
||||||
|
totalElements: InputSignal<number> = input<number>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event emitter for when a source is selected.
|
||||||
|
* Emits the ID of the selected source.
|
||||||
|
*/
|
||||||
|
sourceSelected = output<string>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event emitter for when the pagination changes.
|
||||||
|
* Emits a string representation of the pagination change.
|
||||||
|
*/
|
||||||
|
paginationChange = output<string>();
|
||||||
|
|
||||||
|
}
|
@@ -12,13 +12,13 @@ import {
|
|||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { take } from 'rxjs/operators';
|
import { take } from 'rxjs/operators';
|
||||||
|
|
||||||
import { Suggestion } from '../../core/notifications/suggestions/models/suggestion.model';
|
import { Suggestion } from '../../../core/notifications/suggestions/models/suggestion.model';
|
||||||
import { Collection } from '../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { ItemType } from '../../core/shared/item-relationships/item-type.model';
|
import { ItemType } from '../../../core/shared/item-relationships/item-type.model';
|
||||||
import { BtnDisabledDirective } from '../../shared/btn-disabled.directive';
|
import { BtnDisabledDirective } from '../../../shared/btn-disabled.directive';
|
||||||
import { ThemedCreateItemParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/themed-create-item-parent-selector.component';
|
import { ThemedCreateItemParentSelectorComponent } from '../../../shared/dso-selector/modal-wrappers/create-item-parent-selector/themed-create-item-parent-selector.component';
|
||||||
import { EntityDropdownComponent } from '../../shared/entity-dropdown/entity-dropdown.component';
|
import { EntityDropdownComponent } from '../../../shared/entity-dropdown/entity-dropdown.component';
|
||||||
import { SuggestionApproveAndImport } from '../suggestion-list-element/suggestion-approve-and-import';
|
import { SuggestionApproveAndImport } from '../list-element/suggestion-approve-and-import';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show and trigger the actions to submit for a suggestion
|
* Show and trigger the actions to submit for a suggestion
|
@@ -1,4 +1,4 @@
|
|||||||
import { Suggestion } from '../../core/notifications/suggestions/models/suggestion.model';
|
import { Suggestion } from '../../../core/notifications/suggestions/models/suggestion.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple interface to unite a specific suggestion and the id of the chosen collection
|
* A simple interface to unite a specific suggestion and the id of the chosen collection
|
@@ -5,9 +5,9 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { SuggestionEvidences } from '../../../core/notifications/suggestions/models/suggestion.model';
|
import { SuggestionEvidences } from '../../../../core/notifications/suggestions/models/suggestion.model';
|
||||||
import { fadeIn } from '../../../shared/animations/fade';
|
import { fadeIn } from '../../../../shared/animations/fade';
|
||||||
import { ObjectKeysPipe } from '../../../shared/utils/object-keys-pipe';
|
import { ObjectKeysPipe } from '../../../../shared/utils/object-keys-pipe';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show suggestion evidences such as score (authorScore, dateScore)
|
* Show suggestion evidences such as score (authorScore, dateScore)
|
@@ -9,10 +9,10 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
import { getTestScheduler } from 'jasmine-marbles';
|
import { getTestScheduler } from 'jasmine-marbles';
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
|
||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { mockSuggestionPublicationOne } from '../../shared/mocks/publication-claim.mock';
|
import { mockSuggestionPublicationOne } from '../../../shared/mocks/publication-claim.mock';
|
||||||
import { ItemSearchResultListElementComponent } from '../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component';
|
import { ItemSearchResultListElementComponent } from '../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component';
|
||||||
import { SuggestionActionsComponent } from '../suggestion-actions/suggestion-actions.component';
|
import { SuggestionActionsComponent } from '../actions/suggestion-actions.component';
|
||||||
import { SuggestionEvidencesComponent } from './suggestion-evidences/suggestion-evidences.component';
|
import { SuggestionEvidencesComponent } from './suggestion-evidences/suggestion-evidences.component';
|
||||||
import { SuggestionListElementComponent } from './suggestion-list-element.component';
|
import { SuggestionListElementComponent } from './suggestion-list-element.component';
|
||||||
|
|
@@ -9,11 +9,11 @@ import {
|
|||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { Suggestion } from 'src/app/core/notifications/suggestions/models/suggestion.model';
|
import { Suggestion } from 'src/app/core/notifications/suggestions/models/suggestion.model';
|
||||||
|
|
||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { fadeIn } from '../../shared/animations/fade';
|
import { fadeIn } from '../../../shared/animations/fade';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { isNotEmpty } from '../../../shared/empty.util';
|
||||||
import { ItemSearchResultListElementComponent } from '../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component';
|
import { ItemSearchResultListElementComponent } from '../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component';
|
||||||
import { SuggestionActionsComponent } from '../suggestion-actions/suggestion-actions.component';
|
import { SuggestionActionsComponent } from '../actions/suggestion-actions.component';
|
||||||
import { SuggestionApproveAndImport } from './suggestion-approve-and-import';
|
import { SuggestionApproveAndImport } from './suggestion-approve-and-import';
|
||||||
import { SuggestionEvidencesComponent } from './suggestion-evidences/suggestion-evidences.component';
|
import { SuggestionEvidencesComponent } from './suggestion-evidences/suggestion-evidences.component';
|
||||||
|
|
@@ -7,9 +7,9 @@ import { RouterLink } from '@angular/router';
|
|||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service';
|
|
||||||
import { SuggestionsService } from '../suggestions.service';
|
import { SuggestionsService } from '../suggestions.service';
|
||||||
|
import { SuggestionTargetsStateService } from '../targets/suggestion-targets.state.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show suggestions notification, used on myDSpace and Profile pages
|
* Show suggestions notification, used on myDSpace and Profile pages
|
@@ -8,10 +8,10 @@ import { ActivatedRoute } from '@angular/router';
|
|||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
|
|
||||||
import { mockSuggestionTargetsObjectOne } from '../../shared/mocks/publication-claim-targets.mock';
|
import { mockSuggestionTargetsObjectOne } from '../../../shared/mocks/publication-claim-targets.mock';
|
||||||
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
import { ActivatedRouteStub } from '../../../shared/testing/active-router.stub';
|
||||||
import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service';
|
|
||||||
import { SuggestionsService } from '../suggestions.service';
|
import { SuggestionsService } from '../suggestions.service';
|
||||||
|
import { SuggestionTargetsStateService } from '../targets/suggestion-targets.state.service';
|
||||||
import { SuggestionsPopupComponent } from './suggestions-popup.component';
|
import { SuggestionsPopupComponent } from './suggestions-popup.component';
|
||||||
|
|
||||||
describe('SuggestionsPopupComponent', () => {
|
describe('SuggestionsPopupComponent', () => {
|
@@ -19,11 +19,11 @@ import {
|
|||||||
takeUntil,
|
takeUntil,
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
import { fromTopEnter } from '../../shared/animations/fromTop';
|
import { fromTopEnter } from '../../../shared/animations/fromTop';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { isNotEmpty } from '../../../shared/empty.util';
|
||||||
import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service';
|
|
||||||
import { SuggestionsService } from '../suggestions.service';
|
import { SuggestionsService } from '../suggestions.service';
|
||||||
|
import { SuggestionTargetsStateService } from '../targets/suggestion-targets.state.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show suggestions on a popover window, used on the homepage
|
* Show suggestions on a popover window, used on the homepage
|
@@ -0,0 +1,14 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<h1 class="border-bottom pb-2">{{'publication-claim.title'| translate}}</h1>
|
||||||
|
<ds-alert [type]="'alert-info'" [content]="'publication-claim.source.description'"></ds-alert>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ds-source-list [loading]="loading$ | async"
|
||||||
|
[paginationConfig]="paginationConfig"
|
||||||
|
[showLastEvent]="false"
|
||||||
|
[sources]="sources$ | async"
|
||||||
|
[totalElements]="totalElements$ | async"
|
||||||
|
(sourceSelected)="onSelect($event)"></ds-source-list>
|
||||||
|
</div>
|
@@ -0,0 +1,121 @@
|
|||||||
|
import {
|
||||||
|
ComponentFixture,
|
||||||
|
TestBed,
|
||||||
|
waitForAsync,
|
||||||
|
} from '@angular/core/testing';
|
||||||
|
import {
|
||||||
|
ActivatedRoute,
|
||||||
|
provideRouter,
|
||||||
|
Router,
|
||||||
|
} from '@angular/router';
|
||||||
|
import {
|
||||||
|
TranslateLoader,
|
||||||
|
TranslateModule,
|
||||||
|
} from '@ngx-translate/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
buildPaginatedList,
|
||||||
|
PaginatedList,
|
||||||
|
} from '../../../core/data/paginated-list.model';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { SuggestionSource } from '../../../core/notifications/suggestions/models/suggestion-source.model';
|
||||||
|
import { SuggestionSourceDataService } from '../../../core/notifications/suggestions/source/suggestion-source-data.service';
|
||||||
|
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||||
|
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||||
|
import { AlertComponent } from '../../../shared/alert/alert.component';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
||||||
|
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
||||||
|
import { TranslateLoaderMock } from '../../../shared/testing/translate-loader.mock';
|
||||||
|
import { SourceListComponent } from '../../shared/source-list.component';
|
||||||
|
import { SuggestionSourcesComponent } from './suggestion-sources.component';
|
||||||
|
|
||||||
|
describe('SuggestionSourcesComponent', () => {
|
||||||
|
let component: SuggestionSourcesComponent;
|
||||||
|
let fixture: ComponentFixture<SuggestionSourcesComponent>;
|
||||||
|
let mockPaginationService: PaginationServiceStub;
|
||||||
|
|
||||||
|
const mockSources: any[] = [
|
||||||
|
{ id: 'source1', total: 5 },
|
||||||
|
{ id: 'source2', total: 10 },
|
||||||
|
];
|
||||||
|
const pageInfo = new PageInfo({
|
||||||
|
elementsPerPage: 5,
|
||||||
|
totalElements: 2,
|
||||||
|
totalPages: 1,
|
||||||
|
currentPage: 1,
|
||||||
|
});
|
||||||
|
const mockPaginatedList: PaginatedList<SuggestionSource> = buildPaginatedList(pageInfo, mockSources) ;
|
||||||
|
const mockPaginatedListRD: Observable<RemoteData<PaginatedList<SuggestionSource>>> = createSuccessfulRemoteDataObject$(mockPaginatedList) ;
|
||||||
|
const mockSuggestionSourceDataService: jasmine.SpyObj<SuggestionSourceDataService> = jasmine.createSpyObj('SuggestionSourceDataService', {
|
||||||
|
'getSources': jasmine.createSpy('getSources'),
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
|
||||||
|
mockPaginationService = new PaginationServiceStub();
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: TranslateLoaderMock,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
SuggestionSourcesComponent,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
provideRouter([]),
|
||||||
|
{ provide: SuggestionSourceDataService, useValue: mockSuggestionSourceDataService },
|
||||||
|
{ provide: PaginationService, useValue: mockPaginationService },
|
||||||
|
],
|
||||||
|
}).overrideComponent(SuggestionSourcesComponent, {
|
||||||
|
remove: {
|
||||||
|
imports: [
|
||||||
|
AlertComponent,
|
||||||
|
SourceListComponent,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
// Mock the suggestion source data service to return an empty list
|
||||||
|
mockSuggestionSourceDataService.getSources.and.returnValue(mockPaginatedListRD);
|
||||||
|
console.log(mockSuggestionSourceDataService);
|
||||||
|
fixture = TestBed.createComponent(SuggestionSourcesComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initialize with default pagination config', () => {
|
||||||
|
expect(component.paginationConfig.pageSize).toBe(10);
|
||||||
|
expect(component.paginationConfig.pageSizeOptions).toEqual([5, 10, 20, 40, 60]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load suggestion sources on init', () => {
|
||||||
|
expect(mockSuggestionSourceDataService.getSources).toHaveBeenCalled();
|
||||||
|
expect(component.sources$.value).toEqual(mockSources);
|
||||||
|
expect(component.totalElements$.value).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update loading status', () => {
|
||||||
|
expect(component.loading$.value).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate to the specified source on select', () => {
|
||||||
|
const router = TestBed.inject(Router);
|
||||||
|
const route = TestBed.inject(ActivatedRoute);
|
||||||
|
spyOn(router, 'navigate');
|
||||||
|
|
||||||
|
const sourceId = 'test-source-id';
|
||||||
|
component.onSelect(sourceId);
|
||||||
|
|
||||||
|
expect(router.navigate).toHaveBeenCalledWith([sourceId], { relativeTo: route });
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,114 @@
|
|||||||
|
import { AsyncPipe } from '@angular/common';
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
import {
|
||||||
|
ActivatedRoute,
|
||||||
|
Router,
|
||||||
|
} from '@angular/router';
|
||||||
|
import { TranslatePipe } from '@ngx-translate/core';
|
||||||
|
import {
|
||||||
|
BehaviorSubject,
|
||||||
|
Observable,
|
||||||
|
} from 'rxjs';
|
||||||
|
import {
|
||||||
|
distinctUntilChanged,
|
||||||
|
map,
|
||||||
|
switchMap,
|
||||||
|
tap,
|
||||||
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { SuggestionSource } from '../../../core/notifications/suggestions/models/suggestion-source.model';
|
||||||
|
import { SuggestionSourceDataService } from '../../../core/notifications/suggestions/source/suggestion-source-data.service';
|
||||||
|
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||||
|
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
||||||
|
import { AlertComponent } from '../../../shared/alert/alert.component';
|
||||||
|
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||||
|
import {
|
||||||
|
SourceListComponent,
|
||||||
|
SourceObject,
|
||||||
|
} from '../../shared/source-list.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-suggestion-sources',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
SourceListComponent,
|
||||||
|
AsyncPipe,
|
||||||
|
AlertComponent,
|
||||||
|
TranslatePipe,
|
||||||
|
],
|
||||||
|
templateUrl: './suggestion-sources.component.html',
|
||||||
|
styleUrl: './suggestion-sources.component.scss',
|
||||||
|
})
|
||||||
|
export class SuggestionSourcesComponent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pagination system configuration for HTML listing.
|
||||||
|
* @type {PaginationComponentOptions}
|
||||||
|
*/
|
||||||
|
public paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
||||||
|
id: 'sl',
|
||||||
|
pageSize: 10,
|
||||||
|
pageSizeOptions: [5, 10, 20, 40, 60],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the information about the loading status of the suggestion sources.
|
||||||
|
*/
|
||||||
|
public loading$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The suggestion source list.
|
||||||
|
*/
|
||||||
|
public sources$: BehaviorSubject<SourceObject[]> = new BehaviorSubject<SourceObject[]>([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total number of Quality Assurance sources.
|
||||||
|
*/
|
||||||
|
public totalElements$: BehaviorSubject<number> = new BehaviorSubject<number>(0);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected paginationService: PaginationService,
|
||||||
|
protected router: Router,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected suggestionSourceDataService: SuggestionSourceDataService) {
|
||||||
|
this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig).pipe(
|
||||||
|
distinctUntilChanged(),
|
||||||
|
switchMap((options: PaginationComponentOptions) => {
|
||||||
|
return this.retrieveSuggestionsSources(options.currentPage, options.pageSize);
|
||||||
|
}),
|
||||||
|
takeUntilDestroyed(),
|
||||||
|
).subscribe((results: Partial<PaginatedList<SuggestionSource>>) => {
|
||||||
|
console.log(results);
|
||||||
|
this.sources$.next(results.page);
|
||||||
|
this.totalElements$.next(results.pageInfo?.totalElements ?? 0);
|
||||||
|
this.loading$.next(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the specified source
|
||||||
|
* @param sourceId
|
||||||
|
*/
|
||||||
|
onSelect(sourceId: string) {
|
||||||
|
this.router.navigate([sourceId], { relativeTo: this.route });
|
||||||
|
}
|
||||||
|
|
||||||
|
private retrieveSuggestionsSources(page: number, pageSize: number): Observable<Partial<PaginatedList<SuggestionSource>>> {
|
||||||
|
this.loading$.next(true);
|
||||||
|
const options = {
|
||||||
|
elementsPerPage: pageSize,
|
||||||
|
currentPage: page,
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.suggestionSourceDataService.getSources(options).pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
tap(console.log),
|
||||||
|
map((result: RemoteData<PaginatedList<SuggestionSource>>) => {
|
||||||
|
return result.hasSucceeded ? result.payload : { page: [], pageInfo: null };
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -5,18 +5,18 @@ import { TestScheduler } from 'rxjs/testing';
|
|||||||
import {
|
import {
|
||||||
SortDirection,
|
SortDirection,
|
||||||
SortOptions,
|
SortOptions,
|
||||||
} from '../core/cache/models/sort-options.model';
|
} from '../../core/cache/models/sort-options.model';
|
||||||
import { FindListOptions } from '../core/data/find-list-options.model';
|
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||||
import { SuggestionTarget } from '../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
import { SuggestionDataService } from '../core/notifications/suggestions/suggestion-data.service';
|
import { SuggestionDataService } from '../../core/notifications/suggestions/suggestion-data.service';
|
||||||
import { SuggestionTargetDataService } from '../core/notifications/suggestions/target/suggestion-target-data.service';
|
import { SuggestionTargetDataService } from '../../core/notifications/suggestions/target/suggestion-target-data.service';
|
||||||
import { ResearcherProfile } from '../core/profile/model/researcher-profile.model';
|
import { ResearcherProfile } from '../../core/profile/model/researcher-profile.model';
|
||||||
import { ResearcherProfileDataService } from '../core/profile/researcher-profile-data.service';
|
import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service';
|
||||||
import { ResourceType } from '../core/shared/resource-type';
|
import { ResourceType } from '../../core/shared/resource-type';
|
||||||
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service';
|
||||||
import { mockSuggestionPublicationOne } from '../shared/mocks/publication-claim.mock';
|
import { mockSuggestionPublicationOne } from '../../shared/mocks/publication-claim.mock';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||||
import { followLink } from '../shared/utils/follow-link-config.model';
|
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||||
import { SuggestionsService } from './suggestions.service';
|
import { SuggestionsService } from './suggestions.service';
|
||||||
|
|
||||||
describe('SuggestionsService test', () => {
|
describe('SuggestionsService test', () => {
|
@@ -12,37 +12,37 @@ import {
|
|||||||
take,
|
take,
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
import { SuggestionConfig } from '../../config/suggestion-config.interfaces';
|
import { SuggestionConfig } from '../../../config/suggestion-config.interfaces';
|
||||||
import { environment } from '../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
import {
|
import {
|
||||||
SortDirection,
|
SortDirection,
|
||||||
SortOptions,
|
SortOptions,
|
||||||
} from '../core/cache/models/sort-options.model';
|
} from '../../core/cache/models/sort-options.model';
|
||||||
import { FindListOptions } from '../core/data/find-list-options.model';
|
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||||
import { PaginatedList } from '../core/data/paginated-list.model';
|
import { PaginatedList } from '../../core/data/paginated-list.model';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { Suggestion } from '../core/notifications/suggestions/models/suggestion.model';
|
import { Suggestion } from '../../core/notifications/suggestions/models/suggestion.model';
|
||||||
import { SuggestionTarget } from '../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
import { SuggestionDataService } from '../core/notifications/suggestions/suggestion-data.service';
|
import { SuggestionDataService } from '../../core/notifications/suggestions/suggestion-data.service';
|
||||||
import { SuggestionTargetDataService } from '../core/notifications/suggestions/target/suggestion-target-data.service';
|
import { SuggestionTargetDataService } from '../../core/notifications/suggestions/target/suggestion-target-data.service';
|
||||||
import { ResearcherProfile } from '../core/profile/model/researcher-profile.model';
|
import { ResearcherProfile } from '../../core/profile/model/researcher-profile.model';
|
||||||
import { ResearcherProfileDataService } from '../core/profile/researcher-profile-data.service';
|
import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service';
|
||||||
import { NoContent } from '../core/shared/NoContent.model';
|
import { NoContent } from '../../core/shared/NoContent.model';
|
||||||
import {
|
import {
|
||||||
getFinishedRemoteData,
|
getFinishedRemoteData,
|
||||||
getFirstCompletedRemoteData,
|
getFirstCompletedRemoteData,
|
||||||
getFirstSucceededRemoteDataPayload,
|
getFirstSucceededRemoteDataPayload,
|
||||||
getFirstSucceededRemoteListPayload,
|
getFirstSucceededRemoteListPayload,
|
||||||
} from '../core/shared/operators';
|
} from '../../core/shared/operators';
|
||||||
import { WorkspaceItem } from '../core/submission/models/workspaceitem.model';
|
import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model';
|
||||||
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service';
|
||||||
import {
|
import {
|
||||||
hasNoValue,
|
hasNoValue,
|
||||||
hasValue,
|
hasValue,
|
||||||
isNotEmpty,
|
isNotEmpty,
|
||||||
} from '../shared/empty.util';
|
} from '../../shared/empty.util';
|
||||||
import { followLink } from '../shared/utils/follow-link-config.model';
|
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||||
import { getSuggestionPageRoute } from '../suggestions-page/suggestions-page-routing-paths';
|
import { getSuggestionPageRoute } from '../../suggestions-page/suggestions-page-routing-paths';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* useful for multiple approvals and ignores operation
|
* useful for multiple approvals and ignores operation
|
@@ -2,7 +2,7 @@ import { AsyncPipe } from '@angular/common';
|
|||||||
import {
|
import {
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
Component,
|
Component,
|
||||||
Input,
|
input,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
@@ -20,13 +20,13 @@ import {
|
|||||||
take,
|
take,
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
import { PaginationService } from '../../../../core/pagination/pagination.service';
|
||||||
import { hasValue } from '../../../shared/empty.util';
|
import { hasValue } from '../../../../shared/empty.util';
|
||||||
import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component';
|
import { ThemedLoadingComponent } from '../../../../shared/loading/themed-loading.component';
|
||||||
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
|
import { PaginationComponent } from '../../../../shared/pagination/pagination.component';
|
||||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
|
||||||
import { getSuggestionPageRoute } from '../../../suggestions-page/suggestions-page-routing-paths';
|
import { getSuggestionPageRoute } from '../../../../suggestions-page/suggestions-page-routing-paths';
|
||||||
import { SuggestionsService } from '../../suggestions.service';
|
import { SuggestionsService } from '../../suggestions.service';
|
||||||
import { SuggestionTargetsStateService } from '../suggestion-targets.state.service';
|
import { SuggestionTargetsStateService } from '../suggestion-targets.state.service';
|
||||||
|
|
||||||
@@ -51,14 +51,14 @@ export class PublicationClaimComponent implements AfterViewInit, OnDestroy, OnIn
|
|||||||
/**
|
/**
|
||||||
* The source for which to list targets
|
* The source for which to list targets
|
||||||
*/
|
*/
|
||||||
@Input() source = '';
|
sourceId = input<string>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The pagination system configuration for HTML listing.
|
* The pagination system configuration for HTML listing.
|
||||||
* @type {PaginationComponentOptions}
|
* @type {PaginationComponentOptions}
|
||||||
*/
|
*/
|
||||||
public paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
public paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
||||||
id: 'stp_' + this.source,
|
id: 'stp_' + this.sourceId,
|
||||||
pageSizeOptions: [5, 10, 20, 40, 60],
|
pageSizeOptions: [5, 10, 20, 40, 60],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -95,8 +95,8 @@ export class PublicationClaimComponent implements AfterViewInit, OnDestroy, OnIn
|
|||||||
* Component initialization.
|
* Component initialization.
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.targets$ = this.suggestionTargetsStateService.getSuggestionTargets(this.source);
|
this.targets$ = this.suggestionTargetsStateService.getSuggestionTargets(this.sourceId());
|
||||||
this.totalElements$ = this.suggestionTargetsStateService.getSuggestionTargetsTotals(this.source);
|
this.totalElements$ = this.suggestionTargetsStateService.getSuggestionTargetsTotals(this.sourceId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,7 +104,7 @@ export class PublicationClaimComponent implements AfterViewInit, OnDestroy, OnIn
|
|||||||
*/
|
*/
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
this.subs.push(
|
this.subs.push(
|
||||||
this.suggestionTargetsStateService.isSuggestionTargetsLoaded(this.source).pipe(
|
this.suggestionTargetsStateService.isSuggestionTargetsLoaded(this.sourceId()).pipe(
|
||||||
take(1),
|
take(1),
|
||||||
).subscribe(() => {
|
).subscribe(() => {
|
||||||
this.getSuggestionTargets();
|
this.getSuggestionTargets();
|
||||||
@@ -119,7 +119,7 @@ export class PublicationClaimComponent implements AfterViewInit, OnDestroy, OnIn
|
|||||||
* 'true' if the targets are loading, 'false' otherwise.
|
* 'true' if the targets are loading, 'false' otherwise.
|
||||||
*/
|
*/
|
||||||
public isTargetsLoading(): Observable<boolean> {
|
public isTargetsLoading(): Observable<boolean> {
|
||||||
return this.suggestionTargetsStateService.isSuggestionTargetsLoading(this.source);
|
return this.suggestionTargetsStateService.isSuggestionTargetsLoading(this.sourceId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,7 +129,7 @@ export class PublicationClaimComponent implements AfterViewInit, OnDestroy, OnIn
|
|||||||
* 'true' if there are operations running on the targets (ex.: a REST call), 'false' otherwise.
|
* 'true' if there are operations running on the targets (ex.: a REST call), 'false' otherwise.
|
||||||
*/
|
*/
|
||||||
public isTargetsProcessing(): Observable<boolean> {
|
public isTargetsProcessing(): Observable<boolean> {
|
||||||
return this.suggestionTargetsStateService.isSuggestionTargetsProcessing(this.source);
|
return this.suggestionTargetsStateService.isSuggestionTargetsProcessing(this.sourceId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -146,7 +146,7 @@ export class PublicationClaimComponent implements AfterViewInit, OnDestroy, OnIn
|
|||||||
* Unsubscribe from all subscriptions.
|
* Unsubscribe from all subscriptions.
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.suggestionTargetsStateService.dispatchClearSuggestionTargetsAction(this.source);
|
this.suggestionTargetsStateService.dispatchClearSuggestionTargetsAction(this.sourceId());
|
||||||
this.subs
|
this.subs
|
||||||
.filter((sub) => hasValue(sub))
|
.filter((sub) => hasValue(sub))
|
||||||
.forEach((sub) => sub.unsubscribe());
|
.forEach((sub) => sub.unsubscribe());
|
||||||
@@ -161,7 +161,7 @@ export class PublicationClaimComponent implements AfterViewInit, OnDestroy, OnIn
|
|||||||
take(1),
|
take(1),
|
||||||
).subscribe((options: PaginationComponentOptions) => {
|
).subscribe((options: PaginationComponentOptions) => {
|
||||||
this.suggestionTargetsStateService.dispatchRetrieveSuggestionTargets(
|
this.suggestionTargetsStateService.dispatchRetrieveSuggestionTargets(
|
||||||
this.source,
|
this.sourceId(),
|
||||||
options.pageSize,
|
options.pageSize,
|
||||||
options.currentPage,
|
options.currentPage,
|
||||||
);
|
);
|
@@ -4,12 +4,12 @@ import {
|
|||||||
MemoizedSelector,
|
MemoizedSelector,
|
||||||
} from '@ngrx/store';
|
} from '@ngrx/store';
|
||||||
|
|
||||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
import { subStateSelector } from '../../submission/selectors';
|
import { subStateSelector } from '../../../submission/selectors';
|
||||||
import {
|
import {
|
||||||
suggestionNotificationsSelector,
|
suggestionNotificationsSelector,
|
||||||
SuggestionNotificationsState,
|
SuggestionNotificationsState,
|
||||||
} from '../notifications.reducer';
|
} from '../../notifications.reducer';
|
||||||
import {
|
import {
|
||||||
SuggestionTargetEntry,
|
SuggestionTargetEntry,
|
||||||
SuggestionTargetState,
|
SuggestionTargetState,
|
@@ -1,8 +1,8 @@
|
|||||||
/* eslint-disable max-classes-per-file */
|
/* eslint-disable max-classes-per-file */
|
||||||
import { Action } from '@ngrx/store';
|
import { Action } from '@ngrx/store';
|
||||||
|
|
||||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
import { type } from '../../shared/ngrx/type';
|
import { type } from '../../../shared/ngrx/type';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For each action type in an action group, make a simple
|
* For each action type in an action group, make a simple
|
@@ -21,10 +21,10 @@ import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators';
|
|||||||
import {
|
import {
|
||||||
AuthActionTypes,
|
AuthActionTypes,
|
||||||
RetrieveAuthenticatedEpersonSuccessAction,
|
RetrieveAuthenticatedEpersonSuccessAction,
|
||||||
} from '../../core/auth/auth.actions';
|
} from '../../../core/auth/auth.actions';
|
||||||
import { PaginatedList } from '../../core/data/paginated-list.model';
|
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
import { SuggestionsService } from '../suggestions.service';
|
import { SuggestionsService } from '../suggestions.service';
|
||||||
import {
|
import {
|
||||||
AddTargetAction,
|
AddTargetAction,
|
@@ -1,4 +1,4 @@
|
|||||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
import {
|
import {
|
||||||
SuggestionTargetActionTypes,
|
SuggestionTargetActionTypes,
|
||||||
SuggestionTargetsActions,
|
SuggestionTargetsActions,
|
@@ -6,8 +6,8 @@ import {
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
import { SuggestionNotificationsState } from '../notifications.reducer';
|
import { SuggestionNotificationsState } from '../../notifications.reducer';
|
||||||
import {
|
import {
|
||||||
getCurrentUserSuggestionTargetsSelector,
|
getCurrentUserSuggestionTargetsSelector,
|
||||||
getCurrentUserSuggestionTargetsVisitedSelector,
|
getCurrentUserSuggestionTargetsVisitedSelector,
|
@@ -29,7 +29,7 @@ import { AuthorizationDataService } from '../core/data/feature-authorization/aut
|
|||||||
import { EPersonDataService } from '../core/eperson/eperson-data.service';
|
import { EPersonDataService } from '../core/eperson/eperson-data.service';
|
||||||
import { EPerson } from '../core/eperson/models/eperson.model';
|
import { EPerson } from '../core/eperson/models/eperson.model';
|
||||||
import { ConfigurationProperty } from '../core/shared/configuration-property.model';
|
import { ConfigurationProperty } from '../core/shared/configuration-property.model';
|
||||||
import { SuggestionsNotificationComponent } from '../notifications/suggestions-notification/suggestions-notification.component';
|
import { SuggestionsNotificationComponent } from '../notifications/suggestions/notification/suggestions-notification.component';
|
||||||
import { ErrorComponent } from '../shared/error/error.component';
|
import { ErrorComponent } from '../shared/error/error.component';
|
||||||
import { ThemedLoadingComponent } from '../shared/loading/themed-loading.component';
|
import { ThemedLoadingComponent } from '../shared/loading/themed-loading.component';
|
||||||
import { NotificationsService } from '../shared/notifications/notifications.service';
|
import { NotificationsService } from '../shared/notifications/notifications.service';
|
||||||
|
@@ -40,7 +40,7 @@ import {
|
|||||||
getFirstCompletedRemoteData,
|
getFirstCompletedRemoteData,
|
||||||
getRemoteDataPayload,
|
getRemoteDataPayload,
|
||||||
} from '../core/shared/operators';
|
} from '../core/shared/operators';
|
||||||
import { SuggestionsNotificationComponent } from '../notifications/suggestions-notification/suggestions-notification.component';
|
import { SuggestionsNotificationComponent } from '../notifications/suggestions/notification/suggestions-notification.component';
|
||||||
import {
|
import {
|
||||||
hasValue,
|
hasValue,
|
||||||
isNotEmpty,
|
isNotEmpty,
|
||||||
|
@@ -2,7 +2,7 @@ import { Route } from '@angular/router';
|
|||||||
|
|
||||||
import { authenticatedGuard } from '../core/auth/authenticated.guard';
|
import { authenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { qualityAssuranceBreadcrumbResolver } from '../core/breadcrumbs/quality-assurance-breadcrumb.resolver';
|
import { sourcesBreadcrumbResolver } from '../core/breadcrumbs/sources-breadcrumb.resolver';
|
||||||
import {
|
import {
|
||||||
NOTIFICATIONS_RECITER_SUGGESTION_PATH,
|
NOTIFICATIONS_RECITER_SUGGESTION_PATH,
|
||||||
QUALITY_ASSURANCE_EDIT_PATH,
|
QUALITY_ASSURANCE_EDIT_PATH,
|
||||||
@@ -39,7 +39,7 @@ export const ROUTES: Route[] = [
|
|||||||
component: QualityAssuranceTopicsPageComponent,
|
component: QualityAssuranceTopicsPageComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: qualityAssuranceBreadcrumbResolver,
|
breadcrumb: sourcesBreadcrumbResolver,
|
||||||
openaireQualityAssuranceTopicsParams: QualityAssuranceTopicsPageResolver,
|
openaireQualityAssuranceTopicsParams: QualityAssuranceTopicsPageResolver,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
@@ -85,7 +85,7 @@ export const ROUTES: Route[] = [
|
|||||||
component: QualityAssuranceEventsPageComponent,
|
component: QualityAssuranceEventsPageComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: qualityAssuranceBreadcrumbResolver,
|
breadcrumb: sourcesBreadcrumbResolver,
|
||||||
openaireQualityAssuranceEventsParams: qualityAssuranceEventsPageResolver,
|
openaireQualityAssuranceEventsParams: qualityAssuranceEventsPageResolver,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
|
@@ -1 +1 @@
|
|||||||
<ds-publication-claim [source]="'openaire'"></ds-publication-claim>
|
<ds-suggestion-sources></ds-suggestion-sources>
|
||||||
|
@@ -7,9 +7,10 @@ import {
|
|||||||
} from '@angular/core/testing';
|
} from '@angular/core/testing';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MockComponent } from 'ng-mocks';
|
||||||
|
|
||||||
import { AdminNotificationsPublicationClaimPageComponent } from '../../admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component';
|
import { AdminNotificationsPublicationClaimPageComponent } from '../../admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component';
|
||||||
import { PublicationClaimComponent } from '../../notifications/suggestion-targets/publication-claim/publication-claim.component';
|
import { SuggestionSourcesComponent } from '../../notifications/suggestions/sources/suggestion-sources.component';
|
||||||
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
||||||
import { NotificationsSuggestionTargetsPageComponent } from './notifications-suggestion-targets-page.component';
|
import { NotificationsSuggestionTargetsPageComponent } from './notifications-suggestion-targets-page.component';
|
||||||
|
|
||||||
@@ -23,19 +24,14 @@ describe('NotificationsSuggestionTargetsPageComponent', () => {
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
TranslateModule.forRoot(),
|
TranslateModule.forRoot(),
|
||||||
NotificationsSuggestionTargetsPageComponent,
|
NotificationsSuggestionTargetsPageComponent,
|
||||||
|
MockComponent(SuggestionSourcesComponent),
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
AdminNotificationsPublicationClaimPageComponent,
|
AdminNotificationsPublicationClaimPageComponent,
|
||||||
{ provide: ActivatedRoute, useValue: new ActivatedRouteStub() },
|
{ provide: ActivatedRoute, useValue: new ActivatedRouteStub() },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA],
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
})
|
}).compileComponents();
|
||||||
.overrideComponent(NotificationsSuggestionTargetsPageComponent, {
|
|
||||||
remove: {
|
|
||||||
imports: [PublicationClaimComponent],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { PublicationClaimComponent } from '../../notifications/suggestion-targets/publication-claim/publication-claim.component';
|
import { SuggestionSourcesComponent } from '../../notifications/suggestions/sources/suggestion-sources.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-notifications-reciter-page',
|
selector: 'ds-notifications-reciter-page',
|
||||||
templateUrl: './notifications-suggestion-targets-page.component.html',
|
templateUrl: './notifications-suggestion-targets-page.component.html',
|
||||||
styleUrls: ['./notifications-suggestion-targets-page.component.scss'],
|
styleUrls: ['./notifications-suggestion-targets-page.component.scss'],
|
||||||
imports: [
|
imports: [
|
||||||
PublicationClaimComponent,
|
SuggestionSourcesComponent,
|
||||||
],
|
],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
|
@@ -34,7 +34,7 @@ export const qualityAssuranceSourceDataResolver: ResolveFn<QualityAssuranceSourc
|
|||||||
): Observable<QualityAssuranceSourceObject[]> => {
|
): Observable<QualityAssuranceSourceObject[]> => {
|
||||||
const pageSize = appConfig.qualityAssuranceConfig.pageSize;
|
const pageSize = appConfig.qualityAssuranceConfig.pageSize;
|
||||||
|
|
||||||
return qualityAssuranceSourceService.getSources(pageSize, 0).pipe(
|
return qualityAssuranceSourceService.getSources(pageSize, 1).pipe(
|
||||||
map((sources: PaginatedList<QualityAssuranceSourceObject>) => {
|
map((sources: PaginatedList<QualityAssuranceSourceObject>) => {
|
||||||
if (sources.page.length === 1) {
|
if (sources.page.length === 1) {
|
||||||
router.navigate([getResolvedUrl(route) + '/' + sources.page[0].id]);
|
router.navigate([getResolvedUrl(route) + '/' + sources.page[0].id]);
|
||||||
|
@@ -1,9 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
|
ChangeDetectorRef,
|
||||||
Component,
|
Component,
|
||||||
|
EventEmitter,
|
||||||
Input,
|
Input,
|
||||||
|
Output,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ThemedComponent } from 'src/app/shared/theme-support/themed.component';
|
import { ThemedComponent } from 'src/app/shared/theme-support/themed.component';
|
||||||
|
|
||||||
|
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||||
|
import { ThemeService } from '../../../theme-support/theme.service';
|
||||||
import { CreateItemParentSelectorComponent } from './create-item-parent-selector.component';
|
import { CreateItemParentSelectorComponent } from './create-item-parent-selector.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,8 +24,17 @@ import { CreateItemParentSelectorComponent } from './create-item-parent-selector
|
|||||||
export class ThemedCreateItemParentSelectorComponent
|
export class ThemedCreateItemParentSelectorComponent
|
||||||
extends ThemedComponent<CreateItemParentSelectorComponent> {
|
extends ThemedComponent<CreateItemParentSelectorComponent> {
|
||||||
@Input() entityType: string;
|
@Input() entityType: string;
|
||||||
|
@Output() select: EventEmitter<DSpaceObject> = new EventEmitter<DSpaceObject>();
|
||||||
|
@Input() emitOnly = false;
|
||||||
|
|
||||||
protected inAndOutputNames: (keyof CreateItemParentSelectorComponent & keyof this)[] = ['entityType'];
|
protected inAndOutputNames: (keyof CreateItemParentSelectorComponent & keyof this)[] = ['entityType', 'select', 'emitOnly'];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected cdr: ChangeDetectorRef,
|
||||||
|
protected themeService: ThemeService,
|
||||||
|
) {
|
||||||
|
super(cdr, themeService);
|
||||||
|
}
|
||||||
|
|
||||||
protected getComponentName(): string {
|
protected getComponentName(): string {
|
||||||
return 'CreateItemParentSelectorComponent';
|
return 'CreateItemParentSelectorComponent';
|
||||||
|
@@ -22,11 +22,11 @@ import { TestScheduler } from 'rxjs/testing';
|
|||||||
import { AuthService } from '../core/auth/auth.service';
|
import { AuthService } from '../core/auth/auth.service';
|
||||||
import { PaginationService } from '../core/pagination/pagination.service';
|
import { PaginationService } from '../core/pagination/pagination.service';
|
||||||
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
||||||
import { SuggestionApproveAndImport } from '../notifications/suggestion-list-element/suggestion-approve-and-import';
|
import { SuggestionApproveAndImport } from '../notifications/suggestions/list-element/suggestion-approve-and-import';
|
||||||
import { SuggestionEvidencesComponent } from '../notifications/suggestion-list-element/suggestion-evidences/suggestion-evidences.component';
|
import { SuggestionEvidencesComponent } from '../notifications/suggestions/list-element/suggestion-evidences/suggestion-evidences.component';
|
||||||
import { SuggestionListElementComponent } from '../notifications/suggestion-list-element/suggestion-list-element.component';
|
import { SuggestionListElementComponent } from '../notifications/suggestions/list-element/suggestion-list-element.component';
|
||||||
import { SuggestionTargetsStateService } from '../notifications/suggestion-targets/suggestion-targets.state.service';
|
import { SuggestionsService } from '../notifications/suggestions/suggestions.service';
|
||||||
import { SuggestionsService } from '../notifications/suggestions.service';
|
import { SuggestionTargetsStateService } from '../notifications/suggestions/targets/suggestion-targets.state.service';
|
||||||
import {
|
import {
|
||||||
mockSuggestionPublicationOne,
|
mockSuggestionPublicationOne,
|
||||||
mockSuggestionPublicationTwo,
|
mockSuggestionPublicationTwo,
|
||||||
|
@@ -43,14 +43,14 @@ import {
|
|||||||
} from '../core/shared/operators';
|
} from '../core/shared/operators';
|
||||||
import { WorkspaceItem } from '../core/submission/models/workspaceitem.model';
|
import { WorkspaceItem } from '../core/submission/models/workspaceitem.model';
|
||||||
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
||||||
import { SuggestionActionsComponent } from '../notifications/suggestion-actions/suggestion-actions.component';
|
import { SuggestionActionsComponent } from '../notifications/suggestions/actions/suggestion-actions.component';
|
||||||
import { SuggestionApproveAndImport } from '../notifications/suggestion-list-element/suggestion-approve-and-import';
|
import { SuggestionApproveAndImport } from '../notifications/suggestions/list-element/suggestion-approve-and-import';
|
||||||
import { SuggestionListElementComponent } from '../notifications/suggestion-list-element/suggestion-list-element.component';
|
import { SuggestionListElementComponent } from '../notifications/suggestions/list-element/suggestion-list-element.component';
|
||||||
import { SuggestionTargetsStateService } from '../notifications/suggestion-targets/suggestion-targets.state.service';
|
|
||||||
import {
|
import {
|
||||||
SuggestionBulkResult,
|
SuggestionBulkResult,
|
||||||
SuggestionsService,
|
SuggestionsService,
|
||||||
} from '../notifications/suggestions.service';
|
} from '../notifications/suggestions/suggestions.service';
|
||||||
|
import { SuggestionTargetsStateService } from '../notifications/suggestions/targets/suggestion-targets.state.service';
|
||||||
import { AlertComponent } from '../shared/alert/alert.component';
|
import { AlertComponent } from '../shared/alert/alert.component';
|
||||||
import { ThemedLoadingComponent } from '../shared/loading/themed-loading.component';
|
import { ThemedLoadingComponent } from '../shared/loading/themed-loading.component';
|
||||||
import { NotificationsService } from '../shared/notifications/notifications.service';
|
import { NotificationsService } from '../shared/notifications/notifications.service';
|
||||||
|
@@ -2832,10 +2832,18 @@
|
|||||||
|
|
||||||
"item.preview.dc.type": "Type:",
|
"item.preview.dc.type": "Type:",
|
||||||
|
|
||||||
|
"item.preview.oaire.version": "Version",
|
||||||
|
|
||||||
"item.preview.oaire.citation.issue": "Issue",
|
"item.preview.oaire.citation.issue": "Issue",
|
||||||
|
|
||||||
"item.preview.oaire.citation.volume": "Volume",
|
"item.preview.oaire.citation.volume": "Volume",
|
||||||
|
|
||||||
|
"item.preview.oaire.citation.title": "Citation container",
|
||||||
|
|
||||||
|
"item.preview.oaire.citation.startPage": "Citation start page",
|
||||||
|
|
||||||
|
"item.preview.oaire.citation.endPage": "Citation end page",
|
||||||
|
|
||||||
"item.preview.dc.relation.issn": "ISSN",
|
"item.preview.dc.relation.issn": "ISSN",
|
||||||
|
|
||||||
"item.preview.dc.identifier.isbn": "ISBN",
|
"item.preview.dc.identifier.isbn": "ISBN",
|
||||||
@@ -2854,6 +2862,8 @@
|
|||||||
|
|
||||||
"item.preview.person.identifier.orcid": "ORCID:",
|
"item.preview.person.identifier.orcid": "ORCID:",
|
||||||
|
|
||||||
|
"item.preview.person.affiliation.name": "Affiliations:",
|
||||||
|
|
||||||
"item.preview.project.funder.name": "Funder:",
|
"item.preview.project.funder.name": "Funder:",
|
||||||
|
|
||||||
"item.preview.project.funder.identifier": "Funder Identifier:",
|
"item.preview.project.funder.identifier": "Funder Identifier:",
|
||||||
@@ -2884,6 +2894,16 @@
|
|||||||
|
|
||||||
"item.preview.dspace.entity.type": "Entity Type:",
|
"item.preview.dspace.entity.type": "Entity Type:",
|
||||||
|
|
||||||
|
"item.preview.creativework.publisher": "Publisher",
|
||||||
|
|
||||||
|
"item.preview.creativeworkseries.issn": "ISSN",
|
||||||
|
|
||||||
|
"item.preview.dc.identifier.issn": "ISSN",
|
||||||
|
|
||||||
|
"item.preview.dc.identifier.openalex": "OpenAlex Identifier",
|
||||||
|
|
||||||
|
"item.preview.dc.description": "Description",
|
||||||
|
|
||||||
"item.select.confirm": "Confirm selected",
|
"item.select.confirm": "Confirm selected",
|
||||||
|
|
||||||
"item.select.empty": "No items to show",
|
"item.select.empty": "No items to show",
|
||||||
@@ -3536,6 +3556,10 @@
|
|||||||
|
|
||||||
"none.listelement.badge": "Item",
|
"none.listelement.badge": "Item",
|
||||||
|
|
||||||
|
"publication-claim.title": "Publication claim",
|
||||||
|
|
||||||
|
"publication-claim.source.description": "Below you can see all the sources.",
|
||||||
|
|
||||||
"quality-assurance.title": "Quality Assurance",
|
"quality-assurance.title": "Quality Assurance",
|
||||||
|
|
||||||
"quality-assurance.topics.description": "Below you can see all the topics received from the subscriptions to {{source}}.",
|
"quality-assurance.topics.description": "Below you can see all the topics received from the subscriptions to {{source}}.",
|
||||||
@@ -4064,6 +4088,8 @@
|
|||||||
|
|
||||||
"suggestion.source.openaire": "OpenAIRE Graph",
|
"suggestion.source.openaire": "OpenAIRE Graph",
|
||||||
|
|
||||||
|
"suggestion.source.openalex": "OpenAlex",
|
||||||
|
|
||||||
"suggestion.from.source": "from the ",
|
"suggestion.from.source": "from the ",
|
||||||
|
|
||||||
"suggestion.count.missing": "You have no publication claims left",
|
"suggestion.count.missing": "You have no publication claims left",
|
||||||
@@ -4854,6 +4880,22 @@
|
|||||||
|
|
||||||
"submission.import-external.source.ror": "Research Organization Registry (ROR)",
|
"submission.import-external.source.ror": "Research Organization Registry (ROR)",
|
||||||
|
|
||||||
|
"submission.import-external.source.openalexPublication": "OpenAlex Search by Title",
|
||||||
|
|
||||||
|
"submission.import-external.source.openalexPublicationByAuthorId": "OpenAlex Search by Author ID",
|
||||||
|
|
||||||
|
"submission.import-external.source.openalexPublicationByDOI": "OpenAlex Search by DOI",
|
||||||
|
|
||||||
|
"submission.import-external.source.openalexPerson": "OpenAlex Search by name",
|
||||||
|
|
||||||
|
"submission.import-external.source.openalexJournal": "OpenAlex Journals",
|
||||||
|
|
||||||
|
"submission.import-external.source.openalexInstitution": "OpenAlex Institutions",
|
||||||
|
|
||||||
|
"submission.import-external.source.openalexPublisher": "OpenAlex Publishers",
|
||||||
|
|
||||||
|
"submission.import-external.source.openalexFunder": "OpenAlex Funders",
|
||||||
|
|
||||||
"submission.import-external.preview.title": "Item Preview",
|
"submission.import-external.preview.title": "Item Preview",
|
||||||
|
|
||||||
"submission.import-external.preview.title.Publication": "Publication Preview",
|
"submission.import-external.preview.title.Publication": "Publication Preview",
|
||||||
|
@@ -11,7 +11,7 @@ import { ThemedHomeNewsComponent } from '../../../../app/home-page/home-news/the
|
|||||||
import { HomePageComponent as BaseComponent } from '../../../../app/home-page/home-page.component';
|
import { HomePageComponent as BaseComponent } from '../../../../app/home-page/home-page.component';
|
||||||
import { RecentItemListComponent } from '../../../../app/home-page/recent-item-list/recent-item-list.component';
|
import { RecentItemListComponent } from '../../../../app/home-page/recent-item-list/recent-item-list.component';
|
||||||
import { ThemedTopLevelCommunityListComponent } from '../../../../app/home-page/top-level-community-list/themed-top-level-community-list.component';
|
import { ThemedTopLevelCommunityListComponent } from '../../../../app/home-page/top-level-community-list/themed-top-level-community-list.component';
|
||||||
import { SuggestionsPopupComponent } from '../../../../app/notifications/suggestions-popup/suggestions-popup.component';
|
import { SuggestionsPopupComponent } from '../../../../app/notifications/suggestions/popup/suggestions-popup.component';
|
||||||
import { ThemedConfigurationSearchPageComponent } from '../../../../app/search-page/themed-configuration-search-page.component';
|
import { ThemedConfigurationSearchPageComponent } from '../../../../app/search-page/themed-configuration-search-page.component';
|
||||||
import { ThemedSearchFormComponent } from '../../../../app/shared/search-form/themed-search-form.component';
|
import { ThemedSearchFormComponent } from '../../../../app/shared/search-form/themed-search-form.component';
|
||||||
import { PageWithSidebarComponent } from '../../../../app/shared/sidebar/page-with-sidebar.component';
|
import { PageWithSidebarComponent } from '../../../../app/shared/sidebar/page-with-sidebar.component';
|
||||||
|
@@ -11,7 +11,7 @@ import {
|
|||||||
import { MyDSpaceNewSubmissionComponent } from '../../../../app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component';
|
import { MyDSpaceNewSubmissionComponent } from '../../../../app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component';
|
||||||
import { MyDSpacePageComponent as BaseComponent } from '../../../../app/my-dspace-page/my-dspace-page.component';
|
import { MyDSpacePageComponent as BaseComponent } from '../../../../app/my-dspace-page/my-dspace-page.component';
|
||||||
import { MyDspaceQaEventsNotificationsComponent } from '../../../../app/my-dspace-page/my-dspace-qa-events-notifications/my-dspace-qa-events-notifications.component';
|
import { MyDspaceQaEventsNotificationsComponent } from '../../../../app/my-dspace-page/my-dspace-qa-events-notifications/my-dspace-qa-events-notifications.component';
|
||||||
import { SuggestionsNotificationComponent } from '../../../../app/notifications/suggestions-notification/suggestions-notification.component';
|
import { SuggestionsNotificationComponent } from '../../../../app/notifications/suggestions/notification/suggestions-notification.component';
|
||||||
import { pushInOut } from '../../../../app/shared/animations/push';
|
import { pushInOut } from '../../../../app/shared/animations/push';
|
||||||
import { RoleDirective } from '../../../../app/shared/roles/role.directive';
|
import { RoleDirective } from '../../../../app/shared/roles/role.directive';
|
||||||
import { ThemedSearchComponent } from '../../../../app/shared/search/themed-search.component';
|
import { ThemedSearchComponent } from '../../../../app/shared/search/themed-search.component';
|
||||||
|
@@ -5,7 +5,7 @@ import {
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { SuggestionsNotificationComponent } from '../../../../app/notifications/suggestions-notification/suggestions-notification.component';
|
import { SuggestionsNotificationComponent } from '../../../../app/notifications/suggestions/notification/suggestions-notification.component';
|
||||||
import { ProfilePageComponent as BaseComponent } from '../../../../app/profile-page/profile-page.component';
|
import { ProfilePageComponent as BaseComponent } from '../../../../app/profile-page/profile-page.component';
|
||||||
import { ThemedProfilePageMetadataFormComponent } from '../../../../app/profile-page/profile-page-metadata-form/themed-profile-page-metadata-form.component';
|
import { ThemedProfilePageMetadataFormComponent } from '../../../../app/profile-page/profile-page-metadata-form/themed-profile-page-metadata-form.component';
|
||||||
import { ProfilePageResearcherFormComponent } from '../../../../app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component';
|
import { ProfilePageResearcherFormComponent } from '../../../../app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component';
|
||||||
|
Reference in New Issue
Block a user