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,
|
||||
} from '@angular/core/testing';
|
||||
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';
|
||||
|
||||
describe('AdminNotificationsPublicationClaimPageComponent', () => {
|
||||
@@ -20,17 +21,10 @@ describe('AdminNotificationsPublicationClaimPageComponent', () => {
|
||||
CommonModule,
|
||||
TranslateModule.forRoot(),
|
||||
AdminNotificationsPublicationClaimPageComponent,
|
||||
],
|
||||
providers: [
|
||||
AdminNotificationsPublicationClaimPageComponent,
|
||||
MockComponent(SuggestionSourcesComponent),
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
}).overrideComponent(AdminNotificationsPublicationClaimPageComponent, {
|
||||
remove: {
|
||||
imports: [PublicationClaimComponent],
|
||||
},
|
||||
})
|
||||
.compileComponents();
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
|
@@ -1,14 +1,12 @@
|
||||
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({
|
||||
selector: 'ds-admin-notifications-publication-claim-page',
|
||||
templateUrl: './admin-notifications-publication-claim-page.component.html',
|
||||
styleUrls: ['./admin-notifications-publication-claim-page.component.scss'],
|
||||
imports: [
|
||||
PublicationClaimComponent,
|
||||
],
|
||||
imports: [ SuggestionSourcesComponent ],
|
||||
standalone: true,
|
||||
})
|
||||
export class AdminNotificationsPublicationClaimPageComponent {
|
||||
|
@@ -2,7 +2,8 @@ import { Route } from '@angular/router';
|
||||
|
||||
import { authenticatedGuard } from '../../core/auth/authenticated.guard';
|
||||
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 { 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';
|
||||
@@ -33,13 +34,28 @@ export const ROUTES: Route[] = [
|
||||
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],
|
||||
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId`,
|
||||
component: QualityAssuranceTopicsPageComponent,
|
||||
pathMatch: 'full',
|
||||
resolve: {
|
||||
breadcrumb: qualityAssuranceBreadcrumbResolver,
|
||||
breadcrumb: sourcesBreadcrumbResolver,
|
||||
openaireQualityAssuranceTopicsParams: QualityAssuranceTopicsPageResolver,
|
||||
},
|
||||
data: {
|
||||
@@ -85,7 +101,7 @@ export const ROUTES: Route[] = [
|
||||
component: QualityAssuranceEventsPageComponent,
|
||||
pathMatch: 'full',
|
||||
resolve: {
|
||||
breadcrumb: qualityAssuranceBreadcrumbResolver,
|
||||
breadcrumb: sourcesBreadcrumbResolver,
|
||||
openaireQualityAssuranceEventsParams: qualityAssuranceEventsPageResolver,
|
||||
},
|
||||
data: {
|
||||
|
@@ -10,6 +10,7 @@ import {
|
||||
import {
|
||||
NoPreloading,
|
||||
provideRouter,
|
||||
withComponentInputBinding,
|
||||
withEnabledBlockingInitialNavigation,
|
||||
withInMemoryScrolling,
|
||||
withPreloading,
|
||||
@@ -109,6 +110,7 @@ export const commonAppConfig: ApplicationConfig = {
|
||||
withInMemoryScrolling(APP_ROUTING_SCROLL_CONF),
|
||||
withEnabledBlockingInitialNavigation(),
|
||||
withPreloading(NoPreloading),
|
||||
withComponentInputBinding(),
|
||||
),
|
||||
{
|
||||
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', () => {
|
||||
let resolver: any;
|
||||
let qualityAssuranceBreadcrumbService: any;
|
||||
let sourcesBreadcrumbService: any;
|
||||
let route: any;
|
||||
const i18nKey = 'breadcrumbKey';
|
||||
const fullPath = '/test/quality-assurance/';
|
||||
const expectedKey = 'testSourceId:testTopicId';
|
||||
const expectedKey = 'breadcrumbKey:testSourceId:testTopicId';
|
||||
|
||||
beforeEach(() => {
|
||||
route = {
|
||||
data: { breadcrumbKey: i18nKey },
|
||||
paramMap: {
|
||||
get: function (param) {
|
||||
return this[param];
|
||||
@@ -18,13 +20,13 @@ describe('qualityAssuranceBreadcrumbResolver', () => {
|
||||
topicId: 'testTopicId',
|
||||
},
|
||||
};
|
||||
qualityAssuranceBreadcrumbService = {};
|
||||
resolver = qualityAssuranceBreadcrumbResolver;
|
||||
sourcesBreadcrumbService = {};
|
||||
resolver = sourcesBreadcrumbResolver;
|
||||
});
|
||||
|
||||
it('should resolve the breadcrumb config', () => {
|
||||
const resolvedConfig = resolver(route as any, { url: fullPath + 'testSourceId' } as any, qualityAssuranceBreadcrumbService);
|
||||
const expectedConfig = { provider: qualityAssuranceBreadcrumbService, key: expectedKey, url: fullPath };
|
||||
const resolvedConfig = resolver(route as any, { url: fullPath + 'testSourceId' } as any, sourcesBreadcrumbService);
|
||||
const expectedConfig = { provider: sourcesBreadcrumbService, key: expectedKey, url: fullPath };
|
||||
expect(resolvedConfig).toEqual(expectedConfig);
|
||||
});
|
||||
});
|
@@ -6,16 +6,17 @@ import {
|
||||
} from '@angular/router';
|
||||
|
||||
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,
|
||||
state: RouterStateSnapshot,
|
||||
breadcrumbService: QualityAssuranceBreadcrumbService = inject(QualityAssuranceBreadcrumbService),
|
||||
breadcrumbService: SourcesBreadcrumbService = inject(SourcesBreadcrumbService),
|
||||
): BreadcrumbConfig<string> => {
|
||||
const breadcrumbKey = route.data.breadcrumbKey;
|
||||
const sourceId = route.paramMap.get('sourceId');
|
||||
const topicId = route.paramMap.get('topicId');
|
||||
let key = sourceId;
|
||||
let key = `${breadcrumbKey}:${sourceId}`;
|
||||
|
||||
if (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({
|
||||
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(
|
||||
private translationService: TranslateService,
|
||||
) {
|
||||
@@ -31,15 +31,16 @@ export class QualityAssuranceBreadcrumbService implements BreadcrumbsProviderSer
|
||||
*/
|
||||
getBreadcrumbs(key: string, url: string): Observable<Breadcrumb[]> {
|
||||
const args = key.split(':');
|
||||
const sourceId = args[0];
|
||||
const topicId = args.length > 2 ? args[args.length - 1] : args[1];
|
||||
const breadcrumbKey = args[0] + this.BREADCRUMB_SUFFIX;
|
||||
const sourceId = args[1];
|
||||
const topicId = args.length > 3 ? args[args.length - 1] : args[2];
|
||||
|
||||
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(topicId, undefined)]);
|
||||
} 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}`)]);
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ import {
|
||||
} from 'src/config/app-config.interface';
|
||||
|
||||
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 { ThemedSearchFormComponent } from '../shared/search-form/themed-search-form.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 { MyDSpacePageComponent } from './my-dspace-page.component';
|
||||
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';
|
||||
|
||||
describe('MyDSpacePageComponent', () => {
|
||||
|
@@ -14,7 +14,7 @@ import { RoleType } from '../core/roles/role-types';
|
||||
import { Context } from '../core/shared/context.model';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
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 { SearchConfigurationOption } from '../shared/search/search-switch-configuration/search-configuration-option.model';
|
||||
import { ThemedSearchComponent } from '../shared/search/themed-search.component';
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { QualityAssuranceSourceEffects } from './qa/source/quality-assurance-source.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 = [
|
||||
QualityAssuranceTopicsEffects,
|
||||
|
@@ -14,7 +14,7 @@ import {
|
||||
import {
|
||||
SuggestionTargetsReducer,
|
||||
SuggestionTargetState,
|
||||
} from './suggestion-targets/suggestion-targets.reducer';
|
||||
} from './suggestions/targets/suggestion-targets.reducer';
|
||||
|
||||
/**
|
||||
* The OpenAIRE State
|
||||
|
@@ -5,65 +5,12 @@
|
||||
<ds-alert [type]="'alert-info'" [content]="'quality-assurance.source.description'"></ds-alert>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h2 class="h4 border-bottom pb-2">{{'quality-assurance.source'| translate}}</h2>
|
||||
|
||||
@if ((isSourceLoading() | async)) {
|
||||
<ds-loading class="container" message="{{'quality-assurance.loading' | translate}}"></ds-loading>
|
||||
}
|
||||
@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>
|
||||
<ds-source-list [loading]="(isSourceLoading() | async) === true || (isSourceProcessing() | async) === true"
|
||||
[paginationConfig]="paginationConfig"
|
||||
[showLastEvent]="true"
|
||||
[sources]="sources$ | async"
|
||||
[totalElements]="totalElements$ | async"
|
||||
(paginationChange)="getQualityAssuranceSource()"
|
||||
(sourceSelected)="onSelect($event)"></ds-source-list>
|
||||
</div>
|
||||
|
||||
|
@@ -16,16 +16,18 @@ import { of as observableOf } from 'rxjs';
|
||||
|
||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||
import { AlertComponent } from '../../../shared/alert/alert.component';
|
||||
import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component';
|
||||
import {
|
||||
getMockNotificationsStateService,
|
||||
qualityAssuranceSourceObjectMoreAbstract,
|
||||
qualityAssuranceSourceObjectMorePid,
|
||||
} from '../../../shared/mocks/notifications.mock';
|
||||
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
|
||||
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
||||
import { createTestComponent } from '../../../shared/testing/utils.test';
|
||||
import { NotificationsStateService } from '../../notifications-state.service';
|
||||
import {
|
||||
SourceListComponent,
|
||||
SourceObject,
|
||||
} from '../../shared/source-list.component';
|
||||
import { QualityAssuranceSourceComponent } from './quality-assurance-source.component';
|
||||
|
||||
describe('QualityAssuranceSourceComponent test suite', () => {
|
||||
@@ -61,8 +63,7 @@ describe('QualityAssuranceSourceComponent test suite', () => {
|
||||
remove: {
|
||||
imports: [
|
||||
AlertComponent,
|
||||
ThemedLoadingComponent,
|
||||
PaginationComponent,
|
||||
SourceListComponent,
|
||||
],
|
||||
},
|
||||
})
|
||||
@@ -119,12 +120,19 @@ describe('QualityAssuranceSourceComponent test suite', () => {
|
||||
it(('Should init component properly'), () => {
|
||||
comp.ngOnInit();
|
||||
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|)', {
|
||||
a: [
|
||||
qualityAssuranceSourceObjectMorePid,
|
||||
qualityAssuranceSourceObjectMoreAbstract,
|
||||
],
|
||||
a: expected,
|
||||
}));
|
||||
expect(comp.totalElements$).toBeObservable(cold('(a|)', {
|
||||
a: 2,
|
||||
|
@@ -8,7 +8,11 @@ import {
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
RouterLink,
|
||||
} from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import {
|
||||
Observable,
|
||||
@@ -16,6 +20,7 @@ import {
|
||||
} from 'rxjs';
|
||||
import {
|
||||
distinctUntilChanged,
|
||||
map,
|
||||
take,
|
||||
} from 'rxjs/operators';
|
||||
|
||||
@@ -29,6 +34,10 @@ import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.c
|
||||
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { NotificationsStateService } from '../../notifications-state.service';
|
||||
import {
|
||||
SourceListComponent,
|
||||
SourceObject,
|
||||
} from '../../shared/source-list.component';
|
||||
|
||||
/**
|
||||
* Component to display the Quality Assurance source list.
|
||||
@@ -38,7 +47,7 @@ import { NotificationsStateService } from '../../notifications-state.service';
|
||||
templateUrl: './quality-assurance-source.component.html',
|
||||
styleUrls: ['./quality-assurance-source.component.scss'],
|
||||
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 {
|
||||
|
||||
@@ -59,7 +68,7 @@ export class QualityAssuranceSourceComponent implements OnDestroy, OnInit, After
|
||||
/**
|
||||
* The Quality Assurance source list.
|
||||
*/
|
||||
public sources$: Observable<QualityAssuranceSourceObject[]>;
|
||||
public sources$: Observable<SourceObject[]>;
|
||||
/**
|
||||
* The total number of Quality Assurance sources.
|
||||
*/
|
||||
@@ -74,10 +83,14 @@ export class QualityAssuranceSourceComponent implements OnDestroy, OnInit, After
|
||||
* Initialize the component variables.
|
||||
* @param {PaginationService} paginationService
|
||||
* @param {NotificationsStateService} notificationsStateService
|
||||
* @param {Router} router
|
||||
* @param {ActivatedRoute} route
|
||||
*/
|
||||
constructor(
|
||||
private paginationService: PaginationService,
|
||||
private notificationsStateService: NotificationsStateService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -85,7 +98,15 @@ export class QualityAssuranceSourceComponent implements OnDestroy, OnInit, After
|
||||
* Component initialization.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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).
|
||||
*
|
||||
|
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 { take } from 'rxjs/operators';
|
||||
|
||||
import { Suggestion } from '../../core/notifications/suggestions/models/suggestion.model';
|
||||
import { Collection } from '../../core/shared/collection.model';
|
||||
import { ItemType } from '../../core/shared/item-relationships/item-type.model';
|
||||
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 { EntityDropdownComponent } from '../../shared/entity-dropdown/entity-dropdown.component';
|
||||
import { SuggestionApproveAndImport } from '../suggestion-list-element/suggestion-approve-and-import';
|
||||
import { Suggestion } from '../../../core/notifications/suggestions/models/suggestion.model';
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import { ItemType } from '../../../core/shared/item-relationships/item-type.model';
|
||||
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 { EntityDropdownComponent } from '../../../shared/entity-dropdown/entity-dropdown.component';
|
||||
import { SuggestionApproveAndImport } from '../list-element/suggestion-approve-and-import';
|
||||
|
||||
/**
|
||||
* 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
|
@@ -5,9 +5,9 @@ import {
|
||||
} from '@angular/core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { SuggestionEvidences } from '../../../core/notifications/suggestions/models/suggestion.model';
|
||||
import { fadeIn } from '../../../shared/animations/fade';
|
||||
import { ObjectKeysPipe } from '../../../shared/utils/object-keys-pipe';
|
||||
import { SuggestionEvidences } from '../../../../core/notifications/suggestions/models/suggestion.model';
|
||||
import { fadeIn } from '../../../../shared/animations/fade';
|
||||
import { ObjectKeysPipe } from '../../../../shared/utils/object-keys-pipe';
|
||||
|
||||
/**
|
||||
* Show suggestion evidences such as score (authorScore, dateScore)
|
@@ -9,10 +9,10 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
import { getTestScheduler } from 'jasmine-marbles';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
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 { SuggestionActionsComponent } from '../suggestion-actions/suggestion-actions.component';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
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 { SuggestionActionsComponent } from '../actions/suggestion-actions.component';
|
||||
import { SuggestionEvidencesComponent } from './suggestion-evidences/suggestion-evidences.component';
|
||||
import { SuggestionListElementComponent } from './suggestion-list-element.component';
|
||||
|
@@ -9,11 +9,11 @@ import {
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { Suggestion } from 'src/app/core/notifications/suggestions/models/suggestion.model';
|
||||
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { fadeIn } from '../../shared/animations/fade';
|
||||
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 { SuggestionActionsComponent } from '../suggestion-actions/suggestion-actions.component';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { fadeIn } from '../../../shared/animations/fade';
|
||||
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 { SuggestionActionsComponent } from '../actions/suggestion-actions.component';
|
||||
import { SuggestionApproveAndImport } from './suggestion-approve-and-import';
|
||||
import { SuggestionEvidencesComponent } from './suggestion-evidences/suggestion-evidences.component';
|
||||
|
@@ -7,9 +7,9 @@ import { RouterLink } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service';
|
||||
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { SuggestionsService } from '../suggestions.service';
|
||||
import { SuggestionTargetsStateService } from '../targets/suggestion-targets.state.service';
|
||||
|
||||
/**
|
||||
* 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 { of as observableOf } from 'rxjs';
|
||||
|
||||
import { mockSuggestionTargetsObjectOne } from '../../shared/mocks/publication-claim-targets.mock';
|
||||
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
||||
import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service';
|
||||
import { mockSuggestionTargetsObjectOne } from '../../../shared/mocks/publication-claim-targets.mock';
|
||||
import { ActivatedRouteStub } from '../../../shared/testing/active-router.stub';
|
||||
import { SuggestionsService } from '../suggestions.service';
|
||||
import { SuggestionTargetsStateService } from '../targets/suggestion-targets.state.service';
|
||||
import { SuggestionsPopupComponent } from './suggestions-popup.component';
|
||||
|
||||
describe('SuggestionsPopupComponent', () => {
|
@@ -19,11 +19,11 @@ import {
|
||||
takeUntil,
|
||||
} from 'rxjs/operators';
|
||||
|
||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { fromTopEnter } from '../../shared/animations/fromTop';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { SuggestionTargetsStateService } from '../suggestion-targets/suggestion-targets.state.service';
|
||||
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { fromTopEnter } from '../../../shared/animations/fromTop';
|
||||
import { isNotEmpty } from '../../../shared/empty.util';
|
||||
import { SuggestionsService } from '../suggestions.service';
|
||||
import { SuggestionTargetsStateService } from '../targets/suggestion-targets.state.service';
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
SortDirection,
|
||||
SortOptions,
|
||||
} from '../core/cache/models/sort-options.model';
|
||||
import { FindListOptions } from '../core/data/find-list-options.model';
|
||||
import { SuggestionTarget } from '../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { SuggestionDataService } from '../core/notifications/suggestions/suggestion-data.service';
|
||||
import { SuggestionTargetDataService } from '../core/notifications/suggestions/target/suggestion-target-data.service';
|
||||
import { ResearcherProfile } from '../core/profile/model/researcher-profile.model';
|
||||
import { ResearcherProfileDataService } from '../core/profile/researcher-profile-data.service';
|
||||
import { ResourceType } from '../core/shared/resource-type';
|
||||
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
||||
import { mockSuggestionPublicationOne } from '../shared/mocks/publication-claim.mock';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
|
||||
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||
} from '../../core/cache/models/sort-options.model';
|
||||
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { SuggestionDataService } from '../../core/notifications/suggestions/suggestion-data.service';
|
||||
import { SuggestionTargetDataService } from '../../core/notifications/suggestions/target/suggestion-target-data.service';
|
||||
import { ResearcherProfile } from '../../core/profile/model/researcher-profile.model';
|
||||
import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service';
|
||||
import { ResourceType } from '../../core/shared/resource-type';
|
||||
import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service';
|
||||
import { mockSuggestionPublicationOne } from '../../shared/mocks/publication-claim.mock';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||
import { SuggestionsService } from './suggestions.service';
|
||||
|
||||
describe('SuggestionsService test', () => {
|
@@ -12,37 +12,37 @@ import {
|
||||
take,
|
||||
} from 'rxjs/operators';
|
||||
|
||||
import { SuggestionConfig } from '../../config/suggestion-config.interfaces';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { SuggestionConfig } from '../../../config/suggestion-config.interfaces';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import {
|
||||
SortDirection,
|
||||
SortOptions,
|
||||
} from '../core/cache/models/sort-options.model';
|
||||
import { FindListOptions } from '../core/data/find-list-options.model';
|
||||
import { PaginatedList } from '../core/data/paginated-list.model';
|
||||
import { RemoteData } from '../core/data/remote-data';
|
||||
import { Suggestion } from '../core/notifications/suggestions/models/suggestion.model';
|
||||
import { SuggestionTarget } from '../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { SuggestionDataService } from '../core/notifications/suggestions/suggestion-data.service';
|
||||
import { SuggestionTargetDataService } from '../core/notifications/suggestions/target/suggestion-target-data.service';
|
||||
import { ResearcherProfile } from '../core/profile/model/researcher-profile.model';
|
||||
import { ResearcherProfileDataService } from '../core/profile/researcher-profile-data.service';
|
||||
import { NoContent } from '../core/shared/NoContent.model';
|
||||
} from '../../core/cache/models/sort-options.model';
|
||||
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||
import { PaginatedList } from '../../core/data/paginated-list.model';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { Suggestion } from '../../core/notifications/suggestions/models/suggestion.model';
|
||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { SuggestionDataService } from '../../core/notifications/suggestions/suggestion-data.service';
|
||||
import { SuggestionTargetDataService } from '../../core/notifications/suggestions/target/suggestion-target-data.service';
|
||||
import { ResearcherProfile } from '../../core/profile/model/researcher-profile.model';
|
||||
import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service';
|
||||
import { NoContent } from '../../core/shared/NoContent.model';
|
||||
import {
|
||||
getFinishedRemoteData,
|
||||
getFirstCompletedRemoteData,
|
||||
getFirstSucceededRemoteDataPayload,
|
||||
getFirstSucceededRemoteListPayload,
|
||||
} from '../core/shared/operators';
|
||||
import { WorkspaceItem } from '../core/submission/models/workspaceitem.model';
|
||||
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
||||
} from '../../core/shared/operators';
|
||||
import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model';
|
||||
import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service';
|
||||
import {
|
||||
hasNoValue,
|
||||
hasValue,
|
||||
isNotEmpty,
|
||||
} from '../shared/empty.util';
|
||||
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||
import { getSuggestionPageRoute } from '../suggestions-page/suggestions-page-routing-paths';
|
||||
} from '../../shared/empty.util';
|
||||
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||
import { getSuggestionPageRoute } from '../../suggestions-page/suggestions-page-routing-paths';
|
||||
|
||||
/**
|
||||
* useful for multiple approvals and ignores operation
|
@@ -2,7 +2,7 @@ import { AsyncPipe } from '@angular/common';
|
||||
import {
|
||||
AfterViewInit,
|
||||
Component,
|
||||
Input,
|
||||
input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
@@ -20,13 +20,13 @@ import {
|
||||
take,
|
||||
} from 'rxjs/operators';
|
||||
|
||||
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||
import { hasValue } from '../../../shared/empty.util';
|
||||
import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component';
|
||||
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { getSuggestionPageRoute } from '../../../suggestions-page/suggestions-page-routing-paths';
|
||||
import { SuggestionTarget } from '../../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { PaginationService } from '../../../../core/pagination/pagination.service';
|
||||
import { hasValue } from '../../../../shared/empty.util';
|
||||
import { ThemedLoadingComponent } from '../../../../shared/loading/themed-loading.component';
|
||||
import { PaginationComponent } from '../../../../shared/pagination/pagination.component';
|
||||
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
|
||||
import { getSuggestionPageRoute } from '../../../../suggestions-page/suggestions-page-routing-paths';
|
||||
import { SuggestionsService } from '../../suggestions.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
|
||||
*/
|
||||
@Input() source = '';
|
||||
sourceId = input<string>();
|
||||
|
||||
/**
|
||||
* The pagination system configuration for HTML listing.
|
||||
* @type {PaginationComponentOptions}
|
||||
*/
|
||||
public paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
||||
id: 'stp_' + this.source,
|
||||
id: 'stp_' + this.sourceId,
|
||||
pageSizeOptions: [5, 10, 20, 40, 60],
|
||||
});
|
||||
|
||||
@@ -95,8 +95,8 @@ export class PublicationClaimComponent implements AfterViewInit, OnDestroy, OnIn
|
||||
* Component initialization.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.targets$ = this.suggestionTargetsStateService.getSuggestionTargets(this.source);
|
||||
this.totalElements$ = this.suggestionTargetsStateService.getSuggestionTargetsTotals(this.source);
|
||||
this.targets$ = this.suggestionTargetsStateService.getSuggestionTargets(this.sourceId());
|
||||
this.totalElements$ = this.suggestionTargetsStateService.getSuggestionTargetsTotals(this.sourceId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +104,7 @@ export class PublicationClaimComponent implements AfterViewInit, OnDestroy, OnIn
|
||||
*/
|
||||
ngAfterViewInit(): void {
|
||||
this.subs.push(
|
||||
this.suggestionTargetsStateService.isSuggestionTargetsLoaded(this.source).pipe(
|
||||
this.suggestionTargetsStateService.isSuggestionTargetsLoaded(this.sourceId()).pipe(
|
||||
take(1),
|
||||
).subscribe(() => {
|
||||
this.getSuggestionTargets();
|
||||
@@ -119,7 +119,7 @@ export class PublicationClaimComponent implements AfterViewInit, OnDestroy, OnIn
|
||||
* 'true' if the targets are loading, 'false' otherwise.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.suggestionTargetsStateService.dispatchClearSuggestionTargetsAction(this.source);
|
||||
this.suggestionTargetsStateService.dispatchClearSuggestionTargetsAction(this.sourceId());
|
||||
this.subs
|
||||
.filter((sub) => hasValue(sub))
|
||||
.forEach((sub) => sub.unsubscribe());
|
||||
@@ -161,7 +161,7 @@ export class PublicationClaimComponent implements AfterViewInit, OnDestroy, OnIn
|
||||
take(1),
|
||||
).subscribe((options: PaginationComponentOptions) => {
|
||||
this.suggestionTargetsStateService.dispatchRetrieveSuggestionTargets(
|
||||
this.source,
|
||||
this.sourceId(),
|
||||
options.pageSize,
|
||||
options.currentPage,
|
||||
);
|
@@ -4,12 +4,12 @@ import {
|
||||
MemoizedSelector,
|
||||
} from '@ngrx/store';
|
||||
|
||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { subStateSelector } from '../../submission/selectors';
|
||||
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { subStateSelector } from '../../../submission/selectors';
|
||||
import {
|
||||
suggestionNotificationsSelector,
|
||||
SuggestionNotificationsState,
|
||||
} from '../notifications.reducer';
|
||||
} from '../../notifications.reducer';
|
||||
import {
|
||||
SuggestionTargetEntry,
|
||||
SuggestionTargetState,
|
@@ -1,8 +1,8 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
import { Action } from '@ngrx/store';
|
||||
|
||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { type } from '../../shared/ngrx/type';
|
||||
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { type } from '../../../shared/ngrx/type';
|
||||
|
||||
/**
|
||||
* For each action type in an action group, make a simple
|
@@ -21,10 +21,10 @@ import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators';
|
||||
import {
|
||||
AuthActionTypes,
|
||||
RetrieveAuthenticatedEpersonSuccessAction,
|
||||
} from '../../core/auth/auth.actions';
|
||||
import { PaginatedList } from '../../core/data/paginated-list.model';
|
||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
} from '../../../core/auth/auth.actions';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { SuggestionsService } from '../suggestions.service';
|
||||
import {
|
||||
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 {
|
||||
SuggestionTargetActionTypes,
|
||||
SuggestionTargetsActions,
|
@@ -6,8 +6,8 @@ import {
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { SuggestionNotificationsState } from '../notifications.reducer';
|
||||
import { SuggestionTarget } from '../../../core/notifications/suggestions/models/suggestion-target.model';
|
||||
import { SuggestionNotificationsState } from '../../notifications.reducer';
|
||||
import {
|
||||
getCurrentUserSuggestionTargetsSelector,
|
||||
getCurrentUserSuggestionTargetsVisitedSelector,
|
@@ -29,7 +29,7 @@ import { AuthorizationDataService } from '../core/data/feature-authorization/aut
|
||||
import { EPersonDataService } from '../core/eperson/eperson-data.service';
|
||||
import { EPerson } from '../core/eperson/models/eperson.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 { ThemedLoadingComponent } from '../shared/loading/themed-loading.component';
|
||||
import { NotificationsService } from '../shared/notifications/notifications.service';
|
||||
|
@@ -40,7 +40,7 @@ import {
|
||||
getFirstCompletedRemoteData,
|
||||
getRemoteDataPayload,
|
||||
} from '../core/shared/operators';
|
||||
import { SuggestionsNotificationComponent } from '../notifications/suggestions-notification/suggestions-notification.component';
|
||||
import { SuggestionsNotificationComponent } from '../notifications/suggestions/notification/suggestions-notification.component';
|
||||
import {
|
||||
hasValue,
|
||||
isNotEmpty,
|
||||
|
@@ -2,7 +2,7 @@ import { Route } from '@angular/router';
|
||||
|
||||
import { authenticatedGuard } from '../core/auth/authenticated.guard';
|
||||
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 {
|
||||
NOTIFICATIONS_RECITER_SUGGESTION_PATH,
|
||||
QUALITY_ASSURANCE_EDIT_PATH,
|
||||
@@ -39,7 +39,7 @@ export const ROUTES: Route[] = [
|
||||
component: QualityAssuranceTopicsPageComponent,
|
||||
pathMatch: 'full',
|
||||
resolve: {
|
||||
breadcrumb: qualityAssuranceBreadcrumbResolver,
|
||||
breadcrumb: sourcesBreadcrumbResolver,
|
||||
openaireQualityAssuranceTopicsParams: QualityAssuranceTopicsPageResolver,
|
||||
},
|
||||
data: {
|
||||
@@ -85,7 +85,7 @@ export const ROUTES: Route[] = [
|
||||
component: QualityAssuranceEventsPageComponent,
|
||||
pathMatch: 'full',
|
||||
resolve: {
|
||||
breadcrumb: qualityAssuranceBreadcrumbResolver,
|
||||
breadcrumb: sourcesBreadcrumbResolver,
|
||||
openaireQualityAssuranceEventsParams: qualityAssuranceEventsPageResolver,
|
||||
},
|
||||
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';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
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 { 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 { NotificationsSuggestionTargetsPageComponent } from './notifications-suggestion-targets-page.component';
|
||||
|
||||
@@ -23,19 +24,14 @@ describe('NotificationsSuggestionTargetsPageComponent', () => {
|
||||
CommonModule,
|
||||
TranslateModule.forRoot(),
|
||||
NotificationsSuggestionTargetsPageComponent,
|
||||
MockComponent(SuggestionSourcesComponent),
|
||||
],
|
||||
providers: [
|
||||
AdminNotificationsPublicationClaimPageComponent,
|
||||
{ provide: ActivatedRoute, useValue: new ActivatedRouteStub() },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
})
|
||||
.overrideComponent(NotificationsSuggestionTargetsPageComponent, {
|
||||
remove: {
|
||||
imports: [PublicationClaimComponent],
|
||||
},
|
||||
})
|
||||
.compileComponents();
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
|
@@ -1,13 +1,13 @@
|
||||
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({
|
||||
selector: 'ds-notifications-reciter-page',
|
||||
templateUrl: './notifications-suggestion-targets-page.component.html',
|
||||
styleUrls: ['./notifications-suggestion-targets-page.component.scss'],
|
||||
imports: [
|
||||
PublicationClaimComponent,
|
||||
SuggestionSourcesComponent,
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
|
@@ -34,7 +34,7 @@ export const qualityAssuranceSourceDataResolver: ResolveFn<QualityAssuranceSourc
|
||||
): Observable<QualityAssuranceSourceObject[]> => {
|
||||
const pageSize = appConfig.qualityAssuranceConfig.pageSize;
|
||||
|
||||
return qualityAssuranceSourceService.getSources(pageSize, 0).pipe(
|
||||
return qualityAssuranceSourceService.getSources(pageSize, 1).pipe(
|
||||
map((sources: PaginatedList<QualityAssuranceSourceObject>) => {
|
||||
if (sources.page.length === 1) {
|
||||
router.navigate([getResolvedUrl(route) + '/' + sources.page[0].id]);
|
||||
|
@@ -1,9 +1,14 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
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';
|
||||
|
||||
/**
|
||||
@@ -19,8 +24,17 @@ import { CreateItemParentSelectorComponent } from './create-item-parent-selector
|
||||
export class ThemedCreateItemParentSelectorComponent
|
||||
extends ThemedComponent<CreateItemParentSelectorComponent> {
|
||||
@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 {
|
||||
return 'CreateItemParentSelectorComponent';
|
||||
|
@@ -22,11 +22,11 @@ import { TestScheduler } from 'rxjs/testing';
|
||||
import { AuthService } from '../core/auth/auth.service';
|
||||
import { PaginationService } from '../core/pagination/pagination.service';
|
||||
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
||||
import { SuggestionApproveAndImport } from '../notifications/suggestion-list-element/suggestion-approve-and-import';
|
||||
import { SuggestionEvidencesComponent } from '../notifications/suggestion-list-element/suggestion-evidences/suggestion-evidences.component';
|
||||
import { SuggestionListElementComponent } from '../notifications/suggestion-list-element/suggestion-list-element.component';
|
||||
import { SuggestionTargetsStateService } from '../notifications/suggestion-targets/suggestion-targets.state.service';
|
||||
import { SuggestionsService } from '../notifications/suggestions.service';
|
||||
import { SuggestionApproveAndImport } from '../notifications/suggestions/list-element/suggestion-approve-and-import';
|
||||
import { SuggestionEvidencesComponent } from '../notifications/suggestions/list-element/suggestion-evidences/suggestion-evidences.component';
|
||||
import { SuggestionListElementComponent } from '../notifications/suggestions/list-element/suggestion-list-element.component';
|
||||
import { SuggestionsService } from '../notifications/suggestions/suggestions.service';
|
||||
import { SuggestionTargetsStateService } from '../notifications/suggestions/targets/suggestion-targets.state.service';
|
||||
import {
|
||||
mockSuggestionPublicationOne,
|
||||
mockSuggestionPublicationTwo,
|
||||
|
@@ -43,14 +43,14 @@ import {
|
||||
} from '../core/shared/operators';
|
||||
import { WorkspaceItem } from '../core/submission/models/workspaceitem.model';
|
||||
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
||||
import { SuggestionActionsComponent } from '../notifications/suggestion-actions/suggestion-actions.component';
|
||||
import { SuggestionApproveAndImport } from '../notifications/suggestion-list-element/suggestion-approve-and-import';
|
||||
import { SuggestionListElementComponent } from '../notifications/suggestion-list-element/suggestion-list-element.component';
|
||||
import { SuggestionTargetsStateService } from '../notifications/suggestion-targets/suggestion-targets.state.service';
|
||||
import { SuggestionActionsComponent } from '../notifications/suggestions/actions/suggestion-actions.component';
|
||||
import { SuggestionApproveAndImport } from '../notifications/suggestions/list-element/suggestion-approve-and-import';
|
||||
import { SuggestionListElementComponent } from '../notifications/suggestions/list-element/suggestion-list-element.component';
|
||||
import {
|
||||
SuggestionBulkResult,
|
||||
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 { ThemedLoadingComponent } from '../shared/loading/themed-loading.component';
|
||||
import { NotificationsService } from '../shared/notifications/notifications.service';
|
||||
|
@@ -2832,10 +2832,18 @@
|
||||
|
||||
"item.preview.dc.type": "Type:",
|
||||
|
||||
"item.preview.oaire.version": "Version",
|
||||
|
||||
"item.preview.oaire.citation.issue": "Issue",
|
||||
|
||||
"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.identifier.isbn": "ISBN",
|
||||
@@ -2854,6 +2862,8 @@
|
||||
|
||||
"item.preview.person.identifier.orcid": "ORCID:",
|
||||
|
||||
"item.preview.person.affiliation.name": "Affiliations:",
|
||||
|
||||
"item.preview.project.funder.name": "Funder:",
|
||||
|
||||
"item.preview.project.funder.identifier": "Funder Identifier:",
|
||||
@@ -2884,6 +2894,16 @@
|
||||
|
||||
"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.empty": "No items to show",
|
||||
@@ -3536,6 +3556,10 @@
|
||||
|
||||
"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.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.openalex": "OpenAlex",
|
||||
|
||||
"suggestion.from.source": "from the ",
|
||||
|
||||
"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.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.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 { 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 { 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 { ThemedSearchFormComponent } from '../../../../app/shared/search-form/themed-search-form.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 { 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 { 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 { RoleDirective } from '../../../../app/shared/roles/role.directive';
|
||||
import { ThemedSearchComponent } from '../../../../app/shared/search/themed-search.component';
|
||||
|
@@ -5,7 +5,7 @@ import {
|
||||
import { Component } from '@angular/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 { 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';
|
||||
|
Reference in New Issue
Block a user