mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
[CST-12109] fixes
This commit is contained in:
@@ -33,7 +33,7 @@ export class QualityAssuranceBreadcrumbService implements BreadcrumbsProviderSer
|
|||||||
*/
|
*/
|
||||||
getBreadcrumbs(key: string, url: string): Observable<Breadcrumb[]> {
|
getBreadcrumbs(key: string, url: string): Observable<Breadcrumb[]> {
|
||||||
const sourceId = key.split(':')[0];
|
const sourceId = key.split(':')[0];
|
||||||
const topicId = key.split(':')[1];
|
const topicId = key.split(':')[2];
|
||||||
|
|
||||||
if (topicId) {
|
if (topicId) {
|
||||||
return this.qualityAssuranceService.getTopic(topicId).pipe(
|
return this.qualityAssuranceService.getTopic(topicId).pipe(
|
||||||
@@ -41,7 +41,7 @@ export class QualityAssuranceBreadcrumbService implements BreadcrumbsProviderSer
|
|||||||
map((topic) => {
|
map((topic) => {
|
||||||
return [new Breadcrumb(this.translationService.instant(this.QUALITY_ASSURANCE_BREADCRUMB_KEY), url),
|
return [new Breadcrumb(this.translationService.instant(this.QUALITY_ASSURANCE_BREADCRUMB_KEY), url),
|
||||||
new Breadcrumb(sourceId, `${url}${sourceId}`),
|
new Breadcrumb(sourceId, `${url}${sourceId}`),
|
||||||
new Breadcrumb(topicId, undefined)];
|
new Breadcrumb(topicId.replace(/[!:]/g, '/'), undefined)];
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -8,7 +8,7 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv
|
|||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
|
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
|
||||||
import { SearchDataImpl } from '../data/base/search-data';
|
import { SearchDataImpl } from '../data/base/search-data';
|
||||||
import { CorrectionType } from './models/correction-type-mode.model';
|
import { CorrectionType } from './models/correctiontype.model';
|
||||||
import { Observable, map } from 'rxjs';
|
import { Observable, map } from 'rxjs';
|
||||||
import { RemoteData } from '../data/remote-data';
|
import { RemoteData } from '../data/remote-data';
|
||||||
import { PaginatedList } from '../data/paginated-list.model';
|
import { PaginatedList } from '../data/paginated-list.model';
|
||||||
|
@@ -6,6 +6,10 @@ import { excludeFromEquals } from '../../utilities/equals.decorators';
|
|||||||
import { HALLink } from '../../shared/hal-link.model';
|
import { HALLink } from '../../shared/hal-link.model';
|
||||||
|
|
||||||
@typedObject
|
@typedObject
|
||||||
|
/**
|
||||||
|
* Represents a correction type. It extends the CacheableObject.
|
||||||
|
* The correction type represents a type of correction that can be applied to a submission.
|
||||||
|
*/
|
||||||
export class CorrectionType extends CacheableObject {
|
export class CorrectionType extends CacheableObject {
|
||||||
static type = new ResourceType('correctiontype');
|
static type = new ResourceType('correctiontype');
|
||||||
|
|
||||||
@@ -15,20 +19,30 @@ export class CorrectionType extends CacheableObject {
|
|||||||
@excludeFromEquals
|
@excludeFromEquals
|
||||||
@autoserialize
|
@autoserialize
|
||||||
type: ResourceType;
|
type: ResourceType;
|
||||||
|
|
||||||
@autoserialize
|
@autoserialize
|
||||||
|
/**
|
||||||
|
* The unique identifier for the correction type mode.
|
||||||
|
*/
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
@autoserialize
|
@autoserialize
|
||||||
|
/**
|
||||||
|
* The topic of the correction type mode.
|
||||||
|
*/
|
||||||
topic: string;
|
topic: string;
|
||||||
|
|
||||||
@autoserialize
|
@autoserialize
|
||||||
|
/**
|
||||||
|
* The discovery configuration for the correction type mode.
|
||||||
|
*/
|
||||||
discoveryConfiguration: string;
|
discoveryConfiguration: string;
|
||||||
|
|
||||||
@autoserialize
|
@autoserialize
|
||||||
|
/**
|
||||||
|
* The form used for creating a correction type.
|
||||||
|
*/
|
||||||
creationForm: string;
|
creationForm: string;
|
||||||
|
|
||||||
@deserialize
|
@deserialize
|
||||||
|
/**
|
||||||
|
* Represents the links associated with the correction type mode.
|
||||||
|
*/
|
||||||
_links: {
|
_links: {
|
||||||
self: HALLink;
|
self: HALLink;
|
||||||
};
|
};
|
@@ -1,4 +1,4 @@
|
|||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing';
|
||||||
import { ItemAlertsComponent } from './item-alerts.component';
|
import { ItemAlertsComponent } from './item-alerts.component';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
@@ -43,6 +43,7 @@ describe('ItemAlertsComponent', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ItemAlertsComponent);
|
fixture = TestBed.createComponent(ItemAlertsComponent);
|
||||||
|
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
component.item = itemMock;
|
component.item = itemMock;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -108,46 +109,16 @@ describe('ItemAlertsComponent', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('show reinstate button', () => {
|
it('should return true when user is not an admin and there is at least one correction with topic REQUEST_REINSTATE', fakeAsync(() => {
|
||||||
it('should return false if user is an admin', () => {
|
const isAdmin = false;
|
||||||
const isAdmin$ = of(true);
|
const correction = [{ topic: 'REQUEST_REINSTATE' }];
|
||||||
authorizationService.isAuthorized.and.returnValue(isAdmin$);
|
authorizationService.isAuthorized.and.returnValue(of(isAdmin));
|
||||||
const result$ = component.showReinstateButton$();
|
correctionTypeDataService.findByItem.and.returnValue(of(correction));
|
||||||
result$.subscribe((result) => {
|
|
||||||
expect(result).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return false if no correction types are found', () => {
|
|
||||||
const isAdmin$ = of(false);
|
|
||||||
authorizationService.isAuthorized.and.returnValue(isAdmin$);
|
|
||||||
correctionTypeDataService.findByItem.and.returnValue(of([]));
|
|
||||||
const result$ = component.showReinstateButton$();
|
|
||||||
result$.subscribe((result) => {
|
|
||||||
expect(result).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return false if no correction type with topic "REQUEST_REINSTATE" is found', () => {
|
|
||||||
const isAdmin$ = of(false);
|
|
||||||
authorizationService.isAuthorized.and.returnValue(isAdmin$);
|
|
||||||
correctionTypeDataService.findByItem.and.returnValue(of([{ topic: 'OTHER_TOPIC' }]));
|
|
||||||
const result$ = component.showReinstateButton$();
|
|
||||||
result$.subscribe((result) => {
|
|
||||||
expect(result).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return true if user is not an admin and correction type with topic "REQUEST_REINSTATE" is found', () => {
|
|
||||||
const isAdmin$ = of(false);
|
|
||||||
authorizationService.isAuthorized.and.returnValue(isAdmin$);
|
|
||||||
correctionTypeDataService.findByItem.and.returnValue(of([{ topic: 'REQUEST_REINSTATE' }]));
|
|
||||||
|
|
||||||
const result$ = component.showReinstateButton$();
|
const result$ = component.showReinstateButton$();
|
||||||
|
tick();
|
||||||
result$.subscribe((result) => {
|
result$.subscribe((result) => {
|
||||||
expect(result).toBe(true);
|
expect(result).toBeTrue();
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
@@ -4,12 +4,14 @@
|
|||||||
class="alert alert-info d-flex flex-row"
|
class="alert alert-info d-flex flex-row"
|
||||||
*ngIf="source.totalEvents > 0"
|
*ngIf="source.totalEvents > 0"
|
||||||
>
|
>
|
||||||
|
<div class="col-2">
|
||||||
<img
|
<img
|
||||||
class="source-logo"
|
class="source-logo"
|
||||||
src="assets/images/qa-{{ source.id }}-logo.png"
|
src="assets/images/qa-{{ source.id }}-logo.png"
|
||||||
onerror="this.src='assets/images/dspace-logo.svg'"
|
onerror="this.src='assets/images/dspace-logo.svg'"
|
||||||
alt="{{ source.id }} logo"
|
alt="{{ source.id }} logo"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
<div class="w-100 d-flex justify-content-between">
|
<div class="w-100 d-flex justify-content-between">
|
||||||
<div class="pl-4 align-self-center">
|
<div class="pl-4 align-self-center">
|
||||||
{{ "mydspace.qa-event-notification.check.notification-info" | translate : { num: source.totalEvents } }}
|
{{ "mydspace.qa-event-notification.check.notification-info" | translate : { num: source.totalEvents } }}
|
||||||
|
@@ -26,7 +26,7 @@ import { QualityAssuranceSourceService } from './qa/source/quality-assurance-sou
|
|||||||
import {
|
import {
|
||||||
QualityAssuranceSourceDataService
|
QualityAssuranceSourceDataService
|
||||||
} from '../core/notifications/qa/source/quality-assurance-source-data.service';
|
} from '../core/notifications/qa/source/quality-assurance-source-data.service';
|
||||||
import { GetEPersonDataPipe } from './qa/events/get-ePerson-data.pipe';
|
import { EPersonDataComponent } from './qa/events/ePerson-data/ePerson-data.component';
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -41,7 +41,8 @@ const MODULES = [
|
|||||||
const COMPONENTS = [
|
const COMPONENTS = [
|
||||||
QualityAssuranceTopicsComponent,
|
QualityAssuranceTopicsComponent,
|
||||||
QualityAssuranceEventsComponent,
|
QualityAssuranceEventsComponent,
|
||||||
QualityAssuranceSourceComponent
|
QualityAssuranceSourceComponent,
|
||||||
|
EPersonDataComponent,
|
||||||
];
|
];
|
||||||
|
|
||||||
const DIRECTIVES = [ ];
|
const DIRECTIVES = [ ];
|
||||||
@@ -60,7 +61,6 @@ const PROVIDERS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const PIPES = [
|
const PIPES = [
|
||||||
GetEPersonDataPipe
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@@ -0,0 +1,10 @@
|
|||||||
|
<ng-container *ngIf="ePersonId">
|
||||||
|
<ng-container *ngIf="getEPersonData$() | async as ePersonData">
|
||||||
|
<ng-container *ngFor="let property of properties">
|
||||||
|
<span *ngIf="ePersonData[property]">
|
||||||
|
{{ ePersonData[property] }}
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
@@ -0,0 +1,58 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { EPersonDataComponent } from './ePerson-data.component';
|
||||||
|
import { EPersonDataService } from './../../../../core/eperson/eperson-data.service';
|
||||||
|
import { EPerson } from 'src/app/core/eperson/models/eperson.model';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from 'src/app/shared/remote-data.utils';
|
||||||
|
|
||||||
|
describe('EPersonDataComponent', () => {
|
||||||
|
let component: EPersonDataComponent;
|
||||||
|
let fixture: ComponentFixture<EPersonDataComponent>;
|
||||||
|
let ePersonDataService = jasmine.createSpyObj('EPersonDataService', ['findById']);
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ EPersonDataComponent ],
|
||||||
|
providers: [ {
|
||||||
|
provide: EPersonDataService,
|
||||||
|
useValue: ePersonDataService
|
||||||
|
} ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(EPersonDataComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should retrieve EPerson data when ePersonId is provided', () => {
|
||||||
|
const ePersonId = '123';
|
||||||
|
const ePersonData = Object.assign(new EPerson(), {
|
||||||
|
id: ePersonId,
|
||||||
|
email: 'john.doe@domain.com',
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
key: 'eperson.firstname',
|
||||||
|
value: 'John'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'eperson.lastname',
|
||||||
|
value: 'Doe'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
const ePersonDataRD$ = createSuccessfulRemoteDataObject$(ePersonData);
|
||||||
|
ePersonDataService.findById.and.returnValue(ePersonDataRD$);
|
||||||
|
component.ePersonId = ePersonId;
|
||||||
|
component.getEPersonData$();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(ePersonDataService.findById).toHaveBeenCalledWith(ePersonId, true);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,45 @@
|
|||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { EPersonDataService } from '../../../../core/eperson/eperson-data.service';
|
||||||
|
import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../../core/shared/operators';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { EPerson } from '../../../../core/eperson/models/eperson.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-eperson-data',
|
||||||
|
templateUrl: './ePerson-data.component.html',
|
||||||
|
styleUrls: ['./ePerson-data.component.scss']
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* Represents the component for displaying ePerson data.
|
||||||
|
*/
|
||||||
|
export class EPersonDataComponent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the ePerson.
|
||||||
|
*/
|
||||||
|
@Input() ePersonId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The properties of the ePerson to display.
|
||||||
|
*/
|
||||||
|
@Input() properties: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of the EPersonDataComponent.
|
||||||
|
* @param ePersonDataService The service for retrieving ePerson data.
|
||||||
|
*/
|
||||||
|
constructor(private ePersonDataService: EPersonDataService) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the EPerson data based on the provided ePersonId.
|
||||||
|
* @returns An Observable that emits the EPerson data.
|
||||||
|
*/
|
||||||
|
getEPersonData$(): Observable<EPerson> {
|
||||||
|
if (this.ePersonId) {
|
||||||
|
return this.ePersonDataService.findById(this.ePersonId, true).pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
getRemoteDataPayload()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,24 +0,0 @@
|
|||||||
import { Pipe, PipeTransform } from '@angular/core';
|
|
||||||
import { Observable, tap } from 'rxjs';
|
|
||||||
import { EPersonDataService } from '../../../core/eperson/eperson-data.service';
|
|
||||||
import { EPerson } from '../../../core/eperson/models/eperson.model';
|
|
||||||
import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../core/shared/operators';
|
|
||||||
|
|
||||||
@Pipe({
|
|
||||||
name: 'dsGetEPersonData'
|
|
||||||
})
|
|
||||||
export class GetEPersonDataPipe implements PipeTransform {
|
|
||||||
constructor(private ePersonDataService: EPersonDataService) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms the personId into an Observable of EPerson.
|
|
||||||
* @param personId The ID of the person.
|
|
||||||
* @returns An Observable of EPerson.
|
|
||||||
*/
|
|
||||||
transform(personId: string): Observable<EPerson> {
|
|
||||||
return this.ePersonDataService.findById(personId, true).pipe(
|
|
||||||
getFirstCompletedRemoteData(),
|
|
||||||
getRemoteDataPayload()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -85,16 +85,14 @@
|
|||||||
<td>
|
<td>
|
||||||
<p>
|
<p>
|
||||||
<span *ngIf="eventElement.event.message">
|
<span *ngIf="eventElement.event.message">
|
||||||
<span class="badge badge-info">{{eventElement.event.message.reason}}</span><br>
|
<span>{{eventElement.event.message.reason}}</span><br>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<p>
|
<p>
|
||||||
<span *ngIf="eventElement.event.originalId">
|
<span *ngIf="eventElement.event.originalId">
|
||||||
<span class="badge badge-info">
|
<ds-eperson-data [ePersonId]="eventElement.event.originalId" [properties]="['email']"></ds-eperson-data>
|
||||||
{{ (eventElement.event.originalId | dsGetEPersonData | async)?.email }}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
@@ -200,7 +198,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row text-right">
|
<div class="row text-right">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<a class="btn btn-outline-secondary" [routerLink]="['/notifications/quality-assurance']">
|
<a class="btn btn-outline-secondary" [routerLink]="['/notifications/quality-assurance', sourceId]">
|
||||||
<i class="fas fa-angle-double-left"></i>
|
<i class="fas fa-angle-double-left"></i>
|
||||||
{{'quality-assurance.events.back' | translate}}
|
{{'quality-assurance.events.back' | translate}}
|
||||||
</a>
|
</a>
|
||||||
|
@@ -34,7 +34,6 @@ import { AuthorizationDataService } from '../../../core/data/feature-authorizati
|
|||||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||||
import { NoContent } from '../../../core/shared/NoContent.model';
|
import { NoContent } from '../../../core/shared/NoContent.model';
|
||||||
import {environment} from '../../../../environments/environment';
|
import {environment} from '../../../../environments/environment';
|
||||||
import { getEntityPageRoute } from 'src/app/item-page/item-page-routing-paths';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to display the Quality Assurance event list.
|
* Component to display the Quality Assurance event list.
|
||||||
@@ -80,6 +79,11 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
|
|||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
public topic: string;
|
public topic: string;
|
||||||
|
/**
|
||||||
|
* The sourceId of the Quality Assurance events.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
sourceId: string;
|
||||||
/**
|
/**
|
||||||
* The rejected/ignore reason.
|
* The rejected/ignore reason.
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@@ -144,9 +148,11 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
|
|||||||
this.isEventPageLoading.next(true);
|
this.isEventPageLoading.next(true);
|
||||||
|
|
||||||
this.isAdmin$ = this.authorizationService.isAuthorized(FeatureID.AdministratorOf);
|
this.isAdmin$ = this.authorizationService.isAuthorized(FeatureID.AdministratorOf);
|
||||||
|
// this.sourceId = this.activatedRoute.snapshot.params.sourceId;
|
||||||
this.activatedRoute.paramMap.pipe(
|
this.activatedRoute.paramMap.pipe(
|
||||||
tap((params) => {
|
tap((params) => {
|
||||||
this.sourceUrlForProjectSearch = environment.qualityAssuranceConfig.sourceUrlMapForProjectSearch[params.get('sourceId')];
|
this.sourceUrlForProjectSearch = environment.qualityAssuranceConfig.sourceUrlMapForProjectSearch[params.get('sourceId')];
|
||||||
|
this.sourceId = params.get('sourceId');
|
||||||
}),
|
}),
|
||||||
map((params) => params.get('topicId')),
|
map((params) => params.get('topicId')),
|
||||||
take(1),
|
take(1),
|
||||||
@@ -459,8 +465,4 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
|
|||||||
delete(qaEvent: QualityAssuranceEventData): Observable<RemoteData<NoContent>> {
|
delete(qaEvent: QualityAssuranceEventData): Observable<RemoteData<NoContent>> {
|
||||||
return this.qualityAssuranceEventRestService.deleteQAEvent(qaEvent);
|
return this.qualityAssuranceEventRestService.deleteQAEvent(qaEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
getEntityPageRoute(itemId: string): string {
|
|
||||||
return getEntityPageRoute('person', itemId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -34,12 +34,12 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let sourceElement of (sources$ | async); let i = index">
|
<tr *ngFor="let sourceElement of (sources$ | async); let i = index">
|
||||||
<td>{{sourceElement.id}}</td>
|
<td>{{sourceElement.id}}</td>
|
||||||
<td>{{formatDate(sourceElement.lastEvent)}}</td>
|
<td>{{sourceElement.lastEvent | date: 'dd/MM/yyyy hh:mm' }}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group edit-field">
|
<div class="btn-group edit-field">
|
||||||
<button
|
<button
|
||||||
class="btn btn-outline-primary btn-sm"
|
class="btn btn-outline-primary btn-sm"
|
||||||
title="{{'quality-assurance.button.detail' | translate }}"
|
title="{{'quality-assurance.source-list.button.detail' | translate : { param: sourceElement.id } }}"
|
||||||
[routerLink]="[sourceElement.id]">
|
[routerLink]="[sourceElement.id]">
|
||||||
<span class="badge badge-info">{{sourceElement.totalEvents}}</span>
|
<span class="badge badge-info">{{sourceElement.totalEvents}}</span>
|
||||||
<i class="fas fa-info fa-fw"></i>
|
<i class="fas fa-info fa-fw"></i>
|
||||||
|
@@ -8,7 +8,7 @@ import { PaginationComponentOptions } from '../../../shared/pagination/paginatio
|
|||||||
import { NotificationsStateService } from '../../notifications-state.service';
|
import { NotificationsStateService } from '../../notifications-state.service';
|
||||||
import { AdminQualityAssuranceSourcePageParams } from '../../../admin/admin-notifications/admin-quality-assurance-source-page-component/admin-quality-assurance-source-page-resolver.service';
|
import { AdminQualityAssuranceSourcePageParams } from '../../../admin/admin-notifications/admin-quality-assurance-source-page-component/admin-quality-assurance-source-page-resolver.service';
|
||||||
import { hasValue } from '../../../shared/empty.util';
|
import { hasValue } from '../../../shared/empty.util';
|
||||||
import { format } from 'date-fns';
|
|
||||||
/**
|
/**
|
||||||
* Component to display the Quality Assurance source list.
|
* Component to display the Quality Assurance source list.
|
||||||
*/
|
*/
|
||||||
@@ -65,20 +65,6 @@ export class QualityAssuranceSourceComponent implements OnInit {
|
|||||||
this.totalElements$ = this.notificationsStateService.getQualityAssuranceSourceTotals();
|
this.totalElements$ = this.notificationsStateService.getQualityAssuranceSourceTotals();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats the given date string into the format 'yyyy-MM-dd HH:mm:ss'.
|
|
||||||
* If the date is falsy, an empty string is returned.
|
|
||||||
*
|
|
||||||
* @param date - The date string to be formatted.
|
|
||||||
* @returns The formatted date string.
|
|
||||||
*/
|
|
||||||
formatDate(date: string): string {
|
|
||||||
if (!date) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return format(new Date(date), 'yyyy-MM-dd HH:mm:ss');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* First Quality Assurance source loading after view initialization.
|
* First Quality Assurance source loading after view initialization.
|
||||||
*/
|
*/
|
||||||
|
@@ -38,12 +38,12 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let topicElement of (topics$ | async); let i = index">
|
<tr *ngFor="let topicElement of (topics$ | async); let i = index">
|
||||||
<td>{{topicElement.name}}</td>
|
<td>{{topicElement.name}}</td>
|
||||||
<td>{{formatDate(topicElement.lastEvent)}}</td>
|
<td>{{topicElement.lastEvent | date: 'dd/MM/yyyy hh:mm' }}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group edit-field">
|
<div class="btn-group edit-field">
|
||||||
<button
|
<button
|
||||||
class="btn btn-outline-primary btn-sm"
|
class="btn btn-outline-primary btn-sm"
|
||||||
title="{{'quality-assurance.button.detail' | translate }}"
|
title="{{'quality-assurance.topics-list.button.detail' | translate : { param: topicElement.name } }}"
|
||||||
[routerLink]="[topicElement.id]">
|
[routerLink]="[topicElement.id]">
|
||||||
<span class="badge badge-info">{{topicElement.totalEvents}}</span>
|
<span class="badge badge-info">{{topicElement.totalEvents}}</span>
|
||||||
<i class="fas fa-info fa-fw"></i>
|
<i class="fas fa-info fa-fw"></i>
|
||||||
@@ -58,4 +58,12 @@
|
|||||||
</ds-pagination>
|
</ds-pagination>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row text-right">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<a class="btn btn-outline-secondary" [routerLink]="['/notifications/quality-assurance']">
|
||||||
|
<i class="fas fa-angle-double-left"></i>
|
||||||
|
{{'quality-assurance.events.back-to-sources' | translate}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
import { distinctUntilChanged, map, take, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, map, take, tap } from 'rxjs/operators';
|
||||||
@@ -20,7 +20,6 @@ import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../core
|
|||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { getItemPageRoute } from '../../../item-page/item-page-routing-paths';
|
import { getItemPageRoute } from '../../../item-page/item-page-routing-paths';
|
||||||
import { getNotificatioQualityAssuranceRoute } from '../../../admin/admin-routing-paths';
|
import { getNotificatioQualityAssuranceRoute } from '../../../admin/admin-routing-paths';
|
||||||
import { format } from 'date-fns';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to display the Quality Assurance topic list.
|
* Component to display the Quality Assurance topic list.
|
||||||
@@ -30,7 +29,7 @@ import { format } from 'date-fns';
|
|||||||
templateUrl: './quality-assurance-topics.component.html',
|
templateUrl: './quality-assurance-topics.component.html',
|
||||||
styleUrls: ['./quality-assurance-topics.component.scss'],
|
styleUrls: ['./quality-assurance-topics.component.scss'],
|
||||||
})
|
})
|
||||||
export class QualityAssuranceTopicsComponent implements OnInit {
|
export class QualityAssuranceTopicsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
/**
|
/**
|
||||||
* The pagination system configuration for HTML listing.
|
* The pagination system configuration for HTML listing.
|
||||||
* @type {PaginationComponentOptions}
|
* @type {PaginationComponentOptions}
|
||||||
@@ -138,7 +137,7 @@ export class QualityAssuranceTopicsComponent implements OnInit {
|
|||||||
* Dispatch the Quality Assurance topics retrival.
|
* Dispatch the Quality Assurance topics retrival.
|
||||||
*/
|
*/
|
||||||
public getQualityAssuranceTopics(source: string, target?: string): void {
|
public getQualityAssuranceTopics(source: string, target?: string): void {
|
||||||
this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig).pipe(
|
this.subs.push(this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig).pipe(
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
).subscribe((options: PaginationComponentOptions) => {
|
).subscribe((options: PaginationComponentOptions) => {
|
||||||
this.notificationsStateService.dispatchRetrieveQualityAssuranceTopics(
|
this.notificationsStateService.dispatchRetrieveQualityAssuranceTopics(
|
||||||
@@ -147,7 +146,7 @@ export class QualityAssuranceTopicsComponent implements OnInit {
|
|||||||
source,
|
source,
|
||||||
target
|
target
|
||||||
);
|
);
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -202,20 +201,6 @@ export class QualityAssuranceTopicsComponent implements OnInit {
|
|||||||
return getNotificatioQualityAssuranceRoute();
|
return getNotificatioQualityAssuranceRoute();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats the given date string into the format 'yyyy-MM-dd HH:mm:ss'.
|
|
||||||
* If the date is falsy, an empty string is returned.
|
|
||||||
*
|
|
||||||
* @param date - The date string to format.
|
|
||||||
* @returns The formatted date string.
|
|
||||||
*/
|
|
||||||
formatDate(date: string): string {
|
|
||||||
if (!date) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return format(new Date(date), 'yyyy-MM-dd HH:mm:ss');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsubscribe from all subscriptions.
|
* Unsubscribe from all subscriptions.
|
||||||
*/
|
*/
|
||||||
|
@@ -9,13 +9,29 @@ import { AuthorizationDataService } from '../../core/data/feature-authorization/
|
|||||||
templateUrl: './item-withdrawn-reinstate-modal.component.html',
|
templateUrl: './item-withdrawn-reinstate-modal.component.html',
|
||||||
styleUrls: ['./item-withdrawn-reinstate-modal.component.scss']
|
styleUrls: ['./item-withdrawn-reinstate-modal.component.scss']
|
||||||
})
|
})
|
||||||
|
/**
|
||||||
|
* Represents a modal component for withdrawing or reinstating an item.
|
||||||
|
* Implements the ModalBeforeDismiss interface.
|
||||||
|
*/
|
||||||
export class ItemWithdrawnReinstateModalComponent implements ModalBeforeDismiss {
|
export class ItemWithdrawnReinstateModalComponent implements ModalBeforeDismiss {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The reason for withdrawing or reinstating a suggestion.
|
||||||
|
*/
|
||||||
reason: string;
|
reason: string;
|
||||||
|
/**
|
||||||
|
* Indicates whether the item can be withdrawn.
|
||||||
|
*/
|
||||||
canWithdraw: boolean;
|
canWithdraw: boolean;
|
||||||
|
/**
|
||||||
|
* BehaviorSubject that represents the submitted state.
|
||||||
|
* Emits a boolean value indicating whether the form has been submitted or not.
|
||||||
|
*/
|
||||||
submitted$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
submitted$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
/**
|
||||||
|
* Event emitter for creating a QA event.
|
||||||
|
* @event createQAEvent
|
||||||
|
*/
|
||||||
@Output() createQAEvent: EventEmitter<string> = new EventEmitter<string>();
|
@Output() createQAEvent: EventEmitter<string> = new EventEmitter<string>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -23,22 +39,36 @@ export class ItemWithdrawnReinstateModalComponent implements ModalBeforeDismiss
|
|||||||
protected authorizationService: AuthorizationDataService,
|
protected authorizationService: AuthorizationDataService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the modal.
|
||||||
|
*/
|
||||||
onModalClose() {
|
onModalClose() {
|
||||||
this.activeModal.close();
|
this.activeModal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the modal can be dismissed.
|
||||||
|
* @returns {boolean} True if the modal can be dismissed, false otherwise.
|
||||||
|
*/
|
||||||
beforeDismiss(): boolean {
|
beforeDismiss(): boolean {
|
||||||
// prevent the modal from being dismissed after version creation is initiated
|
// prevent the modal from being dismissed after version creation is initiated
|
||||||
return !this.submitted$.getValue();
|
return !this.submitted$.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the submission of the modal form.
|
||||||
|
* Emits the reason for withdrawal or reinstatement through the createQAEvent output.
|
||||||
|
*/
|
||||||
onModalSubmit() {
|
onModalSubmit() {
|
||||||
this.submitted$.next(true);
|
this.submitted$.next(true);
|
||||||
this.createQAEvent.emit(this.reason);
|
this.createQAEvent.emit(this.reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the withdrawal state of the component.
|
||||||
|
* @param state The new withdrawal state.
|
||||||
|
*/
|
||||||
public setWithdraw(state: boolean) {
|
public setWithdraw(state: boolean) {
|
||||||
this.canWithdraw = state;
|
this.canWithdraw = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -23,6 +23,9 @@ export const REQUEST_REINSTATE = 'REQUEST/REINSTATE';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
|
/**
|
||||||
|
* Service for managing the withdrawn/reinstate modal for a DSO.
|
||||||
|
*/
|
||||||
export class DsoWithdrawnReinstateModalService {
|
export class DsoWithdrawnReinstateModalService {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@@ -518,7 +518,7 @@
|
|||||||
|
|
||||||
"admin.quality-assurance.page.title": "Quality Assurance",
|
"admin.quality-assurance.page.title": "Quality Assurance",
|
||||||
|
|
||||||
"admin.notifications.source.breadcrumbs": "Quality Assurance Source",
|
"admin.notifications.source.breadcrumbs": "Quality Assurance",
|
||||||
|
|
||||||
"admin.access-control.groups.form.tooltip.editGroupPage": "On this page, you can modify the properties and members of a group. In the top section, you can edit the group name and description, unless this is an admin group for a collection or community, in which case the group name and description are auto-generated and cannot be edited. In the following sections, you can edit group membership. See [the wiki](https://wiki.lyrasis.org/display/DSDOC7x/Create+or+manage+a+user+group) for more details.",
|
"admin.access-control.groups.form.tooltip.editGroupPage": "On this page, you can modify the properties and members of a group. In the top section, you can edit the group name and description, unless this is an admin group for a collection or community, in which case the group name and description are auto-generated and cannot be edited. In the following sections, you can edit group membership. See [the wiki](https://wiki.lyrasis.org/display/DSDOC7x/Create+or+manage+a+user+group) for more details.",
|
||||||
|
|
||||||
@@ -2480,9 +2480,9 @@
|
|||||||
|
|
||||||
"item.page.version.create": "Create new version",
|
"item.page.version.create": "Create new version",
|
||||||
|
|
||||||
"item.page.withdrawn": "Withdraw this Item",
|
"item.page.withdrawn": "Request a withdrawal for this item",
|
||||||
|
|
||||||
"item.page.reinstate": "Reinstate this Item",
|
"item.page.reinstate": "Request reinstatement",
|
||||||
|
|
||||||
"item.page.version.hasDraft": "A new version cannot be created because there is an inprogress submission in the version history",
|
"item.page.version.hasDraft": "A new version cannot be created because there is an inprogress submission in the version history",
|
||||||
|
|
||||||
@@ -3190,7 +3190,9 @@
|
|||||||
|
|
||||||
"quality-assurance.table.actions": "Actions",
|
"quality-assurance.table.actions": "Actions",
|
||||||
|
|
||||||
"quality-assurance.button.detail": "Show details",
|
"quality-assurance.source-list.button.detail": "Show topics for {{param}}",
|
||||||
|
|
||||||
|
"quality-assurance.topics-list.button.detail": "Show suggestions for {{param}}",
|
||||||
|
|
||||||
"quality-assurance.noTopics": "No topics found.",
|
"quality-assurance.noTopics": "No topics found.",
|
||||||
|
|
||||||
@@ -3256,6 +3258,8 @@
|
|||||||
|
|
||||||
"quality-assurance.events.back": "Back to topics",
|
"quality-assurance.events.back": "Back to topics",
|
||||||
|
|
||||||
|
"quality-assurance.events.back-to-sources": "Back to sources",
|
||||||
|
|
||||||
"quality-assurance.event.table.less": "Show less",
|
"quality-assurance.event.table.less": "Show less",
|
||||||
|
|
||||||
"quality-assurance.event.table.more": "Show more",
|
"quality-assurance.event.table.more": "Show more",
|
||||||
|
Reference in New Issue
Block a user