mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-17 23:13:04 +00:00
[CST-11045] Handle coar-notify section errors
This commit is contained in:
@@ -45,17 +45,17 @@ export const notifyPatterns = [
|
||||
category: 'Announcements'
|
||||
},
|
||||
{
|
||||
name: 'Request Endorsement',
|
||||
name: 'endorsement',
|
||||
description: 'This pattern is used to request endorsement of a resource owned by the origin system.',
|
||||
category: 'Requests'
|
||||
},
|
||||
{
|
||||
name: 'Request Ingest',
|
||||
name: 'ingest',
|
||||
description: 'This pattern is used to request that the target system ingest a resource.',
|
||||
category: 'Requests'
|
||||
},
|
||||
{
|
||||
name: 'Request Review',
|
||||
name: 'review',
|
||||
description: 'This pattern is used to request a review of a resource owned by the origin system.',
|
||||
category: 'Requests'
|
||||
},
|
||||
|
@@ -1,74 +0,0 @@
|
||||
export const REQUEST_REVIEW_DROPDOWN = {
|
||||
element: {
|
||||
container: 'custom-control custom-select pl-1',
|
||||
control: 'custom-select',
|
||||
label: 'custom-control-label pt-1'
|
||||
}
|
||||
};
|
||||
|
||||
export const REQUEST_ENDORSEMENT_DROPDOWN = {
|
||||
element: {
|
||||
container: 'custom-control custom-select pl-1',
|
||||
control: 'custom-select',
|
||||
label: 'custom-control-label pt-1'
|
||||
}
|
||||
};
|
||||
|
||||
export const REQUEST_INGEST_DROPDOWN = {
|
||||
element: {
|
||||
container: 'custom-control custom-select pl-1',
|
||||
control: 'custom-select',
|
||||
label: 'custom-control-label pt-1'
|
||||
}
|
||||
};
|
||||
|
||||
export const SECTION_COAR_FORM_LAYOUT = {
|
||||
requestReview: REQUEST_REVIEW_DROPDOWN,
|
||||
requestEndorsement: REQUEST_ENDORSEMENT_DROPDOWN,
|
||||
requestIngest: REQUEST_INGEST_DROPDOWN
|
||||
};
|
||||
|
||||
export const SECTION_COAR_FORM_MODEL = [
|
||||
{
|
||||
id: 'requestReview',
|
||||
label: 'submission.sections.license.request-review-label',
|
||||
required: false,
|
||||
value: '',
|
||||
validators: {
|
||||
required: null
|
||||
},
|
||||
errorMessages: {
|
||||
required: 'submission.sections.license.required',
|
||||
notgranted: 'submission.sections.license.notgranted'
|
||||
},
|
||||
type: 'SELECT',
|
||||
},
|
||||
{
|
||||
id: 'requestEndorsement',
|
||||
label: 'submission.sections.license.request-endorsement-label',
|
||||
required: false,
|
||||
value: '',
|
||||
validators: {
|
||||
required: null
|
||||
},
|
||||
errorMessages: {
|
||||
required: 'submission.sections.license.required',
|
||||
notgranted: 'submission.sections.license.notgranted'
|
||||
},
|
||||
type: 'SELECT',
|
||||
},
|
||||
{
|
||||
id: 'requestIngest',
|
||||
label: 'submission.sections.license.request-ingest-label',
|
||||
required: false,
|
||||
value: '',
|
||||
validators: {
|
||||
required: null
|
||||
},
|
||||
errorMessages: {
|
||||
required: 'submission.sections.license.required',
|
||||
notgranted: 'submission.sections.license.notgranted'
|
||||
},
|
||||
type: 'SELECT',
|
||||
}
|
||||
];
|
@@ -1,7 +1,7 @@
|
||||
<div>
|
||||
<ng-container>
|
||||
<div *ngFor="let pattern of patterns; let i = index" class="col mt-3">
|
||||
<label class="mt-2 row"
|
||||
<div class="container-fluid">
|
||||
<ng-container *ngIf="patterns.length > 0">
|
||||
<div *ngFor="let pattern of patterns; let i = index" class="col">
|
||||
<label class="row col-form-label"
|
||||
>
|
||||
{{'submission.section.section-coar-notify.control.label' | translate : {pattern : pattern} }}
|
||||
</label
|
||||
@@ -21,6 +21,7 @@
|
||||
type="text"
|
||||
[readonly]="true"
|
||||
ngbDropdownAnchor
|
||||
[ngClass]="{'border-danger': (getShownSectionErrors$(pattern, serviceIndex) | async)?.length > 0}"
|
||||
class="form-control w-100 scrollable-dropdown-input"
|
||||
[value]="ldnServiceByPattern[pattern][serviceIndex]?.name"
|
||||
(click)="myDropdown.open()"
|
||||
@@ -68,17 +69,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO: NEXT version
|
||||
<div
|
||||
class="col-sm-1"
|
||||
>
|
||||
<button
|
||||
(click)="removeService(pattern, serviceIndex)"
|
||||
class="btn btn-outline-dark trash-button"
|
||||
>
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div> -->
|
||||
</div>
|
||||
<small
|
||||
class="row text-muted"
|
||||
@@ -86,6 +76,11 @@
|
||||
>
|
||||
{{'submission.section.section-coar-notify.small.notification' | translate : {pattern : pattern} }}
|
||||
</small>
|
||||
<ng-container *ngIf="(getShownSectionErrors$(pattern, serviceIndex) | async)?.length > 0">
|
||||
<small class="row text-danger" *ngFor="let error of (getShownSectionErrors$(pattern, serviceIndex) | async)">
|
||||
{{ error.message | translate}}
|
||||
</small>
|
||||
</ng-container>
|
||||
<div
|
||||
class="row mt-1"
|
||||
*ngIf="ldnServiceByPattern[pattern][serviceIndex]"
|
||||
@@ -94,19 +89,24 @@
|
||||
class="alert alert-info w-100 d-flex align-items-center flex-row"
|
||||
>
|
||||
<i class="fa-solid fa-circle-info fa-xl ml-2"></i>
|
||||
<div class="mt-1 ml-4">
|
||||
<b>{{ 'submission.section.section-coar-notify.selection.description' | translate }}</b>
|
||||
<br />
|
||||
<div class="ml-4">
|
||||
<div>{{ 'submission.section.section-coar-notify.selection.description' | translate }}</div>
|
||||
<div *ngIf="ldnServiceByPattern[pattern][serviceIndex]?.description; else noDesc">
|
||||
{{ ldnServiceByPattern[pattern][serviceIndex].description }}
|
||||
</div>
|
||||
<ng-template #noDesc>
|
||||
<span class="text-muted">
|
||||
{{ 'submission.section.section-coar-notify.selection.no-description' | translate }}
|
||||
</span>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-1" *ngIf="sectionData.errorsToShow.length > 0">
|
||||
<!--TODO: get error message and display here -->
|
||||
</div>
|
||||
<div class="row" *ngIf="(getShownSectionErrors$(pattern, serviceIndex) | async)?.length > 0">
|
||||
<div
|
||||
class="alert alert-danger w-100 d-flex align-items-center flex-row"
|
||||
>
|
||||
<div class="mt-1 ml-4">
|
||||
<div class="ml-4">
|
||||
<span>
|
||||
{{ 'submission.section.section-coar-notify.notification.error' | translate }}
|
||||
</span>
|
||||
@@ -114,12 +114,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO: NEXT version
|
||||
<div class="row mt-1">
|
||||
<span (click)="addService(pattern, newService)" class="ds-form-add-more btn btn-link mb-2">
|
||||
{{ 'ldn-new-service.form.label.addPattern' | translate }}
|
||||
</span>
|
||||
</div> -->
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="patterns.length === 0">
|
||||
<p>
|
||||
{{'submission.section.section-coar-notify.info.no-pattern' | translate }}
|
||||
</p>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// Getting styles for NgbDropdown
|
||||
@import '../../../shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.scss';
|
||||
@import '../../../shared/form/form.component.scss';
|
||||
|
||||
|
@@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SubmissionSectionCoarNotifyComponent } from './section-coar-notify.component';
|
||||
|
||||
describe('LdnServiceComponent', () => {
|
||||
fdescribe('LdnServiceComponent', () => {
|
||||
let component: SubmissionSectionCoarNotifyComponent;
|
||||
let fixture: ComponentFixture<SubmissionSectionCoarNotifyComponent>;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { ChangeDetectorRef, Component, Inject } from '@angular/core';
|
||||
import { Observable, Subscription, of } from 'rxjs';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { SectionModelComponent } from '../models/section.model';
|
||||
import { renderSectionFor } from '../sections-decorator';
|
||||
import { SectionsType } from '../sections-type';
|
||||
@@ -9,18 +9,15 @@ import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/jso
|
||||
import { SectionsService } from '../sections.service';
|
||||
import { SectionDataObject } from '../models/section-data.model';
|
||||
|
||||
import { hasNoValue, hasValue, isNotEmpty } from '../../../shared/empty.util';
|
||||
import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util';
|
||||
|
||||
import { getFirstCompletedRemoteData, getPaginatedListPayload, getRemoteDataPayload } from '../../../core/shared/operators';
|
||||
import { LdnServicesService } from '../../../admin/admin-ldn-services/ldn-services-data/ldn-services-data.service';
|
||||
import { LdnService } from '../../../admin/admin-ldn-services/ldn-services-model/ldn-services.model';
|
||||
import { CoarNotifyConfigDataService } from './coar-notify-config-data.service';
|
||||
import { filter, map, tap } from 'rxjs/operators';
|
||||
import { filter, map, take, tap } from 'rxjs/operators';
|
||||
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
export interface CoarNotifyDropdownSelector {
|
||||
ldnService: LdnService;
|
||||
}
|
||||
import { SubmissionSectionError } from '../../objects/submission-section-error.model';
|
||||
|
||||
/**
|
||||
* This component represents a section that contains the submission section-coar-notify form.
|
||||
@@ -34,7 +31,14 @@ export interface CoarNotifyDropdownSelector {
|
||||
@renderSectionFor(SectionsType.CoarNotify)
|
||||
export class SubmissionSectionCoarNotifyComponent extends SectionModelComponent {
|
||||
|
||||
/**
|
||||
* Contains an array of string patterns.
|
||||
*/
|
||||
patterns: string[] = [];
|
||||
/**
|
||||
* An object that maps string keys to arrays of LdnService objects.
|
||||
* Used to store LdnService objects by pattern.
|
||||
*/
|
||||
ldnServiceByPattern: { [key: string]: LdnService[] } = {};
|
||||
/**
|
||||
* A map representing all services for each pattern
|
||||
@@ -49,8 +53,6 @@ export class SubmissionSectionCoarNotifyComponent extends SectionModelComponent
|
||||
*/
|
||||
previousServices: { [key: string]: {[key: number]: number} } = {};
|
||||
|
||||
private _ldnServicesPerPattern: Map<string, LdnService[]> = new Map();
|
||||
|
||||
/**
|
||||
* The [[JsonPatchOperationPathCombiner]] object
|
||||
* @type {JsonPatchOperationPathCombiner}
|
||||
@@ -84,6 +86,7 @@ export class SubmissionSectionCoarNotifyComponent extends SectionModelComponent
|
||||
*/
|
||||
onSectionInit() {
|
||||
this.setCoarNotifyConfig();
|
||||
this.getSectionServerErrorsAndSetErrorsToDisplay();
|
||||
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id);
|
||||
}
|
||||
|
||||
@@ -162,7 +165,6 @@ export class SubmissionSectionCoarNotifyComponent extends SectionModelComponent
|
||||
this.filterServices(pattern)
|
||||
.subscribe((services: LdnService[]) => {
|
||||
const selectedServices = services.filter((service) => {
|
||||
this._ldnServicesPerPattern.set(pattern, services);
|
||||
const selection = (this.sectionData.data[pattern] as LdnService[]).find((s: LdnService) => s.id === service.id);
|
||||
this.addService(pattern, selection);
|
||||
return this.sectionData.data[pattern].includes(service.id);
|
||||
@@ -177,6 +179,11 @@ export class SubmissionSectionCoarNotifyComponent extends SectionModelComponent
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new service to the selected services for the given pattern.
|
||||
* @param pattern - The pattern to add the new service to.
|
||||
* @param newService - The new service to add.
|
||||
*/
|
||||
addService(pattern: string, newService: LdnService) {
|
||||
// Your logic to add a new service to the selected services for the pattern
|
||||
// Example: Push the newService to the array corresponding to the pattern
|
||||
@@ -186,6 +193,10 @@ export class SubmissionSectionCoarNotifyComponent extends SectionModelComponent
|
||||
this.ldnServiceByPattern[pattern].push(newService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the service at the specified index from the array corresponding to the pattern.
|
||||
* (part of next phase of implementation)
|
||||
*/
|
||||
removeService(pattern: string, serviceIndex: number) {
|
||||
if (this.ldnServiceByPattern[pattern]) {
|
||||
// Remove the service at the specified index from the array
|
||||
@@ -193,45 +204,6 @@ export class SubmissionSectionCoarNotifyComponent extends SectionModelComponent
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the specified form field has already a value stored
|
||||
*
|
||||
* @param fieldId
|
||||
* the section data retrieved from the serverù
|
||||
* @param index
|
||||
* the section data retrieved from the server
|
||||
*/
|
||||
hasStoredValue(fieldId, index): boolean {
|
||||
if (isNotEmpty(this.sectionData.data)) {
|
||||
return this.sectionData.data.hasOwnProperty(fieldId) &&
|
||||
isNotEmpty(this.sectionData.data[fieldId][index]) &&
|
||||
!this.isFieldToRemove(fieldId, index);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the specified field is on the way to be removed
|
||||
*
|
||||
* @param fieldId
|
||||
* the section data retrieved from the serverù
|
||||
* @param index
|
||||
* the section data retrieved from the server
|
||||
*/
|
||||
isFieldToRemove(fieldId, index) {
|
||||
return this.fieldsOnTheirWayToBeRemoved.has(fieldId) && this.fieldsOnTheirWayToBeRemoved.get(fieldId).includes(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from all subscriptions
|
||||
*/
|
||||
onSectionDestroy() {
|
||||
this.subs
|
||||
.filter((subscription) => hasValue(subscription))
|
||||
.forEach((subscription) => subscription.unsubscribe());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when dropdowns for the section are initialized
|
||||
* Retrieve services with corresponding patterns to the dropdowns.
|
||||
@@ -252,15 +224,71 @@ export class SubmissionSectionCoarNotifyComponent extends SectionModelComponent
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given service has the specified inbound pattern type.
|
||||
* @param service - The service to check.
|
||||
* @param patternType - The inbound pattern type to look for.
|
||||
* @returns True if the service has the specified inbound pattern type, false otherwise.
|
||||
*/
|
||||
hasInboundPattern(service: any, patternType: string): boolean {
|
||||
return service.notifyServiceInboundPatterns.some((pattern: { pattern: string }) => {
|
||||
return pattern.pattern === patternType;
|
||||
});
|
||||
}
|
||||
|
||||
protected getSectionStatus(): Observable<boolean> {
|
||||
// TODO: check if section is valid
|
||||
return of(true);
|
||||
/**
|
||||
* Retrieves server errors for the current section and sets them to display.
|
||||
* @returns An Observable that emits the validation errors for the current section.
|
||||
*/
|
||||
private getSectionServerErrorsAndSetErrorsToDisplay() {
|
||||
this.subs.push(
|
||||
this.sectionService.getSectionServerErrors(this.submissionId, this.sectionData.id).pipe(
|
||||
take(1),
|
||||
filter((validationErrors) => isNotEmpty(validationErrors)),
|
||||
).subscribe((validationErrors: SubmissionSectionError[]) => {
|
||||
if (isNotEmpty(validationErrors)) {
|
||||
validationErrors.forEach((error) => {
|
||||
this.sectionService.setSectionError(this.submissionId, this.sectionData.id, error);
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an observable of the errors for the current section that match the given pattern and index.
|
||||
* @param pattern - The pattern to match against the error paths.
|
||||
* @param index - The index to match against the error paths.
|
||||
* @returns An observable of the errors for the current section that match the given pattern and index.
|
||||
*/
|
||||
public getShownSectionErrors$(pattern: string, index: number): Observable<SubmissionSectionError[]> {
|
||||
return this.sectionService.getShownSectionErrors(this.submissionId, this.sectionData.id, this.sectionData.sectionType)
|
||||
.pipe(
|
||||
take(1),
|
||||
filter((validationErrors) => isNotEmpty(validationErrors)),
|
||||
map((validationErrors: SubmissionSectionError[]) => {
|
||||
return validationErrors.filter((error) => {
|
||||
const path = `${pattern}/${index}`;
|
||||
return error.path.includes(path);
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns An observable that emits a boolean indicating whether the section has any server errors or not.
|
||||
*/
|
||||
protected getSectionStatus(): Observable<boolean> {
|
||||
return this.sectionService.getSectionServerErrors(this.submissionId, this.sectionData.id).pipe(
|
||||
map((validationErrors) => isEmpty(validationErrors)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from all subscriptions
|
||||
*/
|
||||
onSectionDestroy() {
|
||||
this.subs
|
||||
.filter((subscription) => hasValue(subscription))
|
||||
.forEach((subscription) => subscription.unsubscribe());
|
||||
}
|
||||
}
|
||||
|
@@ -5020,8 +5020,14 @@
|
||||
|
||||
"submission.section.section-coar-notify.selection.description": "Selected service's description:",
|
||||
|
||||
"submission.section.section-coar-notify.selection.no-description": "No further information is available",
|
||||
|
||||
"submission.section.section-coar-notify.notification.error": "The selected service is not suitable for the current item. Please check the description for details about which record can be managed by this service.",
|
||||
|
||||
"submission.section.section-coar-notify.info.no-pattern": "No patterns found in the submission.",
|
||||
|
||||
"error.validation.coarnotify.invalidfilter": "Invalid filter, try to select another service or none.",
|
||||
|
||||
"submitter.empty": "N/A",
|
||||
|
||||
"subscriptions.title": "Subscriptions",
|
||||
|
Reference in New Issue
Block a user