mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
CST-12180
This commit is contained in:
@@ -1,34 +1,34 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { I18nBreadcrumbResolver } from 'src/app/core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||
import { LdnServicesOverviewComponent } from './ldn-services-directory/ldn-services-directory.component';
|
||||
import { LdnServiceNewComponent } from './ldn-service-new/ldn-service-new.component';
|
||||
import { LdnServiceFormEditComponent } from './ldn-service-form-edit/ldn-service-form-edit.component';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {I18nBreadcrumbResolver} from 'src/app/core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||
import {LdnServicesOverviewComponent} from './ldn-services-directory/ldn-services-directory.component';
|
||||
import {LdnServiceNewComponent} from './ldn-service-new/ldn-service-new.component';
|
||||
import {LdnServiceFormEditComponent} from './ldn-service-form-edit/ldn-service-form-edit.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
component: LdnServicesOverviewComponent,
|
||||
resolve: {breadcrumb: I18nBreadcrumbResolver},
|
||||
data: {title: 'ldn-registered-services.title', breadcrumbKey: 'ldn-registered-services.new'},
|
||||
},
|
||||
{
|
||||
path: 'new',
|
||||
resolve: {breadcrumb: I18nBreadcrumbResolver},
|
||||
component: LdnServiceNewComponent,
|
||||
data: {title: 'ldn-register-new-service.title', breadcrumbKey: 'ldn-register-new-service'}
|
||||
},
|
||||
{
|
||||
path: 'edit/:serviceId',
|
||||
resolve: {breadcrumb: I18nBreadcrumbResolver},
|
||||
component: LdnServiceFormEditComponent,
|
||||
data: {title: 'ldn-edit-service.title', breadcrumbKey: 'ldn-edit-service'}
|
||||
},
|
||||
]),
|
||||
]
|
||||
imports: [
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
component: LdnServicesOverviewComponent,
|
||||
resolve: {breadcrumb: I18nBreadcrumbResolver},
|
||||
data: {title: 'ldn-registered-services.title', breadcrumbKey: 'ldn-registered-services.new'},
|
||||
},
|
||||
{
|
||||
path: 'new',
|
||||
resolve: {breadcrumb: I18nBreadcrumbResolver},
|
||||
component: LdnServiceNewComponent,
|
||||
data: {title: 'ldn-register-new-service.title', breadcrumbKey: 'ldn-register-new-service'}
|
||||
},
|
||||
{
|
||||
path: 'edit/:serviceId',
|
||||
resolve: {breadcrumb: I18nBreadcrumbResolver},
|
||||
component: LdnServiceFormEditComponent,
|
||||
data: {title: 'ldn-edit-service.title', breadcrumbKey: 'ldn-edit-service'}
|
||||
},
|
||||
]),
|
||||
]
|
||||
})
|
||||
export class AdminLdnServicesRoutingModule {
|
||||
|
||||
|
@@ -1,29 +1,29 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { AdminLdnServicesRoutingModule } from './admin-ldn-services-routing.module';
|
||||
import { LdnServicesOverviewComponent } from './ldn-services-directory/ldn-services-directory.component';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { LdnServiceNewComponent } from './ldn-service-new/ldn-service-new.component';
|
||||
import { LdnServiceFormComponent } from './ldn-service-form/ldn-service-form.component';
|
||||
import { LdnServiceFormEditComponent } from './ldn-service-form-edit/ldn-service-form-edit.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { LdnItemfiltersService } from './ldn-services-data/ldn-itemfilters-data.service';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {AdminLdnServicesRoutingModule} from './admin-ldn-services-routing.module';
|
||||
import {LdnServicesOverviewComponent} from './ldn-services-directory/ldn-services-directory.component';
|
||||
import {SharedModule} from '../../shared/shared.module';
|
||||
import {LdnServiceNewComponent} from './ldn-service-new/ldn-service-new.component';
|
||||
import {LdnServiceFormComponent} from './ldn-service-form/ldn-service-form.component';
|
||||
import {LdnServiceFormEditComponent} from './ldn-service-form-edit/ldn-service-form-edit.component';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {LdnItemfiltersService} from './ldn-services-data/ldn-itemfilters-data.service';
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
AdminLdnServicesRoutingModule,
|
||||
FormsModule
|
||||
],
|
||||
declarations: [
|
||||
LdnServicesOverviewComponent,
|
||||
LdnServiceNewComponent,
|
||||
LdnServiceFormComponent,
|
||||
LdnServiceFormEditComponent,
|
||||
],
|
||||
providers: [LdnItemfiltersService]
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
AdminLdnServicesRoutingModule,
|
||||
FormsModule
|
||||
],
|
||||
declarations: [
|
||||
LdnServicesOverviewComponent,
|
||||
LdnServiceNewComponent,
|
||||
LdnServiceFormComponent,
|
||||
LdnServiceFormEditComponent,
|
||||
],
|
||||
providers: [LdnItemfiltersService]
|
||||
})
|
||||
export class AdminLdnServicesModule {
|
||||
}
|
||||
|
@@ -1,281 +1,412 @@
|
||||
<div class="container">
|
||||
<form (ngSubmit)="onSubmit()" [formGroup]="formModel">
|
||||
<div class="d-flex">
|
||||
<h2 class="flex-grow-1">{{ 'ldn-edit-registered-service.title' | translate }}</h2>
|
||||
<form (ngSubmit)="onSubmit()" [formGroup]="formModel">
|
||||
<div class="d-flex">
|
||||
<h2 class="flex-grow-1">{{ 'ldn-edit-registered-service.title' | translate }}</h2>
|
||||
</div>
|
||||
<!-- In the toggle section -->
|
||||
<div class="toggle-switch-container">
|
||||
<label class="status-label font-weight-bold" for="enabled">{{ 'ldn-service-status' | translate }}</label>
|
||||
<div>
|
||||
<input formControlName="enabled" hidden id="enabled" name="enabled" type="checkbox">
|
||||
<div (click)="toggleEnabled()" [class.checked]="formModel.get('enabled').value" class="toggle-switch">
|
||||
<div class="slider"></div>
|
||||
</div>
|
||||
<!-- In the toggle section -->
|
||||
<div class="toggle-switch-container">
|
||||
<label class="status-label" for="enabled">{{ 'ldn-service-status' | translate }}</label>
|
||||
<div>
|
||||
<input formControlName="enabled" hidden id="enabled" name="enabled" type="checkbox">
|
||||
<div (click)="toggleEnabled()" [class.checked]="formModel.get('enabled').value" class="toggle-switch">
|
||||
<div class="slider"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- In the Name section -->
|
||||
<div class="mb-5">
|
||||
<label for="name" class="font-weight-bold">{{ 'ldn-new-service.form.label.name' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('name').invalid && formModel.get('name').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.name' | translate" class="form-control"
|
||||
formControlName="name"
|
||||
id="name"
|
||||
name="name"
|
||||
type="text">
|
||||
<div *ngIf="formModel.get('name').invalid && formModel.get('name').touched" class="error-text">
|
||||
{{ 'ldn-new-service.form.error.name' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- In the description section -->
|
||||
<div class="mb-5 mt-5 d-flex flex-column">
|
||||
<label for="description" class="font-weight-bold">{{ 'ldn-new-service.form.label.description' | translate }}</label>
|
||||
<textarea [placeholder]="'ldn-new-service.form.placeholder.description' | translate"
|
||||
class="form-control" formControlName="description" id="description" name="description"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- In the url section -->
|
||||
<div class="mb-5 mt-5">
|
||||
<label for="url" class="font-weight-bold">{{ 'ldn-new-service.form.label.url' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('url').invalid && formModel.get('url').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.url' | translate" class="form-control"
|
||||
formControlName="url"
|
||||
id="url"
|
||||
name="url"
|
||||
type="text">
|
||||
<div *ngIf="formModel.get('url').invalid && formModel.get('url').touched" class="error-text">
|
||||
{{ 'ldn-new-service.form.error.url' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- In the ldnUrl section -->
|
||||
<div class="mb-5 mt-5">
|
||||
<label for="ldnUrl" class="font-weight-bold">{{ 'ldn-new-service.form.label.ldnUrl' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('ldnUrl').invalid && formModel.get('ldnUrl').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.ldnUrl' | translate" class="form-control"
|
||||
formControlName="ldnUrl"
|
||||
id="ldnUrl"
|
||||
name="ldnUrl"
|
||||
type="text">
|
||||
<div *ngIf="formModel.get('ldnUrl').invalid && formModel.get('ldnUrl').touched" class="error-text">
|
||||
{{ 'ldn-new-service.form.error.ldnurl' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- In the score section -->
|
||||
<div class="mb-2">
|
||||
<label for="score" class="font-weight-bold">{{ 'ldn-new-service.form.label.score' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('score').invalid && formModel.get('score').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.score' | translate" formControlName="score"
|
||||
id="score"
|
||||
name="score"
|
||||
class="form-control"
|
||||
type="text">
|
||||
<div *ngIf="formModel.get('score').invalid && formModel.get('score').touched" class="error-text">
|
||||
{{ 'ldn-new-service.form.error.score' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- In the Inbound Patterns Labels section -->
|
||||
<div class="row mb-1 mt-5">
|
||||
<div class="col">
|
||||
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.inboundPattern' | translate }} </label>
|
||||
</div>
|
||||
<ng-container *ngIf="!!(formModel.get('notifyServiceInboundPatterns')['controls'][0]?.value?.pattern)">
|
||||
<div class="col">
|
||||
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.ItemFilter' | translate }}</label>
|
||||
</div>
|
||||
<div class="col-sm-1">
|
||||
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.automatic' | translate }}</label>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="col-sm-2">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- In the Inbound Patterns section -->
|
||||
<div *ngFor="let patternGroup of formModel.get('notifyServiceInboundPatterns')['controls']; let i = index"
|
||||
[class.marked-for-deletion]="markedForDeletionInboundPattern.includes(i)"
|
||||
formGroupName="notifyServiceInboundPatterns">
|
||||
|
||||
<ng-container [formGroupName]="i">
|
||||
|
||||
|
||||
<div class="row mb-1">
|
||||
<div class="col">
|
||||
<div #inboundPatternDropdown="ngbDropdown" class="w-80" display="dynamic"
|
||||
id="additionalInboundPattern{{i}}"
|
||||
ngbDropdown placement="bottom-start">
|
||||
<div class="position-relative right-addon" role="combobox">
|
||||
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
|
||||
ngbDropdownToggle></i>
|
||||
<input
|
||||
(click)="inboundPatternDropdown.open();"
|
||||
[readonly]="true"
|
||||
class="form-control w-80 scrollable-dropdown-input"
|
||||
formControlName="patternLabel"
|
||||
id="inboundPatternDropdownButton"
|
||||
ngbDropdownAnchor
|
||||
type="text"
|
||||
/>
|
||||
<div aria-labelledby="inboundPatternDropdownButton"
|
||||
class="dropdown-menu scrollable-dropdown-menu w-100 "
|
||||
ngbDropdownMenu>
|
||||
<div class="scrollable-menu" role="listbox">
|
||||
<button (click)="selectInboundPattern(pattern, i); $event.stopPropagation()"
|
||||
*ngFor="let pattern of inboundPatterns; let internalIndex = index"
|
||||
[title]="'ldn-service.form.pattern.' + pattern + '.description' | translate"
|
||||
class="dropdown-item collection-item text-truncate w-100"
|
||||
ngbDropdownItem
|
||||
type="button">
|
||||
<div>{{ 'ldn-service.form.pattern.' + pattern + '.label' | translate }}</div>
|
||||
<div
|
||||
class="small-text">{{ 'ldn-service.form.pattern.' + pattern + '.description' | translate }}</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label for="name">{{ 'ldn-new-service.form.label.name' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('name').invalid && formModel.get('name').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.name' | translate" formControlName="name" id="name"
|
||||
name="name"
|
||||
type="text">
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
|
||||
</div>
|
||||
|
||||
<!-- In the description section -->
|
||||
<div class="mb-2 d-flex flex-column">
|
||||
<label for="description">{{ 'ldn-new-service.form.label.description' | translate }}</label>
|
||||
<textarea [placeholder]="'ldn-new-service.form.placeholder.description' | translate"
|
||||
formControlName="description" id="description" name="description"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
|
||||
</div>
|
||||
|
||||
<!-- In the url section -->
|
||||
<div class="mb-2">
|
||||
<label for="url">{{ 'ldn-new-service.form.label.url' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('url').invalid && formModel.get('url').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.url' | translate" formControlName="url" id="url"
|
||||
name="url"
|
||||
type="text">
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
|
||||
</div>
|
||||
|
||||
<!-- In the ldnUrl section -->
|
||||
<div class="mb-2">
|
||||
<label for="ldnUrl">{{ 'ldn-new-service.form.label.ldnUrl' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('ldnUrl').invalid && formModel.get('ldnUrl').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.ldnUrl' | translate" formControlName="ldnUrl"
|
||||
id="ldnUrl"
|
||||
name="ldnUrl"
|
||||
type="text">
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label>{{ 'ldn-new-service.form.label.inboundPattern' | translate }} </label>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label-box">{{ 'ldn-new-service.form.label.ItemFilter' | translate }}</label>
|
||||
</div>
|
||||
<div class="col-sm1 ">
|
||||
<label class="label-box-2">{{ 'ldn-new-service.form.label.automatic' | translate }}</label>
|
||||
</div>
|
||||
<div class="col-sm-1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let patternGroup of formModel.get('notifyServiceInboundPatterns')['controls']; let i = index"
|
||||
formGroupName="notifyServiceInboundPatterns">
|
||||
|
||||
<ng-container [formGroupName]="i">
|
||||
|
||||
|
||||
<div [class.marked-for-deletion]="markedForDeletionInboundPattern.includes(i)" class="row mb-1">
|
||||
<div class="col">
|
||||
<select #inboundPattern formControlName="pattern" id="additionalInboundPattern{{i}}"
|
||||
name="additionalInboundPattern{{i}}" required>
|
||||
<option value="">{{ 'ldn-new-service.form.label.placeholder.inboundPattern' | translate }}</option>
|
||||
<option *ngFor="let pattern of inboundPatterns"
|
||||
[ngValue]="pattern.name">{{ pattern.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<ng-container *ngIf="inboundPattern.value">
|
||||
<select formControlName="constraint" id="constraint{{i}}" name="constraint{{i}}">
|
||||
<option value="">{{ 'ldn-new-service.form.label.placeholder.selectedItemFilter' | translate }}</option>
|
||||
<option *ngFor="let itemFilter of (itemfiltersRD$ | async)?.payload?.page"
|
||||
[value]="itemFilter.id">{{ itemFilter.id }}</option>
|
||||
</select>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div [style.visibility]="inboundPattern.value ? 'visible' : 'hidden'" class="col-sm1">
|
||||
<input formControlName="automatic" hidden id="automatic{{i}}" name="automatic{{i}}"
|
||||
type="checkbox">
|
||||
<div (click)="toggleAutomatic(i)"
|
||||
[class.checked]="formModel.get('notifyServiceInboundPatterns.' + i + '.automatic').value"
|
||||
class="toggle-switch">
|
||||
<div class="slider"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-1 btn-group">
|
||||
<button (click)="markForInboundPatternDeletion(i)" class="btn btn-outline-dark trash-button" type="button">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<button (click)="unmarkForInboundPatternDeletion(i)" *ngIf="markedForDeletionInboundPattern.includes(i)" class="btn btn-outline-dark undo-button"
|
||||
type="button">
|
||||
<i class="fas fa-undo"></i>
|
||||
</button>
|
||||
<div class="col">
|
||||
<ng-container
|
||||
*ngIf="!!(formModel.get('notifyServiceInboundPatterns')['controls'][i].value.pattern)">
|
||||
<div #inboundItemfilterDropdown="ngbDropdown" class="w-100" id="constraint{{i}}" ngbDropdown
|
||||
placement="bottom-start">
|
||||
<div class="position-relative right-addon" role="combobox">
|
||||
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
|
||||
ngbDropdownToggle></i>
|
||||
<input
|
||||
(click)="inboundItemfilterDropdown.open();"
|
||||
[readonly]="true"
|
||||
[value]="selectedInboundItemfilters"
|
||||
class="form-control w-100 scrollable-dropdown-input"
|
||||
formControlName="constraint"
|
||||
id="inboundItemfilterDropdown"
|
||||
ngbDropdownAnchor
|
||||
type="text"
|
||||
/>
|
||||
<div aria-labelledby="inboundItemfilterDropdownButton"
|
||||
class="dropdown-menu scrollable-dropdown-menu w-100 "
|
||||
ngbDropdownMenu>
|
||||
<div class="scrollable-menu" role="listbox">
|
||||
<button (click)="selectInboundItemFilter(constraint.id, i); $event.stopPropagation()"
|
||||
*ngFor="let constraint of (itemfiltersRD$ | async)?.payload?.page; let internalIndex = index"
|
||||
class="dropdown-item collection-item text-truncate w-100"
|
||||
ngbDropdownItem
|
||||
type="button">
|
||||
<div>{{ constraint.id }}</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div
|
||||
[style.visibility]="formModel.get('notifyServiceInboundPatterns')['controls'][i].value.pattern ? 'visible' : 'hidden'"
|
||||
class="col-sm-1">
|
||||
<input formControlName="automatic" hidden id="automatic{{i}}" name="automatic{{i}}"
|
||||
type="checkbox">
|
||||
<div (click)="toggleAutomatic(i)"
|
||||
[class.checked]="formModel.get('notifyServiceInboundPatterns.' + i + '.automatic').value"
|
||||
class="toggle-switch">
|
||||
<div class="slider"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-2">
|
||||
<div class="btn-group">
|
||||
<button (click)="markForInboundPatternDeletion(i)" class="btn btn-outline-dark trash-button"
|
||||
type="button">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
|
||||
|
||||
<button (click)="unmarkForInboundPatternDeletion(i)"
|
||||
*ngIf="markedForDeletionInboundPattern.includes(i)"
|
||||
class="btn btn-warning "
|
||||
type="button">
|
||||
<i class="fas fa-undo"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
|
||||
<span (click)="addInboundPattern()"
|
||||
class="add-pattern-link mb-2">{{ 'ldn-new-service.form.label.addPattern' | translate }}</span>
|
||||
<span (click)="addInboundPattern()"
|
||||
class="add-pattern-link mb-2">{{ 'ldn-new-service.form.label.addPattern' | translate }}</span>
|
||||
|
||||
<div class="mb-4">
|
||||
|
||||
<!-- In the Outbound Patterns Labels section -->
|
||||
<div class="row mb-1 mt-5">
|
||||
<div class="col">
|
||||
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.outboundPattern' | translate }} </label>
|
||||
</div>
|
||||
<ng-container *ngIf="!!(formModel.get('notifyServiceOutboundPatterns')['controls'][0]?.value?.pattern)">
|
||||
<div class="col">
|
||||
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.ItemFilter' | translate }}</label>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="col-sm-1 ">
|
||||
<label class="label-box-2" style="visibility: hidden;">
|
||||
{{ 'ldn-new-service.form.label.automatic' | translate }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-2 ">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label>{{ 'ldn-new-service.form.label.outboundPattern' | translate }}</label>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label-box-3">{{ 'ldn-new-service.form.label.ItemFilter' | translate }}</label>
|
||||
</div>
|
||||
<div class="col-sm-1 ">
|
||||
<label class="label-box-2"></label>
|
||||
</div>
|
||||
<div class="col-sm-1 ">
|
||||
</div>
|
||||
</div>
|
||||
<!-- In the Outbound Patterns section -->
|
||||
<div *ngFor="let patternGroup of formModel.get('notifyServiceOutboundPatterns')['controls']; let i = index"
|
||||
[class.marked-for-deletion]="markedForDeletionOutboundPattern.includes(i)"
|
||||
formGroupName="notifyServiceOutboundPatterns">
|
||||
|
||||
<div *ngFor="let patternGroup of formModel.get('notifyServiceOutboundPatterns')['controls']; let i = index"
|
||||
formGroupName="notifyServiceOutboundPatterns">
|
||||
<ng-container [formGroupName]="i">
|
||||
|
||||
<ng-container [formGroupName]="i">
|
||||
|
||||
<!-- Input elements in a separate row -->
|
||||
<div [class.marked-for-deletion]="markedForDeletionOutboundPattern.includes(i)" class="row mb-1">
|
||||
<div class="col">
|
||||
<select #outboundPattern formControlName="pattern" id="additionalOutboundPattern{{i}}"
|
||||
name="additionalOutboundPattern{{i}}"
|
||||
required>
|
||||
<option value="">{{ 'ldn-new-service.form.label.placeholder.outboundPattern' | translate }}</option>
|
||||
<option *ngFor="let pattern of outboundPatterns"
|
||||
[ngValue]="pattern.name">{{ pattern.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<ng-container *ngIf="outboundPattern.value">
|
||||
<select formControlName="constraint" id="constraint{{i}}" name="constraint{{i}}">
|
||||
<option value="">{{ 'ldn-new-service.form.label.placeholder.selectedItemFilter' | translate }}</option>
|
||||
<option *ngFor="let itemFilter of (itemfiltersRD$ | async)?.payload?.page"
|
||||
[value]="itemFilter.id">{{ itemFilter.id }}</option>
|
||||
</select>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div [style.visibility]="'hidden'" class="col-sm1">
|
||||
<input hidden id="automatic{{i}}" name="automatic{{i}}" type="checkbox">
|
||||
<div
|
||||
class="toggle-switch">
|
||||
<div class="slider"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1 btn-group">
|
||||
<button (click)="markForOutboundPatternDeletion(i)" class="btn btn-outline-dark trash-button" type="button">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<button (click)="unmarkForOutboundPatternDeletion(i)" *ngIf="markedForDeletionOutboundPattern.includes(i)" class="btn btn-outline-dark undo-button"
|
||||
type="button">
|
||||
<i class="fas fa-undo"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="row mb-1">
|
||||
<div class="col">
|
||||
<div #outboundPatternDropdown="ngbDropdown" class="w-100" id="additionalOutboundPattern{{i}}"
|
||||
ngbDropdown
|
||||
placement="bottom-start">
|
||||
<div class="position-relative right-addon" role="combobox">
|
||||
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
|
||||
ngbDropdownToggle></i>
|
||||
<input
|
||||
(click)="outboundPatternDropdown.open();"
|
||||
[readonly]="true"
|
||||
[value]="selectedOutboundPatterns"
|
||||
class="form-control w-100 scrollable-dropdown-input"
|
||||
formControlName="patternLabel"
|
||||
id="outboundPatternDropdownButton"
|
||||
ngbDropdownAnchor
|
||||
type="text"
|
||||
/>
|
||||
<div aria-labelledby="outboundPatternDropdownButton"
|
||||
class="dropdown-menu scrollable-dropdown-menu w-100 "
|
||||
ngbDropdownMenu>
|
||||
<div class="scrollable-menu" role="listbox">
|
||||
<button (click)="selectOutboundPattern(pattern, i); $event.stopPropagation()"
|
||||
*ngFor="let pattern of outboundPatterns; let internalIndex = index"
|
||||
[title]="'ldn-service.form.pattern.' + pattern + '.description' | translate"
|
||||
class="dropdown-item collection-item text-truncate w-100"
|
||||
ngbDropdownItem
|
||||
type="button">
|
||||
<div>{{ 'ldn-service.form.pattern.' + pattern + '.label' | translate }}</div>
|
||||
<div
|
||||
class="small-text">{{ 'ldn-service.form.pattern.' + pattern + '.description' | translate }}</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<span
|
||||
(click)="addOutboundPattern()"
|
||||
class="add-pattern-link mb-2">{{ 'ldn-new-service.form.label.addPattern' | translate }}
|
||||
</span>
|
||||
|
||||
<div class="mb-5">
|
||||
|
||||
</div>
|
||||
<div aria-label="Basic example" class="submission-form-footer mt-1 mb-1 position-sticky" role="group">
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<span><i class="fas fa-save"></i> {{ 'ldn-new-service.form.label.submit' | translate }}</span>
|
||||
</button>
|
||||
<div class="d-flex">
|
||||
<button (click)="this.openResetFormModal(this.resetFormModal)" class="btn btn-danger" type="button">
|
||||
<span><i class="fas fa-trash"></i> {{ 'submission.general.discard.submit' | translate }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<ng-container
|
||||
*ngIf="!!(formModel.get('notifyServiceOutboundPatterns')['controls'][i].value.pattern)">
|
||||
<div #outboundItemfilterDropdown="ngbDropdown" class="w-100" id="constraint{{i}}"
|
||||
ngbDropdown
|
||||
placement="bottom-start">
|
||||
<div class="position-relative right-addon" role="combobox">
|
||||
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
|
||||
ngbDropdownToggle></i>
|
||||
<input
|
||||
(click)="outboundItemfilterDropdown.open();"
|
||||
[readonly]="true"
|
||||
[value]="selectedOutboundItemfilters"
|
||||
class="form-control w-100 scrollable-dropdown-input"
|
||||
formControlName="constraint"
|
||||
id="outboundItemfilterDropdown"
|
||||
ngbDropdownAnchor
|
||||
type="text"
|
||||
/>
|
||||
<div aria-labelledby="outboundItemfilterDropdownButton"
|
||||
class="dropdown-menu scrollable-dropdown-menu w-100 "
|
||||
ngbDropdownMenu>
|
||||
<div class="scrollable-menu" role="listbox">
|
||||
<button (click)="selectOutboundItemFilter(constraint.id, i); $event.stopPropagation()"
|
||||
*ngFor="let constraint of (itemfiltersRD$ | async)?.payload?.page; let internalIndex = index"
|
||||
class="dropdown-item collection-item text-truncate w-100"
|
||||
ngbDropdownItem
|
||||
type="button">
|
||||
<div>{{ constraint.id }}</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div [style.visibility]="'hidden'" class="col-sm-1">
|
||||
<input hidden id="automatic{{i}}" name="automatic{{i}}" type="checkbox">
|
||||
<div
|
||||
class="toggle-switch">
|
||||
<div class="slider"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<div class="btn-group">
|
||||
<button (click)="markForOutboundPatternDeletion(i)"
|
||||
class="btn btn-outline-dark trash-button" type="button">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<button (click)="unmarkForOutboundPatternDeletion(i)"
|
||||
*ngIf="markedForDeletionOutboundPattern.includes(i)"
|
||||
class="btn btn-warning "
|
||||
type="button">
|
||||
<i class="fas fa-undo"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div
|
||||
(click)="addOutboundPattern()"
|
||||
class="add-pattern-link mb-4">{{ 'ldn-new-service.form.label.addPattern' | translate }}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="submission-form-footer my-1 position-sticky d-flex justify-content-between" role="group">
|
||||
<button (click)="openResetFormModal(resetFormModal)" class="btn btn-danger" type="button">
|
||||
<span><i class="fas fa-trash"></i> {{ 'submission.general.discard.submit' | translate }}</span>
|
||||
</button>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<span><i class="fas fa-save"></i> {{ 'ldn-new-service.form.label.submit' | translate }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<ng-template #confirmModal>
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h4>{{'service.overview.edit.modal' | translate }}</h4>
|
||||
</div>
|
||||
<button (click)="closeModal()" aria-label="Close"
|
||||
class="close" type="button">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h4>{{'service.overview.edit.modal' | translate }}</h4>
|
||||
</div>
|
||||
<button (click)="closeModal()" aria-label="Close"
|
||||
class="close" type="button">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{ 'service.overview.edit.body' | translate }}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button (click)="closeModal()" class="btn btn-danger mr-2"
|
||||
id="delete-confirm">{{ 'service.detail.return' | translate }}
|
||||
</button>
|
||||
<button (click)="this.patchService()"
|
||||
class="btn btn-primary custom-btn">{{ 'service.detail.update' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{ 'service.overview.edit.body' | translate }}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button (click)="closeModal()" class="btn btn-danger mr-2"
|
||||
id="delete-confirm">{{ 'service.detail.return' | translate }}
|
||||
</button>
|
||||
<button (click)="patchService()"
|
||||
class="btn btn-primary">{{ 'service.detail.update' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #resetFormModal>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h4>{{'service.overview.reset-form.modal' | translate }}</h4>
|
||||
</div>
|
||||
<button (click)="closeModal()" aria-label="Close"
|
||||
class="close" type="button">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{ 'service.overview.reset-form.body' | translate }}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button (click)="closeModal()" class="mr-2 btn btn-danger"
|
||||
id="reset-delete">{{ 'service.overview.reset-form.reset-confirm' | translate }}
|
||||
</button>
|
||||
<button (click)="resetFormAndLeave()"
|
||||
class="btn btn-primary custom-btn"
|
||||
id="reset-confirm">{{ 'service.overview.reset-form.reset-return' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h4>{{'service.overview.reset-form.modal' | translate }}</h4>
|
||||
</div>
|
||||
<button (click)="closeModal()" aria-label="Close"
|
||||
class="close" type="button">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{ 'service.overview.reset-form.body' | translate }}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button (click)="closeModal()" class="mr-2 btn btn-danger"
|
||||
id="reset-delete">{{ 'service.overview.reset-form.reset-confirm' | translate }}
|
||||
</button>
|
||||
<button (click)="resetFormAndLeave()"
|
||||
class="btn btn-primary"
|
||||
id="reset-confirm">{{ 'service.overview.reset-form.reset-return' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
@import '../../../shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.scss';
|
||||
@import '../../../shared/form/form.component.scss';
|
||||
|
||||
form {
|
||||
max-width: 800px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
@@ -30,7 +31,6 @@ textarea {
|
||||
.add-pattern-link {
|
||||
color: #0048ff;
|
||||
cursor: pointer;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.remove-pattern-link {
|
||||
@@ -49,6 +49,12 @@ textarea {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
color: red;
|
||||
font-size: 0.8em;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.toggle-switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -95,6 +101,11 @@ textarea {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.small-text {
|
||||
font-size: 0.7em;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.toggle-switch {
|
||||
cursor: pointer;
|
||||
margin-top: 3px;
|
||||
@@ -113,11 +124,6 @@ textarea {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
form button.btn.btn-primary[type="submit"] {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.submission-form-footer {
|
||||
border-radius: var(--bs-card-border-radius);
|
||||
bottom: 0;
|
||||
@@ -127,7 +133,7 @@ form button.btn.btn-primary[type="submit"] {
|
||||
}
|
||||
|
||||
.marked-for-deletion {
|
||||
background-color: #ffcccc;
|
||||
background-color: lighten($red, 30%);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,23 +1,80 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import { LdnServiceFormEditComponent } from './ldn-service-form-edit.component';
|
||||
import {NgbDropdownModule, NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {LdnServiceFormEditComponent} from './ldn-service-form-edit.component';
|
||||
import {ChangeDetectorRef, EventEmitter} from '@angular/core';
|
||||
import {FormBuilder, ReactiveFormsModule} from '@angular/forms';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {TranslateModule, TranslateService} from '@ngx-translate/core';
|
||||
import {PaginationService} from 'ngx-pagination';
|
||||
import {NotificationsService} from '../../../shared/notifications/notifications.service';
|
||||
import {LdnItemfiltersService} from '../ldn-services-data/ldn-itemfilters-data.service';
|
||||
import {LdnServicesService} from '../ldn-services-data/ldn-services-data.service';
|
||||
import {RouterStub} from '../../../shared/testing/router.stub';
|
||||
import {MockActivatedRoute} from '../../../shared/mocks/active-router.mock';
|
||||
import {NotificationsServiceStub} from '../../../shared/testing/notifications-service.stub';
|
||||
import {of} from 'rxjs';
|
||||
import {RouteService} from '../../../core/services/route.service';
|
||||
import {provideMockStore} from '@ngrx/store/testing';
|
||||
|
||||
describe('LdnServiceFormEditComponent', () => {
|
||||
let component: LdnServiceFormEditComponent;
|
||||
let fixture: ComponentFixture<LdnServiceFormEditComponent>;
|
||||
let component: LdnServiceFormEditComponent;
|
||||
let fixture: ComponentFixture<LdnServiceFormEditComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [LdnServiceFormEditComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
let ldnServicesService: any;
|
||||
let ldnItemfiltersService: any;
|
||||
let cdRefStub: any;
|
||||
let modalService: any;
|
||||
|
||||
fixture = TestBed.createComponent(LdnServiceFormEditComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
const translateServiceStub = {
|
||||
get: () => of('translated-text'),
|
||||
instant: () => 'translated-text',
|
||||
onLangChange: new EventEmitter(),
|
||||
onTranslationChange: new EventEmitter(),
|
||||
onDefaultLangChange: new EventEmitter()
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
ldnServicesService = {
|
||||
update: () => ({}),
|
||||
};
|
||||
ldnItemfiltersService = {
|
||||
findAll: () => of(['item1', 'item2']),
|
||||
};
|
||||
cdRefStub = Object.assign({
|
||||
detectChanges: () => fixture.detectChanges()
|
||||
});
|
||||
modalService = {
|
||||
open: () => {/*comment*/
|
||||
}
|
||||
};
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ReactiveFormsModule, TranslateModule.forRoot(), NgbDropdownModule],
|
||||
declarations: [LdnServiceFormEditComponent],
|
||||
providers: [
|
||||
{provide: LdnServicesService, useValue: ldnServicesService},
|
||||
{provide: LdnItemfiltersService, useValue: ldnItemfiltersService},
|
||||
{provide: Router, useValue: new RouterStub()},
|
||||
{provide: ActivatedRoute, useValue: new MockActivatedRoute()},
|
||||
{provide: ChangeDetectorRef, useValue: cdRefStub},
|
||||
{provide: NgbModal, useValue: modalService},
|
||||
{provide: NotificationsService, useValue: NotificationsServiceStub},
|
||||
{provide: TranslateService, useValue: translateServiceStub},
|
||||
{provide: PaginationService, useValue: {}},
|
||||
FormBuilder,
|
||||
RouteService,
|
||||
provideMockStore({}),
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LdnServiceFormEditComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@@ -1,409 +1,579 @@
|
||||
import { ChangeDetectorRef, Component, Input, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { LDN_SERVICE } from '../ldn-services-model/ldn-service.resource-type';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { LdnServicesService } from '../ldn-services-data/ldn-services-data.service';
|
||||
import { notifyPatterns } from '../ldn-services-patterns/ldn-service-coar-patterns';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { LdnService } from '../ldn-services-model/ldn-services.model';
|
||||
import { RemoteData } from 'src/app/core/data/remote-data';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
||||
import { LdnItemfiltersService } from '../ldn-services-data/ldn-itemfilters-data.service';
|
||||
import { Itemfilter } from '../ldn-services-model/ldn-service-itemfilters';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import {ChangeDetectorRef, Component, Input, OnInit, TemplateRef, ViewChild} from '@angular/core';
|
||||
import {FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||
import {LDN_SERVICE} from '../ldn-services-model/ldn-service.resource-type';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {LdnServicesService} from '../ldn-services-data/ldn-services-data.service';
|
||||
import {notifyPatterns} from '../ldn-services-patterns/ldn-service-coar-patterns';
|
||||
import {animate, state, style, transition, trigger} from '@angular/animations';
|
||||
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {NotificationsService} from '../../../shared/notifications/notifications.service';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
import {LdnService} from '../ldn-services-model/ldn-services.model';
|
||||
import {RemoteData} from 'src/app/core/data/remote-data';
|
||||
import {Operation} from 'fast-json-patch';
|
||||
import {getFirstCompletedRemoteData} from '../../../core/shared/operators';
|
||||
import {LdnItemfiltersService} from '../ldn-services-data/ldn-itemfilters-data.service';
|
||||
import {Itemfilter} from '../ldn-services-model/ldn-service-itemfilters';
|
||||
import {PaginatedList} from '../../../core/data/paginated-list.model';
|
||||
import {Observable} from 'rxjs';
|
||||
import {PaginationService} from '../../../core/pagination/pagination.service';
|
||||
import {FindListOptions} from '../../../core/data/find-list-options.model';
|
||||
import {PaginationComponentOptions} from '../../../shared/pagination/pagination-component-options.model';
|
||||
import {NotifyServicePattern} from '../ldn-services-model/ldn-service-patterns.model';
|
||||
|
||||
|
||||
/**
|
||||
* Component for editing LDN service through a form that allows to edit the properties of the selected service
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-ldn-service-form-edit',
|
||||
templateUrl: './ldn-service-form-edit.component.html',
|
||||
styleUrls: ['./ldn-service-form-edit.component.scss'],
|
||||
animations: [
|
||||
trigger('toggleAnimation', [
|
||||
state('true', style({})),
|
||||
state('false', style({})),
|
||||
transition('true <=> false', animate('300ms ease-in')),
|
||||
]),
|
||||
],
|
||||
selector: 'ds-ldn-service-form-edit',
|
||||
templateUrl: './ldn-service-form-edit.component.html',
|
||||
styleUrls: ['./ldn-service-form-edit.component.scss'],
|
||||
animations: [
|
||||
trigger('toggleAnimation', [
|
||||
state('true', style({})),
|
||||
state('false', style({})),
|
||||
transition('true <=> false', animate('300ms ease-in')),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class LdnServiceFormEditComponent implements OnInit {
|
||||
formModel: FormGroup;
|
||||
@ViewChild('confirmModal', {static: true}) confirmModal: TemplateRef<any>;
|
||||
@ViewChild('resetFormModal', {static: true}) resetFormModal: TemplateRef<any>;
|
||||
formModel: FormGroup;
|
||||
@ViewChild('confirmModal', {static: true}) confirmModal: TemplateRef<any>;
|
||||
@ViewChild('resetFormModal', {static: true}) resetFormModal: TemplateRef<any>;
|
||||
|
||||
public inboundPatterns: object[] = notifyPatterns;
|
||||
public outboundPatterns: object[] = notifyPatterns;
|
||||
itemfiltersRD$: Observable<RemoteData<PaginatedList<Itemfilter>>>;
|
||||
config: FindListOptions = Object.assign(new FindListOptions(), {
|
||||
elementsPerPage: 20
|
||||
public inboundPatterns: string[] = notifyPatterns;
|
||||
public outboundPatterns: string[] = notifyPatterns;
|
||||
itemfiltersRD$: Observable<RemoteData<PaginatedList<Itemfilter>>>;
|
||||
config: FindListOptions = Object.assign(new FindListOptions(), {
|
||||
elementsPerPage: 20
|
||||
});
|
||||
pageConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
||||
id: 'po',
|
||||
pageSize: 20
|
||||
});
|
||||
@Input() public name: string;
|
||||
@Input() public description: string;
|
||||
@Input() public url: string;
|
||||
@Input() public ldnUrl: string;
|
||||
@Input() public score: number;
|
||||
@Input() public inboundPattern: string;
|
||||
@Input() public outboundPattern: string;
|
||||
@Input() public constraint: string;
|
||||
@Input() public automatic: boolean;
|
||||
@Input() public headerKey: string;
|
||||
markedForDeletionInboundPattern: number[] = [];
|
||||
markedForDeletionOutboundPattern: number[] = [];
|
||||
selectedOutboundPatterns: string[];
|
||||
selectedInboundItemfilters: string[];
|
||||
selectedOutboundItemfilters: string[];
|
||||
selectedInboundPatterns: string[];
|
||||
protected serviceId: string;
|
||||
private deletedInboundPatterns: number[] = [];
|
||||
private deletedOutboundPatterns: number[] = [];
|
||||
private modalRef: any;
|
||||
private service: LdnService;
|
||||
private selectPatternDefaultLabeli18Key = 'ldn-service.form.label.placeholder.default-select';
|
||||
|
||||
constructor(
|
||||
protected ldnServicesService: LdnServicesService,
|
||||
private ldnItemfiltersService: LdnItemfiltersService,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private cdRef: ChangeDetectorRef,
|
||||
protected modalService: NgbModal,
|
||||
private notificationService: NotificationsService,
|
||||
private translateService: TranslateService,
|
||||
protected paginationService: PaginationService
|
||||
) {
|
||||
|
||||
this.formModel = this.formBuilder.group({
|
||||
id: [''],
|
||||
name: ['', Validators.required],
|
||||
description: ['', Validators.required],
|
||||
url: ['', Validators.required],
|
||||
ldnUrl: ['', Validators.required],
|
||||
score: ['', [Validators.required, Validators.pattern('^0*(\.[0-9]+)?$|^1(\.0+)?$')]], inboundPattern: [''],
|
||||
outboundPattern: [''],
|
||||
constraintPattern: [''],
|
||||
enabled: [''],
|
||||
notifyServiceInboundPatterns: this.formBuilder.array([this.createInboundPatternFormGroup()]),
|
||||
notifyServiceOutboundPatterns: this.formBuilder.array([this.createOutboundPatternFormGroup()]),
|
||||
type: LDN_SERVICE.value,
|
||||
});
|
||||
pageConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
||||
id: 'po',
|
||||
pageSize: 20
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.route.params.subscribe((params) => {
|
||||
this.serviceId = params.serviceId;
|
||||
if (this.serviceId) {
|
||||
this.fetchServiceData(this.serviceId);
|
||||
}
|
||||
});
|
||||
@Input() public name: string;
|
||||
@Input() public description: string;
|
||||
@Input() public url: string;
|
||||
@Input() public ldnUrl: string;
|
||||
@Input() public inboundPattern: string;
|
||||
@Input() public outboundPattern: string;
|
||||
@Input() public constraint: string;
|
||||
@Input() public automatic: boolean;
|
||||
@Input() public headerKey: string;
|
||||
markedForDeletionInboundPattern: number[] = [];
|
||||
markedForDeletionOutboundPattern: number[] = [];
|
||||
protected serviceId: string;
|
||||
private originalInboundPatterns: any[] = [];
|
||||
private originalOutboundPatterns: any[] = [];
|
||||
private deletedInboundPatterns: number[] = [];
|
||||
private deletedOutboundPatterns: number[] = [];
|
||||
private modalRef: any;
|
||||
private service: LdnService;
|
||||
this.setItemfilters();
|
||||
}
|
||||
|
||||
constructor(
|
||||
protected ldnServicesService: LdnServicesService,
|
||||
private ldnItemfiltersService: LdnItemfiltersService,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private cdRef: ChangeDetectorRef,
|
||||
protected modalService: NgbModal,
|
||||
private notificationService: NotificationsService,
|
||||
private translateService: TranslateService,
|
||||
protected paginationService: PaginationService
|
||||
) {
|
||||
/**
|
||||
* Sets item filters using LDN item filters service
|
||||
*/
|
||||
setItemfilters() {
|
||||
this.itemfiltersRD$ = this.ldnItemfiltersService.findAll().pipe(
|
||||
getFirstCompletedRemoteData());
|
||||
}
|
||||
|
||||
this.formModel = this.formBuilder.group({
|
||||
id: [''],
|
||||
name: ['', Validators.required],
|
||||
description: ['', Validators.required],
|
||||
url: ['', Validators.required],
|
||||
ldnUrl: ['', Validators.required],
|
||||
inboundPattern: [''],
|
||||
outboundPattern: [''],
|
||||
constraintPattern: [''],
|
||||
enabled: [''],
|
||||
notifyServiceInboundPatterns: this.formBuilder.array([this.createInboundPatternFormGroup()]),
|
||||
notifyServiceOutboundPatterns: this.formBuilder.array([this.createOutboundPatternFormGroup()]),
|
||||
type: LDN_SERVICE.value,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Fetches LDN service data by ID and updates the form
|
||||
* @param serviceId - The ID of the LDN service
|
||||
*/
|
||||
fetchServiceData(serviceId: string): void {
|
||||
this.ldnServicesService.findById(serviceId).pipe(
|
||||
getFirstCompletedRemoteData()
|
||||
).subscribe(
|
||||
(data: RemoteData<LdnService>) => {
|
||||
if (data.hasSucceeded) {
|
||||
this.service = data.payload;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.route.params.subscribe((params) => {
|
||||
this.serviceId = params.serviceId;
|
||||
if (this.serviceId) {
|
||||
this.fetchServiceData(this.serviceId);
|
||||
}
|
||||
});
|
||||
this.setItemfilters();
|
||||
}
|
||||
|
||||
setItemfilters() {
|
||||
this.itemfiltersRD$ = this.ldnItemfiltersService.findAll().pipe(
|
||||
getFirstCompletedRemoteData());
|
||||
}
|
||||
|
||||
|
||||
fetchServiceData(serviceId: string): void {
|
||||
this.ldnServicesService.findById(serviceId).pipe(
|
||||
getFirstCompletedRemoteData()
|
||||
).subscribe(
|
||||
(data: RemoteData<LdnService>) => {
|
||||
if (data.hasSucceeded) {
|
||||
this.service = data.payload;
|
||||
|
||||
this.formModel.patchValue({
|
||||
id: this.service.id,
|
||||
name: this.service.name,
|
||||
description: this.service.description,
|
||||
url: this.service.url,
|
||||
ldnUrl: this.service.ldnUrl,
|
||||
type: this.service.type,
|
||||
enabled: this.service.enabled
|
||||
});
|
||||
|
||||
const inboundPatternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
|
||||
inboundPatternsArray.clear();
|
||||
|
||||
this.service.notifyServiceInboundPatterns.forEach((pattern: any) => {
|
||||
const patternFormGroup = this.initializeInboundPatternFormGroup();
|
||||
patternFormGroup.patchValue(pattern);
|
||||
inboundPatternsArray.push(patternFormGroup);
|
||||
this.cdRef.detectChanges();
|
||||
});
|
||||
|
||||
const outboundPatternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
|
||||
outboundPatternsArray.clear();
|
||||
|
||||
this.service.notifyServiceOutboundPatterns.forEach((pattern: any) => {
|
||||
const patternFormGroup = this.initializeOutboundPatternFormGroup();
|
||||
patternFormGroup.patchValue(pattern);
|
||||
outboundPatternsArray.push(patternFormGroup);
|
||||
|
||||
this.cdRef.detectChanges();
|
||||
});
|
||||
this.originalInboundPatterns = [...this.service.notifyServiceInboundPatterns];
|
||||
this.originalOutboundPatterns = [...this.service.notifyServiceOutboundPatterns];
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
generatePatchOperations(): any[] {
|
||||
const patchOperations: any[] = [];
|
||||
|
||||
this.createReplaceOperation(patchOperations, 'name', '/name');
|
||||
this.createReplaceOperation(patchOperations, 'description', '/description');
|
||||
this.createReplaceOperation(patchOperations, 'ldnUrl', '/ldnurl');
|
||||
this.createReplaceOperation(patchOperations, 'url', '/url');
|
||||
|
||||
this.handlePatterns(patchOperations, 'notifyServiceInboundPatterns');
|
||||
this.handlePatterns(patchOperations, 'notifyServiceOutboundPatterns');
|
||||
|
||||
this.deletedInboundPatterns.forEach(index => {
|
||||
const removeOperation: Operation = {
|
||||
op: 'remove',
|
||||
path: `notifyServiceInboundPatterns[${index}]`
|
||||
};
|
||||
patchOperations.push(removeOperation);
|
||||
});
|
||||
|
||||
this.deletedOutboundPatterns.forEach(index => {
|
||||
const removeOperation: Operation = {
|
||||
op: 'remove',
|
||||
path: `notifyServiceOutboundPatterns[${index}]`
|
||||
};
|
||||
patchOperations.push(removeOperation);
|
||||
});
|
||||
|
||||
return patchOperations;
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.openConfirmModal(this.confirmModal);
|
||||
}
|
||||
|
||||
addInboundPattern() {
|
||||
const notifyServiceInboundPatternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
|
||||
notifyServiceInboundPatternsArray.push(this.createInboundPatternFormGroup());
|
||||
}
|
||||
|
||||
addOutboundPattern() {
|
||||
const notifyServiceOutboundPatternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
|
||||
notifyServiceOutboundPatternsArray.push(this.createOutboundPatternFormGroup());
|
||||
}
|
||||
|
||||
|
||||
toggleAutomatic(i: number) {
|
||||
const automaticControl = this.formModel.get(`notifyServiceInboundPatterns.${i}.automatic`);
|
||||
if (automaticControl) {
|
||||
automaticControl.setValue(!automaticControl.value);
|
||||
this.formModel.patchValue({
|
||||
id: this.service.id,
|
||||
name: this.service.name,
|
||||
description: this.service.description,
|
||||
url: this.service.url,
|
||||
score: this.service.score, ldnUrl: this.service.ldnUrl,
|
||||
type: this.service.type,
|
||||
enabled: this.service.enabled
|
||||
});
|
||||
this.filterPatternObjectsAndPickLabel('notifyServiceInboundPatterns', false);
|
||||
this.filterPatternObjectsAndPickLabel('notifyServiceOutboundPatterns', true);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters pattern objects, initializes form groups, assigns labels, and adds them to the specified form array so the correct string is shown in the dropdown..
|
||||
* @param formArrayName - The name of the form array to be populated
|
||||
* @param isOutbound - A boolean indicating whether the patterns are outbound (true) or inbound (false)
|
||||
*/
|
||||
filterPatternObjectsAndPickLabel(formArrayName: string, isOutbound: boolean) {
|
||||
const PatternsArray = this.formModel.get(formArrayName) as FormArray;
|
||||
PatternsArray.clear();
|
||||
let servicesToUse;
|
||||
if (isOutbound) {
|
||||
servicesToUse = this.service.notifyServiceOutboundPatterns;
|
||||
} else {
|
||||
servicesToUse = this.service.notifyServiceInboundPatterns;
|
||||
}
|
||||
|
||||
toggleEnabled() {
|
||||
const newStatus = !this.formModel.get('enabled').value;
|
||||
servicesToUse.forEach((patternObj: NotifyServicePattern) => {
|
||||
let patternFormGroup;
|
||||
if (isOutbound) {
|
||||
patternFormGroup = this.initializeOutboundPatternFormGroup();
|
||||
} else {
|
||||
patternFormGroup = this.initializeInboundPatternFormGroup();
|
||||
}
|
||||
const newPatternObjWithLabel = Object.assign(new NotifyServicePattern(), {
|
||||
...patternObj,
|
||||
patternLabel: this.translateService.instant('ldn-service.form.pattern.' + patternObj?.pattern + '.label')
|
||||
});
|
||||
patternFormGroup.patchValue(newPatternObjWithLabel);
|
||||
|
||||
const patchOperation: Operation = {
|
||||
op: 'replace',
|
||||
path: '/enabled',
|
||||
value: newStatus,
|
||||
};
|
||||
PatternsArray.push(patternFormGroup);
|
||||
this.cdRef.detectChanges();
|
||||
});
|
||||
|
||||
this.ldnServicesService.patch(this.service, [patchOperation]).pipe(
|
||||
getFirstCompletedRemoteData()
|
||||
).subscribe(
|
||||
() => {
|
||||
|
||||
this.formModel.get('enabled').setValue(newStatus);
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array of patch operations based on form changes
|
||||
* @returns Array of patch operations
|
||||
*/
|
||||
generatePatchOperations(): any[] {
|
||||
const patchOperations: any[] = [];
|
||||
|
||||
this.createReplaceOperation(patchOperations, 'name', '/name');
|
||||
this.createReplaceOperation(patchOperations, 'description', '/description');
|
||||
this.createReplaceOperation(patchOperations, 'ldnUrl', '/ldnurl');
|
||||
this.createReplaceOperation(patchOperations, 'url', '/url');
|
||||
this.createReplaceOperation(patchOperations, 'score', '/score');
|
||||
|
||||
this.handlePatterns(patchOperations, 'notifyServiceInboundPatterns');
|
||||
this.handlePatterns(patchOperations, 'notifyServiceOutboundPatterns');
|
||||
|
||||
|
||||
this.deletedInboundPatterns.forEach(index => {
|
||||
const removeOperation: Operation = {
|
||||
op: 'remove',
|
||||
path: `notifyServiceInboundPatterns[${index}]`
|
||||
};
|
||||
patchOperations.push(removeOperation);
|
||||
});
|
||||
|
||||
this.deletedOutboundPatterns.forEach(index => {
|
||||
const removeOperation: Operation = {
|
||||
op: 'remove',
|
||||
path: `notifyServiceOutboundPatterns[${index}]`
|
||||
};
|
||||
patchOperations.push(removeOperation);
|
||||
});
|
||||
|
||||
return patchOperations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the form by opening the confirmation modal
|
||||
*/
|
||||
onSubmit() {
|
||||
this.openConfirmModal(this.confirmModal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new inbound pattern form group to the array of inbound patterns in the form
|
||||
*/
|
||||
addInboundPattern() {
|
||||
const notifyServiceInboundPatternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
|
||||
notifyServiceInboundPatternsArray.push(this.createInboundPatternFormGroup());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new outbound pattern form group to the array of outbound patterns in the form
|
||||
*/
|
||||
addOutboundPattern() {
|
||||
const notifyServiceOutboundPatternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
|
||||
notifyServiceOutboundPatternsArray.push(this.createOutboundPatternFormGroup());
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects an outbound pattern by updating its values based on the provided pattern value and index
|
||||
* @param patternValue - The selected pattern value
|
||||
* @param index - The index of the outbound pattern in the array
|
||||
*/
|
||||
selectOutboundPattern(patternValue: string, index: number): void {
|
||||
const patternArray = (this.formModel.get('notifyServiceOutboundPatterns') as FormArray);
|
||||
patternArray.controls[index].patchValue({pattern: patternValue});
|
||||
patternArray.controls[index].patchValue({patternLabel: this.translateService.instant('ldn-service.form.pattern.' + patternValue + '.label')});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects an outbound item filter by updating its value based on the provided filter value and index
|
||||
* @param filterValue - The selected filter value
|
||||
* @param index - The index of the inbound pattern in the array
|
||||
*/
|
||||
selectOutboundItemFilter(filterValue: string, index: number) {
|
||||
const filterArray = (this.formModel.get('notifyServiceOutboundPatterns') as FormArray);
|
||||
filterArray.controls[index].patchValue({constraint: filterValue});
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects an inbound pattern by updating its values based on the provided pattern value and index
|
||||
* @param patternValue - The selected pattern value
|
||||
* @param index - The index of the inbound pattern in the array
|
||||
*/
|
||||
selectInboundPattern(patternValue: string, index: number): void {
|
||||
const patternArray = (this.formModel.get('notifyServiceInboundPatterns') as FormArray);
|
||||
patternArray.controls[index].patchValue({pattern: patternValue});
|
||||
patternArray.controls[index].patchValue({patternLabel: this.translateService.instant('ldn-service.form.pattern.' + patternValue + '.label')});
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects an inbound item filter by updating its value based on the provided filter value and index
|
||||
* @param filterValue - The selected filter value
|
||||
* @param index - The index of the inbound pattern in the array
|
||||
*/
|
||||
selectInboundItemFilter(filterValue: string, index: number): void {
|
||||
const filterArray = (this.formModel.get('notifyServiceInboundPatterns') as FormArray);
|
||||
filterArray.controls[index].patchValue({constraint: filterValue});
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the automatic property of an inbound pattern at the specified index
|
||||
* @param i - The index of the inbound pattern in the array
|
||||
*/
|
||||
toggleAutomatic(i: number) {
|
||||
const automaticControl = this.formModel.get(`notifyServiceInboundPatterns.${i}.automatic`);
|
||||
if (automaticControl) {
|
||||
automaticControl.setValue(!automaticControl.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the enabled status of the LDN service by sending a patch request
|
||||
*/
|
||||
toggleEnabled() {
|
||||
const newStatus = !this.formModel.get('enabled').value;
|
||||
|
||||
closeModal() {
|
||||
this.modalRef.close();
|
||||
const patchOperation: Operation = {
|
||||
op: 'replace',
|
||||
path: '/enabled',
|
||||
value: newStatus,
|
||||
};
|
||||
|
||||
this.ldnServicesService.patch(this.service, [patchOperation]).pipe(
|
||||
getFirstCompletedRemoteData()
|
||||
).subscribe(
|
||||
() => {
|
||||
|
||||
this.formModel.get('enabled').setValue(newStatus);
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
openConfirmModal(content) {
|
||||
this.modalRef = this.modalService.open(content);
|
||||
}
|
||||
/**
|
||||
* Closes the modal
|
||||
*/
|
||||
closeModal() {
|
||||
this.modalRef.close();
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
|
||||
openResetFormModal(content) {
|
||||
this.modalRef = this.modalService.open(content);
|
||||
}
|
||||
/**
|
||||
* Opens a confirmation modal with the specified content
|
||||
* @param content - The content to be displayed in the modal
|
||||
*/
|
||||
openConfirmModal(content) {
|
||||
this.modalRef = this.modalService.open(content);
|
||||
}
|
||||
|
||||
patchService() {
|
||||
this.deleteMarkedInboundPatterns();
|
||||
this.deleteMarkedOutboundPatterns();
|
||||
const patchOperations = this.generatePatchOperations();
|
||||
/**
|
||||
* Opens a reset form modal with the specified content
|
||||
* @param content - The content to be displayed in the modal
|
||||
*/
|
||||
openResetFormModal(content) {
|
||||
this.modalRef = this.modalService.open(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches the LDN service by retrieving and sending patch operations geenrated in generatePatchOperations()
|
||||
*/
|
||||
patchService() {
|
||||
this.deleteMarkedInboundPatterns();
|
||||
this.deleteMarkedOutboundPatterns();
|
||||
|
||||
const patchOperations = this.generatePatchOperations();
|
||||
|
||||
|
||||
this.ldnServicesService.patch(this.service, patchOperations).pipe(
|
||||
getFirstCompletedRemoteData()
|
||||
).subscribe(
|
||||
() => {
|
||||
|
||||
this.closeModal();
|
||||
this.sendBack();
|
||||
this.notificationService.success(this.translateService.get('admin.registries.services-formats.modify.success.head'),
|
||||
this.translateService.get('admin.registries.services-formats.modify.success.content'));
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
resetFormAndLeave() {
|
||||
this.sendBack();
|
||||
this.closeModal();
|
||||
}
|
||||
|
||||
markForInboundPatternDeletion(index: number) {
|
||||
if (!this.markedForDeletionInboundPattern.includes(index)) {
|
||||
this.markedForDeletionInboundPattern.push(index);
|
||||
this.ldnServicesService.patch(this.service, patchOperations).pipe(
|
||||
getFirstCompletedRemoteData()
|
||||
).subscribe(
|
||||
(rd: RemoteData<LdnService>) => {
|
||||
if (rd.hasSucceeded) {
|
||||
this.closeModal();
|
||||
this.sendBack();
|
||||
this.notificationService.success(this.translateService.get('admin.registries.services-formats.modify.success.head'),
|
||||
this.translateService.get('admin.registries.services-formats.modify.success.content'));
|
||||
} else {
|
||||
this.notificationService.error(this.translateService.get('admin.registries.services-formats.modify.failure.head'),
|
||||
this.translateService.get('admin.registries.services-formats.modify.failure.content'));
|
||||
this.closeModal();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
unmarkForInboundPatternDeletion(index: number) {
|
||||
const i = this.markedForDeletionInboundPattern.indexOf(index);
|
||||
if (i !== -1) {
|
||||
this.markedForDeletionInboundPattern.splice(i, 1);
|
||||
/**
|
||||
* Resets the form and navigates back to the LDN services page
|
||||
*/
|
||||
resetFormAndLeave() {
|
||||
this.sendBack();
|
||||
this.closeModal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the specified inbound pattern for deletion
|
||||
* @param index - The index of the inbound pattern in the array
|
||||
*/
|
||||
markForInboundPatternDeletion(index: number) {
|
||||
if (!this.markedForDeletionInboundPattern.includes(index)) {
|
||||
this.markedForDeletionInboundPattern.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarks the specified inbound pattern for deletion
|
||||
* @param index - The index of the inbound pattern in the array
|
||||
*/
|
||||
unmarkForInboundPatternDeletion(index: number) {
|
||||
const i = this.markedForDeletionInboundPattern.indexOf(index);
|
||||
if (i !== -1) {
|
||||
this.markedForDeletionInboundPattern.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the specified outbound pattern for deletion
|
||||
* @param index - The index of the outbound pattern in the array
|
||||
*/
|
||||
markForOutboundPatternDeletion(index: number) {
|
||||
if (!this.markedForDeletionOutboundPattern.includes(index)) {
|
||||
this.markedForDeletionOutboundPattern.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarks the specified outbound pattern for deletion
|
||||
* @param index - The index of the outbound pattern in the array
|
||||
*/
|
||||
unmarkForOutboundPatternDeletion(index: number) {
|
||||
const i = this.markedForDeletionOutboundPattern.indexOf(index);
|
||||
if (i !== -1) {
|
||||
this.markedForDeletionOutboundPattern.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes marked inbound patterns from the form model
|
||||
*/
|
||||
deleteMarkedInboundPatterns() {
|
||||
this.markedForDeletionInboundPattern.sort((a, b) => b - a);
|
||||
const patternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
|
||||
|
||||
for (const index of this.markedForDeletionInboundPattern) {
|
||||
if (index >= 0 && index < patternsArray.length) {
|
||||
const patternGroup = patternsArray.at(index) as FormGroup;
|
||||
const patternValue = patternGroup.value;
|
||||
if (patternValue.isNew) {
|
||||
patternsArray.removeAt(index);
|
||||
} else {
|
||||
this.deletedInboundPatterns.push(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
markForOutboundPatternDeletion(index: number) {
|
||||
if (!this.markedForDeletionOutboundPattern.includes(index)) {
|
||||
this.markedForDeletionOutboundPattern.push(index);
|
||||
this.markedForDeletionInboundPattern = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes marked outbound patterns from the form model
|
||||
*/
|
||||
deleteMarkedOutboundPatterns() {
|
||||
this.markedForDeletionOutboundPattern.sort((a, b) => b - a);
|
||||
const patternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
|
||||
|
||||
for (const index of this.markedForDeletionOutboundPattern) {
|
||||
if (index >= 0 && index < patternsArray.length) {
|
||||
const patternGroup = patternsArray.at(index) as FormGroup;
|
||||
const patternValue = patternGroup.value;
|
||||
if (patternValue.isNew) {
|
||||
patternsArray.removeAt(index);
|
||||
} else {
|
||||
|
||||
this.deletedOutboundPatterns.push(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unmarkForOutboundPatternDeletion(index: number) {
|
||||
const i = this.markedForDeletionOutboundPattern.indexOf(index);
|
||||
if (i !== -1) {
|
||||
this.markedForDeletionOutboundPattern.splice(i, 1);
|
||||
this.markedForDeletionOutboundPattern = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a replace operation and adds it to the patch operations if the form control is dirty
|
||||
* @param patchOperations - The array to store patch operations
|
||||
* @param formControlName - The name of the form control
|
||||
* @param path - The JSON Patch path for the operation
|
||||
*/
|
||||
private createReplaceOperation(patchOperations: any[], formControlName: string, path: string): void {
|
||||
if (this.formModel.get(formControlName).dirty) {
|
||||
patchOperations.push({
|
||||
op: 'replace',
|
||||
path,
|
||||
value: this.formModel.get(formControlName).value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles patterns in the form array, checking if an add or replace operations is required
|
||||
* @param patchOperations - The array to store patch operations
|
||||
* @param formArrayName - The name of the form array
|
||||
*/
|
||||
private handlePatterns(patchOperations: any[], formArrayName: string): void {
|
||||
const patternsArray = this.formModel.get(formArrayName) as FormArray;
|
||||
|
||||
|
||||
for (let i = 0; i < patternsArray.length; i++) {
|
||||
const patternGroup = patternsArray.at(i) as FormGroup;
|
||||
|
||||
const patternValue = patternGroup.value;
|
||||
if (patternGroup.touched) {
|
||||
delete patternValue?.patternLabel;
|
||||
if (patternValue.isNew) {
|
||||
delete patternValue.isNew;
|
||||
const addOperation = {
|
||||
op: 'add',
|
||||
path: `${formArrayName}/-`,
|
||||
value: patternValue,
|
||||
};
|
||||
patchOperations.push(addOperation);
|
||||
} else {
|
||||
const replaceOperation = {
|
||||
op: 'replace',
|
||||
path: `${formArrayName}[${i}]`,
|
||||
value: patternValue,
|
||||
};
|
||||
patchOperations.push(replaceOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deleteMarkedInboundPatterns() {
|
||||
this.markedForDeletionInboundPattern.sort((a, b) => b - a);
|
||||
const patternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
|
||||
/**
|
||||
* Navigates back to the LDN services page
|
||||
*/
|
||||
private sendBack() {
|
||||
this.router.navigateByUrl('admin/ldn/services');
|
||||
}
|
||||
|
||||
for (const index of this.markedForDeletionInboundPattern) {
|
||||
if (index >= 0 && index < patternsArray.length) {
|
||||
const patternGroup = patternsArray.at(index) as FormGroup;
|
||||
const patternValue = patternGroup.value;
|
||||
if (patternValue.isNew) {
|
||||
patternsArray.removeAt(index);
|
||||
} else {
|
||||
this.deletedInboundPatterns.push(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a form group for outbound patterns
|
||||
* @returns The form group for outbound patterns
|
||||
*/
|
||||
private createOutboundPatternFormGroup(): FormGroup {
|
||||
return this.formBuilder.group({
|
||||
pattern: '',
|
||||
patternLabel: this.translateService.instant(this.selectPatternDefaultLabeli18Key),
|
||||
constraint: '',
|
||||
isNew: true,
|
||||
});
|
||||
}
|
||||
|
||||
this.markedForDeletionInboundPattern = [];
|
||||
}
|
||||
/**
|
||||
* Creates a form group for inbound patterns
|
||||
* @returns The form group for inbound patterns
|
||||
*/
|
||||
private createInboundPatternFormGroup(): FormGroup {
|
||||
return this.formBuilder.group({
|
||||
pattern: '',
|
||||
patternLabel: this.translateService.instant(this.selectPatternDefaultLabeli18Key),
|
||||
constraint: '',
|
||||
automatic: false,
|
||||
isNew: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an existing form group for outbound patterns
|
||||
* @returns The initialized form group for outbound patterns
|
||||
*/
|
||||
private initializeOutboundPatternFormGroup(): FormGroup {
|
||||
return this.formBuilder.group({
|
||||
pattern: '',
|
||||
patternLabel: '',
|
||||
constraint: '',
|
||||
});
|
||||
}
|
||||
|
||||
deleteMarkedOutboundPatterns() {
|
||||
this.markedForDeletionOutboundPattern.sort((a, b) => b - a);
|
||||
const patternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
|
||||
|
||||
for (const index of this.markedForDeletionOutboundPattern) {
|
||||
if (index >= 0 && index < patternsArray.length) {
|
||||
const patternGroup = patternsArray.at(index) as FormGroup;
|
||||
const patternValue = patternGroup.value;
|
||||
if (patternValue.isNew) {
|
||||
patternsArray.removeAt(index);
|
||||
} else {
|
||||
|
||||
this.deletedOutboundPatterns.push(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.markedForDeletionOutboundPattern = [];
|
||||
}
|
||||
|
||||
private createReplaceOperation(patchOperations: any[], formControlName: string, path: string): void {
|
||||
if (this.formModel.get(formControlName).dirty) {
|
||||
patchOperations.push({
|
||||
op: 'replace',
|
||||
path,
|
||||
value: this.formModel.get(formControlName).value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private handlePatterns(patchOperations: any[], formArrayName: string): void {
|
||||
const patternsArray = this.formModel.get(formArrayName) as FormArray;
|
||||
|
||||
for (let i = 0; i < patternsArray.length; i++) {
|
||||
const patternGroup = patternsArray.at(i) as FormGroup;
|
||||
const patternValue = patternGroup.value;
|
||||
|
||||
if (patternGroup.dirty) {
|
||||
if (patternValue.isNew) {
|
||||
delete patternValue.isNew;
|
||||
const addOperation = {
|
||||
op: 'add',
|
||||
path: `${formArrayName}/-`,
|
||||
value: patternValue,
|
||||
};
|
||||
patchOperations.push(addOperation);
|
||||
} else {
|
||||
const replaceOperation = {
|
||||
op: 'replace',
|
||||
path: `${formArrayName}[${i}]`,
|
||||
value: patternValue,
|
||||
};
|
||||
patchOperations.push(replaceOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sendBack() {
|
||||
this.router.navigateByUrl('admin/ldn/services');
|
||||
}
|
||||
|
||||
private createOutboundPatternFormGroup(): FormGroup {
|
||||
return this.formBuilder.group({
|
||||
pattern: '',
|
||||
constraint: '',
|
||||
isNew: true,
|
||||
});
|
||||
}
|
||||
|
||||
private createInboundPatternFormGroup(): FormGroup {
|
||||
return this.formBuilder.group({
|
||||
pattern: '',
|
||||
constraint: '',
|
||||
automatic: false,
|
||||
isNew: true
|
||||
});
|
||||
}
|
||||
|
||||
private initializeOutboundPatternFormGroup(): FormGroup {
|
||||
return this.formBuilder.group({
|
||||
pattern: '',
|
||||
constraint: '',
|
||||
});
|
||||
}
|
||||
|
||||
private initializeInboundPatternFormGroup(): FormGroup {
|
||||
return this.formBuilder.group({
|
||||
pattern: '',
|
||||
constraint: '',
|
||||
automatic: '',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Initializes an existing form group for inbound patterns
|
||||
* @returns The initialized form group for inbound patterns
|
||||
*/
|
||||
private initializeInboundPatternFormGroup(): FormGroup {
|
||||
return this.formBuilder.group({
|
||||
pattern: '',
|
||||
patternLabel: '',
|
||||
constraint: '',
|
||||
automatic: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -1,266 +1,386 @@
|
||||
<div class="container">
|
||||
<form (ngSubmit)="onSubmit()" [formGroup]="formModel">
|
||||
<div class="d-flex">
|
||||
<h2 class="flex-grow-1">{{ 'ldn-create-service.title' | translate }}</h2>
|
||||
<form (ngSubmit)="onSubmit()" [formGroup]="formModel">
|
||||
<div class="d-flex mb-5">
|
||||
<h2 class="flex-grow-1">{{ 'ldn-create-service.title' | translate }}</h2>
|
||||
</div>
|
||||
<!-- In the name section -->
|
||||
<div class="mb-5">
|
||||
<label for="name">{{ 'ldn-new-service.form.label.name' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('name').invalid && formModel.get('name').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.name' | translate" class="form-control"
|
||||
formControlName="name"
|
||||
id="name"
|
||||
name="name"
|
||||
type="text">
|
||||
<div *ngIf="formModel.get('name').invalid && formModel.get('name').touched" class="error-text">
|
||||
{{ 'ldn-new-service.form.error.name' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- In the description section -->
|
||||
<div class="mb-5 mt-5 d-flex flex-column">
|
||||
<label for="description">{{ 'ldn-new-service.form.label.description' | translate }}</label>
|
||||
<textarea [placeholder]="'ldn-new-service.form.placeholder.description' | translate"
|
||||
class="form-control" formControlName="description" id="description" name="description"></textarea>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- In the url section -->
|
||||
<div class="mb-5 mt-5">
|
||||
<label for="url">{{ 'ldn-new-service.form.label.url' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('url').invalid && formModel.get('url').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.url' | translate" class="form-control"
|
||||
formControlName="url"
|
||||
id="url"
|
||||
name="url"
|
||||
type="text">
|
||||
<div *ngIf="formModel.get('url').invalid && formModel.get('url').touched" class="error-text">
|
||||
{{ 'ldn-new-service.form.error.url' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- In the ldnUrl section -->
|
||||
<div class="mb-5 mt-5">
|
||||
<label for="ldnUrl">{{ 'ldn-new-service.form.label.ldnUrl' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('ldnUrl').invalid && formModel.get('ldnUrl').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.ldnUrl' | translate" class="form-control"
|
||||
formControlName="ldnUrl"
|
||||
id="ldnUrl"
|
||||
name="ldnUrl"
|
||||
type="text">
|
||||
<div *ngIf="formModel.get('ldnUrl').invalid && formModel.get('ldnUrl').touched" class="error-text">
|
||||
{{ 'ldn-new-service.form.error.ldnurl' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- In the score section -->
|
||||
<div class="mb-2">
|
||||
<label for="score">{{ 'ldn-new-service.form.label.score' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('score').invalid && formModel.get('score').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.score' | translate" formControlName="score"
|
||||
id="score"
|
||||
name="score"
|
||||
class="form-control"
|
||||
type="text">
|
||||
<div *ngIf="formModel.get('score').invalid && formModel.get('score').touched" class="error-text">
|
||||
{{ 'ldn-new-service.form.error.score' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- In the Inbound Patterns Labels section -->
|
||||
<div class="row mb-2 mt-5">
|
||||
<div class="col">
|
||||
<label>{{ 'ldn-new-service.form.label.inboundPattern' | translate }} </label>
|
||||
</div>
|
||||
<ng-container *ngIf="!!(formModel.get('notifyServiceInboundPatterns')['controls'][0]?.value?.pattern)">
|
||||
<div class="col">
|
||||
<label>{{ 'ldn-new-service.form.label.ItemFilter' | translate }}</label>
|
||||
</div>
|
||||
<!-- In the name section -->
|
||||
<div class="mb-2">
|
||||
<label for="name">{{ 'ldn-new-service.form.label.name' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('name').invalid && formModel.get('name').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.name' | translate" formControlName="name" id="name"
|
||||
name="name"
|
||||
type="text">
|
||||
<div class="col-sm-1">
|
||||
<label class="">{{ 'ldn-new-service.form.label.automatic' | translate }}</label>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="col-sm-1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
|
||||
</div>
|
||||
<!-- In the Inbound Patterns section -->
|
||||
<div *ngFor="let patternGroup of formModel.get('notifyServiceInboundPatterns')['controls']; let i = index"
|
||||
formGroupName="notifyServiceInboundPatterns">
|
||||
|
||||
<!-- In the description section -->
|
||||
<div class="mb-2 d-flex flex-column">
|
||||
<label for="description">{{ 'ldn-new-service.form.label.description' | translate }}</label>
|
||||
<textarea [placeholder]="'ldn-new-service.form.placeholder.description' | translate"
|
||||
formControlName="description" id="description" name="description"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
|
||||
</div>
|
||||
|
||||
<!-- In the url section -->
|
||||
<div class="mb-2">
|
||||
<label for="url">{{ 'ldn-new-service.form.label.url' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('url').invalid && formModel.get('url').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.url' | translate" formControlName="url" id="url"
|
||||
name="url"
|
||||
type="text">
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
|
||||
</div>
|
||||
|
||||
<!-- In the ldnUrl section -->
|
||||
<div class="mb-2">
|
||||
<label for="ldnUrl">{{ 'ldn-new-service.form.label.ldnUrl' | translate }}</label>
|
||||
<input [class.invalid-field]="formModel.get('ldnUrl').invalid && formModel.get('ldnUrl').touched"
|
||||
[placeholder]="'ldn-new-service.form.placeholder.ldnUrl' | translate" formControlName="ldnUrl"
|
||||
id="ldnUrl"
|
||||
name="ldnUrl"
|
||||
type="text">
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
|
||||
</div>
|
||||
|
||||
<!-- In the Inbound Patterns section -->
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label>{{ 'ldn-new-service.form.label.inboundPattern' | translate }} </label>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label-box">{{ 'ldn-new-service.form.label.ItemFilter' | translate }}</label>
|
||||
</div>
|
||||
<div class="col-sm1 ">
|
||||
<label class="label-box-2">{{ 'ldn-new-service.form.label.automatic' | translate }}</label>
|
||||
</div>
|
||||
<div class="col-sm-1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let patternGroup of formModel.get('notifyServiceInboundPatterns')['controls']; let i = index"
|
||||
formGroupName="notifyServiceInboundPatterns">
|
||||
|
||||
<ng-container [formGroupName]="i">
|
||||
<ng-container [formGroupName]="i">
|
||||
|
||||
|
||||
<div class="row mb-1">
|
||||
<div class="col">
|
||||
<select #inboundPattern formControlName="pattern" id="additionalInboundPattern{{i}}"
|
||||
name="additionalInboundPattern{{i}}" required>
|
||||
<option value="">{{ 'ldn-new-service.form.label.placeholder.inboundPattern' | translate }}</option>
|
||||
<option *ngFor="let pattern of inboundPatterns"
|
||||
[ngValue]="pattern.name">{{ pattern.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row mb-1">
|
||||
<div class="col">
|
||||
<div #inboundPatternDropdown="ngbDropdown" class="w-100" id="additionalInboundPattern{{i}}" ngbDropdown
|
||||
placement="bottom-start">
|
||||
<div class="position-relative right-addon" role="combobox">
|
||||
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
|
||||
ngbDropdownToggle>
|
||||
|
||||
|
||||
<div class="col">
|
||||
<ng-container *ngIf="inboundPattern.value">
|
||||
<select formControlName="constraint" id="constraint{{i}}" name="constraint{{i}}">
|
||||
<option value="">{{ 'ldn-new-service.form.label.placeholder.selectedItemFilter' | translate }}</option>
|
||||
<option *ngFor="let itemFilter of (itemfiltersRD$ | async)?.payload?.page"
|
||||
[value]="itemFilter.id">{{ itemFilter.id }}</option>
|
||||
</select>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div [style.visibility]="inboundPattern.value ? 'visible' : 'hidden'" class="col-sm-1">
|
||||
<input formControlName="automatic" hidden id="automatic{{i}}" name="automatic{{i}}"
|
||||
type="checkbox">
|
||||
<div (click)="toggleAutomatic(i)"
|
||||
[class.checked]="formModel.get('notifyServiceInboundPatterns.' + i + '.automatic').value"
|
||||
class="toggle-switch">
|
||||
<div class="slider"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<button (click)="removeInboundPattern(i)" class="btn btn-outline-dark trash-button">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</i>
|
||||
<input
|
||||
(click)="inboundPatternDropdown.open();"
|
||||
[readonly]="true"
|
||||
[value]="selectedInboundPatterns"
|
||||
class="form-control w-100 scrollable-dropdown-input"
|
||||
formControlName="patternLabel"
|
||||
id="inboundPatternDropdownButton"
|
||||
ngbDropdownAnchor
|
||||
type="text"
|
||||
/>
|
||||
<div aria-labelledby="inboundPatternDropdownButton"
|
||||
class="dropdown-menu scrollable-dropdown-menu w-100 "
|
||||
ngbDropdownMenu>
|
||||
<div class="scrollable-menu" role="listbox">
|
||||
<button (click)="selectInboundPattern(pattern, i); $event.stopPropagation()"
|
||||
*ngFor="let pattern of inboundPatterns"
|
||||
[title]="'ldn-service.form.pattern.' + pattern + '.description' | translate"
|
||||
class="dropdown-item collection-item text-truncate w-100"
|
||||
ngbDropdownItem
|
||||
type="button">
|
||||
<div>{{ 'ldn-service.form.pattern.' + pattern + '.label' | translate }}</div>
|
||||
<div
|
||||
class="small-text">{{ 'ldn-service.form.pattern.' + pattern + '.description' | translate }}</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<span (click)="addInboundPattern()"
|
||||
class="add-pattern-link mb-2">{{ 'ldn-new-service.form.label.addPattern' | translate }}</span>
|
||||
|
||||
|
||||
<div class="mb-4">
|
||||
|
||||
</div>
|
||||
|
||||
<!-- In the Outbound Patterns section -->
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label>{{ 'ldn-new-service.form.label.outboundPattern' | translate }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label-box">{{ 'ldn-new-service.form.label.ItemFilter' | translate }}</label>
|
||||
</div>
|
||||
<div class="col-sm-1 ">
|
||||
</div>
|
||||
<div class="col-sm-1 ">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let patternGroup of formModel.get('notifyServiceOutboundPatterns')['controls']; let i = index"
|
||||
formGroupName="notifyServiceOutboundPatterns">
|
||||
|
||||
<ng-container [formGroupName]="i">
|
||||
|
||||
<!-- Input elements in a separate row -->
|
||||
<div class="row mb-1">
|
||||
<div class="col">
|
||||
<select #outboundPattern formControlName="pattern" id="additionalOutboundPattern{{i}}"
|
||||
name="additionalOutboundPattern{{i}}"
|
||||
required>
|
||||
<option value="">{{ 'ldn-new-service.form.label.placeholder.outboundPattern' | translate }}</option>
|
||||
<option *ngFor="let pattern of outboundPatterns"
|
||||
[ngValue]="pattern.name">{{ pattern.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<ng-container *ngIf="outboundPattern.value">
|
||||
<select formControlName="constraint" id="constraint{{i}}" name="constraint{{i}}">
|
||||
<option value="">{{ 'ldn-new-service.form.label.placeholder.selectedItemFilter' | translate }}</option>
|
||||
<option *ngFor="let itemFilter of (itemfiltersRD$ | async)?.payload?.page"
|
||||
[value]="itemFilter.id">{{ itemFilter.id }}</option>
|
||||
</select>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div [style.visibility]="'hidden'" class="col-sm1">
|
||||
<input hidden id="automatic{{i}}" name="automatic{{i}}" type="checkbox">
|
||||
<div
|
||||
class="toggle-switch">
|
||||
<div class="slider"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<button (click)="removeOutboundPattern(i)" class="btn btn-outline-dark trash-button">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<div class="col">
|
||||
<ng-container
|
||||
*ngIf="!!(formModel.get('notifyServiceInboundPatterns')['controls'][i].value.pattern)">
|
||||
<div #inboundItemfilterDropdown="ngbDropdown" class="w-100" id="constraint{{i}}" ngbDropdown
|
||||
placement="bottom-start">
|
||||
<div class="position-relative right-addon" role="combobox">
|
||||
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
|
||||
ngbDropdownToggle></i>
|
||||
<input
|
||||
(click)="inboundItemfilterDropdown.open();"
|
||||
[readonly]="true"
|
||||
[value]="selectedInboundItemfilters"
|
||||
class="form-control w-100 scrollable-dropdown-input"
|
||||
formControlName="constraint"
|
||||
id="inboundItemfilterDropdown"
|
||||
ngbDropdownAnchor
|
||||
type="text"
|
||||
/>
|
||||
<div aria-labelledby="inboundItemfilterDropdownButton"
|
||||
class="dropdown-menu scrollable-dropdown-menu w-100 "
|
||||
ngbDropdownMenu>
|
||||
<div class="scrollable-menu" role="listbox">
|
||||
<button (click)="selectInboundItemFilter(constraint.id, i); $event.stopPropagation() "
|
||||
*ngFor="let constraint of (itemfiltersRD$ | async)?.payload?.page; let internalIndex = index"
|
||||
class="dropdown-item collection-item text-truncate w-100"
|
||||
ngbDropdownItem
|
||||
type="button">
|
||||
<div>{{ constraint.id }}</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div
|
||||
[style.visibility]="formModel.get('notifyServiceInboundPatterns')['controls'][i]?.value?.pattern ? 'visible' : 'hidden'"
|
||||
class="col-sm-1">
|
||||
<input formControlName="automatic" hidden id="automatic{{i}}" name="automatic{{i}}"
|
||||
type="checkbox">
|
||||
<div (click)="toggleAutomatic(i)"
|
||||
[class.checked]="formModel.get('notifyServiceInboundPatterns.' + i + '.automatic').value"
|
||||
class="toggle-switch">
|
||||
<div class="slider"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span (click)="addOutboundPattern()"
|
||||
class="add-pattern-link">{{ 'ldn-new-service.form.label.addPattern' | translate }}
|
||||
</span>
|
||||
<div class="mb-4">
|
||||
|
||||
</div>
|
||||
|
||||
<div aria-label="Basic example" class="submission-form-footer mt-1 mb-1 position-sticky" role="group">
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<span><i class="fas fa-save"></i> {{ 'ldn-new-service.form.label.submit' | translate }}</span>
|
||||
<div class="col-sm-1">
|
||||
<button (click)="removeInboundPattern(i)" class="btn btn-outline-dark trash-button">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<div class="d-flex">
|
||||
<button (click)="this.openResetFormModal(this.resetFormModal)" class="btn btn-danger" type="button">
|
||||
<span><i class="fas fa-trash"></i> {{ 'submission.general.discard.submit' | translate }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<span (click)="addInboundPattern()"
|
||||
class="add-pattern-link mb-4">{{ 'ldn-new-service.form.label.addPattern' | translate }}</span>
|
||||
|
||||
|
||||
</form>
|
||||
<!-- In the Outbound Patterns Labels section -->
|
||||
<div class="row mb-1 mt-5">
|
||||
<div class="col">
|
||||
<label>{{ 'ldn-new-service.form.label.outboundPattern' | translate }}</label>
|
||||
</div>
|
||||
<ng-container *ngIf="!!(formModel.get('notifyServiceOutboundPatterns')['controls'][0]?.value?.pattern)">
|
||||
<div class="col">
|
||||
<label class="">{{ 'ldn-new-service.form.label.ItemFilter' | translate }}</label>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="col-sm-2">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- In the Outbound Patterns section -->
|
||||
<div *ngFor="let patternGroup of formModel.get('notifyServiceOutboundPatterns')['controls']; let i = index"
|
||||
formGroupName="notifyServiceOutboundPatterns">
|
||||
|
||||
<ng-container [formGroupName]="i">
|
||||
|
||||
<div class="row mb-1">
|
||||
<div class="col">
|
||||
<div #outboundPatternDropdown="ngbDropdown" class="w-100" id="additionalOutboundPattern{{i}}"
|
||||
ngbDropdown
|
||||
placement="bottom-start">
|
||||
<div class="position-relative right-addon" role="combobox">
|
||||
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
|
||||
ngbDropdownToggle></i>
|
||||
<input
|
||||
(click)="outboundPatternDropdown.open();"
|
||||
[readonly]="true"
|
||||
[value]="selectedOutboundPatterns"
|
||||
class="form-control w-100 scrollable-dropdown-input"
|
||||
formControlName="patternLabel"
|
||||
id="outboundPatternDropdownButton"
|
||||
ngbDropdownAnchor
|
||||
type="text"
|
||||
/>
|
||||
<div aria-labelledby="outboundPatternDropdownButton"
|
||||
class="dropdown-menu scrollable-dropdown-menu w-100 "
|
||||
ngbDropdownMenu>
|
||||
<div class="scrollable-menu" role="listbox">
|
||||
<button (click)="selectOutboundPattern(pattern, i); $event.stopPropagation()"
|
||||
*ngFor="let pattern of outboundPatterns"
|
||||
[title]="'ldn-service.form.pattern.' + pattern + '.description' | translate"
|
||||
class="dropdown-item collection-item text-truncate w-100"
|
||||
ngbDropdownItem
|
||||
type="button">
|
||||
<div>{{ 'ldn-service.form.pattern.' + pattern + '.label' | translate }}</div>
|
||||
<div
|
||||
class="small-text">{{ 'ldn-service.form.pattern.' + pattern + '.description' | translate }}</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<ng-container
|
||||
*ngIf="!!(formModel.get('notifyServiceOutboundPatterns')['controls'][i].value.pattern)">
|
||||
<div #outboundItemfilterDropdown="ngbDropdown" class="w-100" id="constraint{{i}}"
|
||||
ngbDropdown
|
||||
placement="bottom-start">
|
||||
<div class="position-relative right-addon" role="combobox">
|
||||
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
|
||||
ngbDropdownToggle></i>
|
||||
<input
|
||||
(click)="outboundItemfilterDropdown.open();"
|
||||
[readonly]="true"
|
||||
[value]="selectedOutboundItemfilters"
|
||||
class="form-control w-100 scrollable-dropdown-input"
|
||||
formControlName="constraint"
|
||||
id="outboundItemfilterDropdown"
|
||||
ngbDropdownAnchor
|
||||
type="text"
|
||||
/>
|
||||
<div aria-labelledby="outboundItemfilterDropdownButton"
|
||||
class="dropdown-menu scrollable-dropdown-menu w-100 "
|
||||
ngbDropdownMenu>
|
||||
<div class="scrollable-menu" role="listbox">
|
||||
<button (click)="selectOutboundItemFilter(constraint.id, i); $event.stopPropagation()"
|
||||
*ngFor="let constraint of (itemfiltersRD$ | async)?.payload?.page; let internalIndex = index"
|
||||
class="dropdown-item collection-item text-truncate w-100"
|
||||
ngbDropdownItem
|
||||
type="button">
|
||||
<div>{{ constraint.id }}</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div [style.visibility]="'hidden'" class="col-sm-1">
|
||||
<input hidden id="automatic{{i}}" name="automatic{{i}}"
|
||||
type="checkbox">
|
||||
<div
|
||||
class="toggle-switch">
|
||||
<div class="slider"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<button (click)="removeOutboundPattern(i)" class="btn btn-outline-dark trash-button">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
|
||||
<div (click)="addOutboundPattern()"
|
||||
class="add-pattern-link mb-4">{{ 'ldn-new-service.form.label.addPattern' | translate }}
|
||||
</div>
|
||||
|
||||
<div class="submission-form-footer my-1 position-sticky d-flex justify-content-between" role="group">
|
||||
<button (click)="this.openResetFormModal(this.resetFormModal)" class="btn btn-danger" type="button">
|
||||
<span><i class="fas fa-trash"></i> {{ 'submission.general.discard.submit' | translate }}</span>
|
||||
</button>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<span><i class="fas fa-save"></i> {{ 'ldn-new-service.form.label.submit' | translate }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<ng-template #confirmModal>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h4>{{'service.overview.create.modal' | translate }}</h4>
|
||||
</div>
|
||||
<button (click)="closeModal()" aria-label="Close"
|
||||
class="close" type="button">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{ 'service.create.body' | translate }}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button (click)="closeModal()" class="btn btn-danger"
|
||||
id="delete-confirm">{{ 'service.refuse.create' | translate }}
|
||||
</button>
|
||||
<button (click)="createService()"
|
||||
class="btn btn-primary mr-2 custom-btn">{{ 'service.confirm.create' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h4>{{'service.overview.create.modal' | translate }}</h4>
|
||||
</div>
|
||||
<button (click)="closeModal()" aria-label="Close"
|
||||
class="close" type="button">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{ 'service.overview.create.body' | translate }}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button (click)="closeModal()" class="btn btn-danger mr-2 "
|
||||
id="delete-confirm">{{ 'service.refuse.create' | translate }}
|
||||
</button>
|
||||
<button (click)="createService()"
|
||||
class="btn btn-primary">{{ 'service.confirm.create' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #resetFormModal>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h4>{{'service.create.reset-form.modal' | translate }}</h4>
|
||||
</div>
|
||||
<button (click)="closeModal()" aria-label="Close"
|
||||
class="close" type="button">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{ 'service.create.reset-form.body' | translate }}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button (click)="resetFormAndLeave()"
|
||||
class="btn btn-primary mr-2 custom-btn"
|
||||
id="reset-confirm">{{ 'service.overview.reset-form.reset-return' | translate }}
|
||||
</button>
|
||||
<button (click)="closeModal()" class="btn btn-danger"
|
||||
id="reset-delete">{{ 'service.overview.reset-form.reset-confirm' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h4>{{'service.overview.reset-form.modal' | translate }}</h4>
|
||||
</div>
|
||||
<button (click)="closeModal()" aria-label="Close"
|
||||
class="close" type="button">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{ 'service.overview.reset-form.body' | translate }}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button (click)="resetFormAndLeave()"
|
||||
class="btn btn-primary mr-2"
|
||||
id="reset-confirm">{{ 'service.overview.reset-form.reset-return' | translate }}
|
||||
</button>
|
||||
<button (click)="closeModal()" class="btn btn-danger"
|
||||
id="reset-delete">{{ 'service.overview.reset-form.reset-confirm' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
|
||||
|
@@ -1,8 +1,13 @@
|
||||
@import '../../../shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.scss';
|
||||
@import '../../../shared/form/form.component.scss';
|
||||
|
||||
form {
|
||||
max-width: 800px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
@@ -30,7 +35,6 @@ textarea {
|
||||
.add-pattern-link {
|
||||
color: #0048ff;
|
||||
cursor: pointer;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.remove-pattern-link {
|
||||
@@ -39,6 +43,11 @@ textarea {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.small-text {
|
||||
font-size: 0.7em;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.status-checkbox {
|
||||
margin-top: 5px;
|
||||
}
|
||||
@@ -49,6 +58,12 @@ textarea {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
color: red;
|
||||
font-size: 0.8em;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.toggle-switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -99,24 +114,6 @@ textarea {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.label-box {
|
||||
margin-left: 11px;
|
||||
}
|
||||
|
||||
.label-box-2 {
|
||||
margin-left: 14px;
|
||||
}
|
||||
|
||||
.label-box-3 {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
form button.btn.btn-primary[type="submit"] {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: -10px;
|
||||
}
|
||||
|
||||
.submission-form-footer {
|
||||
border-radius: var(--bs-card-border-radius);
|
||||
bottom: 0;
|
||||
|
@@ -1,25 +1,88 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import { LdnServiceFormComponent } from './ldn-service-form.component';
|
||||
import {LdnServiceFormComponent} from './ldn-service-form.component';
|
||||
import {FormBuilder, ReactiveFormsModule} from '@angular/forms';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {NgbDropdownModule, NgbModal, NgbModalModule} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {TranslateModule, TranslateService} from '@ngx-translate/core';
|
||||
import {LdnItemfiltersService} from '../ldn-services-data/ldn-itemfilters-data.service';
|
||||
import {LdnServicesService} from '../ldn-services-data/ldn-services-data.service';
|
||||
import {NotificationsService} from 'src/app/shared/notifications/notifications.service';
|
||||
import {Router} from '@angular/router';
|
||||
import {RouterStub} from 'src/app/shared/testing/router.stub';
|
||||
import {createPaginatedList} from 'src/app/shared/testing/utils.test';
|
||||
import {Itemfilter} from '../ldn-services-model/ldn-service-itemfilters';
|
||||
import {createSuccessfulRemoteDataObject$} from 'src/app/shared/remote-data.utils';
|
||||
import {of} from 'rxjs';
|
||||
import {EventEmitter} from '@angular/core';
|
||||
|
||||
describe('LdnServiceFormComponent', () => {
|
||||
let component: LdnServiceFormComponent;
|
||||
let fixture: ComponentFixture<LdnServiceFormComponent>;
|
||||
let component: LdnServiceFormComponent;
|
||||
let fixture: ComponentFixture<LdnServiceFormComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [LdnServiceFormComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
let ldnServicesService: any;
|
||||
let ldnItemfiltersService: any;
|
||||
let notificationsService: any;
|
||||
|
||||
const itemFiltersRdPL$ = createSuccessfulRemoteDataObject$(createPaginatedList([new Itemfilter()]));
|
||||
|
||||
const translateServiceStub = {
|
||||
get: () => of('translated-text'),
|
||||
instant: () => 'translated-text',
|
||||
onLangChange: new EventEmitter(),
|
||||
onTranslationChange: new EventEmitter(),
|
||||
onDefaultLangChange: new EventEmitter()
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
ldnItemfiltersService = jasmine.createSpyObj('ldnItemfiltersService', {
|
||||
findAll: jasmine.createSpy('findAll'),
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LdnServiceFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
ldnServicesService = jasmine.createSpyObj('ldnServicesService', {
|
||||
create: jasmine.createSpy('create'),
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
notificationsService = jasmine.createSpyObj('notificationsService', {
|
||||
success: jasmine.createSpy('success'),
|
||||
error: jasmine.createSpy('error'),
|
||||
});
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
ReactiveFormsModule,
|
||||
RouterTestingModule,
|
||||
NgbModalModule,
|
||||
TranslateModule.forRoot(),
|
||||
NgbDropdownModule
|
||||
],
|
||||
providers: [
|
||||
{provide: LdnItemfiltersService, useValue: ldnItemfiltersService},
|
||||
{provide: LdnServicesService, useValue: ldnServicesService},
|
||||
{provide: NotificationsService, useValue: notificationsService},
|
||||
{provide: TranslateService, useValue: translateServiceStub},
|
||||
{provide: Router, useValue: new RouterStub()},
|
||||
{
|
||||
provide: NgbModal, useValue: {
|
||||
open: () => {/*comment*/
|
||||
}
|
||||
}
|
||||
},
|
||||
FormBuilder
|
||||
],
|
||||
declarations: [LdnServiceFormComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LdnServiceFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
ldnItemfiltersService.findAll.and.returnValue(itemFiltersRdPL$);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@@ -1,222 +1,369 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnInit,
|
||||
Output,
|
||||
TemplateRef,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { LdnServicesService } from '../ldn-services-data/ldn-services-data.service';
|
||||
import { notifyPatterns } from '../ldn-services-patterns/ldn-service-coar-patterns';
|
||||
import { LDN_SERVICE } from '../ldn-services-model/ldn-service.resource-type';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { LdnService } from '../ldn-services-model/ldn-services.model';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||
import { Itemfilter } from '../ldn-services-model/ldn-service-itemfilters';
|
||||
import { Observable } from 'rxjs';
|
||||
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { LdnItemfiltersService } from '../ldn-services-data/ldn-itemfilters-data.service';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import {ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, TemplateRef, ViewChild} from '@angular/core';
|
||||
import {FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||
import {Router} from '@angular/router';
|
||||
|
||||
import {LdnServicesService} from '../ldn-services-data/ldn-services-data.service';
|
||||
import {notifyPatterns} from '../ldn-services-patterns/ldn-service-coar-patterns';
|
||||
import {LDN_SERVICE} from '../ldn-services-model/ldn-service.resource-type';
|
||||
import {animate, state, style, transition, trigger} from '@angular/animations';
|
||||
import {getFirstCompletedRemoteData} from '../../../core/shared/operators';
|
||||
import {RemoteData} from '../../../core/data/remote-data';
|
||||
import {LdnService} from '../ldn-services-model/ldn-services.model';
|
||||
import {NotificationsService} from '../../../shared/notifications/notifications.service';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
import {PaginatedList} from '../../../core/data/paginated-list.model';
|
||||
import {Itemfilter} from '../ldn-services-model/ldn-service-itemfilters';
|
||||
import {Observable} from 'rxjs';
|
||||
import {FindListOptions} from '../../../core/data/find-list-options.model';
|
||||
import {PaginationComponentOptions} from '../../../shared/pagination/pagination-component-options.model';
|
||||
import {LdnItemfiltersService} from '../ldn-services-data/ldn-itemfilters-data.service';
|
||||
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
/**
|
||||
* Angular component representing the form for creating or editing LDN services.
|
||||
* This component handles the creation, validation, and submission of LDN service data.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-ldn-service-form',
|
||||
templateUrl: './ldn-service-form.component.html',
|
||||
styleUrls: ['./ldn-service-form.component.scss'],
|
||||
animations: [
|
||||
trigger('toggleAnimation', [
|
||||
state('true', style({})),
|
||||
state('false', style({})),
|
||||
transition('true <=> false', animate('300ms ease-in')),
|
||||
]),
|
||||
],
|
||||
selector: 'ds-ldn-service-form',
|
||||
templateUrl: './ldn-service-form.component.html',
|
||||
styleUrls: ['./ldn-service-form.component.scss'],
|
||||
animations: [
|
||||
trigger('toggleAnimation', [
|
||||
state('true', style({})),
|
||||
state('false', style({})),
|
||||
transition('true <=> false', animate('300ms ease-in')),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class LdnServiceFormComponent implements OnInit {
|
||||
formModel: FormGroup;
|
||||
@ViewChild('confirmModal', {static: true}) confirmModal: TemplateRef<any>;
|
||||
@ViewChild('resetFormModal', {static: true}) resetFormModal: TemplateRef<any>;
|
||||
public inboundPatterns: object[] = notifyPatterns;
|
||||
public outboundPatterns: object[] = notifyPatterns;
|
||||
itemfiltersRD$: Observable<RemoteData<PaginatedList<Itemfilter>>>;
|
||||
config: FindListOptions = Object.assign(new FindListOptions(), {
|
||||
elementsPerPage: 20
|
||||
formModel: FormGroup;
|
||||
@ViewChild('confirmModal', {static: true}) confirmModal: TemplateRef<any>;
|
||||
@ViewChild('resetFormModal', {static: true}) resetFormModal: TemplateRef<any>;
|
||||
public inboundPatterns: string[] = notifyPatterns;
|
||||
public outboundPatterns: string[] = notifyPatterns;
|
||||
itemfiltersRD$: Observable<RemoteData<PaginatedList<Itemfilter>>>;
|
||||
config: FindListOptions = Object.assign(new FindListOptions(), {
|
||||
elementsPerPage: 20
|
||||
});
|
||||
pageConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
||||
id: 'po',
|
||||
pageSize: 20
|
||||
});
|
||||
@Input() public name: string;
|
||||
@Input() public description: string;
|
||||
@Input() public url: string;
|
||||
@Input() public score: string;
|
||||
@Input() public ldnUrl: string;
|
||||
@Input() public inboundPattern: string;
|
||||
@Input() public outboundPattern: string;
|
||||
@Input() public constraint: string;
|
||||
@Input() public automatic: boolean;
|
||||
@Input() public headerKey: string;
|
||||
@Output() submitForm: EventEmitter<any> = new EventEmitter();
|
||||
@Output() cancelForm: EventEmitter<any> = new EventEmitter();
|
||||
selectedOutboundPatterns: string[];
|
||||
selectedInboundPatterns: string[];
|
||||
selectedInboundItemfilters: string[];
|
||||
selectedOutboundItemfilters: string[];
|
||||
hasInboundPattern: boolean;
|
||||
hasOutboundPattern: boolean;
|
||||
isScoreValid: boolean;
|
||||
private modalRef: any;
|
||||
private selectPatternDefaultLabeli18Key = 'ldn-service.form.label.placeholder.default-select';
|
||||
|
||||
constructor(
|
||||
private ldnServicesService: LdnServicesService,
|
||||
private ldnItemfiltersService: LdnItemfiltersService,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private notificationsService: NotificationsService,
|
||||
private translateService: TranslateService,
|
||||
private cdRef: ChangeDetectorRef,
|
||||
protected modalService: NgbModal,
|
||||
) {
|
||||
|
||||
this.formModel = this.formBuilder.group({
|
||||
enabled: true,
|
||||
id: [''],
|
||||
name: ['', Validators.required],
|
||||
description: [''],
|
||||
url: ['', Validators.required],
|
||||
score: ['', [Validators.required, Validators.pattern('^0*(\.[0-9]+)?$|^1(\.0+)?$')]],
|
||||
ldnUrl: ['', Validators.required],
|
||||
inboundPattern: [''],
|
||||
outboundPattern: [''],
|
||||
constraintPattern: [''],
|
||||
notifyServiceInboundPatterns: this.formBuilder.array([this.createInboundPatternFormGroup()]),
|
||||
notifyServiceOutboundPatterns: this.formBuilder.array([this.createOutboundPatternFormGroup()]),
|
||||
type: LDN_SERVICE.value,
|
||||
});
|
||||
pageConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
||||
id: 'po',
|
||||
pageSize: 20
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.setItemfilters();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the item filters by fetching and observing the paginated list of item filters.
|
||||
*/
|
||||
setItemfilters() {
|
||||
this.itemfiltersRD$ = this.ldnItemfiltersService.findAll().pipe(
|
||||
getFirstCompletedRemoteData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the form submission by opening the confirmation modal.
|
||||
*/
|
||||
onSubmit() {
|
||||
this.openConfirmModal(this.confirmModal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the confirmation modal.
|
||||
*
|
||||
* @param {any} content - The content of the modal.
|
||||
*/
|
||||
openConfirmModal(content) {
|
||||
this.modalRef = this.modalService.open(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the reset form modal.
|
||||
*
|
||||
* @param {any} content - The content of the modal.
|
||||
*/
|
||||
openResetFormModal(content) {
|
||||
this.modalRef = this.modalService.open(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the creation of an LDN service by retrieving and validating form fields,
|
||||
* and submitting the form data to the LDN services endpoint.
|
||||
*/
|
||||
createService() {
|
||||
this.formModel.get('name').markAsTouched();
|
||||
this.formModel.get('score').markAsTouched();
|
||||
this.formModel.get('url').markAsTouched();
|
||||
this.formModel.get('ldnUrl').markAsTouched();
|
||||
this.formModel.get('notifyServiceInboundPatterns').markAsTouched();
|
||||
this.formModel.get('notifyServiceOutboundPatterns').markAsTouched();
|
||||
|
||||
const name = this.formModel.get('name').value;
|
||||
const url = this.formModel.get('url').value;
|
||||
const score = this.formModel.get('score').value;
|
||||
const ldnUrl = this.formModel.get('ldnUrl').value;
|
||||
|
||||
const hasInboundPattern = this.checkPatterns(this.formModel.get('notifyServiceInboundPatterns') as FormArray);
|
||||
const hasOutboundPattern = this.checkPatterns(this.formModel.get('notifyServiceOutboundPatterns') as FormArray);
|
||||
|
||||
if (!name || !url || !ldnUrl || !score || (!hasInboundPattern && !hasOutboundPattern)) {
|
||||
this.closeModal();
|
||||
return;
|
||||
}
|
||||
|
||||
this.formModel.value.notifyServiceInboundPatterns = this.formModel.value.notifyServiceInboundPatterns.map((pattern: {
|
||||
pattern: string;
|
||||
patternLabel: string
|
||||
}) => {
|
||||
const {patternLabel, ...rest} = pattern;
|
||||
return rest;
|
||||
});
|
||||
@Input() public name: string;
|
||||
@Input() public description: string;
|
||||
@Input() public url: string;
|
||||
@Input() public ldnUrl: string;
|
||||
@Input() public inboundPattern: string;
|
||||
@Input() public outboundPattern: string;
|
||||
@Input() public constraint: string;
|
||||
@Input() public automatic: boolean;
|
||||
@Input() public headerKey: string;
|
||||
@Output() submitForm: EventEmitter<any> = new EventEmitter();
|
||||
@Output() cancelForm: EventEmitter<any> = new EventEmitter();
|
||||
private modalRef: any;
|
||||
|
||||
constructor(
|
||||
private ldnServicesService: LdnServicesService,
|
||||
private ldnItemfiltersService: LdnItemfiltersService,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private notificationsService: NotificationsService,
|
||||
private translateService: TranslateService,
|
||||
private cdRef: ChangeDetectorRef,
|
||||
protected modalService: NgbModal,
|
||||
) {
|
||||
this.formModel.value.notifyServiceOutboundPatterns = this.formModel.value.notifyServiceOutboundPatterns.map((pattern: {
|
||||
pattern: string;
|
||||
patternLabel: string
|
||||
}) => {
|
||||
const {patternLabel, ...rest} = pattern;
|
||||
return rest;
|
||||
});
|
||||
|
||||
this.formModel = this.formBuilder.group({
|
||||
enabled: true,
|
||||
id: [''],
|
||||
name: ['', Validators.required],
|
||||
description: [''],
|
||||
url: ['', Validators.required],
|
||||
ldnUrl: ['', Validators.required],
|
||||
inboundPattern: [''],
|
||||
outboundPattern: [''],
|
||||
constraintPattern: [''],
|
||||
notifyServiceInboundPatterns: this.formBuilder.array([this.createInboundPatternFormGroup()]),
|
||||
notifyServiceOutboundPatterns: this.formBuilder.array([this.createOutboundPatternFormGroup()]),
|
||||
type: LDN_SERVICE.value,
|
||||
});
|
||||
}
|
||||
const values = this.formModel.value;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.setItemfilters();
|
||||
const ldnServiceData = this.ldnServicesService.create(values);
|
||||
|
||||
}
|
||||
ldnServiceData.pipe(
|
||||
getFirstCompletedRemoteData()
|
||||
).subscribe((rd: RemoteData<LdnService>) => {
|
||||
if (rd.hasSucceeded) {
|
||||
this.notificationsService.success(this.translateService.get('ldn-service-notification.created.success.title'),
|
||||
this.translateService.get('ldn-service-notification.created.success.body'));
|
||||
|
||||
setItemfilters() {
|
||||
this.itemfiltersRD$ = this.ldnItemfiltersService.findAll().pipe(
|
||||
getFirstCompletedRemoteData());
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.openConfirmModal(this.confirmModal);
|
||||
}
|
||||
|
||||
openConfirmModal(content) {
|
||||
this.modalRef = this.modalService.open(content);
|
||||
}
|
||||
|
||||
openResetFormModal(content) {
|
||||
this.modalRef = this.modalService.open(content);
|
||||
}
|
||||
|
||||
createService() {
|
||||
this.formModel.get('name').markAsTouched();
|
||||
this.formModel.get('url').markAsTouched();
|
||||
this.formModel.get('ldnUrl').markAsTouched();
|
||||
|
||||
const name = this.formModel.get('name').value;
|
||||
const url = this.formModel.get('url').value;
|
||||
const ldnUrl = this.formModel.get('ldnUrl').value;
|
||||
|
||||
if (!name || !url || !ldnUrl) {
|
||||
this.closeModal();
|
||||
return;
|
||||
}
|
||||
|
||||
const values = this.formModel.value;
|
||||
|
||||
const inboundPatternValue = this.formModel.get('inboundPattern').value;
|
||||
const outboundPatternValue = this.formModel.get('outboundPattern').value;
|
||||
|
||||
if (inboundPatternValue === '') {
|
||||
values.notifyServiceInboundPatterns = [];
|
||||
}
|
||||
if (outboundPatternValue === '') {
|
||||
values.notifyServiceOutboundPatterns = [];
|
||||
}
|
||||
|
||||
const ldnServiceData = this.ldnServicesService.create(values);
|
||||
|
||||
ldnServiceData.pipe(
|
||||
getFirstCompletedRemoteData()
|
||||
).subscribe((rd: RemoteData<LdnService>) => {
|
||||
if (rd.hasSucceeded) {
|
||||
this.notificationsService.success(this.translateService.get('ldn-service-notification.created.success.title'),
|
||||
this.translateService.get('ldn-service-notification.created.success.body'));
|
||||
|
||||
this.sendBack();
|
||||
this.closeModal();
|
||||
} else {
|
||||
this.notificationsService.error(this.translateService.get('notification.created.failure'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
resetFormAndLeave() {
|
||||
this.sendBack();
|
||||
this.closeModal();
|
||||
}
|
||||
} else {
|
||||
this.notificationsService.error(this.translateService.get('ldn-service-notification.created.failure.title'),
|
||||
this.translateService.get('ldn-service-notification.created.failure.body'));
|
||||
this.closeModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
closeModal() {
|
||||
this.modalRef.close();
|
||||
this.cdRef.detectChanges();
|
||||
/**
|
||||
* Checks if at least one pattern in the specified form array has a value.
|
||||
*
|
||||
* @param {FormArray} formArray - The form array containing patterns to check.
|
||||
* @returns {boolean} - True if at least one pattern has a value, otherwise false.
|
||||
*/
|
||||
checkPatterns(formArray: FormArray): boolean {
|
||||
for (let i = 0; i < formArray.length; i++) {
|
||||
const pattern = formArray.at(i).get('pattern').value;
|
||||
if (pattern) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
addInboundPattern() {
|
||||
const notifyServiceInboundPatternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
|
||||
notifyServiceInboundPatternsArray.push(this.createInboundPatternFormGroup());
|
||||
/**
|
||||
* Closes the currently open modal and returns to the services directory..
|
||||
*/
|
||||
resetFormAndLeave() {
|
||||
this.sendBack();
|
||||
this.closeModal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the currently open modal and triggers change detection.
|
||||
*/
|
||||
closeModal() {
|
||||
this.modalRef.close();
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new inbound pattern form group to the notifyServiceInboundPatterns form array.
|
||||
*/
|
||||
addInboundPattern() {
|
||||
const notifyServiceInboundPatternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
|
||||
notifyServiceInboundPatternsArray.push(this.createInboundPatternFormGroup());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the inbound pattern form group at the specified index from the notifyServiceInboundPatterns form array.
|
||||
*
|
||||
* @param {number} index - The index of the inbound pattern form group to remove.
|
||||
* @memberof LdnServiceFormComponent
|
||||
*/
|
||||
removeInboundPattern(index: number) {
|
||||
const notifyServiceInboundPatternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
|
||||
notifyServiceInboundPatternsArray.removeAt(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new outbound pattern form group to the notifyServiceOutboundPatterns form array.
|
||||
*/
|
||||
addOutboundPattern() {
|
||||
const notifyServiceOutboundPatternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
|
||||
notifyServiceOutboundPatternsArray.push(this.createOutboundPatternFormGroup());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the outbound pattern form group at the specified index from the notifyServiceOutboundPatterns form array.
|
||||
*
|
||||
* @param {number} index - The index of the outbound pattern form group to remove.
|
||||
*/
|
||||
removeOutboundPattern(index: number) {
|
||||
const notifyServiceOutboundPatternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
|
||||
notifyServiceOutboundPatternsArray.removeAt(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the value of the 'automatic' control at the specified index in the notifyServiceInboundPatterns form array.
|
||||
*
|
||||
* @param {number} i - The index of the 'automatic' control to toggle.
|
||||
* @memberof LdnServiceFormComponent
|
||||
*/
|
||||
toggleAutomatic(i: number) {
|
||||
const automaticControl = this.formModel.get(`notifyServiceInboundPatterns.${i}.automatic`);
|
||||
if (automaticControl) {
|
||||
automaticControl.setValue(!automaticControl.value);
|
||||
}
|
||||
}
|
||||
|
||||
removeInboundPattern(index: number) {
|
||||
const notifyServiceInboundPatternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
|
||||
notifyServiceInboundPatternsArray.removeAt(index);
|
||||
}
|
||||
/**
|
||||
* Selects an outbound pattern for a specific index in the notifyServiceOutboundPatterns form array.
|
||||
*
|
||||
* @param {string} patternValue - The selected pattern value.
|
||||
* @param {number} index - The index of the outbound pattern in the form array.
|
||||
*/
|
||||
selectOutboundPattern(patternValue: string, index: number): void {
|
||||
const patternArray = (this.formModel.get('notifyServiceOutboundPatterns') as FormArray);
|
||||
patternArray.controls[index].patchValue({pattern: patternValue});
|
||||
patternArray.controls[index].patchValue({patternLabel: this.translateService.instant('ldn-service.form.pattern.' + patternValue + '.label')});
|
||||
|
||||
addOutboundPattern() {
|
||||
const notifyServiceOutboundPatternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
|
||||
notifyServiceOutboundPatternsArray.push(this.createOutboundPatternFormGroup());
|
||||
}
|
||||
}
|
||||
|
||||
removeOutboundPattern(index: number) {
|
||||
const notifyServiceOutboundPatternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
|
||||
notifyServiceOutboundPatternsArray.removeAt(index);
|
||||
}
|
||||
/**
|
||||
* Selects an inbound pattern for a specific index in the form array.
|
||||
*
|
||||
* @param {string} patternValue - The selected pattern value.
|
||||
* @param {number} index - The index of the inbound pattern in the form array.
|
||||
*/
|
||||
selectInboundPattern(patternValue: string, index: number): void {
|
||||
const patternArray = (this.formModel.get('notifyServiceInboundPatterns') as FormArray);
|
||||
patternArray.controls[index].patchValue({pattern: patternValue});
|
||||
patternArray.controls[index].patchValue({patternLabel: this.translateService.instant('ldn-service.form.pattern.' + patternValue + '.label')});
|
||||
|
||||
toggleAutomatic(i: number) {
|
||||
const automaticControl = this.formModel.get(`notifyServiceInboundPatterns.${i}.automatic`);
|
||||
if (automaticControl) {
|
||||
automaticControl.setValue(!automaticControl.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sendBack() {
|
||||
this.router.navigateByUrl('admin/ldn/services');
|
||||
}
|
||||
/**
|
||||
* Selects an inbound item filter for a specific index in the form array.
|
||||
*
|
||||
* @param {string} filterValue - The selected item filter value.
|
||||
* @param {number} index - The index of the inbound item filter in the form array.
|
||||
*/
|
||||
selectInboundItemFilter(filterValue: string, index: number): void {
|
||||
const filterArray = (this.formModel.get('notifyServiceInboundPatterns') as FormArray);
|
||||
filterArray.controls[index].patchValue({constraint: filterValue});
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects an outbound item filter for a specific index in the form array.
|
||||
*
|
||||
* @param {string} filterValue - The selected item filter value.
|
||||
* @param {number} index - The index of the outbound item filter in the form array.
|
||||
*/
|
||||
selectOutboundItemFilter(filterValue: string, index: number) {
|
||||
const filterArray = (this.formModel.get('notifyServiceOutboundPatterns') as FormArray);
|
||||
filterArray.controls[index].patchValue({constraint: filterValue});
|
||||
}
|
||||
|
||||
private createOutboundPatternFormGroup(): FormGroup {
|
||||
return this.formBuilder.group({
|
||||
pattern: [''],
|
||||
constraint: [''],
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Sends the user back to the LDN services list.
|
||||
*/
|
||||
private sendBack() {
|
||||
this.router.navigateByUrl('admin/ldn/services');
|
||||
}
|
||||
|
||||
private createInboundPatternFormGroup(): FormGroup {
|
||||
return this.formBuilder.group({
|
||||
pattern: [''],
|
||||
constraint: [''],
|
||||
automatic: false
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Creates a form group for an outbound pattern in the notifyServiceOutboundPatterns form array.
|
||||
*
|
||||
* @private
|
||||
* @returns {FormGroup} - The created form group.
|
||||
*/
|
||||
private createOutboundPatternFormGroup(): FormGroup {
|
||||
return this.formBuilder.group({
|
||||
pattern: [''],
|
||||
constraint: [''],
|
||||
patternLabel: this.translateService.instant(this.selectPatternDefaultLabeli18Key),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a form group for an inbound pattern in the notifyServiceInboundPatterns form array.
|
||||
*
|
||||
* @private
|
||||
* @returns {FormGroup} - The created form group.
|
||||
*/
|
||||
private createInboundPatternFormGroup(): FormGroup {
|
||||
return this.formBuilder.group({
|
||||
pattern: [''],
|
||||
constraint: [''],
|
||||
automatic: false,
|
||||
patternLabel: this.translateService.instant(this.selectPatternDefaultLabeli18Key),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -1,25 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import { LdnServiceNewComponent } from './ldn-service-new.component';
|
||||
import {LdnServiceNewComponent} from './ldn-service-new.component';
|
||||
|
||||
describe('LdnServiceNewComponent', () => {
|
||||
let component: LdnServiceNewComponent;
|
||||
let fixture: ComponentFixture<LdnServiceNewComponent>;
|
||||
let component: LdnServiceNewComponent;
|
||||
let fixture: ComponentFixture<LdnServiceNewComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [LdnServiceNewComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [LdnServiceNewComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LdnServiceNewComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LdnServiceNewComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@@ -1,27 +1,9 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { LdnService } from "../ldn-services-model/ldn-services.model";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { ProcessDataService } from "../../../core/data/processes/process-data.service";
|
||||
import { LinkService } from "../../../core/cache/builders/link.service";
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-ldn-service-new',
|
||||
templateUrl: './ldn-service-new.component.html',
|
||||
styleUrls: ['./ldn-service-new.component.scss']
|
||||
selector: 'ds-ldn-service-new',
|
||||
templateUrl: './ldn-service-new.component.html',
|
||||
styleUrls: ['./ldn-service-new.component.scss']
|
||||
})
|
||||
export class LdnServiceNewComponent implements OnInit {
|
||||
/**
|
||||
* Emits preselected process if there is one
|
||||
*/
|
||||
ldnService$?: Observable<LdnService>;
|
||||
|
||||
constructor(private route: ActivatedRoute, private processService: ProcessDataService, private linkService: LinkService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* If there's an id parameter, use this the process with this identifier as presets for the form
|
||||
*/
|
||||
ngOnInit() {
|
||||
}
|
||||
export class LdnServiceNewComponent {
|
||||
}
|
||||
|
@@ -1,69 +1,126 @@
|
||||
import { LdnService } from '../ldn-services-model/ldn-services.model';
|
||||
import { LDN_SERVICE } from '../ldn-services-model/ldn-service.resource-type';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||
import { Observable, of } from 'rxjs';
|
||||
// Create a mock data object for a single LDN notify service
|
||||
import {LdnService} from '../ldn-services-model/ldn-services.model';
|
||||
import {LDN_SERVICE} from '../ldn-services-model/ldn-service.resource-type';
|
||||
import {RemoteData} from '../../../core/data/remote-data';
|
||||
import {PaginatedList} from '../../../core/data/paginated-list.model';
|
||||
import {Observable, of} from 'rxjs';
|
||||
import {createSuccessfulRemoteDataObject$} from '../../../shared/remote-data.utils';
|
||||
|
||||
export const mockLdnService: LdnService = {
|
||||
id: 1,
|
||||
name: 'Service Name',
|
||||
description: 'Service Description',
|
||||
url: 'Service URL',
|
||||
ldnUrl: 'Service LDN URL',
|
||||
notifyServiceInboundPatterns: [
|
||||
{
|
||||
pattern: 'patternA',
|
||||
constraint: 'itemFilterA',
|
||||
automatic: false,
|
||||
},
|
||||
{
|
||||
pattern: 'patternB',
|
||||
constraint: 'itemFilterB',
|
||||
automatic: true,
|
||||
},
|
||||
],
|
||||
notifyServiceOutboundPatterns: [
|
||||
{
|
||||
pattern: 'patternC',
|
||||
constraint: 'itemFilterC',
|
||||
},
|
||||
],
|
||||
type: LDN_SERVICE,
|
||||
_links: {
|
||||
self: {
|
||||
href: 'http://localhost/api/ldn/ldnservices/1',
|
||||
},
|
||||
uuid: '1',
|
||||
enabled: false,
|
||||
score: 0,
|
||||
id: 1,
|
||||
name: 'Service Name',
|
||||
description: 'Service Description',
|
||||
url: 'Service URL',
|
||||
ldnUrl: 'Service LDN URL',
|
||||
notifyServiceInboundPatterns: [
|
||||
{
|
||||
pattern: 'patternA',
|
||||
constraint: 'itemFilterA',
|
||||
automatic: 'false',
|
||||
},
|
||||
{
|
||||
pattern: 'patternB',
|
||||
constraint: 'itemFilterB',
|
||||
automatic: 'true',
|
||||
},
|
||||
],
|
||||
notifyServiceOutboundPatterns: [
|
||||
{
|
||||
pattern: 'patternC',
|
||||
constraint: 'itemFilterC',
|
||||
automatic: 'true',
|
||||
},
|
||||
],
|
||||
type: LDN_SERVICE,
|
||||
_links: {
|
||||
self: {
|
||||
href: 'http://localhost/api/ldn/ldnservices/1'
|
||||
},
|
||||
},
|
||||
get self(): string {
|
||||
return '';
|
||||
},
|
||||
};
|
||||
|
||||
export const mockLdnServiceRD$ = createSuccessfulRemoteDataObject$(mockLdnService);
|
||||
|
||||
const mockLdnServices = {
|
||||
payload: {
|
||||
elementsPerPage: 20,
|
||||
totalPages: 1,
|
||||
totalElements: 1,
|
||||
currentPage: 1,
|
||||
first: undefined,
|
||||
prev: undefined,
|
||||
next: undefined,
|
||||
last: undefined,
|
||||
page: [mockLdnService],
|
||||
type: LDN_SERVICE,
|
||||
self: undefined,
|
||||
getPageLength: function () {
|
||||
return this.page.length;
|
||||
},
|
||||
_links: {
|
||||
self: {
|
||||
href: 'http://localhost/api/ldn/ldnservices/1',
|
||||
},
|
||||
page: [],
|
||||
},
|
||||
|
||||
export const mockLdnServices: LdnService[] = [{
|
||||
uuid: '1',
|
||||
enabled: false,
|
||||
score: 0,
|
||||
id: 1,
|
||||
name: 'Service Name',
|
||||
description: 'Service Description',
|
||||
url: 'Service URL',
|
||||
ldnUrl: 'Service LDN URL',
|
||||
notifyServiceInboundPatterns: [
|
||||
{
|
||||
pattern: 'patternA',
|
||||
constraint: 'itemFilterA',
|
||||
automatic: 'false',
|
||||
},
|
||||
hasSucceeded: true,
|
||||
msToLive: 0,
|
||||
};
|
||||
|
||||
|
||||
// Create a mock ldnServicesRD$ observable
|
||||
{
|
||||
pattern: 'patternB',
|
||||
constraint: 'itemFilterB',
|
||||
automatic: 'true',
|
||||
},
|
||||
],
|
||||
notifyServiceOutboundPatterns: [
|
||||
{
|
||||
pattern: 'patternC',
|
||||
constraint: 'itemFilterC',
|
||||
automatic: 'true',
|
||||
},
|
||||
],
|
||||
type: LDN_SERVICE,
|
||||
_links: {
|
||||
self: {
|
||||
href: 'http://localhost/api/ldn/ldnservices/1'
|
||||
},
|
||||
},
|
||||
get self(): string {
|
||||
return '';
|
||||
},
|
||||
}, {
|
||||
uuid: '2',
|
||||
enabled: false,
|
||||
score: 0,
|
||||
id: 2,
|
||||
name: 'Service Name',
|
||||
description: 'Service Description',
|
||||
url: 'Service URL',
|
||||
ldnUrl: 'Service LDN URL',
|
||||
notifyServiceInboundPatterns: [
|
||||
{
|
||||
pattern: 'patternA',
|
||||
constraint: 'itemFilterA',
|
||||
automatic: 'false',
|
||||
},
|
||||
{
|
||||
pattern: 'patternB',
|
||||
constraint: 'itemFilterB',
|
||||
automatic: 'true',
|
||||
},
|
||||
],
|
||||
notifyServiceOutboundPatterns: [
|
||||
{
|
||||
pattern: 'patternC',
|
||||
constraint: 'itemFilterC',
|
||||
automatic: 'true',
|
||||
},
|
||||
],
|
||||
type: LDN_SERVICE,
|
||||
_links: {
|
||||
self: {
|
||||
href: 'http://localhost/api/ldn/ldnservices/1'
|
||||
},
|
||||
},
|
||||
get self(): string {
|
||||
return '';
|
||||
},
|
||||
}
|
||||
];
|
||||
export const mockLdnServicesRD$: Observable<RemoteData<PaginatedList<LdnService>>> = of((mockLdnServices as unknown) as RemoteData<PaginatedList<LdnService>>);
|
||||
|
@@ -1,20 +1,20 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { dataService } from '../../../core/data/base/data-service.decorator';
|
||||
import { LDN_SERVICE_CONSTRAINT_FILTERS } from '../ldn-services-model/ldn-service.resource-type';
|
||||
import { IdentifiableDataService } from '../../../core/data/base/identifiable-data.service';
|
||||
import { FindAllData, FindAllDataImpl } from '../../../core/data/base/find-all-data';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {dataService} from '../../../core/data/base/data-service.decorator';
|
||||
import {LDN_SERVICE_CONSTRAINT_FILTERS} from '../ldn-services-model/ldn-service.resource-type';
|
||||
import {IdentifiableDataService} from '../../../core/data/base/identifiable-data.service';
|
||||
import {FindAllData, FindAllDataImpl} from '../../../core/data/base/find-all-data';
|
||||
|
||||
import { RequestService } from '../../../core/data/request.service';
|
||||
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
||||
import { HALEndpointService } from '../../../core/shared/hal-endpoint.service';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { Itemfilter } from '../ldn-services-model/ldn-service-itemfilters';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||
import {RequestService} from '../../../core/data/request.service';
|
||||
import {RemoteDataBuildService} from '../../../core/cache/builders/remote-data-build.service';
|
||||
import {ObjectCacheService} from '../../../core/cache/object-cache.service';
|
||||
import {HALEndpointService} from '../../../core/shared/hal-endpoint.service';
|
||||
import {NotificationsService} from '../../../shared/notifications/notifications.service';
|
||||
import {FindListOptions} from '../../../core/data/find-list-options.model';
|
||||
import {FollowLinkConfig} from '../../../shared/utils/follow-link-config.model';
|
||||
import {Observable} from 'rxjs';
|
||||
import {RemoteData} from '../../../core/data/remote-data';
|
||||
import {Itemfilter} from '../ldn-services-model/ldn-service-itemfilters';
|
||||
import {PaginatedList} from '../../../core/data/paginated-list.model';
|
||||
|
||||
|
||||
/**
|
||||
@@ -23,25 +23,39 @@ import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||
@Injectable()
|
||||
@dataService(LDN_SERVICE_CONSTRAINT_FILTERS)
|
||||
export class LdnItemfiltersService extends IdentifiableDataService<Itemfilter> implements FindAllData<Itemfilter> {
|
||||
private findAllData: FindAllDataImpl<Itemfilter>;
|
||||
private findAllData: FindAllDataImpl<Itemfilter>;
|
||||
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
) {
|
||||
super('itemfilters', requestService, rdbService, objectCache, halService);
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
) {
|
||||
super('itemfilters', requestService, rdbService, objectCache, halService);
|
||||
|
||||
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||
}
|
||||
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||
}
|
||||
|
||||
getEndpoint() {
|
||||
return this.halService.getEndpoint(this.linkPath);
|
||||
}
|
||||
/**
|
||||
* Gets the endpoint URL for the itemfilters.
|
||||
*
|
||||
* @returns {string} - The endpoint URL.
|
||||
*/
|
||||
getEndpoint() {
|
||||
return this.halService.getEndpoint(this.linkPath);
|
||||
}
|
||||
|
||||
findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<Itemfilter>[]): Observable<RemoteData<PaginatedList<Itemfilter>>> {
|
||||
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
/**
|
||||
* Finds all itemfilters based on the provided options and link configurations.
|
||||
*
|
||||
* @param {FindListOptions} options - The options for finding a list of itemfilters.
|
||||
* @param {boolean} useCachedVersionIfAvailable - Whether to use the cached version if available.
|
||||
* @param {boolean} reRequestOnStale - Whether to re-request the data if it's stale.
|
||||
* @param {...FollowLinkConfig<Itemfilter>[]} linksToFollow - Configurations for following specific links.
|
||||
* @returns {Observable<RemoteData<PaginatedList<Itemfilter>>>} - An observable of remote data containing a paginated list of itemfilters.
|
||||
*/
|
||||
findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<Itemfilter>[]): Observable<RemoteData<PaginatedList<Itemfilter>>> {
|
||||
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
}
|
||||
|
@@ -1,126 +1,208 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { dataService } from '../../../core/data/base/data-service.decorator';
|
||||
import { LDN_SERVICE } from '../ldn-services-model/ldn-service.resource-type';
|
||||
import { IdentifiableDataService } from '../../../core/data/base/identifiable-data.service';
|
||||
import { FindAllData, FindAllDataImpl } from '../../../core/data/base/find-all-data';
|
||||
import { DeleteData, DeleteDataImpl } from '../../../core/data/base/delete-data';
|
||||
import { RequestService } from '../../../core/data/request.service';
|
||||
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
||||
import { HALEndpointService } from '../../../core/shared/hal-endpoint.service';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||
import { NoContent } from '../../../core/shared/NoContent.model';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
import { URLCombiner } from '../../../core/url-combiner/url-combiner';
|
||||
import { MultipartPostRequest } from '../../../core/data/request.models';
|
||||
import { RestRequest } from '../../../core/data/rest-request.model';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {dataService} from '../../../core/data/base/data-service.decorator';
|
||||
import {LDN_SERVICE} from '../ldn-services-model/ldn-service.resource-type';
|
||||
import {IdentifiableDataService} from '../../../core/data/base/identifiable-data.service';
|
||||
import {FindAllData, FindAllDataImpl} from '../../../core/data/base/find-all-data';
|
||||
import {DeleteData, DeleteDataImpl} from '../../../core/data/base/delete-data';
|
||||
import {RequestService} from '../../../core/data/request.service';
|
||||
import {RemoteDataBuildService} from '../../../core/cache/builders/remote-data-build.service';
|
||||
import {ObjectCacheService} from '../../../core/cache/object-cache.service';
|
||||
import {HALEndpointService} from '../../../core/shared/hal-endpoint.service';
|
||||
import {NotificationsService} from '../../../shared/notifications/notifications.service';
|
||||
import {FindListOptions} from '../../../core/data/find-list-options.model';
|
||||
import {FollowLinkConfig} from '../../../shared/utils/follow-link-config.model';
|
||||
import {Observable} from 'rxjs';
|
||||
import {RemoteData} from '../../../core/data/remote-data';
|
||||
import {PaginatedList} from '../../../core/data/paginated-list.model';
|
||||
import {NoContent} from '../../../core/shared/NoContent.model';
|
||||
import {map, take} from 'rxjs/operators';
|
||||
import {URLCombiner} from '../../../core/url-combiner/url-combiner';
|
||||
import {MultipartPostRequest} from '../../../core/data/request.models';
|
||||
import {RestRequest} from '../../../core/data/rest-request.model';
|
||||
|
||||
|
||||
import { LdnService } from '../ldn-services-model/ldn-services.model';
|
||||
import {LdnService} from '../ldn-services-model/ldn-services.model';
|
||||
|
||||
import { PatchData, PatchDataImpl } from '../../../core/data/base/patch-data';
|
||||
import { ChangeAnalyzer } from '../../../core/data/change-analyzer';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { RestRequestMethod } from 'src/app/core/data/rest-request-method';
|
||||
import { CreateData, CreateDataImpl } from '../../../core/data/base/create-data';
|
||||
import { ldnServiceConstrain } from '../ldn-services-model/ldn-service.constrain.model';
|
||||
import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators';
|
||||
import { hasValue } from 'src/app/shared/empty.util';
|
||||
import {PatchData, PatchDataImpl} from '../../../core/data/base/patch-data';
|
||||
import {ChangeAnalyzer} from '../../../core/data/change-analyzer';
|
||||
import {Operation} from 'fast-json-patch';
|
||||
import {RestRequestMethod} from '../../../core/data/rest-request-method';
|
||||
import {CreateData, CreateDataImpl} from '../../../core/data/base/create-data';
|
||||
import {LdnServiceConstrain} from '../ldn-services-model/ldn-service.constrain.model';
|
||||
import {getFirstCompletedRemoteData} from '../../../core/shared/operators';
|
||||
import {hasValue} from '../../../shared/empty.util';
|
||||
import {SearchDataImpl} from '../../../core/data/base/search-data';
|
||||
import {RequestParam} from '../../../core/cache/models/request-param.model';
|
||||
|
||||
/**
|
||||
* A service responsible for fetching/sending data from/to the REST API on the ldnservices endpoint
|
||||
* Injectable service responsible for fetching/sending data from/to the REST API on the ldnservices endpoint.
|
||||
*
|
||||
* @export
|
||||
* @class LdnServicesService
|
||||
* @extends {IdentifiableDataService<LdnService>}
|
||||
* @implements {FindAllData<LdnService>}
|
||||
* @implements {DeleteData<LdnService>}
|
||||
* @implements {PatchData<LdnService>}
|
||||
* @implements {CreateData<LdnService>}
|
||||
*/
|
||||
@Injectable()
|
||||
@dataService(LDN_SERVICE)
|
||||
export class LdnServicesService extends IdentifiableDataService<LdnService> implements FindAllData<LdnService>, DeleteData<LdnService>, PatchData<LdnService>, CreateData<LdnService> {
|
||||
createData: CreateDataImpl<LdnService>;
|
||||
private findAllData: FindAllDataImpl<LdnService>;
|
||||
private deleteData: DeleteDataImpl<LdnService>;
|
||||
private patchData: PatchDataImpl<LdnService>;
|
||||
private comparator: ChangeAnalyzer<LdnService>;
|
||||
createData: CreateDataImpl<LdnService>;
|
||||
private findAllData: FindAllDataImpl<LdnService>;
|
||||
private deleteData: DeleteDataImpl<LdnService>;
|
||||
private patchData: PatchDataImpl<LdnService>;
|
||||
private comparator: ChangeAnalyzer<LdnService>;
|
||||
private searchData: SearchDataImpl<LdnService>;
|
||||
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
) {
|
||||
super('ldnservices', requestService, rdbService, objectCache, halService);
|
||||
private findByPatternEndpoint = 'byInboundPattern';
|
||||
|
||||
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
|
||||
this.patchData = new PatchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.comparator, this.responseMsToLive, this.constructIdEndpoint);
|
||||
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
|
||||
}
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
) {
|
||||
super('ldnservices', requestService, rdbService, objectCache, halService);
|
||||
|
||||
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
|
||||
this.patchData = new PatchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.comparator, this.responseMsToLive, this.constructIdEndpoint);
|
||||
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
|
||||
}
|
||||
|
||||
create(object: LdnService): Observable<RemoteData<LdnService>> {
|
||||
return this.createData.create(object);
|
||||
}
|
||||
/**
|
||||
* Creates an LDN service by sending a POST request to the REST API.
|
||||
*
|
||||
* @param {LdnService} object - The LDN service object to be created.
|
||||
* @returns {Observable<RemoteData<LdnService>>} - Observable containing the result of the creation operation.
|
||||
*/
|
||||
create(object: LdnService): Observable<RemoteData<LdnService>> {
|
||||
return this.createData.create(object);
|
||||
}
|
||||
|
||||
patch(object: LdnService, operations: Operation[]): Observable<RemoteData<LdnService>> {
|
||||
return this.patchData.patch(object, operations);
|
||||
}
|
||||
/**
|
||||
* Updates an LDN service by applying a set of operations through a PATCH request to the REST API.
|
||||
*
|
||||
* @param {LdnService} object - The LDN service object to be updated.
|
||||
* @param {Operation[]} operations - The patch operations to be applied.
|
||||
* @returns {Observable<RemoteData<LdnService>>} - Observable containing the result of the update operation.
|
||||
*/
|
||||
patch(object: LdnService, operations: Operation[]): Observable<RemoteData<LdnService>> {
|
||||
return this.patchData.patch(object, operations);
|
||||
}
|
||||
|
||||
update(object: LdnService): Observable<RemoteData<LdnService>> {
|
||||
return this.patchData.update(object);
|
||||
}
|
||||
/**
|
||||
* Updates an LDN service by sending a PUT request to the REST API.
|
||||
*
|
||||
* @param {LdnService} object - The LDN service object to be updated.
|
||||
* @returns {Observable<RemoteData<LdnService>>} - Observable containing the result of the update operation.
|
||||
*/
|
||||
update(object: LdnService): Observable<RemoteData<LdnService>> {
|
||||
return this.patchData.update(object);
|
||||
}
|
||||
|
||||
commitUpdates(method?: RestRequestMethod): void {
|
||||
return this.patchData.commitUpdates(method);
|
||||
}
|
||||
/**
|
||||
* Commits pending updates by sending a PATCH request to the REST API.
|
||||
*
|
||||
* @param {RestRequestMethod} [method] - The HTTP method to be used for the request.
|
||||
*/
|
||||
commitUpdates(method?: RestRequestMethod): void {
|
||||
return this.patchData.commitUpdates(method);
|
||||
}
|
||||
|
||||
createPatchFromCache(object: LdnService): Observable<Operation[]> {
|
||||
return this.patchData.createPatchFromCache(object);
|
||||
}
|
||||
/**
|
||||
* Creates a patch representing the changes made to the LDN service in the cache.
|
||||
*
|
||||
* @param {LdnService} object - The LDN service object for which to create the patch.
|
||||
* @returns {Observable<Operation[]>} - Observable containing the patch operations.
|
||||
*/
|
||||
createPatchFromCache(object: LdnService): Observable<Operation[]> {
|
||||
return this.patchData.createPatchFromCache(object);
|
||||
}
|
||||
|
||||
findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<LdnService>[]): Observable<RemoteData<PaginatedList<LdnService>>> {
|
||||
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
/**
|
||||
* Retrieves all LDN services from the REST API based on the provided options.
|
||||
*
|
||||
* @param {FindListOptions} [options] - The options to be applied to the request.
|
||||
* @param {boolean} [useCachedVersionIfAvailable] - Flag indicating whether to use cached data if available.
|
||||
* @param {boolean} [reRequestOnStale] - Flag indicating whether to re-request data if it's stale.
|
||||
* @param {...FollowLinkConfig<LdnService>[]} linksToFollow - Optional links to follow during the request.
|
||||
* @returns {Observable<RemoteData<PaginatedList<LdnService>>>} - Observable containing the result of the request.
|
||||
*/
|
||||
findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<LdnService>[]): Observable<RemoteData<PaginatedList<LdnService>>> {
|
||||
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
|
||||
public delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
|
||||
return this.deleteData.delete(objectId, copyVirtualMetadata);
|
||||
}
|
||||
/**
|
||||
* Retrieves LDN services based on the inbound pattern from the REST API.
|
||||
*
|
||||
* @param {string} pattern - The inbound pattern to be used in the search.
|
||||
* @param {FindListOptions} [options] - The options to be applied to the request.
|
||||
* @param {boolean} [useCachedVersionIfAvailable] - Flag indicating whether to use cached data if available.
|
||||
* @param {boolean} [reRequestOnStale] - Flag indicating whether to re-request data if it's stale.
|
||||
* @param {...FollowLinkConfig<LdnService>[]} linksToFollow - Optional links to follow during the request.
|
||||
* @returns {Observable<RemoteData<PaginatedList<LdnService>>>} - Observable containing the result of the request.
|
||||
*/
|
||||
findByInboundPattern(pattern: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<LdnService>[]): Observable<RemoteData<PaginatedList<LdnService>>> {
|
||||
const params = [new RequestParam('pattern', pattern)];
|
||||
const findListOptions = Object.assign(new FindListOptions(), options, {searchParams: params});
|
||||
return this.searchData.searchBy(this.findByPatternEndpoint, findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
|
||||
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
|
||||
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
|
||||
}
|
||||
/**
|
||||
* Deletes an LDN service by sending a DELETE request to the REST API.
|
||||
*
|
||||
* @param {string} objectId - The ID of the LDN service to be deleted.
|
||||
* @param {string[]} [copyVirtualMetadata] - Optional virtual metadata to be copied during the deletion.
|
||||
* @returns {Observable<RemoteData<NoContent>>} - Observable containing the result of the deletion operation.
|
||||
*/
|
||||
public delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
|
||||
return this.deleteData.delete(objectId, copyVirtualMetadata);
|
||||
}
|
||||
|
||||
public invoke(serviceName: string, serviceId: string, parameters: ldnServiceConstrain[], files: File[]): Observable<RemoteData<LdnService>> {
|
||||
const requestId = this.requestService.generateRequestId();
|
||||
this.getBrowseEndpoint().pipe(
|
||||
take(1),
|
||||
map((endpoint: string) => new URLCombiner(endpoint, serviceName, 'processes', serviceId).toString()),
|
||||
map((endpoint: string) => {
|
||||
const body = this.getInvocationFormData(parameters, files);
|
||||
return new MultipartPostRequest(requestId, endpoint, body);
|
||||
})
|
||||
).subscribe((request: RestRequest) => this.requestService.send(request));
|
||||
/**
|
||||
* Deletes an LDN service by its HATEOAS link.
|
||||
*
|
||||
* @param {string} href - The HATEOAS link of the LDN service to be deleted.
|
||||
* @param {string[]} [copyVirtualMetadata] - Optional virtual metadata to be copied during the deletion.
|
||||
* @returns {Observable<RemoteData<NoContent>>} - Observable containing the result of the deletion operation.
|
||||
*/
|
||||
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
|
||||
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
|
||||
}
|
||||
|
||||
return this.rdbService.buildFromRequestUUID<LdnService>(requestId);
|
||||
}
|
||||
public invoke(serviceName: string, serviceId: string, parameters: LdnServiceConstrain[], files: File[]): Observable<RemoteData<LdnService>> {
|
||||
const requestId = this.requestService.generateRequestId();
|
||||
this.getBrowseEndpoint().pipe(
|
||||
take(1),
|
||||
map((endpoint: string) => new URLCombiner(endpoint, serviceName, 'processes', serviceId).toString()),
|
||||
map((endpoint: string) => {
|
||||
const body = this.getInvocationFormData(parameters, files);
|
||||
return new MultipartPostRequest(requestId, endpoint, body);
|
||||
})
|
||||
).subscribe((request: RestRequest) => this.requestService.send(request));
|
||||
|
||||
public ldnServiceWithNameExistsAndCanExecute(scriptName: string): Observable<boolean> {
|
||||
return this.findById(scriptName).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
map((rd: RemoteData<LdnService>) => {
|
||||
return hasValue(rd.payload);
|
||||
}),
|
||||
);
|
||||
}
|
||||
return this.rdbService.buildFromRequestUUID<LdnService>(requestId);
|
||||
}
|
||||
|
||||
private getInvocationFormData(constrain: ldnServiceConstrain[], files: File[]): FormData {
|
||||
const form: FormData = new FormData();
|
||||
form.set('properties', JSON.stringify(constrain));
|
||||
files.forEach((file: File) => {
|
||||
form.append('file', file);
|
||||
});
|
||||
return form;
|
||||
}
|
||||
public ldnServiceWithNameExistsAndCanExecute(scriptName: string): Observable<boolean> {
|
||||
return this.findById(scriptName).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
map((rd: RemoteData<LdnService>) => {
|
||||
return hasValue(rd.payload);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private getInvocationFormData(constrain: LdnServiceConstrain[], files: File[]): FormData {
|
||||
const form: FormData = new FormData();
|
||||
form.set('properties', JSON.stringify(constrain));
|
||||
files.forEach((file: File) => {
|
||||
form.append('file', file);
|
||||
});
|
||||
return form;
|
||||
}
|
||||
}
|
||||
|
@@ -1,83 +1,84 @@
|
||||
<div class="container">
|
||||
<div class="d-flex">
|
||||
<h2 class="flex-grow-1">{{ 'ldn-registered-services.title' | translate }}</h2>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<button class="btn btn-success" routerLink="/admin/ldn/services/new"><i
|
||||
class="fas fa-plus pr-2"></i>{{ 'process.overview.new' | translate }}</button>
|
||||
</div>
|
||||
<ds-pagination *ngIf="(ldnServicesRD$ | async)?.payload?.totalElements > 0"
|
||||
[collectionSize]="(ldnServicesRD$ | async)?.payload?.totalElements"
|
||||
[hideGear]="true"
|
||||
[hidePagerWhenSinglePage]="true"
|
||||
[pageInfoState]="(ldnServicesRD$ | async)?.payload"
|
||||
[paginationOptions]="pageConfig">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{ 'service.overview.table.name' | translate }}</th>
|
||||
<th scope="col">{{ 'service.overview.table.description' | translate }}</th>
|
||||
<th scope="col">{{ 'service.overview.table.status' | translate }}</th>
|
||||
<th scope="col">{{ 'service.overview.table.actions' | translate }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let ldnService of (ldnServicesRD$ | async)?.payload?.page">
|
||||
<td>{{ ldnService.name }}</td>
|
||||
<td>{{ ldnService.description }}</td>
|
||||
<td>
|
||||
<div class="d-flex">
|
||||
<h2 class="flex-grow-1">{{ 'ldn-registered-services.title' | translate }}</h2>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<button class="btn btn-success" routerLink="/admin/ldn/services/new"><i
|
||||
class="fas fa-plus pr-2"></i>{{ 'process.overview.new' | translate }}</button>
|
||||
</div>
|
||||
<ds-pagination *ngIf="(ldnServicesRD$ | async)?.payload?.totalElements > 0"
|
||||
[collectionSize]="(ldnServicesRD$ | async)?.payload?.totalElements"
|
||||
[hideGear]="true"
|
||||
[hidePagerWhenSinglePage]="true"
|
||||
[pageInfoState]="(ldnServicesRD$ | async)?.payload"
|
||||
[paginationOptions]="pageConfig">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{ 'service.overview.table.name' | translate }}</th>
|
||||
<th scope="col">{{ 'service.overview.table.description' | translate }}</th>
|
||||
<th scope="col">{{ 'service.overview.table.status' | translate }}</th>
|
||||
<th scope="col">{{ 'service.overview.table.actions' | translate }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let ldnService of (ldnServicesRD$ | async)?.payload?.page">
|
||||
<td>{{ ldnService.name }}</td>
|
||||
<td>{{ ldnService.description }}</td>
|
||||
<td>
|
||||
<span (click)="toggleStatus(ldnService, ldnServicesService)"
|
||||
[ngClass]="{ 'status-enabled': ldnService.enabled, 'status-disabled': !ldnService.enabled }" [title]="ldnService.enabled ? ('ldn-service.overview.table.clickToDisable' | translate) : ('ldn-service.overview.table.clickToEnable' | translate)"
|
||||
[ngClass]="{ 'status-enabled': ldnService.enabled, 'status-disabled': !ldnService.enabled }"
|
||||
[title]="ldnService.enabled ? ('ldn-service.overview.table.clickToDisable' | translate) : ('ldn-service.overview.table.clickToEnable' | translate)"
|
||||
class="status-indicator">
|
||||
{{ ldnService.enabled ? ('ldn-service.overview.table.enabled' | translate) : ('ldn-service.overview.table.disabled' | translate) }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
<button (click)="selectServiceToDelete(ldnService.id)" class="btn btn-outline-danger">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<button [routerLink]="['/admin/ldn/services/edit/', ldnService.id]"
|
||||
class="btn btn-outline-dark">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</ds-pagination>
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
<button (click)="selectServiceToDelete(ldnService.id)" class="btn btn-outline-danger">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<button [routerLink]="['/admin/ldn/services/edit/', ldnService.id]"
|
||||
class="btn btn-outline-dark">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</ds-pagination>
|
||||
</div>
|
||||
|
||||
<ng-template #deleteModal>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h4>{{'service.overview.delete.header' | translate }}</h4>
|
||||
</div>
|
||||
<button (click)="closeModal()" aria-label="Close"
|
||||
class="close" type="button">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{ 'service.overview.delete.body' | translate }}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button (click)="closeModal()"
|
||||
class="btn btn-primary mr-2">{{ 'service.detail.delete.cancel' | translate }}</button>
|
||||
<button (click)="deleteSelected(this.selectedServiceId.toString(), ldnServicesService)"
|
||||
class="btn btn-danger"
|
||||
id="delete-confirm">{{ 'service.overview.delete' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h4>{{'service.overview.delete.header' | translate }}</h4>
|
||||
</div>
|
||||
<button (click)="closeModal()" aria-label="Close"
|
||||
class="close" type="button">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{ 'service.overview.delete.body' | translate }}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button (click)="closeModal()"
|
||||
class="btn btn-primary mr-2">{{ 'service.detail.delete.cancel' | translate }}</button>
|
||||
<button (click)="deleteSelected(this.selectedServiceId.toString(), ldnServicesService)"
|
||||
class="btn btn-danger"
|
||||
id="delete-confirm">{{ 'service.overview.delete' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
|
@@ -1,25 +1,144 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
|
||||
import {ChangeDetectorRef, EventEmitter} from '@angular/core';
|
||||
import {NotificationsService} from '../../../shared/notifications/notifications.service';
|
||||
import {NotificationsServiceStub} from '../../../shared/testing/notifications-service.stub';
|
||||
import {TranslateModule, TranslateService} from '@ngx-translate/core';
|
||||
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {LdnServicesService} from '../ldn-services-data/ldn-services-data.service';
|
||||
import {PaginationService} from '../../../core/pagination/pagination.service';
|
||||
import {PaginationServiceStub} from '../../../shared/testing/pagination-service.stub';
|
||||
import {of} from 'rxjs';
|
||||
import {LdnService} from '../ldn-services-model/ldn-services.model';
|
||||
import {PaginatedList} from '../../../core/data/paginated-list.model';
|
||||
import {RemoteData} from '../../../core/data/remote-data';
|
||||
import {LdnServicesOverviewComponent} from './ldn-services-directory.component';
|
||||
import {createSuccessfulRemoteDataObject$} from '../../../shared/remote-data.utils';
|
||||
import {createPaginatedList} from '../../../shared/testing/utils.test';
|
||||
|
||||
import { ServicesDirectoryComponent } from './services-directory.component';
|
||||
describe('LdnServicesOverviewComponent', () => {
|
||||
let component: LdnServicesOverviewComponent;
|
||||
let fixture: ComponentFixture<LdnServicesOverviewComponent>;
|
||||
let ldnServicesService;
|
||||
let paginationService;
|
||||
let modalService: NgbModal;
|
||||
let notificationsService: NotificationsService;
|
||||
let translateService: TranslateService;
|
||||
|
||||
describe('ServicesDirectoryComponent', () => {
|
||||
let component: ServicesDirectoryComponent;
|
||||
let fixture: ComponentFixture<ServicesDirectoryComponent>;
|
||||
const translateServiceStub = {
|
||||
get: () => of('translated-text'),
|
||||
onLangChange: new EventEmitter(),
|
||||
onTranslationChange: new EventEmitter(),
|
||||
onDefaultLangChange: new EventEmitter()
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ServicesDirectoryComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
beforeEach(async () => {
|
||||
paginationService = new PaginationServiceStub();
|
||||
ldnServicesService = jasmine.createSpyObj('LdnServicesService', ['findAll', 'delete', 'patch']);
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot()],
|
||||
declarations: [LdnServicesOverviewComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: LdnServicesService,
|
||||
useValue: ldnServicesService
|
||||
},
|
||||
{provide: PaginationService, useValue: paginationService},
|
||||
{
|
||||
provide: NgbModal, useValue: {
|
||||
open: () => { /*comment*/
|
||||
}
|
||||
}
|
||||
},
|
||||
{provide: ChangeDetectorRef, useValue: {}},
|
||||
{provide: NotificationsService, useValue: NotificationsServiceStub},
|
||||
{provide: TranslateService, useValue: translateServiceStub},
|
||||
]
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LdnServicesOverviewComponent);
|
||||
component = fixture.componentInstance;
|
||||
ldnServicesService = TestBed.inject(LdnServicesService);
|
||||
paginationService = TestBed.inject(PaginationService);
|
||||
modalService = TestBed.inject(NgbModal);
|
||||
notificationsService = TestBed.inject(NotificationsService);
|
||||
translateService = TestBed.inject(TranslateService);
|
||||
component.modalRef = jasmine.createSpyObj({close: null});
|
||||
component.isProcessingSub = jasmine.createSpyObj({unsubscribe: null});
|
||||
component.ldnServicesRD$ = of({} as RemoteData<PaginatedList<LdnService>>);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('ngOnInit', () => {
|
||||
it('should call setLdnServices', fakeAsync(() => {
|
||||
spyOn(component, 'setLdnServices').and.callThrough();
|
||||
component.ngOnInit();
|
||||
tick();
|
||||
expect(component.setLdnServices).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should set ldnServicesRD$ with mock data', fakeAsync(() => {
|
||||
spyOn(component, 'setLdnServices').and.callThrough();
|
||||
const testData: LdnService[] = Object.assign([new LdnService()], [
|
||||
{id: 1, name: 'Service 1', description: 'Description 1', enabled: true},
|
||||
{id: 2, name: 'Service 2', description: 'Description 2', enabled: false},
|
||||
{id: 3, name: 'Service 3', description: 'Description 3', enabled: true}]);
|
||||
|
||||
const mockLdnServicesRD = createPaginatedList(testData);
|
||||
component.ldnServicesRD$ = createSuccessfulRemoteDataObject$(mockLdnServicesRD);
|
||||
fixture.detectChanges();
|
||||
|
||||
const tableRows = fixture.debugElement.nativeElement.querySelectorAll('tbody tr');
|
||||
expect(tableRows.length).toBe(testData.length);
|
||||
const firstRowContent = tableRows[0].textContent;
|
||||
expect(firstRowContent).toContain('Service 1');
|
||||
expect(firstRowContent).toContain('Description 1');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('ngOnDestroy', () => {
|
||||
it('should call paginationService.clearPagination and unsubscribe', () => {
|
||||
// spyOn(paginationService, 'clearPagination');
|
||||
// spyOn(component.isProcessingSub, 'unsubscribe');
|
||||
component.ngOnDestroy();
|
||||
expect(paginationService.clearPagination).toHaveBeenCalledWith(component.pageConfig.id);
|
||||
expect(component.isProcessingSub.unsubscribe).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ServicesDirectoryComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
describe('openDeleteModal', () => {
|
||||
it('should open delete modal', () => {
|
||||
spyOn(modalService, 'open');
|
||||
component.openDeleteModal(component.deleteModal);
|
||||
expect(modalService.open).toHaveBeenCalledWith(component.deleteModal);
|
||||
});
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
describe('closeModal', () => {
|
||||
it('should close modal and detect changes', () => {
|
||||
// spyOn(component.modalRef, 'close');
|
||||
spyOn(component.cdRef, 'detectChanges');
|
||||
component.closeModal();
|
||||
expect(component.modalRef.close).toHaveBeenCalled();
|
||||
expect(component.cdRef.detectChanges).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteSelected', () => {
|
||||
it('should delete selected service and update data', fakeAsync(() => {
|
||||
const serviceId = '123';
|
||||
const mockRemoteData = { /* just an empty object to retrieve as as RemoteData<PaginatedList<LdnService>> */};
|
||||
spyOn(component, 'setLdnServices').and.callThrough();
|
||||
const deleteSpy = ldnServicesService.delete.and.returnValue(of(mockRemoteData as RemoteData<PaginatedList<LdnService>>));
|
||||
component.selectedServiceId = serviceId;
|
||||
component.deleteSelected(serviceId, ldnServicesService);
|
||||
tick();
|
||||
expect(deleteSpy).toHaveBeenCalledWith(serviceId);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@@ -7,140 +7,170 @@ import {
|
||||
TemplateRef,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||
import { LdnService } from '../ldn-services-model/ldn-services.model';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { LdnServicesService } from 'src/app/admin/admin-ldn-services/ldn-services-data/ldn-services-data.service';
|
||||
import { PaginationService } from 'src/app/core/pagination/pagination.service';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { hasValue } from '../../../shared/empty.util';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {RemoteData} from '../../../core/data/remote-data';
|
||||
import {PaginatedList} from '../../../core/data/paginated-list.model';
|
||||
import {FindListOptions} from '../../../core/data/find-list-options.model';
|
||||
import {LdnService} from '../ldn-services-model/ldn-services.model';
|
||||
import {PaginationComponentOptions} from '../../../shared/pagination/pagination-component-options.model';
|
||||
import {map, switchMap} from 'rxjs/operators';
|
||||
import {LdnServicesService} from 'src/app/admin/admin-ldn-services/ldn-services-data/ldn-services-data.service';
|
||||
import {PaginationService} from 'src/app/core/pagination/pagination.service';
|
||||
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {hasValue} from '../../../shared/empty.util';
|
||||
import {Operation} from 'fast-json-patch';
|
||||
import {getFirstCompletedRemoteData} from '../../../core/shared/operators';
|
||||
import {NotificationsService} from '../../../shared/notifications/notifications.service';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
|
||||
|
||||
/**
|
||||
* The `LdnServicesOverviewComponent` is a component that provides an overview of LDN (Linked Data Notifications) services.
|
||||
* It displays a paginated list of LDN services, allows users to edit and delete services,
|
||||
* toggle the status of each service directly form the page and allows for creation of new services redirecting the user on the creation/edit form
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-ldn-services-directory',
|
||||
templateUrl: './ldn-services-directory.component.html',
|
||||
styleUrls: ['./ldn-services-directory.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.Default
|
||||
selector: 'ds-ldn-services-directory',
|
||||
templateUrl: './ldn-services-directory.component.html',
|
||||
styleUrls: ['./ldn-services-directory.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.Default
|
||||
})
|
||||
export class LdnServicesOverviewComponent implements OnInit, OnDestroy {
|
||||
|
||||
selectedServiceId: string | number | null = null;
|
||||
servicesData: any[] = [];
|
||||
@ViewChild('deleteModal', {static: true}) deleteModal: TemplateRef<any>;
|
||||
ldnServicesRD$: Observable<RemoteData<PaginatedList<LdnService>>>;
|
||||
config: FindListOptions = Object.assign(new FindListOptions(), {
|
||||
elementsPerPage: 20
|
||||
});
|
||||
pageConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
||||
id: 'po',
|
||||
pageSize: 20
|
||||
});
|
||||
isProcessingSub: Subscription;
|
||||
private modalRef: any;
|
||||
selectedServiceId: string | number | null = null;
|
||||
servicesData: any[] = [];
|
||||
@ViewChild('deleteModal', {static: true}) deleteModal: TemplateRef<any>;
|
||||
ldnServicesRD$: Observable<RemoteData<PaginatedList<LdnService>>>;
|
||||
config: FindListOptions = Object.assign(new FindListOptions(), {
|
||||
elementsPerPage: 20
|
||||
});
|
||||
pageConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
||||
id: 'po',
|
||||
pageSize: 20
|
||||
});
|
||||
isProcessingSub: Subscription;
|
||||
modalRef: any;
|
||||
|
||||
|
||||
constructor(
|
||||
protected ldnServicesService: LdnServicesService,
|
||||
protected paginationService: PaginationService,
|
||||
protected modalService: NgbModal,
|
||||
private cdRef: ChangeDetectorRef,
|
||||
private notificationService: NotificationsService,
|
||||
private translateService: TranslateService,
|
||||
) {
|
||||
constructor(
|
||||
protected ldnServicesService: LdnServicesService,
|
||||
protected paginationService: PaginationService,
|
||||
protected modalService: NgbModal,
|
||||
public cdRef: ChangeDetectorRef,
|
||||
private notificationService: NotificationsService,
|
||||
private translateService: TranslateService,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.setLdnServices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the LDN services by fetching and observing the paginated list of services.
|
||||
*/
|
||||
setLdnServices() {
|
||||
this.ldnServicesRD$ = this.paginationService.getFindListOptions(this.pageConfig.id, this.config).pipe(
|
||||
switchMap((config) => this.ldnServicesService.findAll(config, false, false).pipe(
|
||||
getFirstCompletedRemoteData()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.paginationService.clearPagination(this.pageConfig.id);
|
||||
if (hasValue(this.isProcessingSub)) {
|
||||
this.isProcessingSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.setLdnServices();
|
||||
}
|
||||
/**
|
||||
* Opens the delete confirmation modal.
|
||||
*
|
||||
* @param {any} content - The content of the modal.
|
||||
*/
|
||||
openDeleteModal(content) {
|
||||
this.modalRef = this.modalService.open(content);
|
||||
}
|
||||
|
||||
setLdnServices() {
|
||||
this.ldnServicesRD$ = this.paginationService.getFindListOptions(this.pageConfig.id, this.config).pipe(
|
||||
switchMap((config) => this.ldnServicesService.findAll(config, false, false).pipe(
|
||||
getFirstCompletedRemoteData()
|
||||
))
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Closes the currently open modal and triggers change detection.
|
||||
*/
|
||||
closeModal() {
|
||||
this.modalRef.close();
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.paginationService.clearPagination(this.pageConfig.id);
|
||||
if (hasValue(this.isProcessingSub)) {
|
||||
this.isProcessingSub.unsubscribe();
|
||||
/**
|
||||
* Sets the selected LDN service ID for deletion and opens the delete confirmation modal.
|
||||
*
|
||||
* @param {number} serviceId - The ID of the service to be deleted.
|
||||
*/
|
||||
selectServiceToDelete(serviceId: number) {
|
||||
this.selectedServiceId = serviceId;
|
||||
this.openDeleteModal(this.deleteModal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the selected LDN service.
|
||||
*
|
||||
* @param {string} serviceId - The ID of the service to be deleted.
|
||||
* @param {LdnServicesService} ldnServicesService - The service for managing LDN services.
|
||||
*/
|
||||
deleteSelected(serviceId: string, ldnServicesService: LdnServicesService): void {
|
||||
if (this.selectedServiceId !== null) {
|
||||
ldnServicesService.delete(serviceId).pipe(getFirstCompletedRemoteData()).subscribe((rd: RemoteData<LdnService>) => {
|
||||
if (rd.hasSucceeded) {
|
||||
this.servicesData = this.servicesData.filter(service => service.id !== serviceId);
|
||||
this.ldnServicesRD$ = this.ldnServicesRD$.pipe(
|
||||
map((remoteData: RemoteData<PaginatedList<LdnService>>) => {
|
||||
if (remoteData.hasSucceeded) {
|
||||
remoteData.payload.page = remoteData.payload.page.filter(service => service.id.toString() !== serviceId);
|
||||
}
|
||||
return remoteData;
|
||||
})
|
||||
);
|
||||
this.cdRef.detectChanges();
|
||||
this.closeModal();
|
||||
this.notificationService.success(this.translateService.get('ldn-service-delete.notification.success.title'),
|
||||
this.translateService.get('ldn-service-delete.notification.success.content'));
|
||||
} else {
|
||||
this.notificationService.error(this.translateService.get('ldn-service-delete.notification.error.title'),
|
||||
this.translateService.get('ldn-service-delete.notification.error.content'));
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
openDeleteModal(content) {
|
||||
this.modalRef = this.modalService.open(content);
|
||||
}
|
||||
/**
|
||||
* Toggles the status (enabled/disabled) of an LDN service.
|
||||
*
|
||||
* @param {any} ldnService - The LDN service object.
|
||||
* @param {LdnServicesService} ldnServicesService - The service for managing LDN services.
|
||||
*/
|
||||
toggleStatus(ldnService: any, ldnServicesService: LdnServicesService): void {
|
||||
const newStatus = !ldnService.enabled;
|
||||
const originalStatus = ldnService.enabled;
|
||||
|
||||
closeModal() {
|
||||
this.modalRef.close();
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
const patchOperation: Operation = {
|
||||
op: 'replace',
|
||||
path: '/enabled',
|
||||
value: newStatus,
|
||||
};
|
||||
|
||||
selectServiceToDelete(serviceId: number) {
|
||||
this.selectedServiceId = serviceId;
|
||||
this.openDeleteModal(this.deleteModal);
|
||||
}
|
||||
|
||||
deleteSelected(serviceId: string, ldnServicesService: LdnServicesService): void {
|
||||
if (this.selectedServiceId !== null) {
|
||||
ldnServicesService.delete(serviceId).pipe(getFirstCompletedRemoteData()).subscribe((rd: RemoteData<LdnService>) => {
|
||||
if (rd.hasSucceeded) {
|
||||
this.servicesData = this.servicesData.filter(service => service.id !== serviceId);
|
||||
this.ldnServicesRD$ = this.ldnServicesRD$.pipe(
|
||||
map((remoteData: RemoteData<PaginatedList<LdnService>>) => {
|
||||
if (remoteData.hasSucceeded) {
|
||||
remoteData.payload.page = remoteData.payload.page.filter(service => service.id.toString() !== serviceId);
|
||||
}
|
||||
return remoteData;
|
||||
})
|
||||
);
|
||||
this.cdRef.detectChanges();
|
||||
this.closeModal();
|
||||
this.notificationService.success(this.translateService.get('ldn-service-delete.notification.success.title'),
|
||||
this.translateService.get('ldn-service-delete.notification.success.content'));
|
||||
} else {
|
||||
this.notificationService.error(this.translateService.get('ldn-service-delete.notification.error.title'),
|
||||
this.translateService.get('ldn-service-delete.notification.error.content'));
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
});
|
||||
ldnServicesService.patch(ldnService, [patchOperation]).pipe(getFirstCompletedRemoteData()).subscribe(
|
||||
(rd: RemoteData<LdnService>) => {
|
||||
if (rd.hasSucceeded) {
|
||||
ldnService.enabled = newStatus;
|
||||
this.notificationService.success(this.translateService.get('ldn-enable-service.notification.success.title'),
|
||||
this.translateService.get('ldn-enable-service.notification.success.content'));
|
||||
} else {
|
||||
ldnService.enabled = originalStatus;
|
||||
this.notificationService.error(this.translateService.get('ldn-enable-service.notification.error.title'),
|
||||
this.translateService.get('ldn-enable-service.notification.error.content'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
toggleStatus(ldnService: any, ldnServicesService: LdnServicesService): void {
|
||||
const newStatus = !ldnService.enabled;
|
||||
const originalStatus = ldnService.enabled;
|
||||
|
||||
const patchOperation: Operation = {
|
||||
op: 'replace',
|
||||
path: '/enabled',
|
||||
value: newStatus,
|
||||
};
|
||||
|
||||
ldnServicesService.patch(ldnService, [patchOperation]).pipe(getFirstCompletedRemoteData()).subscribe(
|
||||
(rd: RemoteData<LdnService>) => {
|
||||
if (rd.hasSucceeded) {
|
||||
ldnService.enabled = newStatus;
|
||||
this.notificationService.success(this.translateService.get('ldn-enable-service.notification.success.title'),
|
||||
this.translateService.get('ldn-enable-service.notification.success.content'));
|
||||
} else {
|
||||
ldnService.enabled = originalStatus;
|
||||
this.notificationService.error(this.translateService.get('ldn-enable-service.notification.error.title'),
|
||||
this.translateService.get('ldn-enable-service.notification.error.content'));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,31 +1,31 @@
|
||||
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
|
||||
import { LDN_SERVICE_CONSTRAINT_FILTER } from './ldn-service.resource-type';
|
||||
import { CacheableObject } from '../../../core/cache/cacheable-object.model';
|
||||
import { typedObject } from '../../../core/cache/builders/build-decorators';
|
||||
import { excludeFromEquals } from '../../../core/utilities/equals.decorators';
|
||||
import { ResourceType } from '../../../core/shared/resource-type';
|
||||
import {autoserialize, deserialize, inheritSerialization} from 'cerialize';
|
||||
import {LDN_SERVICE_CONSTRAINT_FILTER} from './ldn-service.resource-type';
|
||||
import {CacheableObject} from '../../../core/cache/cacheable-object.model';
|
||||
import {typedObject} from '../../../core/cache/builders/build-decorators';
|
||||
import {excludeFromEquals} from '../../../core/utilities/equals.decorators';
|
||||
import {ResourceType} from '../../../core/shared/resource-type';
|
||||
|
||||
/** A single filter value and its properties. */
|
||||
@typedObject
|
||||
@inheritSerialization(CacheableObject)
|
||||
export class Itemfilter extends CacheableObject {
|
||||
static type = LDN_SERVICE_CONSTRAINT_FILTER;
|
||||
static type = LDN_SERVICE_CONSTRAINT_FILTER;
|
||||
|
||||
@excludeFromEquals
|
||||
@autoserialize
|
||||
type: ResourceType;
|
||||
@excludeFromEquals
|
||||
@autoserialize
|
||||
type: ResourceType;
|
||||
|
||||
@autoserialize
|
||||
id: string;
|
||||
@autoserialize
|
||||
id: string;
|
||||
|
||||
@deserialize
|
||||
_links: {
|
||||
self: {
|
||||
href: string;
|
||||
};
|
||||
@deserialize
|
||||
_links: {
|
||||
self: {
|
||||
href: string;
|
||||
};
|
||||
};
|
||||
|
||||
get self(): string {
|
||||
return this._links.self.href;
|
||||
}
|
||||
get self(): string {
|
||||
return this._links.self.href;
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import { autoserialize } from 'cerialize';
|
||||
import {autoserialize} from 'cerialize';
|
||||
|
||||
/**
|
||||
* notify service patterns
|
||||
* A single notify service pattern and his properties
|
||||
*/
|
||||
export class NotifyServicePattern {
|
||||
@autoserialize
|
||||
pattern: string;
|
||||
@autoserialize
|
||||
constraint: string;
|
||||
@autoserialize
|
||||
automatic: string;
|
||||
@autoserialize
|
||||
pattern: string;
|
||||
@autoserialize
|
||||
constraint: string;
|
||||
@autoserialize
|
||||
automatic: string;
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* List of services statuses
|
||||
*/
|
||||
export enum LdnServiceStatus {
|
||||
UNKOWN,
|
||||
DISABLED,
|
||||
ENABLED,
|
||||
UNKOWN,
|
||||
DISABLED,
|
||||
ENABLED,
|
||||
}
|
||||
|
@@ -1,3 +1,3 @@
|
||||
export class ldnServiceConstrain {
|
||||
void: any;
|
||||
export class LdnServiceConstrain {
|
||||
void: any;
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@
|
||||
* Needs to be in a separate file to prevent circular
|
||||
* dependencies in webpack.
|
||||
*/
|
||||
import { ResourceType } from '../../../core/shared/resource-type';
|
||||
import {ResourceType} from '../../../core/shared/resource-type';
|
||||
|
||||
export const LDN_SERVICE = new ResourceType('ldnservice');
|
||||
export const LDN_SERVICE_CONSTRAINT_FILTERS = new ResourceType('itemfilters');
|
||||
|
@@ -1,57 +1,60 @@
|
||||
import { ResourceType } from '../../../core/shared/resource-type';
|
||||
import { CacheableObject } from '../../../core/cache/cacheable-object.model';
|
||||
import { autoserialize, deserialize, deserializeAs, inheritSerialization } from 'cerialize';
|
||||
import { LDN_SERVICE } from './ldn-service.resource-type';
|
||||
import { excludeFromEquals } from '../../../core/utilities/equals.decorators';
|
||||
import { typedObject } from '../../../core/cache/builders/build-decorators';
|
||||
import { NotifyServicePattern } from './ldn-service-patterns.model';
|
||||
import {ResourceType} from '../../../core/shared/resource-type';
|
||||
import {CacheableObject} from '../../../core/cache/cacheable-object.model';
|
||||
import {autoserialize, deserialize, deserializeAs, inheritSerialization} from 'cerialize';
|
||||
import {LDN_SERVICE} from './ldn-service.resource-type';
|
||||
import {excludeFromEquals} from '../../../core/utilities/equals.decorators';
|
||||
import {typedObject} from '../../../core/cache/builders/build-decorators';
|
||||
import {NotifyServicePattern} from './ldn-service-patterns.model';
|
||||
|
||||
|
||||
/** An LdnService and its properties. */
|
||||
@typedObject
|
||||
@inheritSerialization(CacheableObject)
|
||||
export class LdnService extends CacheableObject {
|
||||
static type = LDN_SERVICE;
|
||||
static type = LDN_SERVICE;
|
||||
|
||||
@excludeFromEquals
|
||||
@autoserialize
|
||||
type: ResourceType;
|
||||
@excludeFromEquals
|
||||
@autoserialize
|
||||
type: ResourceType;
|
||||
|
||||
@autoserialize
|
||||
id: number;
|
||||
@autoserialize
|
||||
id: number;
|
||||
|
||||
@deserializeAs('id')
|
||||
uuid: string;
|
||||
@deserializeAs('id')
|
||||
uuid: string;
|
||||
|
||||
@autoserialize
|
||||
name: string;
|
||||
@autoserialize
|
||||
name: string;
|
||||
|
||||
@autoserialize
|
||||
description: string;
|
||||
@autoserialize
|
||||
description: string;
|
||||
|
||||
@autoserialize
|
||||
url: string;
|
||||
@autoserialize
|
||||
url: string;
|
||||
|
||||
@autoserialize
|
||||
enabled: boolean;
|
||||
@autoserialize
|
||||
score: number;
|
||||
|
||||
@autoserialize
|
||||
ldnUrl: string;
|
||||
@autoserialize
|
||||
enabled: boolean;
|
||||
|
||||
@autoserialize
|
||||
notifyServiceInboundPatterns?: NotifyServicePattern[];
|
||||
@autoserialize
|
||||
ldnUrl: string;
|
||||
|
||||
@autoserialize
|
||||
notifyServiceOutboundPatterns?: NotifyServicePattern[];
|
||||
@autoserialize
|
||||
notifyServiceInboundPatterns?: NotifyServicePattern[];
|
||||
|
||||
@deserialize
|
||||
_links: {
|
||||
self: {
|
||||
href: string;
|
||||
};
|
||||
@autoserialize
|
||||
notifyServiceOutboundPatterns?: NotifyServicePattern[];
|
||||
|
||||
@deserialize
|
||||
_links: {
|
||||
self: {
|
||||
href: string;
|
||||
};
|
||||
};
|
||||
|
||||
get self(): string {
|
||||
return this._links.self.href;
|
||||
}
|
||||
get self(): string {
|
||||
return this._links.self.href;
|
||||
}
|
||||
}
|
||||
|
@@ -2,9 +2,9 @@
|
||||
* List of parameter types used for scripts
|
||||
*/
|
||||
export enum LdnServiceConstrainType {
|
||||
STRING = 'String',
|
||||
DATE = 'date',
|
||||
BOOLEAN = 'boolean',
|
||||
FILE = 'InputStream',
|
||||
OUTPUT = 'OutputStream'
|
||||
STRING = 'String',
|
||||
DATE = 'date',
|
||||
BOOLEAN = 'boolean',
|
||||
FILE = 'InputStream',
|
||||
OUTPUT = 'OutputStream'
|
||||
}
|
||||
|
@@ -1,69 +1,31 @@
|
||||
export const notifyPatterns = [
|
||||
{
|
||||
name: 'Acknowledge and Accept',
|
||||
description: 'This pattern is used to acknowledge and accept a request (offer). It implies an intention to act on the request.',
|
||||
category: 'Acknowledgements'
|
||||
},
|
||||
{
|
||||
name: 'Acknowledge and Reject',
|
||||
description: 'This pattern is used to acknowledge and reject a request (offer). It signifies no further action regarding the request.',
|
||||
category: 'Acknowledgements'
|
||||
},
|
||||
{
|
||||
name: 'Acknowledge and Tentatively Accept',
|
||||
description: 'This pattern is used to acknowledge and tentatively accept a request (offer). It implies an intention to act, which may change.',
|
||||
category: 'Acknowledgements'
|
||||
},
|
||||
{
|
||||
name: 'Acknowledge and Tentatively Reject',
|
||||
description: 'This pattern is used to acknowledge and tentatively reject a request (offer). It signifies no further action, subject to change.',
|
||||
category: 'Acknowledgements'
|
||||
},
|
||||
{
|
||||
name: 'Announce Endorsement',
|
||||
description: 'This pattern is used to announce the existence of an endorsement, referencing the endorsed resource.',
|
||||
category: 'Announcements'
|
||||
},
|
||||
{
|
||||
name: 'Announce Ingest',
|
||||
description: 'This pattern is used to announce that a resource has been ingested.',
|
||||
category: 'Announcements'
|
||||
},
|
||||
{
|
||||
name: 'Announce Relationship',
|
||||
description: 'This pattern is used to announce a relationship between two resources.',
|
||||
category: 'Announcements'
|
||||
},
|
||||
{
|
||||
name: 'Announce Review',
|
||||
description: 'This pattern is used to announce the existence of a review, referencing the reviewed resource.',
|
||||
category: 'Announcements'
|
||||
},
|
||||
{
|
||||
name: 'Announce Service Result',
|
||||
description: 'This pattern is used to announce the existence of a "service result", referencing the relevant resource.',
|
||||
category: 'Announcements'
|
||||
},
|
||||
{
|
||||
name: 'Request Endorsement',
|
||||
description: 'This pattern is used to request endorsement of a resource owned by the origin system.',
|
||||
category: 'Requests'
|
||||
},
|
||||
{
|
||||
name: 'Request Ingest',
|
||||
description: 'This pattern is used to request that the target system ingest a resource.',
|
||||
category: 'Requests'
|
||||
},
|
||||
{
|
||||
name: 'Request Review',
|
||||
description: 'This pattern is used to request a review of a resource owned by the origin system.',
|
||||
category: 'Requests'
|
||||
},
|
||||
{
|
||||
name: 'Undo Offer',
|
||||
description: 'This pattern is used to undo (retract) an offer previously made.',
|
||||
category: 'Undo'
|
||||
}
|
||||
|
||||
'ack-accept',
|
||||
|
||||
'ack-reject',
|
||||
|
||||
'ack-tentative-accept',
|
||||
|
||||
'ack-tentative-reject',
|
||||
|
||||
'announce-endorsement',
|
||||
|
||||
'announce-ingest',
|
||||
|
||||
'announce-relationship',
|
||||
|
||||
'announce-review',
|
||||
|
||||
'announce-service-result',
|
||||
|
||||
'request-endorsement',
|
||||
|
||||
'request-ingest',
|
||||
|
||||
'request-review',
|
||||
|
||||
'undo-offer',
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
@@ -1,17 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LdnDirectoryService } from './ldn-directory.service';
|
||||
|
||||
describe('LdnDirectoryService', () => {
|
||||
let service: LdnDirectoryService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(LdnDirectoryService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
// @ts-ignore
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -1,29 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import { LdnServicesService } from "../ldn-services-data/ldn-services-data.service";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LdnDirectoryService {
|
||||
private itemFilterEndpoint = 'http://localhost:8080/server/api/config/itemfilters';
|
||||
|
||||
|
||||
constructor(private http: HttpClient,
|
||||
private ldnServicesService: LdnServicesService) {
|
||||
}
|
||||
|
||||
public getItemFilters(): Observable<any> {
|
||||
|
||||
return this.ldnServicesService.findAll().pipe(
|
||||
map((servicesData) => {
|
||||
return servicesData;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,17 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LdnServicesBulkDeleteService } from './ldn-service-bulk-delete.service';
|
||||
|
||||
describe('LdnServiceBulkDeleteService', () => {
|
||||
let service: LdnServicesBulkDeleteService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(LdnServicesBulkDeleteService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
// @ts-ignore
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -49,6 +49,21 @@ import { SourceDataResolver } from './admin-quality-assurance-source-page-compon
|
||||
showBreadcrumbsFluid: false
|
||||
}
|
||||
},
|
||||
{
|
||||
canActivate: [ AuthenticatedGuard ],
|
||||
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId/target/:targetId`,
|
||||
component: AdminQualityAssuranceTopicsPageComponent,
|
||||
pathMatch: 'full',
|
||||
resolve: {
|
||||
breadcrumb: I18nBreadcrumbResolver,
|
||||
openaireQualityAssuranceTopicsParams: AdminQualityAssuranceTopicsPageResolver
|
||||
},
|
||||
data: {
|
||||
title: 'admin.quality-assurance.page.title',
|
||||
breadcrumbKey: 'admin.quality-assurance',
|
||||
showBreadcrumbsFluid: false
|
||||
}
|
||||
},
|
||||
{
|
||||
canActivate: [ AuthenticatedGuard ],
|
||||
path: `${QUALITY_ASSURANCE_EDIT_PATH}`,
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<p [innerHTML]="('coar-notify-support-title.content' | translate)"></p>
|
||||
|
||||
<h2>{{ 'coar-notify-support.ldn-inbox.title' | translate }}</h2>
|
||||
<p [innerHTML]="('coar-notify-support.ldn-inbox.content' | translate).replace('{restApiUrl}', coarRestApiUrl)"></p>
|
||||
<p [innerHTML]="('coar-notify-support.ldn-inbox.content' | translate).replace('{ldnInboxUrl}', (coarRestApiUrl | async)?.join(', '))"></p>
|
||||
|
||||
<h2>{{ 'coar-notify-support.message-moderation.title' | translate }}</h2>
|
||||
<p [innerHTML]="('coar-notify-support.message-moderation.content' | translate)"></p>
|
||||
|
@@ -1,14 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NotifyInfoComponent } from './notify-info.component';
|
||||
import { NotifyInfoService } from './notify-info.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
describe('NotifyInfoComponent', () => {
|
||||
let component: NotifyInfoComponent;
|
||||
let fixture: ComponentFixture<NotifyInfoComponent>;
|
||||
let notifyInfoServiceSpy: any;
|
||||
|
||||
beforeEach(async () => {
|
||||
notifyInfoServiceSpy = jasmine.createSpyObj('NotifyInfoService', ['getCoarLdnLocalInboxUrls']);
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ NotifyInfoComponent ]
|
||||
imports: [TranslateModule.forRoot()],
|
||||
declarations: [ NotifyInfoComponent ],
|
||||
providers: [
|
||||
{ provide: NotifyInfoService, useValue: notifyInfoServiceSpy }
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
@@ -1,26 +1,24 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { NotifyInfoService } from './notify-info.service';
|
||||
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-notify-info',
|
||||
templateUrl: './notify-info.component.html',
|
||||
styleUrls: ['./notify-info.component.scss']
|
||||
selector: 'ds-notify-info',
|
||||
templateUrl: './notify-info.component.html',
|
||||
styleUrls: ['./notify-info.component.scss'],
|
||||
})
|
||||
/**
|
||||
* Component for displaying COAR notification information.
|
||||
*/
|
||||
export class NotifyInfoComponent implements OnInit {
|
||||
coarLdnEnabled: boolean;
|
||||
coarRestApiUrl: string;
|
||||
/**
|
||||
* Observable containing the COAR REST INBOX API URLs.
|
||||
*/
|
||||
coarRestApiUrl: Observable<string[]> = of([]);
|
||||
|
||||
constructor(
|
||||
public notifyInfoService: NotifyInfoService,
|
||||
) {
|
||||
}
|
||||
constructor(private notifyInfoService: NotifyInfoService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.coarRestApiUrl = this.notifyInfoService.getCoarLdnRestApiUrl();
|
||||
|
||||
this.notifyInfoService.isCoarConfigEnabled().subscribe(value => {
|
||||
this.coarLdnEnabled = value;
|
||||
});
|
||||
}
|
||||
ngOnInit() {
|
||||
this.coarRestApiUrl = this.notifyInfoService.getCoarLdnLocalInboxUrls();
|
||||
}
|
||||
}
|
||||
|
@@ -1,16 +1,49 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NotifyInfoGuard } from './notify-info.guard';
|
||||
import { Router } from '@angular/router';
|
||||
import { NotifyInfoService } from './notify-info.service';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
describe('NotifyInfoGuard', () => {
|
||||
let guard: NotifyInfoGuard;
|
||||
let notifyInfoServiceSpy: any;
|
||||
let router: any;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
notifyInfoServiceSpy = jasmine.createSpyObj('NotifyInfoService', ['isCoarConfigEnabled']);
|
||||
router = jasmine.createSpyObj('Router', ['parseUrl']);
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
NotifyInfoGuard,
|
||||
{ provide: NotifyInfoService, useValue: notifyInfoServiceSpy},
|
||||
{ provide: Router, useValue: router}
|
||||
]
|
||||
});
|
||||
guard = TestBed.inject(NotifyInfoGuard);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(guard).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return true if COAR config is enabled', (done) => {
|
||||
notifyInfoServiceSpy.isCoarConfigEnabled.and.returnValue(of(true));
|
||||
|
||||
guard.canActivate(null, null).subscribe((result) => {
|
||||
expect(result).toBe(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call parseUrl method of Router if COAR config is not enabled', (done) => {
|
||||
notifyInfoServiceSpy.isCoarConfigEnabled.and.returnValue(of(false));
|
||||
router.parseUrl.and.returnValue(of('/404'));
|
||||
|
||||
guard.canActivate(null, null).subscribe(() => {
|
||||
expect(router.parseUrl).toHaveBeenCalledWith('/404');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -16,7 +16,7 @@ export class NotifyInfoGuard implements CanActivate {
|
||||
canActivate(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot
|
||||
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
||||
): Observable<boolean | UrlTree> {
|
||||
return this.notifyInfoService.isCoarConfigEnabled().pipe(
|
||||
map(coarLdnEnabled => {
|
||||
if (coarLdnEnabled) {
|
||||
|
@@ -1,16 +1,50 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NotifyInfoService } from './notify-info.service';
|
||||
import { ConfigurationDataService } from '../../data/configuration-data.service';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
describe('NotifyInfoService', () => {
|
||||
let service: NotifyInfoService;
|
||||
let configurationDataService: any;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
configurationDataService = {
|
||||
findByPropertyName: jasmine.createSpy('findByPropertyName').and.returnValue(of({})),
|
||||
};
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
NotifyInfoService,
|
||||
{ provide: ConfigurationDataService, useValue: configurationDataService },
|
||||
]
|
||||
});
|
||||
service = TestBed.inject(NotifyInfoService);
|
||||
configurationDataService = TestBed.inject(ConfigurationDataService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should retrieve and map coar configuration', () => {
|
||||
const mockResponse = { payload: { values: ['true'] } };
|
||||
(configurationDataService.findByPropertyName as jasmine.Spy).and.returnValue(of(mockResponse));
|
||||
|
||||
service.isCoarConfigEnabled().subscribe((result) => {
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should retrieve and map LDN local inbox URLs', () => {
|
||||
const mockResponse = { values: ['inbox1', 'inbox2'] };
|
||||
(configurationDataService.findByPropertyName as jasmine.Spy).and.returnValue(of(mockResponse));
|
||||
|
||||
service.getCoarLdnLocalInboxUrls().subscribe((result) => {
|
||||
expect(result).toEqual(['inbox1', 'inbox2']);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the inbox relation link', () => {
|
||||
expect(service.getInboxRelationLink()).toBe('http://www.w3.org/ns/ldp#inbox');
|
||||
});
|
||||
});
|
||||
|
@@ -1,14 +1,19 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { getFirstSucceededRemoteData } from '../../shared/operators';
|
||||
import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../../shared/operators';
|
||||
import { ConfigurationDataService } from '../../data/configuration-data.service';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import { DefaultAppConfig } from '../../../../config/default-app-config';
|
||||
import { ConfigurationProperty } from '../../shared/configuration-property.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class NotifyInfoService {
|
||||
|
||||
/**
|
||||
* The relation link for the inbox
|
||||
*/
|
||||
private _inboxRelationLink = 'http://www.w3.org/ns/ldp#inbox';
|
||||
|
||||
constructor(
|
||||
private configService: ConfigurationDataService,
|
||||
) {}
|
||||
@@ -24,15 +29,25 @@ export class NotifyInfoService {
|
||||
);
|
||||
}
|
||||
|
||||
getCoarLdnRestApiUrl(): string {
|
||||
const appConfig = new DefaultAppConfig();
|
||||
const restConfig = appConfig.rest;
|
||||
/**
|
||||
* Get the url of the local inbox from the REST configuration
|
||||
* @returns the url of the local inbox
|
||||
*/
|
||||
getCoarLdnLocalInboxUrls(): Observable<string[]> {
|
||||
return this.configService.findByPropertyName('ldn.notify.inbox').pipe(
|
||||
getFirstSucceededRemoteData(),
|
||||
getRemoteDataPayload(),
|
||||
map((response: ConfigurationProperty) => {
|
||||
return response.values;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const ssl = restConfig.ssl;
|
||||
const host = restConfig.host;
|
||||
const port = restConfig.port;
|
||||
const namespace = restConfig.nameSpace;
|
||||
|
||||
return `${ssl ? 'https' : 'http'}://${host}:${port}${namespace}`;
|
||||
/**
|
||||
* Method to get the relation link for the inbox
|
||||
* @returns the relation link for the inbox
|
||||
*/
|
||||
getInboxRelationLink(): string {
|
||||
return this._inboxRelationLink;
|
||||
}
|
||||
}
|
||||
|
@@ -190,7 +190,11 @@ import { SuggestionSource } from './suggestion-notifications/reciter-suggestions
|
||||
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 { LdnItemfiltersService } from '../admin/admin-ldn-services/ldn-services-data/ldn-itemfilters-data.service';
|
||||
import { Itemfilter } from "../admin/admin-ldn-services/ldn-services-model/ldn-service-itemfilters";
|
||||
import { Itemfilter } from '../admin/admin-ldn-services/ldn-services-model/ldn-service-itemfilters';
|
||||
import {
|
||||
CoarNotifyConfigDataService
|
||||
} from '../submission/sections/section-coar-notify/coar-notify-config-data.service';
|
||||
import { SubmissionCoarNotifyConfig } from '../submission/sections/section-coar-notify/submission-coar-notify.config';
|
||||
|
||||
/**
|
||||
* When not in production, endpoint responses can be mocked for testing purposes
|
||||
@@ -315,7 +319,8 @@ const PROVIDERS = [
|
||||
OrcidHistoryDataService,
|
||||
SupervisionOrderDataService,
|
||||
LdnServicesService,
|
||||
LdnItemfiltersService
|
||||
LdnItemfiltersService,
|
||||
CoarNotifyConfigDataService
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -398,7 +403,8 @@ export const models =
|
||||
SuggestionTarget,
|
||||
SuggestionSource,
|
||||
LdnService,
|
||||
Itemfilter
|
||||
Itemfilter,
|
||||
SubmissionCoarNotifyConfig
|
||||
|
||||
];
|
||||
|
||||
|
@@ -25,7 +25,6 @@ import { RequestParam } from '../cache/models/request-param.model';
|
||||
import { ObjectCacheEntry } from '../cache/object-cache.reducer';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { DSpaceSerializer } from '../dspace-rest/dspace.serializer';
|
||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { getFirstCompletedRemoteData, getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators';
|
||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||
|
@@ -26,3 +26,5 @@ export type WorkspaceitemSectionDataType
|
||||
| WorkspaceitemSectionSherpaPoliciesObject
|
||||
| WorkspaceitemSectionIdentifiersObject
|
||||
| string;
|
||||
|
||||
|
||||
|
@@ -19,7 +19,6 @@ import { Item } from '../shared/item.model';
|
||||
import { WorkspaceItem } from './models/workspaceitem.model';
|
||||
import { RequestEntry } from '../data/request-entry.model';
|
||||
import { CoreState } from '../core-state.model';
|
||||
import { testSearchDataImplementation } from '../data/base/search-data.spec';
|
||||
import { testDeleteDataImplementation } from '../data/base/delete-data.spec';
|
||||
|
||||
describe('WorkspaceitemDataService test', () => {
|
||||
@@ -84,17 +83,19 @@ describe('WorkspaceitemDataService test', () => {
|
||||
function initTestService() {
|
||||
hrefOnlyDataService = getMockHrefOnlyDataService();
|
||||
return new WorkspaceitemDataService(
|
||||
comparator,
|
||||
halService,
|
||||
http,
|
||||
notificationsService,
|
||||
requestService,
|
||||
rdbService,
|
||||
objectCache,
|
||||
halService,
|
||||
notificationsService,
|
||||
store,
|
||||
);
|
||||
}
|
||||
|
||||
describe('composition', () => {
|
||||
const initService = () => new WorkspaceitemDataService(null, null, null, null, null);
|
||||
testSearchDataImplementation(initService);
|
||||
const initService = () => new WorkspaceitemDataService(null, null, null, null, null, null, null, null);
|
||||
testDeleteDataImplementation(initService);
|
||||
});
|
||||
|
||||
@@ -126,7 +127,7 @@ describe('WorkspaceitemDataService test', () => {
|
||||
service = initTestService();
|
||||
|
||||
spyOn((service as any), 'findByHref').and.callThrough();
|
||||
spyOn((service as any), 'getSearchByHref').and.returnValue(searchRequestURL$);
|
||||
spyOn((service as any), 'getIDHref').and.callThrough();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -138,7 +139,7 @@ describe('WorkspaceitemDataService test', () => {
|
||||
scheduler.schedule(() => service.findByItem('1234-1234', true, true, pageInfo));
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).findByHref).toHaveBeenCalledWith(searchRequestURL$, true, true);
|
||||
expect((service as any).findByHref).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return a RemoteData<WorkspaceItem> for the search', () => {
|
||||
|
@@ -50,6 +50,20 @@ export class WorkspaceitemDataService extends IdentifiableDataService<WorkspaceI
|
||||
public delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
|
||||
return this.deleteData.delete(objectId, copyVirtualMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an existing object on the server
|
||||
* @param href The self link of the object to be removed
|
||||
* @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual
|
||||
* metadata should be saved as real metadata
|
||||
* @return A RemoteData observable with an empty payload, but still representing the state of the request: statusCode,
|
||||
* errorMessage, timeCompleted, etc
|
||||
* Only emits once all request related to the DSO has been invalidated.
|
||||
*/
|
||||
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
|
||||
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the WorkspaceItem object found through the UUID of an item
|
||||
*
|
||||
|
@@ -84,6 +84,16 @@ export class QualityAssuranceEventDataService extends IdentifiableDataService<Qu
|
||||
return this.searchData.searchBy('findByTopic', options, true, true, ...linksToFollow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Service for retrieving Quality Assurance events by topic and target.
|
||||
* @param options (Optional) The search options to use when retrieving the events.
|
||||
* @param linksToFollow (Optional) The links to follow when retrieving the events.
|
||||
* @returns An observable of the remote data containing the paginated list of Quality Assurance events.
|
||||
*/
|
||||
public searchEventsByTopic(options: FindListOptions = {}, ...linksToFollow: FollowLinkConfig<QualityAssuranceEventObject>[]): Observable<RemoteData<PaginatedList<QualityAssuranceEventObject>>> {
|
||||
return this.searchData.searchBy('findByTopic', options, true, true, ...linksToFollow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear findByTopic requests from cache
|
||||
*/
|
||||
|
@@ -16,6 +16,7 @@ import { PaginatedList } from '../../../data/paginated-list.model';
|
||||
import { FindListOptions } from '../../../data/find-list-options.model';
|
||||
import { IdentifiableDataService } from '../../../data/base/identifiable-data.service';
|
||||
import { FindAllData, FindAllDataImpl } from '../../../data/base/find-all-data';
|
||||
import { SearchData, SearchDataImpl } from 'src/app/core/data/base/search-data';
|
||||
|
||||
/**
|
||||
* The service handling all Quality Assurance source REST requests.
|
||||
@@ -25,6 +26,9 @@ import { FindAllData, FindAllDataImpl } from '../../../data/base/find-all-data';
|
||||
export class QualityAssuranceSourceDataService extends IdentifiableDataService<QualityAssuranceSourceObject> {
|
||||
|
||||
private findAllData: FindAllData<QualityAssuranceSourceObject>;
|
||||
private searchAllData: SearchData<QualityAssuranceSourceObject>;
|
||||
|
||||
private searchByTargetMethod = 'byTarget';
|
||||
|
||||
/**
|
||||
* Initialize service variables
|
||||
@@ -43,6 +47,7 @@ export class QualityAssuranceSourceDataService extends IdentifiableDataService<Q
|
||||
) {
|
||||
super('qualityassurancesources', requestService, rdbService, objectCache, halService);
|
||||
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||
this.searchAllData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,4 +89,16 @@ export class QualityAssuranceSourceDataService extends IdentifiableDataService<Q
|
||||
public getSource(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<QualityAssuranceSourceObject>[]): Observable<RemoteData<QualityAssuranceSourceObject>> {
|
||||
return this.findById(id, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a paginated list of QualityAssuranceSourceObject objects that are associated with a given target object.
|
||||
* @param options The options for the search query.
|
||||
* @param useCachedVersionIfAvailable Whether to use a cached version of the data if available.
|
||||
* @param reRequestOnStale Whether to re-request the data if the cached version is stale.
|
||||
* @param linksToFollow The links to follow to retrieve the data.
|
||||
* @returns An observable that emits a RemoteData object containing the paginated list of QualityAssuranceSourceObject objects.
|
||||
*/
|
||||
public getSourcesByTarget(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<QualityAssuranceSourceObject>[]): Observable<RemoteData<PaginatedList<QualityAssuranceSourceObject>>> {
|
||||
return this.searchAllData.searchBy(this.searchByTargetMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
}
|
||||
|
@@ -80,22 +80,53 @@ describe('QualityAssuranceTopicDataService', () => {
|
||||
notificationsService
|
||||
);
|
||||
|
||||
spyOn((service as any).findAllData, 'findAll').and.callThrough();
|
||||
spyOn((service as any).searchData, 'searchBy').and.callThrough();
|
||||
spyOn((service as any), 'findById').and.callThrough();
|
||||
});
|
||||
|
||||
describe('getTopics', () => {
|
||||
it('should call findListByHref', (done) => {
|
||||
service.getTopics().subscribe(
|
||||
(res) => {
|
||||
expect((service as any).findAllData.findAll).toHaveBeenCalledWith({}, true, true);
|
||||
}
|
||||
describe('searchTopicsByTarget', () => {
|
||||
it('should call searchData.searchBy with the correct parameters', () => {
|
||||
const options = { elementsPerPage: 10 };
|
||||
const useCachedVersionIfAvailable = true;
|
||||
const reRequestOnStale = true;
|
||||
|
||||
service.searchTopicsByTarget(options, useCachedVersionIfAvailable, reRequestOnStale);
|
||||
|
||||
expect((service as any).searchData.searchBy).toHaveBeenCalledWith(
|
||||
'byTarget',
|
||||
options,
|
||||
useCachedVersionIfAvailable,
|
||||
reRequestOnStale
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should return a RemoteData<PaginatedList<QualityAssuranceTopicObject>> for the object with the given URL', () => {
|
||||
const result = service.getTopics();
|
||||
const result = service.searchTopicsByTarget();
|
||||
const expected = cold('(a)', {
|
||||
a: paginatedListRD
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('searchTopicsBySource', () => {
|
||||
it('should call searchData.searchBy with the correct parameters', () => {
|
||||
const options = { elementsPerPage: 10 };
|
||||
const useCachedVersionIfAvailable = true;
|
||||
const reRequestOnStale = true;
|
||||
|
||||
service.searchTopicsBySource(options, useCachedVersionIfAvailable, reRequestOnStale);
|
||||
|
||||
expect((service as any).searchData.searchBy).toHaveBeenCalledWith(
|
||||
'bySource',
|
||||
options,
|
||||
useCachedVersionIfAvailable,
|
||||
reRequestOnStale,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return a RemoteData<PaginatedList<QualityAssuranceTopicObject>> for the object with the given URL', () => {
|
||||
const result = service.searchTopicsBySource();
|
||||
const expected = cold('(a)', {
|
||||
a: paginatedListRD
|
||||
});
|
||||
@@ -121,5 +152,4 @@ describe('QualityAssuranceTopicDataService', () => {
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -15,6 +15,7 @@ import { FindListOptions } from '../../../data/find-list-options.model';
|
||||
import { IdentifiableDataService } from '../../../data/base/identifiable-data.service';
|
||||
import { dataService } from '../../../data/base/data-service.decorator';
|
||||
import { QUALITY_ASSURANCE_TOPIC_OBJECT } from '../models/quality-assurance-topic-object.resource-type';
|
||||
import { SearchData, SearchDataImpl } from '../../../../core/data/base/search-data';
|
||||
import { FindAllData, FindAllDataImpl } from '../../../data/base/find-all-data';
|
||||
|
||||
/**
|
||||
@@ -25,6 +26,10 @@ import { FindAllData, FindAllDataImpl } from '../../../data/base/find-all-data';
|
||||
export class QualityAssuranceTopicDataService extends IdentifiableDataService<QualityAssuranceTopicObject> {
|
||||
|
||||
private findAllData: FindAllData<QualityAssuranceTopicObject>;
|
||||
private searchData: SearchData<QualityAssuranceTopicObject>;
|
||||
|
||||
private searchByTargetMethod = 'byTarget';
|
||||
private searchBySourceMethod = 'bySource';
|
||||
|
||||
/**
|
||||
* Initialize service variables
|
||||
@@ -43,23 +48,31 @@ export class QualityAssuranceTopicDataService extends IdentifiableDataService<Qu
|
||||
) {
|
||||
super('qualityassurancetopics', requestService, rdbService, objectCache, halService);
|
||||
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of Quality Assurance topics.
|
||||
*
|
||||
* @param options Find list options object.
|
||||
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
|
||||
* no valid cached version. Defaults to true
|
||||
* @param reRequestOnStale Whether or not the request should automatically be re-
|
||||
* requested after the response becomes stale
|
||||
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved.
|
||||
*
|
||||
* @return Observable<RemoteData<PaginatedList<QualityAssuranceTopicObject>>>
|
||||
* The list of Quality Assurance topics.
|
||||
* Search for Quality Assurance topics.
|
||||
* @param options The search options.
|
||||
* @param useCachedVersionIfAvailable Whether to use cached version if available.
|
||||
* @param reRequestOnStale Whether to re-request on stale.
|
||||
* @param linksToFollow The links to follow.
|
||||
* @returns An observable of remote data containing a paginated list of Quality Assurance topics.
|
||||
*/
|
||||
public getTopics(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<QualityAssuranceTopicObject>[]): Observable<RemoteData<PaginatedList<QualityAssuranceTopicObject>>> {
|
||||
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
public searchTopicsByTarget(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<QualityAssuranceTopicObject>[]): Observable<RemoteData<PaginatedList<QualityAssuranceTopicObject>>> {
|
||||
return this.searchData.searchBy(this.searchByTargetMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for quality assurance topics by source.
|
||||
* @param options The search options.
|
||||
* @param useCachedVersionIfAvailable Whether to use a cached version if available.
|
||||
* @param reRequestOnStale Whether to re-request the data if it's stale.
|
||||
* @param linksToFollow The links to follow.
|
||||
* @returns An observable of the remote data containing the paginated list of quality assurance topics.
|
||||
*/
|
||||
public searchTopicsBySource(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<QualityAssuranceTopicObject>[]): Observable<RemoteData<PaginatedList<QualityAssuranceTopicObject>>> {
|
||||
return this.searchData.searchBy(this.searchBySourceMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,5 +1,5 @@
|
||||
// ... test imports
|
||||
import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { ComponentFixture, fakeAsync, inject, TestBed,waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
|
||||
|
||||
@@ -9,6 +9,7 @@ import { By } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { StoreModule } from '@ngrx/store';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
// Load the implementations that should be tested
|
||||
import { FooterComponent } from './footer.component';
|
||||
@@ -17,15 +18,21 @@ import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock';
|
||||
import { storeModuleConfig } from '../app.reducer';
|
||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||
import { AuthorizationDataServiceStub } from '../shared/testing/authorization-service.stub';
|
||||
import { NotifyInfoService } from '../core/coar-notify/notify-info/notify-info.service';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
|
||||
let comp: FooterComponent;
|
||||
let compAny: any;
|
||||
let fixture: ComponentFixture<FooterComponent>;
|
||||
let de: DebugElement;
|
||||
let el: HTMLElement;
|
||||
|
||||
describe('Footer component', () => {
|
||||
let notifyInfoService = {
|
||||
isCoarConfigEnabled: () => of(true)
|
||||
};
|
||||
|
||||
// waitForAsync beforeEach
|
||||
describe('Footer component', () => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
return TestBed.configureTestingModule({
|
||||
imports: [CommonModule, StoreModule.forRoot({}, storeModuleConfig), TranslateModule.forRoot({
|
||||
@@ -38,6 +45,7 @@ describe('Footer component', () => {
|
||||
providers: [
|
||||
FooterComponent,
|
||||
{ provide: AuthorizationDataService, useClass: AuthorizationDataServiceStub },
|
||||
{ provide: NotifyInfoService, useValue: notifyInfoService }
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
@@ -46,9 +54,8 @@ describe('Footer component', () => {
|
||||
// synchronous beforeEach
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FooterComponent);
|
||||
|
||||
comp = fixture.componentInstance; // component test instance
|
||||
|
||||
comp = fixture.componentInstance;
|
||||
compAny = comp as any;
|
||||
// query for the title <p> by CSS element selector
|
||||
de = fixture.debugElement.query(By.css('p'));
|
||||
el = de.nativeElement;
|
||||
@@ -59,4 +66,56 @@ describe('Footer component', () => {
|
||||
expect(app).toBeTruthy();
|
||||
}));
|
||||
|
||||
|
||||
it('should set showPrivacyPolicy to the value of environment.info.enablePrivacyStatement', () => {
|
||||
expect(comp.showPrivacyPolicy).toBe(environment.info.enablePrivacyStatement);
|
||||
});
|
||||
|
||||
it('should set showEndUserAgreement to the value of environment.info.enableEndUserAgreement', () => {
|
||||
expect(comp.showEndUserAgreement).toBe(environment.info.enableEndUserAgreement);
|
||||
});
|
||||
|
||||
describe('showCookieSettings', () => {
|
||||
it('should call cookies.showSettings() if cookies is defined', () => {
|
||||
const cookies = jasmine.createSpyObj('cookies', ['showSettings']);
|
||||
compAny.cookies = cookies;
|
||||
comp.showCookieSettings();
|
||||
expect(cookies.showSettings).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call cookies.showSettings() if cookies is undefined', () => {
|
||||
compAny.cookies = undefined;
|
||||
expect(() => comp.showCookieSettings()).not.toThrow();
|
||||
});
|
||||
|
||||
it('should return false', () => {
|
||||
expect(comp.showCookieSettings()).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when coarLdnEnabled is true', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(notifyInfoService, 'isCoarConfigEnabled').and.returnValue(of(true));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should set coarLdnEnabled based on notifyInfoService', () => {
|
||||
expect(comp.coarLdnEnabled).toBeTruthy();
|
||||
// Check if COAR Notify section is rendered
|
||||
const notifySection = fixture.debugElement.query(By.css('.notify-enabled'));
|
||||
expect(notifySection).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should redirect to info/coar-notify-support', () => {
|
||||
// Check if the link to the COAR Notify support page is present
|
||||
const routerLink = fixture.debugElement.query(By.css('a[routerLink="info/coar-notify-support"].coar-notify-support-route'));
|
||||
expect(routerLink).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have an img tag with the class "n-coar" when coarLdnEnabled is true', fakeAsync(() => {
|
||||
// Check if the img tag with the class "n-coar" is present
|
||||
const imgTag = fixture.debugElement.query(By.css('.notify-enabled img.n-coar'));
|
||||
expect(imgTag).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@@ -1,22 +1,50 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Component, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Site } from '../core/shared/site.model';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { isPlatformServer } from '@angular/common';
|
||||
import { ServerResponseService } from '../core/services/server-response.service';
|
||||
import { NotifyInfoService } from '../core/coar-notify/notify-info/notify-info.service';
|
||||
import { LinkDefinition, LinkHeadService } from '../core/services/link-head.service';
|
||||
import { isNotEmpty } from '../shared/empty.util';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-home-page',
|
||||
styleUrls: ['./home-page.component.scss'],
|
||||
templateUrl: './home-page.component.html'
|
||||
})
|
||||
export class HomePageComponent implements OnInit {
|
||||
export class HomePageComponent implements OnInit, OnDestroy {
|
||||
|
||||
site$: Observable<Site>;
|
||||
recentSubmissionspageSize: number;
|
||||
/**
|
||||
* An array of LinkDefinition objects representing inbox links for the home page.
|
||||
*/
|
||||
inboxLinks: LinkDefinition[] = [];
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private responseService: ServerResponseService,
|
||||
private notifyInfoService: NotifyInfoService,
|
||||
protected linkHeadService: LinkHeadService,
|
||||
@Inject(PLATFORM_ID) private platformId: string
|
||||
) {
|
||||
this.recentSubmissionspageSize = environment.homePage.recentSubmissions.pageSize;
|
||||
// Get COAR REST API URLs from REST configuration
|
||||
// only if COAR configuration is enabled
|
||||
this.notifyInfoService.isCoarConfigEnabled().pipe(
|
||||
switchMap((coarLdnEnabled: boolean) => {
|
||||
if (coarLdnEnabled) {
|
||||
return this.notifyInfoService.getCoarLdnLocalInboxUrls();
|
||||
}
|
||||
})
|
||||
).subscribe((coarRestApiUrls: string[]) => {
|
||||
if (coarRestApiUrls.length > 0) {
|
||||
this.initPageLinks(coarRestApiUrls);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -24,4 +52,38 @@ export class HomePageComponent implements OnInit {
|
||||
map((data) => data.site as Site),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes page links for COAR REST API URLs.
|
||||
* @param coarRestApiUrls An array of COAR REST API URLs.
|
||||
*/
|
||||
private initPageLinks(coarRestApiUrls: string[]): void {
|
||||
const rel = this.notifyInfoService.getInboxRelationLink();
|
||||
let links = '';
|
||||
coarRestApiUrls.forEach((coarRestApiUrl: string) => {
|
||||
// Add link to head
|
||||
let tag: LinkDefinition = {
|
||||
href: coarRestApiUrl,
|
||||
rel: rel
|
||||
};
|
||||
this.inboxLinks.push(tag);
|
||||
this.linkHeadService.addTag(tag);
|
||||
|
||||
links = links + (isNotEmpty(links) ? ', ' : '') + `<${coarRestApiUrl}> ; rel="${rel}"`;
|
||||
});
|
||||
|
||||
if (isPlatformServer(this.platformId)) {
|
||||
// Add link to response header
|
||||
this.responseService.setHeader('Link', links);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It removes the inbox links from the head of the html.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.inboxLinks.forEach((link: LinkDefinition) => {
|
||||
this.linkHeadService.removeTag(`href='${link.href}'`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import { RemoteData } from '../../core/data/remote-data';
|
||||
import { ServerResponseService } from '../../core/services/server-response.service';
|
||||
import { SignpostingDataService } from '../../core/data/signposting-data.service';
|
||||
import { LinkHeadService } from '../../core/services/link-head.service';
|
||||
import { NotifyInfoService } from '../../core/coar-notify/notify-info/notify-info.service';
|
||||
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||
@@ -61,6 +62,7 @@ describe('FullItemPageComponent', () => {
|
||||
let serverResponseService: jasmine.SpyObj<ServerResponseService>;
|
||||
let signpostingDataService: jasmine.SpyObj<SignpostingDataService>;
|
||||
let linkHeadService: jasmine.SpyObj<LinkHeadService>;
|
||||
let notifyInfoService: jasmine.SpyObj<NotifyInfoService>;
|
||||
|
||||
const mocklink = {
|
||||
href: 'http://test.org',
|
||||
@@ -105,6 +107,12 @@ describe('FullItemPageComponent', () => {
|
||||
removeTag: jasmine.createSpy('removeTag'),
|
||||
});
|
||||
|
||||
notifyInfoService = jasmine.createSpyObj('NotifyInfoService', {
|
||||
isCoarConfigEnabled: observableOf(true),
|
||||
getCoarLdnLocalInboxUrls: observableOf(['http://test.org']),
|
||||
getInboxRelationLink: observableOf('http://test.org'),
|
||||
});
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot({
|
||||
loader: {
|
||||
@@ -122,6 +130,7 @@ describe('FullItemPageComponent', () => {
|
||||
{ provide: ServerResponseService, useValue: serverResponseService },
|
||||
{ provide: SignpostingDataService, useValue: signpostingDataService },
|
||||
{ provide: LinkHeadService, useValue: linkHeadService },
|
||||
{ provide: NotifyInfoService, useValue: notifyInfoService },
|
||||
{ provide: PLATFORM_ID, useValue: 'server' }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
@@ -178,7 +187,7 @@ describe('FullItemPageComponent', () => {
|
||||
|
||||
it('should add the signposting links', () => {
|
||||
expect(serverResponseService.setHeader).toHaveBeenCalled();
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(2);
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
||||
describe('when the item is withdrawn and the user is not an admin', () => {
|
||||
@@ -207,7 +216,7 @@ describe('FullItemPageComponent', () => {
|
||||
|
||||
it('should add the signposting links', () => {
|
||||
expect(serverResponseService.setHeader).toHaveBeenCalled();
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(2);
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -224,7 +233,7 @@ describe('FullItemPageComponent', () => {
|
||||
|
||||
it('should add the signposting links', () => {
|
||||
expect(serverResponseService.setHeader).toHaveBeenCalled();
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(2);
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -19,6 +19,7 @@ import { AuthorizationDataService } from '../../core/data/feature-authorization/
|
||||
import { ServerResponseService } from '../../core/services/server-response.service';
|
||||
import { SignpostingDataService } from '../../core/data/signposting-data.service';
|
||||
import { LinkHeadService } from '../../core/services/link-head.service';
|
||||
import { NotifyInfoService } from '../../core/coar-notify/notify-info/notify-info.service';
|
||||
|
||||
/**
|
||||
* This component renders a full item page.
|
||||
@@ -55,9 +56,10 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit,
|
||||
protected responseService: ServerResponseService,
|
||||
protected signpostingDataService: SignpostingDataService,
|
||||
protected linkHeadService: LinkHeadService,
|
||||
protected notifyInfoService: NotifyInfoService,
|
||||
@Inject(PLATFORM_ID) protected platformId: string,
|
||||
) {
|
||||
super(route, router, items, authService, authorizationService, responseService, signpostingDataService, linkHeadService, platformId);
|
||||
super(route, router, items, authService, authorizationService, responseService, signpostingDataService, linkHeadService, notifyInfoService, platformId);
|
||||
}
|
||||
|
||||
/*** AoT inheritance fix, will hopefully be resolved in the near future **/
|
||||
|
@@ -60,6 +60,7 @@ import { ThemedItemAlertsComponent } from './alerts/themed-item-alerts.component
|
||||
import {
|
||||
ThemedFullFileSectionComponent
|
||||
} from './full/field-components/file-section/themed-full-file-section.component';
|
||||
import { QaEventNotificationComponent } from './simple/qa-event-notification/qa-event-notification.component';
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
// put only entry components that use custom decorator
|
||||
@@ -103,6 +104,7 @@ const DECLARATIONS = [
|
||||
ItemAlertsComponent,
|
||||
ThemedItemAlertsComponent,
|
||||
BitstreamRequestACopyPageComponent,
|
||||
QaEventNotificationComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@@ -2,6 +2,7 @@
|
||||
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
|
||||
<div *ngIf="itemRD?.payload as item">
|
||||
<ds-themed-item-alerts [item]="item"></ds-themed-item-alerts>
|
||||
<ds-qa-event-notification [item]="item"></ds-qa-event-notification>
|
||||
<ds-item-versions-notice [item]="item"></ds-item-versions-notice>
|
||||
<ds-view-tracker [object]="item"></ds-view-tracker>
|
||||
<ds-listable-object-component-loader *ngIf="!item.isWithdrawn || (isAdmin$|async)" [object]="item" [viewMode]="viewMode"></ds-listable-object-component-loader>
|
||||
|
@@ -26,6 +26,7 @@ import { ServerResponseService } from '../../core/services/server-response.servi
|
||||
import { SignpostingDataService } from '../../core/data/signposting-data.service';
|
||||
import { LinkDefinition, LinkHeadService } from '../../core/services/link-head.service';
|
||||
import { SignpostingLink } from '../../core/data/signposting-links.model';
|
||||
import { NotifyInfoService } from '../../core/coar-notify/notify-info/notify-info.service';
|
||||
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||
@@ -62,6 +63,7 @@ describe('ItemPageComponent', () => {
|
||||
let serverResponseService: jasmine.SpyObj<ServerResponseService>;
|
||||
let signpostingDataService: jasmine.SpyObj<SignpostingDataService>;
|
||||
let linkHeadService: jasmine.SpyObj<LinkHeadService>;
|
||||
let notifyInfoService: jasmine.SpyObj<NotifyInfoService>;
|
||||
|
||||
const mockMetadataService = {
|
||||
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
|
||||
@@ -73,6 +75,8 @@ describe('ItemPageComponent', () => {
|
||||
data: observableOf({ dso: createSuccessfulRemoteDataObject(mockItem) })
|
||||
});
|
||||
|
||||
const getCoarLdnLocalInboxUrls = ['http://InboxUrls.org', 'http://InboxUrls2.org'];
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
authService = jasmine.createSpyObj('authService', {
|
||||
isAuthenticated: observableOf(true),
|
||||
@@ -94,6 +98,12 @@ describe('ItemPageComponent', () => {
|
||||
removeTag: jasmine.createSpy('removeTag'),
|
||||
});
|
||||
|
||||
notifyInfoService = jasmine.createSpyObj('NotifyInfoService', {
|
||||
getInboxRelationLink: 'http://www.w3.org/ns/ldp#inbox',
|
||||
isCoarConfigEnabled: observableOf(true),
|
||||
getCoarLdnLocalInboxUrls: observableOf(getCoarLdnLocalInboxUrls),
|
||||
});
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot({
|
||||
loader: {
|
||||
@@ -112,6 +122,7 @@ describe('ItemPageComponent', () => {
|
||||
{ provide: ServerResponseService, useValue: serverResponseService },
|
||||
{ provide: SignpostingDataService, useValue: signpostingDataService },
|
||||
{ provide: LinkHeadService, useValue: linkHeadService },
|
||||
{ provide: NotifyInfoService, useValue: notifyInfoService},
|
||||
{ provide: PLATFORM_ID, useValue: 'server' },
|
||||
],
|
||||
|
||||
@@ -166,7 +177,7 @@ describe('ItemPageComponent', () => {
|
||||
|
||||
it('should add the signposting links', () => {
|
||||
expect(serverResponseService.setHeader).toHaveBeenCalled();
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(2);
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
|
||||
@@ -175,7 +186,7 @@ describe('ItemPageComponent', () => {
|
||||
expect(comp.signpostingLinks).toEqual([mocklink, mocklink2]);
|
||||
|
||||
// Check if linkHeadService.addTag() was called with the correct arguments
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(mockSignpostingLinks.length);
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(mockSignpostingLinks.length + getCoarLdnLocalInboxUrls.length);
|
||||
let expected: LinkDefinition = mockSignpostingLinks[0] as LinkDefinition;
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledWith(expected);
|
||||
expected = {
|
||||
@@ -186,8 +197,7 @@ describe('ItemPageComponent', () => {
|
||||
});
|
||||
|
||||
it('should set Link header on the server', () => {
|
||||
|
||||
expect(serverResponseService.setHeader).toHaveBeenCalledWith('Link', '<http://test.org> ; rel="rel1" ; type="type1" , <http://test2.org> ; rel="rel2" ');
|
||||
expect(serverResponseService.setHeader).toHaveBeenCalledWith('Link', '<http://test.org> ; rel="rel1" ; type="type1" , <http://test2.org> ; rel="rel2" , <http://InboxUrls.org> ; rel="http://www.w3.org/ns/ldp#inbox", <http://InboxUrls2.org> ; rel="http://www.w3.org/ns/ldp#inbox"');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -215,9 +225,9 @@ describe('ItemPageComponent', () => {
|
||||
expect(objectLoader.nativeElement).toBeDefined();
|
||||
});
|
||||
|
||||
it('should add the signposting links', () => {
|
||||
it('should add the signposti`ng links`', () => {
|
||||
expect(serverResponseService.setHeader).toHaveBeenCalled();
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(2);
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -234,7 +244,7 @@ describe('ItemPageComponent', () => {
|
||||
|
||||
it('should add the signposting links', () => {
|
||||
expect(serverResponseService.setHeader).toHaveBeenCalled();
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(2);
|
||||
expect(linkHeadService.addTag).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -2,8 +2,8 @@ import { ChangeDetectionStrategy, Component, Inject, OnDestroy, OnInit, PLATFORM
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { isPlatformServer } from '@angular/common';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
import { Observable, combineLatest } from 'rxjs';
|
||||
import { map, switchMap, take } from 'rxjs/operators';
|
||||
|
||||
import { ItemDataService } from '../../core/data/item-data.service';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
@@ -21,6 +21,7 @@ import { SignpostingDataService } from '../../core/data/signposting-data.service
|
||||
import { SignpostingLink } from '../../core/data/signposting-links.model';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { LinkDefinition, LinkHeadService } from '../../core/services/link-head.service';
|
||||
import { NotifyInfoService } from 'src/app/core/coar-notify/notify-info/notify-info.service';
|
||||
|
||||
/**
|
||||
* This component renders a simple item page.
|
||||
@@ -32,7 +33,7 @@ import { LinkDefinition, LinkHeadService } from '../../core/services/link-head.s
|
||||
styleUrls: ['./item-page.component.scss'],
|
||||
templateUrl: './item-page.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [fadeInOut]
|
||||
animations: [fadeInOut],
|
||||
})
|
||||
export class ItemPageComponent implements OnInit, OnDestroy {
|
||||
|
||||
@@ -68,6 +69,13 @@ export class ItemPageComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
signpostingLinks: SignpostingLink[] = [];
|
||||
|
||||
/**
|
||||
* An array of LinkDefinition objects representing inbox links for the item page.
|
||||
*/
|
||||
inboxTags: LinkDefinition[] = [];
|
||||
|
||||
coarRestApiUrls: string[] = [];
|
||||
|
||||
constructor(
|
||||
protected route: ActivatedRoute,
|
||||
protected router: Router,
|
||||
@@ -77,6 +85,7 @@ export class ItemPageComponent implements OnInit, OnDestroy {
|
||||
protected responseService: ServerResponseService,
|
||||
protected signpostingDataService: SignpostingDataService,
|
||||
protected linkHeadService: LinkHeadService,
|
||||
protected notifyInfoService: NotifyInfoService,
|
||||
@Inject(PLATFORM_ID) protected platformId: string
|
||||
) {
|
||||
this.initPageLinks();
|
||||
@@ -106,7 +115,8 @@ export class ItemPageComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
private initPageLinks(): void {
|
||||
this.route.params.subscribe(params => {
|
||||
this.signpostingDataService.getLinks(params.id).pipe(take(1)).subscribe((signpostingLinks: SignpostingLink[]) => {
|
||||
combineLatest([this.signpostingDataService.getLinks(params.id).pipe(take(1)), this.getCoarLdnLocalInboxUrls()])
|
||||
.subscribe(([signpostingLinks, coarRestApiUrls]) => {
|
||||
let links = '';
|
||||
this.signpostingLinks = signpostingLinks;
|
||||
|
||||
@@ -124,6 +134,11 @@ export class ItemPageComponent implements OnInit, OnDestroy {
|
||||
this.linkHeadService.addTag(tag);
|
||||
});
|
||||
|
||||
if (coarRestApiUrls.length > 0) {
|
||||
let inboxLinks = this.initPageInboxLinks(coarRestApiUrls);
|
||||
links = links + (isNotEmpty(links) ? ', ' : '') + inboxLinks;
|
||||
}
|
||||
|
||||
if (isPlatformServer(this.platformId)) {
|
||||
this.responseService.setHeader('Link', links);
|
||||
}
|
||||
@@ -131,9 +146,49 @@ export class ItemPageComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the COAR LDN local inbox URL if COAR configuration is enabled.
|
||||
* If the COAR LDN local inbox URL is retrieved successfully, initializes the page inbox links.
|
||||
*/
|
||||
private getCoarLdnLocalInboxUrls(): Observable<string[]> {
|
||||
return this.notifyInfoService.isCoarConfigEnabled().pipe(
|
||||
switchMap((coarLdnEnabled: boolean) => {
|
||||
if (coarLdnEnabled) {
|
||||
return this.notifyInfoService.getCoarLdnLocalInboxUrls();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the page inbox links.
|
||||
* @param coarRestApiUrls - An array of COAR REST API URLs.
|
||||
*/
|
||||
private initPageInboxLinks(coarRestApiUrls: string[]): string {
|
||||
const rel = this.notifyInfoService.getInboxRelationLink();
|
||||
let links = '';
|
||||
|
||||
coarRestApiUrls.forEach((coarRestApiUrl: string) => {
|
||||
// Add link to head
|
||||
let tag: LinkDefinition = {
|
||||
href: coarRestApiUrl,
|
||||
rel: rel
|
||||
};
|
||||
this.inboxTags.push(tag);
|
||||
this.linkHeadService.addTag(tag);
|
||||
|
||||
links = links + (isNotEmpty(links) ? ', ' : '') + `<${coarRestApiUrl}> ; rel="${rel}"`;
|
||||
});
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.signpostingLinks.forEach((link: SignpostingLink) => {
|
||||
this.linkHeadService.removeTag(`href='${link.href}'`);
|
||||
});
|
||||
this.inboxTags.forEach((link: LinkDefinition) => {
|
||||
this.linkHeadService.removeTag(`href='${link.href}'`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -84,6 +84,18 @@
|
||||
[label]="'item.page.uri'">
|
||||
</ds-item-page-uri-field>
|
||||
<ds-item-page-collections [item]="object"></ds-item-page-collections>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['notify.relation.endorsedBy']"
|
||||
[label]="'item.page.endorsed-by'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['datacite.relation.isReviewedBy']"
|
||||
[label]="'item.page.is-reviewed-by'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['datacite.relation.isSupplementedBy']"
|
||||
[label]="'item.page.is-supplemented-by'">
|
||||
</ds-generic-item-page-field>
|
||||
<div>
|
||||
<a class="btn btn-outline-primary" role="button" [routerLink]="[itemPageRoute + '/full']">
|
||||
<i class="fas fa-info-circle"></i> {{"item.page.link.full" | translate}}
|
||||
|
@@ -0,0 +1,14 @@
|
||||
<ng-container *ngIf="(getQualityAssuranceSources$() | async)?.length > 0">
|
||||
<ng-container *ngFor="let source of (getQualityAssuranceSources$() | async)">
|
||||
<div class="alert alert-info d-flex flex-row" *ngIf="source.totalEvents > 0">
|
||||
<img class="source-logo" src="assets/images/qa-{{(source.id | dsSplit: ':')[0]}}-logo.png" alt="{{source.id}} logo">
|
||||
<div class="w-100 d-flex justify-content-between">
|
||||
<div class="pl-4 align-self-center">{{'item.qa-event-notification.check.notification-info' | translate : {num:
|
||||
source.totalEvents } }} </div>
|
||||
<button [routerLink]="['/admin/notifications/quality-assurance', (source.id | dsSplit: ':')[0], 'target', item.id]"
|
||||
class="btn btn-primary align-self-center">{{'item.qa-event-notification-info.check.button' | translate
|
||||
}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
@@ -0,0 +1,8 @@
|
||||
|
||||
.source-logo {
|
||||
max-height: var(--ds-header-logo-height);
|
||||
}
|
||||
|
||||
.sections-gap {
|
||||
gap: 1rem;
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { QaEventNotificationComponent } from './qa-event-notification.component';
|
||||
import { QualityAssuranceSourceDataService } from '../../../core/suggestion-notifications/qa/source/quality-assurance-source-data.service';
|
||||
import { createPaginatedList } from '../../../shared/testing/utils.test';
|
||||
import { QualityAssuranceSourceObject } from '../../../core/suggestion-notifications/qa/models/quality-assurance-source.model';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SplitPipe } from '../../../shared/utils/split.pipe';
|
||||
import { RequestService } from '../../../core/data/request.service';
|
||||
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
||||
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { HALEndpointService } from '../../../core/shared/hal-endpoint.service';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { HALEndpointServiceStub } from '../../../shared/testing/hal-endpoint-service.stub';
|
||||
|
||||
describe('QaEventNotificationComponent', () => {
|
||||
let component: QaEventNotificationComponent;
|
||||
let fixture: ComponentFixture<QaEventNotificationComponent>;
|
||||
|
||||
let qualityAssuranceSourceDataServiceStub: any;
|
||||
|
||||
const obj = createSuccessfulRemoteDataObject$(createPaginatedList([new QualityAssuranceSourceObject()]));
|
||||
const item = Object.assign({ uuid: '1234' });
|
||||
|
||||
beforeEach(async () => {
|
||||
qualityAssuranceSourceDataServiceStub = {
|
||||
getSourcesByTarget: () => obj
|
||||
};
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [CommonModule, TranslateModule.forRoot()],
|
||||
declarations: [QaEventNotificationComponent, SplitPipe],
|
||||
providers: [
|
||||
{ provide: QualityAssuranceSourceDataService, useValue: qualityAssuranceSourceDataServiceStub },
|
||||
{ provide: RequestService, useValue: {} },
|
||||
{ provide: NotificationsService, useValue: {} },
|
||||
{ provide: HALEndpointService, useValue: new HALEndpointServiceStub('test')},
|
||||
ObjectCacheService,
|
||||
RemoteDataBuildService,
|
||||
provideMockStore({})
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(QaEventNotificationComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.item = item;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,53 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { getFirstCompletedRemoteData, getPaginatedListPayload, getRemoteDataPayload } from '../../../core/shared/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AlertType } from '../../../shared/alert/aletr-type';
|
||||
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||
import { RequestParam } from '../../../core/cache/models/request-param.model';
|
||||
import { QualityAssuranceSourceDataService } from '../../../core/suggestion-notifications/qa/source/quality-assurance-source-data.service';
|
||||
import { QualityAssuranceSourceObject } from '../../../core/suggestion-notifications/qa/models/quality-assurance-source.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-qa-event-notification',
|
||||
templateUrl: './qa-event-notification.component.html',
|
||||
styleUrls: ['./qa-event-notification.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [QualityAssuranceSourceDataService]
|
||||
})
|
||||
/**
|
||||
* Component for displaying quality assurance event notifications for an item.
|
||||
*/
|
||||
export class QaEventNotificationComponent {
|
||||
|
||||
/**
|
||||
* The item to display quality assurance event notifications for.
|
||||
*/
|
||||
@Input() item: Item;
|
||||
|
||||
/**
|
||||
* The type of alert to display for the notification.
|
||||
*/
|
||||
AlertTypeInfo = AlertType.Info;
|
||||
|
||||
constructor(
|
||||
private qualityAssuranceSourceDataService: QualityAssuranceSourceDataService,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Returns an Observable of QualityAssuranceSourceObject[] for the current item.
|
||||
* @returns An Observable of QualityAssuranceSourceObject[] for the current item.
|
||||
* Note: sourceId is composed as: id: "sourceName:<target>"
|
||||
*/
|
||||
getQualityAssuranceSources$(): Observable<QualityAssuranceSourceObject[]> {
|
||||
const findListTopicOptions: FindListOptions = {
|
||||
searchParams: [new RequestParam('target', this.item.uuid)]
|
||||
};
|
||||
return this.qualityAssuranceSourceDataService.getSourcesByTarget(findListTopicOptions)
|
||||
.pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
getRemoteDataPayload(),
|
||||
getPaginatedListPayload(),
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
<div class="container">
|
||||
<ds-my-dspace-qa-events-notifications></ds-my-dspace-qa-events-notifications>
|
||||
<ds-my-dspace-new-submission *dsShowOnlyForRole="[roleTypeEnum.Submitter]"></ds-my-dspace-new-submission>
|
||||
<ds-suggestions-notification></ds-suggestions-notification>
|
||||
</div>
|
||||
|
@@ -16,6 +16,7 @@ import { ThemedMyDSpacePageComponent } from './themed-my-dspace-page.component';
|
||||
import { SearchModule } from '../shared/search/search.module';
|
||||
import { UploadModule } from '../shared/upload/upload.module';
|
||||
import { SuggestionNotificationsModule } from '../suggestion-notifications/suggestion-notifications.module';
|
||||
import { MyDspaceQaEventsNotificationsComponent } from './my-dspace-qa-events-notifications/my-dspace-qa-events-notifications.component';
|
||||
|
||||
const DECLARATIONS = [
|
||||
MyDSpacePageComponent,
|
||||
@@ -23,7 +24,8 @@ const DECLARATIONS = [
|
||||
MyDSpaceNewSubmissionComponent,
|
||||
CollectionSelectorComponent,
|
||||
MyDSpaceNewSubmissionDropdownComponent,
|
||||
MyDSpaceNewExternalDropdownComponent
|
||||
MyDSpaceNewExternalDropdownComponent,
|
||||
MyDspaceQaEventsNotificationsComponent,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@@ -0,0 +1,28 @@
|
||||
<ng-container *ngIf="(sources$ | async)?.length > 0">
|
||||
<ng-container *ngFor="let source of sources$ | async">
|
||||
<div
|
||||
class="alert alert-info d-flex flex-row"
|
||||
*ngIf="source.totalEvents > 0"
|
||||
>
|
||||
<img
|
||||
class="source-logo"
|
||||
src="assets/images/qa-{{ source.id }}-logo.png"
|
||||
alt="{{ source.id }} logo"
|
||||
/>
|
||||
<div class="w-100 d-flex justify-content-between">
|
||||
<div class="pl-4 align-self-center">
|
||||
{{
|
||||
"mydspace.qa-event-notification.check.notification-info"
|
||||
| translate : { num: source.totalEvents }
|
||||
}}
|
||||
</div>
|
||||
<button
|
||||
[routerLink]="['/admin/notifications/quality-assurance', source.id]"
|
||||
class="btn btn-primary align-self-center"
|
||||
>
|
||||
{{ "mydspace.qa-event-notification-info.check.button" | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
@@ -0,0 +1,8 @@
|
||||
|
||||
.source-logo {
|
||||
max-height: var(--ds-header-logo-height);
|
||||
}
|
||||
|
||||
.sections-gap {
|
||||
gap: 1rem;
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MyDspaceQaEventsNotificationsComponent } from './my-dspace-qa-events-notifications.component';
|
||||
import { QualityAssuranceSourceDataService } from '../../core/suggestion-notifications/qa/source/quality-assurance-source-data.service';
|
||||
import { createSuccessfulRemoteDataObject$ } from 'src/app/shared/remote-data.utils';
|
||||
import { createPaginatedList } from 'src/app/shared/testing/utils.test';
|
||||
import { QualityAssuranceSourceObject } from 'src/app/core/suggestion-notifications/qa/models/quality-assurance-source.model';
|
||||
|
||||
describe('MyDspaceQaEventsNotificationsComponent', () => {
|
||||
let component: MyDspaceQaEventsNotificationsComponent;
|
||||
let fixture: ComponentFixture<MyDspaceQaEventsNotificationsComponent>;
|
||||
|
||||
let qualityAssuranceSourceDataServiceStub: any;
|
||||
const obj = createSuccessfulRemoteDataObject$(createPaginatedList([new QualityAssuranceSourceObject()]));
|
||||
|
||||
beforeEach(async () => {
|
||||
qualityAssuranceSourceDataServiceStub = {
|
||||
getSources: () => obj
|
||||
};
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MyDspaceQaEventsNotificationsComponent ],
|
||||
providers: [
|
||||
{ provide: QualityAssuranceSourceDataService, useValue: qualityAssuranceSourceDataServiceStub }
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(MyDspaceQaEventsNotificationsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,44 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { QualityAssuranceSourceDataService } from '../../core/suggestion-notifications/qa/source/quality-assurance-source-data.service';
|
||||
import { getFirstCompletedRemoteData, getPaginatedListPayload, getRemoteDataPayload } from '../../core/shared/operators';
|
||||
import { Observable, of, tap } from 'rxjs';
|
||||
import { QualityAssuranceSourceObject } from 'src/app/core/suggestion-notifications/qa/models/quality-assurance-source.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-my-dspace-qa-events-notifications',
|
||||
templateUrl: './my-dspace-qa-events-notifications.component.html',
|
||||
styleUrls: ['./my-dspace-qa-events-notifications.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MyDspaceQaEventsNotificationsComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* An Observable that emits an array of QualityAssuranceSourceObject.
|
||||
*/
|
||||
sources$: Observable<QualityAssuranceSourceObject[]> = of([]);
|
||||
|
||||
constructor(private qualityAssuranceSourceDataService: QualityAssuranceSourceDataService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getSources();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the sources for Quality Assurance.
|
||||
* @returns An Observable of the sources for Quality Assurance.
|
||||
* @throws An error if the retrieval of Quality Assurance sources fails.
|
||||
*/
|
||||
getSources() {
|
||||
this.sources$ = this.qualityAssuranceSourceDataService.getSources()
|
||||
.pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
tap((rd) => {
|
||||
if (rd.hasFailed) {
|
||||
throw new Error('Can\'t retrieve Quality Assurance sources');
|
||||
}
|
||||
}),
|
||||
getRemoteDataPayload(),
|
||||
getPaginatedListPayload(),
|
||||
);
|
||||
}
|
||||
}
|
@@ -1838,8 +1838,8 @@ export function getMockNotificationsStateService(): any {
|
||||
*/
|
||||
export function getMockQualityAssuranceTopicRestService(): QualityAssuranceTopicDataService {
|
||||
return jasmine.createSpyObj('QualityAssuranceTopicDataService', {
|
||||
getTopics: jasmine.createSpy('getTopics'),
|
||||
getTopic: jasmine.createSpy('getTopic'),
|
||||
searchTopicsBySource: jasmine.createSpy('searchTopicsBySource'),
|
||||
searchTopicsByTarget: jasmine.createSpy('searchTopicsByTarget'),
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { SearchResult } from '../search/models/search-result.model';
|
||||
import { SuggestionsService } from '../../suggestion-notifications/reciter-suggestions/suggestions.service';
|
||||
|
||||
// REST Mock ---------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------
|
||||
|
@@ -284,6 +284,7 @@ import {
|
||||
} from '../item-page/simple/field-components/specific-field/title/themed-item-page-field.component';
|
||||
import { BitstreamListItemComponent } from './object-list/bitstream-list-item/bitstream-list-item.component';
|
||||
import { NgxPaginationModule } from 'ngx-pagination';
|
||||
import { SplitPipe } from './utils/split.pipe';
|
||||
|
||||
const MODULES = [
|
||||
CommonModule,
|
||||
@@ -323,7 +324,8 @@ const PIPES = [
|
||||
ObjNgFor,
|
||||
BrowserOnlyPipe,
|
||||
MarkdownPipe,
|
||||
ShortNumberPipe
|
||||
ShortNumberPipe,
|
||||
SplitPipe,
|
||||
];
|
||||
|
||||
const COMPONENTS = [
|
||||
|
12
src/app/shared/utils/split.pipe.ts
Normal file
12
src/app/shared/utils/split.pipe.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'dsSplit'
|
||||
})
|
||||
export class SplitPipe implements PipeTransform {
|
||||
|
||||
transform(value: string, separator: string): string[] {
|
||||
return value.split(separator);
|
||||
}
|
||||
|
||||
}
|
@@ -9,7 +9,8 @@
|
||||
|
||||
<div class="submission-form-header-item mb-3 mb-sm-0 flex-sm-grow-1 flex-md-grow-0">
|
||||
<ng-container *ngIf="!isSectionHidden">
|
||||
<ds-submission-form-collection [currentCollectionId]="collectionId"
|
||||
<ds-submission-form-collection
|
||||
[currentCollectionId]="collectionId"
|
||||
[currentDefinition]="definitionId"
|
||||
[submissionId]="submissionId"
|
||||
[collectionModifiable]="collectionModifiable"
|
||||
@@ -30,10 +31,12 @@
|
||||
<ng-container *ngFor="let object of (submissionSections | async)">
|
||||
<ds-submission-section-container [collectionId]="collectionId"
|
||||
[submissionId]="submissionId"
|
||||
[sectionData]="object"></ds-submission-section-container>
|
||||
[sectionData]="object">
|
||||
</ds-submission-section-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div *ngIf="!(isLoading() | async)" class="submission-form-footer mt-3 mb-3 position-sticky">
|
||||
<ds-submission-form-footer [submissionId]="submissionId"></ds-submission-form-footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -0,0 +1,122 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { dataService } from '../../../core/data/base/data-service.decorator';
|
||||
import { IdentifiableDataService } from '../../../core/data/base/identifiable-data.service';
|
||||
import { FindAllData, FindAllDataImpl } from '../../../core/data/base/find-all-data';
|
||||
import { DeleteData, DeleteDataImpl } from '../../../core/data/base/delete-data';
|
||||
import { RequestService } from '../../../core/data/request.service';
|
||||
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
||||
import { HALEndpointService } from '../../../core/shared/hal-endpoint.service';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||
import { NoContent } from '../../../core/shared/NoContent.model';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
import { URLCombiner } from '../../../core/url-combiner/url-combiner';
|
||||
import { MultipartPostRequest } from '../../../core/data/request.models';
|
||||
import { RestRequest } from '../../../core/data/rest-request.model';
|
||||
import { SUBMISSION_COAR_NOTIFY_CONFIG } from './section-coar-notify-service.resource-type';
|
||||
import { SubmissionCoarNotifyConfig } from './submission-coar-notify.config';
|
||||
import { CreateData, CreateDataImpl } from '../../../core/data/base/create-data';
|
||||
import { PatchData, PatchDataImpl } from '../../../core/data/base/patch-data';
|
||||
import { ChangeAnalyzer } from '../../../core/data/change-analyzer';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { RestRequestMethod } from '../../../core/data/rest-request-method';
|
||||
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
||||
import { hasValue } from '../../../shared/empty.util';
|
||||
|
||||
|
||||
/**
|
||||
* A service responsible for fetching/sending data from/to the REST API on the CoarNotifyConfig endpoint
|
||||
*/
|
||||
@Injectable()
|
||||
@dataService(SUBMISSION_COAR_NOTIFY_CONFIG)
|
||||
export class CoarNotifyConfigDataService extends IdentifiableDataService<SubmissionCoarNotifyConfig> implements FindAllData<SubmissionCoarNotifyConfig>, DeleteData<SubmissionCoarNotifyConfig>, PatchData<SubmissionCoarNotifyConfig>, CreateData<SubmissionCoarNotifyConfig> {
|
||||
createData: CreateDataImpl<SubmissionCoarNotifyConfig>;
|
||||
private findAllData: FindAllDataImpl<SubmissionCoarNotifyConfig>;
|
||||
private deleteData: DeleteDataImpl<SubmissionCoarNotifyConfig>;
|
||||
private patchData: PatchDataImpl<SubmissionCoarNotifyConfig>;
|
||||
private comparator: ChangeAnalyzer<SubmissionCoarNotifyConfig>;
|
||||
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
) {
|
||||
super('submissioncoarnotifyconfigs', requestService, rdbService, objectCache, halService);
|
||||
|
||||
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
|
||||
this.patchData = new PatchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.comparator, this.responseMsToLive, this.constructIdEndpoint);
|
||||
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
|
||||
}
|
||||
|
||||
|
||||
create(object: SubmissionCoarNotifyConfig): Observable<RemoteData<SubmissionCoarNotifyConfig>> {
|
||||
return this.createData.create(object);
|
||||
}
|
||||
|
||||
patch(object: SubmissionCoarNotifyConfig, operations: Operation[]): Observable<RemoteData<SubmissionCoarNotifyConfig>> {
|
||||
return this.patchData.patch(object, operations);
|
||||
}
|
||||
|
||||
update(object: SubmissionCoarNotifyConfig): Observable<RemoteData<SubmissionCoarNotifyConfig>> {
|
||||
return this.patchData.update(object);
|
||||
}
|
||||
|
||||
commitUpdates(method?: RestRequestMethod): void {
|
||||
return this.patchData.commitUpdates(method);
|
||||
}
|
||||
|
||||
createPatchFromCache(object: SubmissionCoarNotifyConfig): Observable<Operation[]> {
|
||||
return this.patchData.createPatchFromCache(object);
|
||||
}
|
||||
|
||||
findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<SubmissionCoarNotifyConfig>[]): Observable<RemoteData<PaginatedList<SubmissionCoarNotifyConfig>>> {
|
||||
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
|
||||
public delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
|
||||
return this.deleteData.delete(objectId, copyVirtualMetadata);
|
||||
}
|
||||
|
||||
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
|
||||
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
|
||||
}
|
||||
|
||||
public invoke(serviceName: string, serviceId: string, files: File[]): Observable<RemoteData<SubmissionCoarNotifyConfig>> {
|
||||
const requestId = this.requestService.generateRequestId();
|
||||
this.getBrowseEndpoint().pipe(
|
||||
take(1),
|
||||
map((endpoint: string) => new URLCombiner(endpoint, serviceName, 'submissioncoarnotifyconfigmodel', serviceId).toString()),
|
||||
map((endpoint: string) => {
|
||||
const body = this.getInvocationFormData(files);
|
||||
return new MultipartPostRequest(requestId, endpoint, body);
|
||||
})
|
||||
).subscribe((request: RestRequest) => this.requestService.send(request));
|
||||
|
||||
return this.rdbService.buildFromRequestUUID<SubmissionCoarNotifyConfig>(requestId);
|
||||
}
|
||||
|
||||
public SubmissionCoarNotifyConfigModelWithNameExistsAndCanExecute(scriptName: string): Observable<boolean> {
|
||||
return this.findById(scriptName).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
map((rd: RemoteData<SubmissionCoarNotifyConfig>) => {
|
||||
return hasValue(rd.payload);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private getInvocationFormData(files: File[]): FormData {
|
||||
const form: FormData = new FormData();
|
||||
files.forEach((file: File) => {
|
||||
form.append('file', file);
|
||||
});
|
||||
return form;
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* The resource type for Ldn-Services
|
||||
*
|
||||
* Needs to be in a separate file to prevent circular
|
||||
* dependencies in webpack.
|
||||
*/
|
||||
import { ResourceType } from '../../../core/shared/resource-type';
|
||||
|
||||
|
||||
export const SUBMISSION_COAR_NOTIFY_CONFIG = new ResourceType('submissioncoarnotifyconfig');
|
||||
|
||||
export const COAR_NOTIFY_WORKSPACEITEM = new ResourceType('workspaceitem');
|
||||
|
@@ -0,0 +1,124 @@
|
||||
<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.' + pattern + '.label' | translate }}
|
||||
</label
|
||||
>
|
||||
<div
|
||||
*ngFor="
|
||||
let service of ldnServiceByPattern[pattern];
|
||||
let serviceIndex = index
|
||||
"
|
||||
>
|
||||
<div class="row">
|
||||
<div ngbDropdown #myDropdown="ngbDropdown" class="w-100">
|
||||
<div class="position-relative right-addon" role="combobox">
|
||||
<i ngbDropdownToggle class="position-absolute scrollable-dropdown-toggle"
|
||||
aria-hidden="true"></i>
|
||||
<input
|
||||
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()"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ngbDropdownMenu
|
||||
class="dropdown-menu scrollable-dropdown-menu w-100"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<div
|
||||
class="scrollable-menu"
|
||||
role="listbox"
|
||||
infiniteScroll
|
||||
[infiniteScrollDistance]="2"
|
||||
[infiniteScrollThrottle]="50"
|
||||
[scrollWindow]="false"
|
||||
>
|
||||
<button
|
||||
*ngIf="(filterServices(pattern) | async)?.length == 0"
|
||||
class="dropdown-item collection-item text-truncate w-100"
|
||||
>
|
||||
{{'submission.section.section-coar-notify.dropdown.no-data' | translate}}
|
||||
</button>
|
||||
<button
|
||||
*ngIf="(filterServices(pattern) | async)?.length > 0"
|
||||
class="dropdown-item collection-item text-truncate w-100"
|
||||
(click)="onChange(pattern, serviceIndex, null)"
|
||||
>
|
||||
{{'submission.section.section-coar-notify.dropdown.select-none' | translate}}
|
||||
</button>
|
||||
<button
|
||||
*ngFor="let serviceOption of filterServices(pattern) | async"
|
||||
[ngClass]="{'bg-light': ldnServiceByPattern[pattern][serviceIndex]?.id == serviceOption.id}"
|
||||
class="dropdown-item collection-item text-truncate w-100"
|
||||
(click)="onChange(pattern, serviceIndex, serviceOption)"
|
||||
>
|
||||
<b>
|
||||
{{ serviceOption.name }}
|
||||
</b>
|
||||
<br />
|
||||
{{ serviceOption.description }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<small
|
||||
class="row text-muted"
|
||||
*ngIf="!ldnServiceByPattern[pattern][serviceIndex]"
|
||||
>
|
||||
{{'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]"
|
||||
>
|
||||
<div
|
||||
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="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>
|
||||
<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="ml-4">
|
||||
<span>
|
||||
{{ 'submission.section.section-coar-notify.notification.error' | translate }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="patterns?.length === 0">
|
||||
<p>
|
||||
{{'submission.section.section-coar-notify.info.no-pattern' | translate }}
|
||||
</p>
|
||||
</ng-container>
|
||||
</div>
|
@@ -0,0 +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';
|
||||
|
@@ -0,0 +1,406 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SubmissionSectionCoarNotifyComponent } from './section-coar-notify.component';
|
||||
import { LdnServicesService } from '../../../admin/admin-ldn-services/ldn-services-data/ldn-services-data.service';
|
||||
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder';
|
||||
import { SectionsService } from '../sections.service';
|
||||
import { CoarNotifyConfigDataService } from './coar-notify-config-data.service';
|
||||
import { ChangeDetectorRef } from '@angular/core';
|
||||
import { SubmissionCoarNotifyConfig } from './submission-coar-notify.config';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
||||
import { createPaginatedList } from '../../../shared/testing/utils.test';
|
||||
import { of } from 'rxjs';
|
||||
import { LdnService } from '../../../admin/admin-ldn-services/ldn-services-model/ldn-services.model';
|
||||
import { NotifyServicePattern } from '../../../admin/admin-ldn-services/ldn-services-model/ldn-service-patterns.model';
|
||||
|
||||
describe('SubmissionSectionCoarNotifyComponent', () => {
|
||||
let component: SubmissionSectionCoarNotifyComponent;
|
||||
let componentAsAny: any;
|
||||
let fixture: ComponentFixture<SubmissionSectionCoarNotifyComponent>;
|
||||
|
||||
let ldnServicesService: jasmine.SpyObj<LdnServicesService>;
|
||||
let coarNotifyConfigDataService: jasmine.SpyObj<CoarNotifyConfigDataService>;
|
||||
let operationsBuilder: jasmine.SpyObj<JsonPatchOperationsBuilder>;
|
||||
let sectionService: jasmine.SpyObj<SectionsService>;
|
||||
let cdRefStub: any;
|
||||
|
||||
const patterns: SubmissionCoarNotifyConfig[] = Object.assign(
|
||||
[new SubmissionCoarNotifyConfig()],
|
||||
{
|
||||
patterns: ['review', 'endorsment'],
|
||||
}
|
||||
);
|
||||
const patternsPL = createPaginatedList(patterns);
|
||||
const coarNotifyConfig = createSuccessfulRemoteDataObject$(patternsPL);
|
||||
|
||||
beforeEach(async () => {
|
||||
ldnServicesService = jasmine.createSpyObj('LdnServicesService', [
|
||||
'findByInboundPattern',
|
||||
]);
|
||||
coarNotifyConfigDataService = jasmine.createSpyObj(
|
||||
'CoarNotifyConfigDataService',
|
||||
['findAll']
|
||||
);
|
||||
operationsBuilder = jasmine.createSpyObj('JsonPatchOperationsBuilder', [
|
||||
'remove',
|
||||
'replace',
|
||||
'add',
|
||||
]);
|
||||
sectionService = jasmine.createSpyObj('SectionsService', [
|
||||
'dispatchRemoveSectionErrors',
|
||||
'getSectionServerErrors',
|
||||
'setSectionError',
|
||||
]);
|
||||
cdRefStub = Object.assign({
|
||||
detectChanges: () => fixture.detectChanges(),
|
||||
});
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [SubmissionSectionCoarNotifyComponent],
|
||||
providers: [
|
||||
{ provide: LdnServicesService, useValue: ldnServicesService },
|
||||
{ provide: CoarNotifyConfigDataService, useValue: coarNotifyConfigDataService},
|
||||
{ provide: JsonPatchOperationsBuilder, useValue: operationsBuilder },
|
||||
{ provide: SectionsService, useValue: sectionService },
|
||||
{ provide: ChangeDetectorRef, useValue: cdRefStub },
|
||||
{ provide: 'collectionIdProvider', useValue: 'collectionId' },
|
||||
{ provide: 'sectionDataProvider', useValue: { id: 'sectionId', data: {} }},
|
||||
{ provide: 'submissionIdProvider', useValue: 'submissionId' },
|
||||
NgbDropdown,
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SubmissionSectionCoarNotifyComponent);
|
||||
component = fixture.componentInstance;
|
||||
componentAsAny = component;
|
||||
|
||||
component.patterns = patterns[0].patterns;
|
||||
coarNotifyConfigDataService.findAll.and.returnValue(coarNotifyConfig);
|
||||
sectionService.getSectionServerErrors.and.returnValue(
|
||||
of(
|
||||
Object.assign([], {
|
||||
path: 'sections/sectionId/data/notifyCoar',
|
||||
message: 'error',
|
||||
})
|
||||
)
|
||||
);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('onSectionInit', () => {
|
||||
it('should call setCoarNotifyConfig and getSectionServerErrorsAndSetErrorsToDisplay', () => {
|
||||
spyOn(component, 'setCoarNotifyConfig');
|
||||
spyOn(componentAsAny, 'getSectionServerErrorsAndSetErrorsToDisplay');
|
||||
|
||||
component.onSectionInit();
|
||||
|
||||
expect(component.setCoarNotifyConfig).toHaveBeenCalled();
|
||||
expect(componentAsAny.getSectionServerErrorsAndSetErrorsToDisplay).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onChange', () => {
|
||||
const pattern = 'review';
|
||||
const index = 0;
|
||||
const selectedService: LdnService = Object.assign(new LdnService(), {
|
||||
id: 1,
|
||||
name: 'service1',
|
||||
notifyServiceInboundPatterns: [
|
||||
{
|
||||
pattern: 'review',
|
||||
},
|
||||
],
|
||||
description: '',
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
component.ldnServiceByPattern[pattern] = [];
|
||||
});
|
||||
|
||||
it('should do nothing if the selected value is the same as the previous one', () => {
|
||||
component.ldnServiceByPattern[pattern][index] = selectedService;
|
||||
component.onChange(pattern, index, selectedService);
|
||||
|
||||
expect(componentAsAny.operationsBuilder.remove).not.toHaveBeenCalled();
|
||||
expect(componentAsAny.operationsBuilder.replace).not.toHaveBeenCalled();
|
||||
expect(componentAsAny.operationsBuilder.add).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should remove the path when the selected value is null', () => {
|
||||
component.ldnServiceByPattern[pattern][index] = selectedService;
|
||||
component.onChange(pattern, index, null);
|
||||
|
||||
expect(componentAsAny.operationsBuilder.remove).toHaveBeenCalledWith(
|
||||
componentAsAny.pathCombiner.getPath([pattern, index.toString()])
|
||||
);
|
||||
expect(component.ldnServiceByPattern[pattern][index]).toBeNull();
|
||||
expect(component.previousServices[pattern][index]).toBeNull();
|
||||
});
|
||||
|
||||
it('should replace the path when there is a previous value stored and it is different from the new one', () => {
|
||||
const previousService: LdnService = Object.assign(new LdnService(), {
|
||||
id: 2,
|
||||
name: 'service2',
|
||||
notifyServiceInboundPatterns: [
|
||||
{
|
||||
pattern: 'endorsement',
|
||||
},
|
||||
],
|
||||
description: 'test',
|
||||
});
|
||||
component.ldnServiceByPattern[pattern][index] = previousService;
|
||||
component.previousServices[pattern] = [];
|
||||
component.previousServices[pattern][index] = previousService.id;
|
||||
component.onChange(pattern, index, selectedService);
|
||||
|
||||
expect(componentAsAny.operationsBuilder.replace).toHaveBeenCalledWith(
|
||||
componentAsAny.pathCombiner.getPath([pattern, index.toString()]),
|
||||
selectedService.id,
|
||||
true
|
||||
);
|
||||
expect(component.ldnServiceByPattern[pattern][index]).toEqual(
|
||||
selectedService
|
||||
);
|
||||
expect(component.previousServices[pattern][index]).toEqual(
|
||||
selectedService.id
|
||||
);
|
||||
});
|
||||
|
||||
it('should add the path when there is no previous value stored', () => {
|
||||
component.onChange(pattern, index, selectedService);
|
||||
|
||||
expect(componentAsAny.operationsBuilder.add).toHaveBeenCalledWith(
|
||||
componentAsAny.pathCombiner.getPath([pattern, '-']),
|
||||
[selectedService.id],
|
||||
false,
|
||||
true
|
||||
);
|
||||
expect(component.ldnServiceByPattern[pattern][index]).toEqual(
|
||||
selectedService
|
||||
);
|
||||
expect(component.previousServices[pattern][index]).toEqual(
|
||||
selectedService.id
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('initSelectedServicesByPattern', () => {
|
||||
const pattern1 = 'review';
|
||||
const pattern2 = 'endorsement';
|
||||
const service1: LdnService = Object.assign(new LdnService(), {
|
||||
id: 1,
|
||||
name: 'service1',
|
||||
notifyServiceInboundPatterns: [
|
||||
Object.assign(new NotifyServicePattern(), {
|
||||
pattern: pattern1,
|
||||
}),
|
||||
],
|
||||
});
|
||||
const service2: LdnService = Object.assign(new LdnService(), {
|
||||
id: 2,
|
||||
name: 'service2',
|
||||
notifyServiceInboundPatterns: [
|
||||
Object.assign(new NotifyServicePattern(), {
|
||||
pattern: pattern2,
|
||||
}),
|
||||
],
|
||||
});
|
||||
const service3: LdnService = Object.assign(new LdnService(), {
|
||||
id: 3,
|
||||
name: 'service3',
|
||||
notifyServiceInboundPatterns: [
|
||||
Object.assign(new NotifyServicePattern(), {
|
||||
pattern: pattern1,
|
||||
}),
|
||||
Object.assign(new NotifyServicePattern(), {
|
||||
pattern: pattern2,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const services = [service1, service2, service3];
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(component, 'filterServices').and.callFake((pattern) => {
|
||||
return of(
|
||||
services.filter((service) =>
|
||||
component.hasInboundPattern(service, pattern)
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should initialize the selected services by pattern', () => {
|
||||
component.patterns = [pattern1, pattern2];
|
||||
component.initSelectedServicesByPattern();
|
||||
|
||||
expect(component.ldnServiceByPattern[pattern1]).toEqual([null]);
|
||||
expect(component.ldnServiceByPattern[pattern2]).toEqual([null]);
|
||||
});
|
||||
|
||||
it('should add the service to the selected services by pattern if the section data has a value for the pattern', () => {
|
||||
component.patterns = [pattern1, pattern2];
|
||||
component.sectionData.data[pattern1] = [service1.id, service3.id];
|
||||
component.sectionData.data[pattern2] = [service2.id, service3.id];
|
||||
component.initSelectedServicesByPattern();
|
||||
|
||||
expect(component.ldnServiceByPattern[pattern1]).toEqual([
|
||||
service1,
|
||||
service3,
|
||||
]);
|
||||
expect(component.ldnServiceByPattern[pattern2]).toEqual([
|
||||
service2,
|
||||
service3,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addService', () => {
|
||||
const pattern = 'review';
|
||||
const service: any = {
|
||||
id: 1,
|
||||
name: 'service1',
|
||||
notifyServiceInboundPatterns: [{ pattern: pattern }],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
component.ldnServiceByPattern[pattern] = [];
|
||||
});
|
||||
|
||||
it('should push the new service to the array corresponding to the pattern', () => {
|
||||
component.addService(pattern, service);
|
||||
|
||||
expect(component.ldnServiceByPattern[pattern]).toEqual([service]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeService', () => {
|
||||
const pattern = 'review';
|
||||
const service1: LdnService = Object.assign(new LdnService(), {
|
||||
id: 1,
|
||||
name: 'service1',
|
||||
notifyServiceInboundPatterns: [
|
||||
Object.assign(new NotifyServicePattern(), {
|
||||
pattern: pattern,
|
||||
}),
|
||||
],
|
||||
});
|
||||
const service2: LdnService = Object.assign(new LdnService(), {
|
||||
id: 1,
|
||||
name: 'service1',
|
||||
notifyServiceInboundPatterns: [
|
||||
Object.assign(new NotifyServicePattern(), {
|
||||
pattern: pattern,
|
||||
}),
|
||||
],
|
||||
});
|
||||
const service3: LdnService = Object.assign(new LdnService(), {
|
||||
id: 1,
|
||||
name: 'service1',
|
||||
notifyServiceInboundPatterns: [
|
||||
Object.assign(new NotifyServicePattern(), {
|
||||
pattern: pattern,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
component.ldnServiceByPattern[pattern] = [service1, service2, service3];
|
||||
});
|
||||
|
||||
it('should remove the service at the specified index from the array corresponding to the pattern', () => {
|
||||
component.removeService(pattern, 1);
|
||||
|
||||
expect(component.ldnServiceByPattern[pattern]).toEqual([
|
||||
service1,
|
||||
service3,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterServices', () => {
|
||||
const pattern = 'review';
|
||||
const service1: any = {
|
||||
id: 1,
|
||||
name: 'service1',
|
||||
notifyServiceInboundPatterns: [{ pattern: pattern }],
|
||||
};
|
||||
const service2: any = {
|
||||
id: 2,
|
||||
name: 'service2',
|
||||
notifyServiceInboundPatterns: [{ pattern: pattern }],
|
||||
};
|
||||
const service3: any = {
|
||||
id: 3,
|
||||
name: 'service3',
|
||||
notifyServiceInboundPatterns: [{ pattern: pattern }],
|
||||
};
|
||||
const services = [service1, service2, service3];
|
||||
|
||||
beforeEach(() => {
|
||||
ldnServicesService.findByInboundPattern.and.returnValue(
|
||||
createSuccessfulRemoteDataObject$(createPaginatedList(services))
|
||||
);
|
||||
});
|
||||
|
||||
it('should return an observable of the services that match the given pattern', () => {
|
||||
component.filterServices(pattern).subscribe((result) => {
|
||||
expect(result).toEqual(services);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasInboundPattern', () => {
|
||||
const pattern = 'review';
|
||||
const service: any = {
|
||||
id: 1,
|
||||
name: 'service1',
|
||||
notifyServiceInboundPatterns: [{ pattern: pattern }],
|
||||
};
|
||||
|
||||
it('should return true if the service has the specified inbound pattern type', () => {
|
||||
expect(component.hasInboundPattern(service, pattern)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return false if the service does not have the specified inbound pattern type', () => {
|
||||
expect(component.hasInboundPattern(service, 'endorsement')).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSectionServerErrorsAndSetErrorsToDisplay', () => {
|
||||
it('should set the validation errors for the current section to display', () => {
|
||||
const validationErrors = [
|
||||
{ path: 'sections/sectionId/data/notifyCoar', message: 'error' },
|
||||
];
|
||||
sectionService.getSectionServerErrors.and.returnValue(
|
||||
of(validationErrors)
|
||||
);
|
||||
|
||||
componentAsAny.getSectionServerErrorsAndSetErrorsToDisplay();
|
||||
|
||||
expect(sectionService.setSectionError).toHaveBeenCalledWith(
|
||||
component.submissionId,
|
||||
component.sectionData.id,
|
||||
validationErrors[0]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onSectionDestroy', () => {
|
||||
it('should unsubscribe from all subscriptions', () => {
|
||||
const sub1 = of(null).subscribe();
|
||||
const sub2 = of(null).subscribe();
|
||||
componentAsAny.subs = [sub1, sub2];
|
||||
spyOn(sub1, 'unsubscribe');
|
||||
spyOn(sub2, 'unsubscribe');
|
||||
component.onSectionDestroy();
|
||||
expect(sub1.unsubscribe).toHaveBeenCalled();
|
||||
expect(sub2.unsubscribe).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,293 @@
|
||||
import { ChangeDetectorRef, Component, Inject } from '@angular/core';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { SectionModelComponent } from '../models/section.model';
|
||||
import { renderSectionFor } from '../sections-decorator';
|
||||
import { SectionsType } from '../sections-type';
|
||||
import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
||||
import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder';
|
||||
import { SectionsService } from '../sections.service';
|
||||
import { SectionDataObject } from '../models/section-data.model';
|
||||
|
||||
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, take, tap } from 'rxjs/operators';
|
||||
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SubmissionSectionError } from '../../objects/submission-section-error.model';
|
||||
|
||||
/**
|
||||
* This component represents a section that contains the submission section-coar-notify form.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-submission-section-coar-notify',
|
||||
templateUrl: './section-coar-notify.component.html',
|
||||
styleUrls: ['./section-coar-notify.component.scss'],
|
||||
providers: [NgbDropdown]
|
||||
})
|
||||
@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
|
||||
* {
|
||||
* 'pattern': {
|
||||
* 'index': 'service.id'
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @type {{ [key: string]: {[key: number]: number} }}
|
||||
* @memberof SubmissionSectionCoarNotifyComponent
|
||||
*/
|
||||
previousServices: { [key: string]: {[key: number]: number} } = {};
|
||||
|
||||
/**
|
||||
* The [[JsonPatchOperationPathCombiner]] object
|
||||
* @type {JsonPatchOperationPathCombiner}
|
||||
*/
|
||||
protected pathCombiner: JsonPatchOperationPathCombiner;
|
||||
/**
|
||||
* A map representing all field on their way to be removed
|
||||
* @type {Map}
|
||||
*/
|
||||
protected fieldsOnTheirWayToBeRemoved: Map<string, number[]> = new Map();
|
||||
/**
|
||||
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||
* @type {Array}
|
||||
*/
|
||||
protected subs: Subscription[] = [];
|
||||
|
||||
constructor(protected ldnServicesService: LdnServicesService,
|
||||
// protected formOperationsService: SectionFormOperationsService,
|
||||
protected operationsBuilder: JsonPatchOperationsBuilder,
|
||||
protected sectionService: SectionsService,
|
||||
protected coarNotifyConfigDataService: CoarNotifyConfigDataService,
|
||||
protected chd: ChangeDetectorRef,
|
||||
@Inject('collectionIdProvider') public injectedCollectionId: string,
|
||||
@Inject('sectionDataProvider') public injectedSectionData: SectionDataObject,
|
||||
@Inject('submissionIdProvider') public injectedSubmissionId: string) {
|
||||
super(injectedCollectionId, injectedSectionData, injectedSubmissionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all instance variables
|
||||
*/
|
||||
onSectionInit() {
|
||||
this.setCoarNotifyConfig();
|
||||
this.getSectionServerErrorsAndSetErrorsToDisplay();
|
||||
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when section is initialized
|
||||
* Retriev available NotifyConfigs
|
||||
*/
|
||||
setCoarNotifyConfig() {
|
||||
this.subs.push(
|
||||
this.coarNotifyConfigDataService.findAll().pipe(
|
||||
getFirstCompletedRemoteData()
|
||||
).subscribe((data) => {
|
||||
if (data.hasSucceeded) {
|
||||
this.patterns = data.payload.page[0].patterns;
|
||||
this.initSelectedServicesByPattern();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the change event of a select element.
|
||||
* @param pattern - The pattern of the select element.
|
||||
* @param index - The index of the select element.
|
||||
*/
|
||||
onChange(pattern: string, index: number, selectedService: LdnService | null) {
|
||||
// do nothing if the selected value is the same as the previous one
|
||||
if (this.ldnServiceByPattern[pattern][index]?.id === selectedService?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// initialize the previousServices object for the pattern if it does not exist
|
||||
if (!this.previousServices[pattern]) {
|
||||
this.previousServices[pattern] = {};
|
||||
}
|
||||
|
||||
if (hasNoValue(selectedService)) {
|
||||
// on value change, remove the path when the selected value is null
|
||||
// and remove the previous value stored for the same index and pattern
|
||||
this.operationsBuilder.remove(this.pathCombiner.getPath([pattern, index.toString()]));
|
||||
this.sectionService.dispatchRemoveSectionErrors(this.submissionId, this.sectionData.id);
|
||||
this.ldnServiceByPattern[pattern][index] = null;
|
||||
this.previousServices[pattern][index] = null;
|
||||
return;
|
||||
}
|
||||
// store the previous value
|
||||
this.previousServices[pattern][index] = this.ldnServiceByPattern[pattern][index]?.id;
|
||||
// set the new value
|
||||
this.ldnServiceByPattern[pattern][index] = selectedService;
|
||||
|
||||
const hasPrevValueStored = hasValue(this.previousServices[pattern][index]) && this.previousServices[pattern][index] !== selectedService.id;
|
||||
if (hasPrevValueStored) {
|
||||
// replace the path
|
||||
// when there is a previous value stored and it is different from the new one
|
||||
this.operationsBuilder.replace(this.pathCombiner.getPath([pattern, index.toString()]), selectedService.id, true);
|
||||
} else {
|
||||
// add the path when there is no previous value stored
|
||||
this.operationsBuilder.add(this.pathCombiner.getPath([pattern, '-']), [selectedService.id], false, true);
|
||||
}
|
||||
// set the previous value to the new value
|
||||
this.previousServices[pattern][index] = this.ldnServiceByPattern[pattern][index].id;
|
||||
this.sectionService.dispatchRemoveSectionErrors(this.submissionId, this.sectionData.id);
|
||||
this.chd.detectChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the selected services by pattern.
|
||||
* Loops through each pattern and filters the services based on the pattern.
|
||||
* If the section data has a value for the pattern, it adds the service to the selected services by pattern.
|
||||
* If the section data does not have a value for the pattern, it adds a null service to the selected services by pattern,
|
||||
* so that the select element is initialized with a null value and to display the default select input.
|
||||
*/
|
||||
initSelectedServicesByPattern(): void {
|
||||
this.patterns.forEach((pattern) => {
|
||||
if (hasValue(this.sectionData.data[pattern])) {
|
||||
this.subs.push(
|
||||
this.filterServices(pattern)
|
||||
.subscribe((services: LdnService[]) => {
|
||||
const selectedServices = services.filter((service) => {
|
||||
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);
|
||||
});
|
||||
this.ldnServiceByPattern[pattern] = selectedServices;
|
||||
})
|
||||
);
|
||||
} else {
|
||||
this.ldnServiceByPattern[pattern] = [];
|
||||
this.addService(pattern, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
if (!this.ldnServiceByPattern[pattern]) {
|
||||
this.ldnServiceByPattern[pattern] = [];
|
||||
}
|
||||
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
|
||||
this.ldnServiceByPattern[pattern].splice(serviceIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when dropdowns for the section are initialized
|
||||
* Retrieve services with corresponding patterns to the dropdowns.
|
||||
*/
|
||||
filterServices(pattern: string): Observable<LdnService[]> {
|
||||
return this.ldnServicesService.findByInboundPattern(pattern).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
tap((rd) => {
|
||||
if (rd.hasFailed) {
|
||||
throw new Error(`Failed to retrieve services for pattern ${pattern}`);
|
||||
}
|
||||
}),
|
||||
filter((rd) => rd.hasSucceeded),
|
||||
getRemoteDataPayload(),
|
||||
getPaginatedListPayload(),
|
||||
map((res: LdnService[]) => res.filter((service) =>
|
||||
this.hasInboundPattern(service, pattern)))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
import { CacheableObject } from '../../../core/cache/cacheable-object.model';
|
||||
import { autoserialize, deserialize, deserializeAs, inheritSerialization } from 'cerialize';
|
||||
|
||||
import { excludeFromEquals } from '../../../core/utilities/equals.decorators';
|
||||
import { typedObject } from '../../../core/cache/builders/build-decorators';
|
||||
import { COAR_NOTIFY_WORKSPACEITEM } from './section-coar-notify-service.resource-type';
|
||||
|
||||
|
||||
/** An CoarNotify and its properties. */
|
||||
@typedObject
|
||||
@inheritSerialization(CacheableObject)
|
||||
export class SubmissionCoarNotifyWorkspaceitemModel extends CacheableObject {
|
||||
static type = COAR_NOTIFY_WORKSPACEITEM;
|
||||
|
||||
@excludeFromEquals
|
||||
@autoserialize
|
||||
endorsement?: number[];
|
||||
|
||||
@deserializeAs('id')
|
||||
review?: number[];
|
||||
|
||||
@autoserialize
|
||||
ingest?: number[];
|
||||
|
||||
@deserialize
|
||||
_links: {
|
||||
self: {
|
||||
href: string;
|
||||
};
|
||||
};
|
||||
|
||||
get self(): string {
|
||||
return this._links.self.href;
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
import { ResourceType } from '../../../core/shared/resource-type';
|
||||
import { CacheableObject } from '../../../core/cache/cacheable-object.model';
|
||||
import { autoserialize, deserialize, deserializeAs, inheritSerialization } from 'cerialize';
|
||||
|
||||
import { excludeFromEquals } from '../../../core/utilities/equals.decorators';
|
||||
import { typedObject } from '../../../core/cache/builders/build-decorators';
|
||||
import { SUBMISSION_COAR_NOTIFY_CONFIG } from './section-coar-notify-service.resource-type';
|
||||
|
||||
|
||||
/** A SubmissionCoarNotifyConfig and its properties. */
|
||||
@typedObject
|
||||
@inheritSerialization(CacheableObject)
|
||||
export class SubmissionCoarNotifyConfig extends CacheableObject {
|
||||
static type = SUBMISSION_COAR_NOTIFY_CONFIG;
|
||||
|
||||
@excludeFromEquals
|
||||
@autoserialize
|
||||
type: ResourceType;
|
||||
|
||||
@autoserialize
|
||||
id: string;
|
||||
|
||||
@deserializeAs('id')
|
||||
uuid: string;
|
||||
|
||||
@autoserialize
|
||||
patterns: string[];
|
||||
|
||||
@deserialize
|
||||
_links: {
|
||||
self: {
|
||||
href: string;
|
||||
};
|
||||
};
|
||||
|
||||
get self(): string {
|
||||
return this._links.self.href;
|
||||
}
|
||||
}
|
@@ -9,4 +9,5 @@ export enum SectionsType {
|
||||
SherpaPolicies = 'sherpaPolicy',
|
||||
Identifiers = 'identifiers',
|
||||
Collection = 'collection',
|
||||
CoarNotify = 'coarnotify'
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ import { SubmissionFormFooterComponent } from './form/footer/submission-form-foo
|
||||
import { SubmissionFormComponent } from './form/submission-form.component';
|
||||
import { SubmissionFormSectionAddComponent } from './form/section-add/submission-form-section-add.component';
|
||||
import { SubmissionSectionContainerComponent } from './sections/container/section-container.component';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CommonModule, NgOptimizedImage } from '@angular/common';
|
||||
import { Action, StoreConfig, StoreModule } from '@ngrx/store';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import { submissionReducers, SubmissionState } from './submission.reducers';
|
||||
@@ -67,6 +67,11 @@ import {
|
||||
} from './sections/sherpa-policies/metadata-information/metadata-information.component';
|
||||
import { SectionFormOperationsService } from './sections/form/section-form-operations.service';
|
||||
import {SubmissionSectionIdentifiersComponent} from './sections/identifiers/section-identifiers.component';
|
||||
import { SubmissionSectionCoarNotifyComponent } from './sections/section-coar-notify/section-coar-notify.component';
|
||||
import {
|
||||
CoarNotifyConfigDataService
|
||||
} from './sections/section-coar-notify/coar-notify-config-data.service';
|
||||
import { LdnServicesService } from '../admin/admin-ldn-services/ldn-services-data/ldn-services-data.service';
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
// put only entry components that use custom decorator
|
||||
@@ -76,6 +81,7 @@ const ENTRY_COMPONENTS = [
|
||||
SubmissionSectionCcLicensesComponent,
|
||||
SubmissionSectionAccessesComponent,
|
||||
SubmissionSectionSherpaPoliciesComponent,
|
||||
SubmissionSectionCoarNotifyComponent
|
||||
];
|
||||
|
||||
const DECLARATIONS = [
|
||||
@@ -109,20 +115,22 @@ const DECLARATIONS = [
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
CoreModule.forRoot(),
|
||||
SharedModule,
|
||||
StoreModule.forFeature('submission', submissionReducers, storeModuleConfig as StoreConfig<SubmissionState, Action>),
|
||||
EffectsModule.forFeature(submissionEffects),
|
||||
JournalEntitiesModule.withEntryComponents(),
|
||||
ResearchEntitiesModule.withEntryComponents(),
|
||||
FormModule,
|
||||
NgbModalModule,
|
||||
NgbCollapseModule,
|
||||
NgbAccordionModule,
|
||||
UploadModule,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
CoreModule.forRoot(),
|
||||
SharedModule,
|
||||
StoreModule.forFeature('submission', submissionReducers, storeModuleConfig as StoreConfig<SubmissionState, Action>),
|
||||
EffectsModule.forFeature(),
|
||||
EffectsModule.forFeature(submissionEffects),
|
||||
JournalEntitiesModule.withEntryComponents(),
|
||||
ResearchEntitiesModule.withEntryComponents(),
|
||||
FormModule,
|
||||
NgbModalModule,
|
||||
NgbCollapseModule,
|
||||
NgbAccordionModule,
|
||||
UploadModule,
|
||||
NgOptimizedImage,
|
||||
],
|
||||
declarations: DECLARATIONS,
|
||||
exports: [
|
||||
...DECLARATIONS,
|
||||
@@ -135,6 +143,8 @@ const DECLARATIONS = [
|
||||
SubmissionAccessesConfigDataService,
|
||||
SectionAccessesService,
|
||||
SectionFormOperationsService,
|
||||
CoarNotifyConfigDataService,
|
||||
LdnServicesService
|
||||
]
|
||||
})
|
||||
|
||||
|
@@ -4,13 +4,19 @@
|
||||
<h2 class="border-bottom pb-2">
|
||||
<div class="d-flex justify-content-between">
|
||||
{{'notifications.events.title'| translate}}
|
||||
<a class="btn btn-outline-secondary" [routerLink]="['/admin/notifications/quality-assurance']">
|
||||
<a class="btn btn-outline-secondary" (click)="goBack()">
|
||||
<i class="fas fa-angle-double-left"></i>
|
||||
{{'quality-assurance.events.back' | translate}}
|
||||
</a>
|
||||
</div>
|
||||
</h2>
|
||||
<ds-alert [type]="'alert-info'" [content]="'quality-assurance.events.description'"></ds-alert>
|
||||
<ds-alert *ngIf="!targetId" [type]="'alert-info'">
|
||||
<span [innerHTML]="'quality-assurance.events.description' | translate : {topic: selectedTopicName, source: sourceId}"></span>
|
||||
</ds-alert>
|
||||
<ds-alert *ngIf="targetId" [type]="'alert-info'">
|
||||
<span [innerHTML]="'quality-assurance.events.description-with-topic-and-target' | translate : {topic: selectedTopicName, source: sourceId}"></span>
|
||||
<a [routerLink]="itemPageUrl" target="_blank">{{(getTargetItemTitle() | async)}}</a>
|
||||
</ds-alert>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -54,6 +60,16 @@
|
||||
target="_blank"
|
||||
[routerLink]="['/items', eventElement?.target?.id]">{{eventElement.title}}</a>
|
||||
<span *ngIf="!eventElement?.target">{{eventElement.title}}</span>
|
||||
<div *ngIf="eventElement?.event?.message?.serviceName">
|
||||
<span class="small pr-1">{{'quality-assurance.event.table.event.message.serviceName' | translate}}</span>
|
||||
<span class="badge badge-info">{{eventElement.event.message.serviceName}}</span>
|
||||
</div>
|
||||
<div *ngIf="eventElement?.event?.message?.href" class="d-flex align-items-center">
|
||||
<span class="small pr-1">{{'quality-assurance.event.table.event.message.link' | translate}}</span>
|
||||
<span [title]="eventElement.event.message.href" class="text-truncate d-inline-block w-75">
|
||||
<a [href]="eventElement.event.message.href" target="_blank">{{eventElement.event.message.href}}</a>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td *ngIf="showTopic.indexOf('/PID') !== -1">
|
||||
<p><span class="small">{{'quality-assurance.event.table.pidtype' | translate}}</span> <span class="badge badge-info">{{eventElement.event.message.type}}</span></p>
|
||||
@@ -157,7 +173,7 @@
|
||||
</div>
|
||||
<div class="row text-right">
|
||||
<div class="col-md-12">
|
||||
<a class="btn btn-outline-secondary" [routerLink]="['/admin/notifications/quality-assurance']">
|
||||
<a class="btn btn-outline-secondary" (click)="goBack()">
|
||||
<i class="fas fa-angle-double-left"></i>
|
||||
{{'quality-assurance.events.back' | translate}}
|
||||
</a>
|
||||
|
@@ -42,6 +42,7 @@ import { SortDirection, SortOptions } from '../../../core/cache/models/sort-opti
|
||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
||||
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||
import { ItemDataService } from 'src/app/core/data/item-data.service';
|
||||
|
||||
describe('QualityAssuranceEventsComponent test suite', () => {
|
||||
let fixture: ComponentFixture<QualityAssuranceEventsComponent>;
|
||||
@@ -118,6 +119,7 @@ describe('QualityAssuranceEventsComponent test suite', () => {
|
||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||
{ provide: TranslateService, useValue: getMockTranslateService() },
|
||||
{ provide: PaginationService, useValue: paginationService },
|
||||
{ provide: ItemDataService, useValue: {} },
|
||||
QualityAssuranceEventsComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Location } from '@angular/common';
|
||||
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@@ -26,10 +27,12 @@ import {
|
||||
ProjectEntryImportModalComponent,
|
||||
QualityAssuranceEventData
|
||||
} from '../project-entry-import-modal/project-entry-import-modal.component';
|
||||
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
||||
import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../core/shared/operators';
|
||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||
import { getItemPageRoute } from '../../../item-page/item-page-routing-paths';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
|
||||
/**
|
||||
* Component to display the Quality Assurance event list.
|
||||
@@ -105,6 +108,26 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
protected subs: Subscription[] = [];
|
||||
|
||||
/**
|
||||
* The target item id, retrieved from the topic-id composition.
|
||||
*/
|
||||
public targetId: string;
|
||||
|
||||
/**
|
||||
* The URL of the item page/target.
|
||||
*/
|
||||
public itemPageUrl: string;
|
||||
|
||||
/**
|
||||
* Plain topic name (without the source id)
|
||||
*/
|
||||
public selectedTopicName: string;
|
||||
|
||||
/**
|
||||
* The source id, retrieved from the topic-id composition.
|
||||
*/
|
||||
public sourceId: string;
|
||||
|
||||
/**
|
||||
* Initialize the component variables.
|
||||
* @param {ActivatedRoute} activatedRoute
|
||||
@@ -120,7 +143,9 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
|
||||
private notificationsService: NotificationsService,
|
||||
private qualityAssuranceEventRestService: QualityAssuranceEventDataService,
|
||||
private paginationService: PaginationService,
|
||||
private translateService: TranslateService
|
||||
private translateService: TranslateService,
|
||||
private itemService: ItemDataService,
|
||||
private _location: Location
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -137,10 +162,13 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
|
||||
const regEx = /!/g;
|
||||
this.showTopic = id.replace(regEx, '/');
|
||||
this.topic = id;
|
||||
const splitList = this.showTopic?.split(':');
|
||||
this.targetId = splitList.length > 2 ? splitList.pop() : null;
|
||||
this.sourceId = splitList[0];
|
||||
this.selectedTopicName = splitList[1];
|
||||
return this.getQualityAssuranceEvents();
|
||||
})
|
||||
).subscribe((events: QualityAssuranceEventData[]) => {
|
||||
console.log(events);
|
||||
this.eventsUpdated$.next(events);
|
||||
this.isEventPageLoading.next(false);
|
||||
});
|
||||
@@ -356,7 +384,6 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
|
||||
if (rd.hasSucceeded) {
|
||||
this.totalElements$.next(rd.payload.totalElements);
|
||||
if (rd.payload.totalElements > 0) {
|
||||
console.log(rd.payload.page);
|
||||
return this.fetchEvents(rd.payload.page);
|
||||
} else {
|
||||
return of([]);
|
||||
@@ -425,4 +452,37 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
|
||||
last()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page route for the given item.
|
||||
* @param item The item to get the page route for.
|
||||
* @returns The page route for the given item.
|
||||
*/
|
||||
public getItemPageRoute(item: Item): string {
|
||||
return getItemPageRoute(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Observable that emits the title of the target item.
|
||||
* The target item is retrieved by its ID using the itemService.
|
||||
* The title is extracted from the first metadata value of the item.
|
||||
* The item page URL is also set in the component.
|
||||
* @returns An Observable that emits the title of the target item.
|
||||
*/
|
||||
public getTargetItemTitle(): Observable<string> {
|
||||
return this.itemService.findById(this.targetId).pipe(
|
||||
take(1),
|
||||
getFirstCompletedRemoteData(),
|
||||
getRemoteDataPayload(),
|
||||
tap((item: Item) => this.itemPageUrl = getItemPageRoute(item)),
|
||||
map((item: Item) => item.firstMetadataValue('dc.title'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous location in the browser's history stack.
|
||||
*/
|
||||
public goBack() {
|
||||
this._location.back();
|
||||
}
|
||||
}
|
||||
|
@@ -25,6 +25,8 @@ export class RetrieveAllTopicsAction implements Action {
|
||||
payload: {
|
||||
elementsPerPage: number;
|
||||
currentPage: number;
|
||||
source: string;
|
||||
target?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -35,10 +37,12 @@ export class RetrieveAllTopicsAction implements Action {
|
||||
* @param currentPage
|
||||
* The page number to retrieve
|
||||
*/
|
||||
constructor(elementsPerPage: number, currentPage: number) {
|
||||
constructor(elementsPerPage: number, currentPage: number, source: string, target?: string) {
|
||||
this.payload = {
|
||||
elementsPerPage,
|
||||
currentPage
|
||||
currentPage,
|
||||
source,
|
||||
target
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,11 @@
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h2 class="border-bottom pb-2">{{'quality-assurance.title'| translate}}</h2>
|
||||
<ds-alert [type]="'alert-info'">{{'quality-assurance.topics.description'| translate:{source: sourceId} }}</ds-alert>
|
||||
<ds-alert *ngIf="!targetId" [type]="'alert-info'">{{'quality-assurance.topics.description'| translate:{source: sourceId} }}</ds-alert>
|
||||
<ds-alert *ngIf="targetId" [type]="'alert-info'">
|
||||
{{'quality-assurance.topics.description-with-target'| translate:{source: sourceId} }}
|
||||
<a [routerLink]="itemPageUrl">{{(getTargetItemTitle() | async)}}</a>
|
||||
</ds-alert>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -15,7 +19,7 @@
|
||||
[collectionSize]="(totalElements$ | async)"
|
||||
[hideGear]="false"
|
||||
[hideSortOptions]="true"
|
||||
(paginationChange)="getQualityAssuranceTopics()">
|
||||
(paginationChange)="getQualityAssuranceTopics(sourceId, targetId)">
|
||||
|
||||
<ds-loading class="container" *ngIf="(isTopicsProcessing() | async)" message="'quality-assurance.loading' | translate"></ds-loading>
|
||||
<ng-container *ngIf="!(isTopicsProcessing() | async)">
|
||||
@@ -40,7 +44,7 @@
|
||||
<button
|
||||
class="btn btn-outline-primary btn-sm"
|
||||
title="{{'quality-assurance.button.detail' | translate }}"
|
||||
[routerLink]="[topicElement.id]">
|
||||
[routerLink]="['/admin/notifications/quality-assurance', sourceId, topicElement.id]">
|
||||
<span class="badge badge-info">{{topicElement.totalEvents}}</span>
|
||||
<i class="fas fa-info fa-fw"></i>
|
||||
</button>
|
||||
|
@@ -16,7 +16,7 @@ import { SuggestionNotificationsStateService } from '../../suggestion-notificati
|
||||
import { cold } from 'jasmine-marbles';
|
||||
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||
import { QualityAssuranceTopicsService } from './quality-assurance-topics.service';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
|
||||
describe('QualityAssuranceTopicsComponent test suite', () => {
|
||||
let fixture: ComponentFixture<QualityAssuranceTopicsComponent>;
|
||||
@@ -44,14 +44,14 @@ describe('QualityAssuranceTopicsComponent test suite', () => {
|
||||
providers: [
|
||||
{ provide: SuggestionNotificationsStateService, useValue: mockNotificationsStateService },
|
||||
{ provide: ActivatedRoute, useValue: { data: observableOf(activatedRouteParams), snapshot: {
|
||||
paramMap: {
|
||||
get: () => 'openaire',
|
||||
params: {
|
||||
sourceId: 'openaire',
|
||||
targetId: null
|
||||
},
|
||||
}}},
|
||||
{ provide: PaginationService, useValue: paginationService },
|
||||
{ provide: ItemDataService, useValue: {} },
|
||||
QualityAssuranceTopicsComponent,
|
||||
// tslint:disable-next-line: no-empty
|
||||
{ provide: QualityAssuranceTopicsService, useValue: { setSourceId: (sourceId: string) => { } }}
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents().then(() => {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { distinctUntilChanged, take } from 'rxjs/operators';
|
||||
import { distinctUntilChanged, map, take, tap } from 'rxjs/operators';
|
||||
|
||||
import { SortOptions } from '../../../core/cache/models/sort-options.model';
|
||||
import {
|
||||
@@ -15,7 +15,10 @@ import {
|
||||
} from '../../../admin/admin-notifications/admin-quality-assurance-topics-page/admin-quality-assurance-topics-page-resolver.service';
|
||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { QualityAssuranceTopicsService } from './quality-assurance-topics.service';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../core/shared/operators';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { getItemPageRoute } from '../../../item-page/item-page-routing-paths';
|
||||
|
||||
/**
|
||||
* Component to display the Quality Assurance topic list.
|
||||
@@ -60,6 +63,17 @@ export class QualityAssuranceTopicsComponent implements OnInit {
|
||||
*/
|
||||
public sourceId: string;
|
||||
|
||||
/**
|
||||
* This property represents a targetId (item-id) which is used to retrive a topic
|
||||
* @type {string}
|
||||
*/
|
||||
public targetId: string;
|
||||
|
||||
/**
|
||||
* The URL of the item page.
|
||||
*/
|
||||
public itemPageUrl: string;
|
||||
|
||||
/**
|
||||
* Initialize the component variables.
|
||||
* @param {PaginationService} paginationService
|
||||
@@ -71,16 +85,16 @@ export class QualityAssuranceTopicsComponent implements OnInit {
|
||||
private paginationService: PaginationService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private notificationsStateService: SuggestionNotificationsStateService,
|
||||
private qualityAssuranceTopicsService: QualityAssuranceTopicsService
|
||||
private itemService: ItemDataService
|
||||
) {
|
||||
this.sourceId = this.activatedRoute.snapshot.params.sourceId;
|
||||
this.targetId = this.activatedRoute.snapshot.params.targetId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component initialization.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.sourceId = this.activatedRoute.snapshot.paramMap.get('sourceId');
|
||||
this.qualityAssuranceTopicsService.setSourceId(this.sourceId);
|
||||
this.topics$ = this.notificationsStateService.getQualityAssuranceTopics();
|
||||
this.totalElements$ = this.notificationsStateService.getQualityAssuranceTopicsTotals();
|
||||
}
|
||||
@@ -93,7 +107,7 @@ export class QualityAssuranceTopicsComponent implements OnInit {
|
||||
this.notificationsStateService.isQualityAssuranceTopicsLoaded().pipe(
|
||||
take(1)
|
||||
).subscribe(() => {
|
||||
this.getQualityAssuranceTopics();
|
||||
this.getQualityAssuranceTopics(this.sourceId, this.targetId);
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -121,13 +135,15 @@ export class QualityAssuranceTopicsComponent implements OnInit {
|
||||
/**
|
||||
* Dispatch the Quality Assurance topics retrival.
|
||||
*/
|
||||
public getQualityAssuranceTopics(): void {
|
||||
public getQualityAssuranceTopics(source: string, target?: string): void {
|
||||
this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig).pipe(
|
||||
distinctUntilChanged(),
|
||||
).subscribe((options: PaginationComponentOptions) => {
|
||||
this.notificationsStateService.dispatchRetrieveQualityAssuranceTopics(
|
||||
options.pageSize,
|
||||
options.currentPage
|
||||
options.currentPage,
|
||||
source,
|
||||
target
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -150,6 +166,32 @@ export class QualityAssuranceTopicsComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Observable that emits the title of the target item.
|
||||
* The target item is retrieved by its ID using the itemService.
|
||||
* The title is extracted from the first metadata value of the item.
|
||||
* The item page URL is also set in the component.
|
||||
* @returns An Observable that emits the title of the target item.
|
||||
*/
|
||||
getTargetItemTitle(): Observable<string> {
|
||||
return this.itemService.findById(this.targetId).pipe(
|
||||
take(1),
|
||||
getFirstCompletedRemoteData(),
|
||||
getRemoteDataPayload(),
|
||||
tap((item: Item) => this.itemPageUrl = getItemPageRoute(item)),
|
||||
map((item: Item) => item.firstMetadataValue('dc.title'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page route for the given item.
|
||||
* @param item The item to get the page route for.
|
||||
* @returns The page route for the given item.
|
||||
*/
|
||||
getItemPageRoute(item: Item): string {
|
||||
return getItemPageRoute(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from all subscriptions.
|
||||
*/
|
||||
|
@@ -37,7 +37,9 @@ export class QualityAssuranceTopicsEffects {
|
||||
switchMap(([action, currentState]: [RetrieveAllTopicsAction, any]) => {
|
||||
return this.qualityAssuranceTopicService.getTopics(
|
||||
action.payload.elementsPerPage,
|
||||
action.payload.currentPage
|
||||
action.payload.currentPage,
|
||||
action.payload.source,
|
||||
action.payload.target
|
||||
).pipe(
|
||||
map((topics: PaginatedList<QualityAssuranceTopicObject>) =>
|
||||
new AddTopicsAction(topics.page, topics.totalPages, topics.currentPage, topics.totalElements)
|
||||
|
@@ -29,7 +29,7 @@ describe('qualityAssuranceTopicsReducer test suite', () => {
|
||||
const expectedState = qualityAssuranceTopicInitialState;
|
||||
expectedState.processing = true;
|
||||
|
||||
const action = new RetrieveAllTopicsAction(elementPerPage, currentPage);
|
||||
const action = new RetrieveAllTopicsAction(elementPerPage, currentPage, 'source', 'target');
|
||||
const newState = qualityAssuranceTopicsReducer(qualityAssuranceTopicInitialState, action);
|
||||
|
||||
expect(newState).toEqual(expectedState);
|
||||
|
@@ -42,31 +42,46 @@ describe('QualityAssuranceTopicsService', () => {
|
||||
beforeEach(() => {
|
||||
restService = TestBed.inject(QualityAssuranceTopicDataService);
|
||||
restServiceAsAny = restService;
|
||||
restServiceAsAny.getTopics.and.returnValue(observableOf(paginatedListRD));
|
||||
restServiceAsAny.searchTopicsBySource.and.returnValue(observableOf(paginatedListRD));
|
||||
restServiceAsAny.searchTopicsByTarget.and.returnValue(observableOf(paginatedListRD));
|
||||
service = new QualityAssuranceTopicsService(restService);
|
||||
serviceAsAny = service;
|
||||
});
|
||||
|
||||
describe('getTopics', () => {
|
||||
it('Should proxy the call to qualityAssuranceTopicRestService.getTopics', () => {
|
||||
describe('getTopicsBySource', () => {
|
||||
it('should proxy the call to qualityAssuranceTopicRestService.searchTopicsBySource', () => {
|
||||
const sortOptions = new SortOptions('name', SortDirection.ASC);
|
||||
const findListOptions: FindListOptions = {
|
||||
elementsPerPage: elementsPerPage,
|
||||
currentPage: currentPage,
|
||||
sort: sortOptions,
|
||||
searchParams: [new RequestParam('source', 'ENRICH!MORE!ABSTRACT')]
|
||||
searchParams: [new RequestParam('source', 'openaire')]
|
||||
};
|
||||
service.setSourceId('ENRICH!MORE!ABSTRACT');
|
||||
const result = service.getTopics(elementsPerPage, currentPage);
|
||||
expect((service as any).qualityAssuranceTopicRestService.getTopics).toHaveBeenCalledWith(findListOptions);
|
||||
const result = service.getTopics(elementsPerPage, currentPage, 'openaire');
|
||||
expect((service as any).qualityAssuranceTopicRestService.searchTopicsBySource).toHaveBeenCalledWith(findListOptions);
|
||||
});
|
||||
|
||||
it('Should return a paginated list of Quality Assurance topics', () => {
|
||||
it('should return a paginated list of Quality Assurance topics', () => {
|
||||
const expected = cold('(a|)', {
|
||||
a: paginatedList
|
||||
});
|
||||
const result = service.getTopics(elementsPerPage, currentPage);
|
||||
const result = service.getTopics(elementsPerPage, currentPage, 'openaire');
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
|
||||
it('should include targetId in searchParams if set', () => {
|
||||
const sortOptions = new SortOptions('name', SortDirection.ASC);
|
||||
const findListOptions: FindListOptions = {
|
||||
elementsPerPage: elementsPerPage,
|
||||
currentPage: currentPage,
|
||||
sort: sortOptions,
|
||||
searchParams: [
|
||||
new RequestParam('source', 'openaire'),
|
||||
new RequestParam('target', '0000-0000-0000-0000-0000')
|
||||
]
|
||||
};
|
||||
const result = service.getTopics(elementsPerPage, currentPage,'openaire', '0000-0000-0000-0000-0000');
|
||||
expect((service as any).qualityAssuranceTopicRestService.searchTopicsByTarget).toHaveBeenCalledWith(findListOptions);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -13,6 +13,7 @@ import {
|
||||
import { RequestParam } from '../../../core/cache/models/request-param.model';
|
||||
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
||||
import { hasValue } from '../../../shared/empty.util';
|
||||
|
||||
/**
|
||||
* The service handling all Quality Assurance topic requests to the REST service.
|
||||
@@ -28,10 +29,6 @@ export class QualityAssuranceTopicsService {
|
||||
private qualityAssuranceTopicRestService: QualityAssuranceTopicDataService
|
||||
) { }
|
||||
|
||||
/**
|
||||
* sourceId used to get topics
|
||||
*/
|
||||
sourceId: string;
|
||||
|
||||
/**
|
||||
* Return the list of Quality Assurance topics managing pagination and errors.
|
||||
@@ -43,17 +40,25 @@ export class QualityAssuranceTopicsService {
|
||||
* @return Observable<PaginatedList<QualityAssuranceTopicObject>>
|
||||
* The list of Quality Assurance topics.
|
||||
*/
|
||||
public getTopics(elementsPerPage, currentPage): Observable<PaginatedList<QualityAssuranceTopicObject>> {
|
||||
public getTopics(elementsPerPage, currentPage, source: string, target?: string): Observable<PaginatedList<QualityAssuranceTopicObject>> {
|
||||
const sortOptions = new SortOptions('name', SortDirection.ASC);
|
||||
|
||||
const findListOptions: FindListOptions = {
|
||||
elementsPerPage: elementsPerPage,
|
||||
currentPage: currentPage,
|
||||
sort: sortOptions,
|
||||
searchParams: [new RequestParam('source', this.sourceId)]
|
||||
searchParams: [new RequestParam('source', source)]
|
||||
};
|
||||
|
||||
return this.qualityAssuranceTopicRestService.getTopics(findListOptions).pipe(
|
||||
let request$: Observable<RemoteData<PaginatedList<QualityAssuranceTopicObject>>>;
|
||||
|
||||
if (hasValue(target)) {
|
||||
findListOptions.searchParams.push(new RequestParam('target', target));
|
||||
request$ = this.qualityAssuranceTopicRestService.searchTopicsByTarget(findListOptions);
|
||||
} else {
|
||||
request$ = this.qualityAssuranceTopicRestService.searchTopicsBySource(findListOptions);
|
||||
}
|
||||
|
||||
return request$.pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
map((rd: RemoteData<PaginatedList<QualityAssuranceTopicObject>>) => {
|
||||
if (rd.hasSucceeded) {
|
||||
@@ -64,12 +69,4 @@ export class QualityAssuranceTopicsService {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* set sourceId which is used to get topics
|
||||
* @param sourceId string
|
||||
*/
|
||||
setSourceId(sourceId: string) {
|
||||
this.sourceId = sourceId;
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,8 @@ describe('SuggestionsPopupComponent', () => {
|
||||
const suggestionStateService = jasmine.createSpyObj('SuggestionTargetsStateService', {
|
||||
hasUserVisitedSuggestions: jasmine.createSpy('hasUserVisitedSuggestions'),
|
||||
getCurrentUserSuggestionTargets: jasmine.createSpy('getCurrentUserSuggestionTargets'),
|
||||
dispatchMarkUserSuggestionsAsVisitedAction: jasmine.createSpy('dispatchMarkUserSuggestionsAsVisitedAction')
|
||||
dispatchMarkUserSuggestionsAsVisitedAction: jasmine.createSpy('dispatchMarkUserSuggestionsAsVisitedAction'),
|
||||
dispatchRefreshUserSuggestionsAction: jasmine.createSpy('dispatchRefreshUserSuggestionsAction')
|
||||
});
|
||||
|
||||
const mockNotificationInterpolation = { count: 12, source: 'source', suggestionId: 'id', displayName: 'displayName' };
|
||||
|
@@ -31,7 +31,6 @@ export class SuggestionsPopupComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public initializePopup() {
|
||||
console.log('POPUP INIT dispatchRefreshUserSuggestionsAction');
|
||||
this.reciterSuggestionStateService.dispatchRefreshUserSuggestionsAction();
|
||||
const notifier = new Subject();
|
||||
this.subscription = combineLatest([
|
||||
|
@@ -271,8 +271,8 @@ describe('NotificationsStateService', () => {
|
||||
it('Should call store.dispatch', () => {
|
||||
const elementsPerPage = 3;
|
||||
const currentPage = 1;
|
||||
const action = new RetrieveAllTopicsAction(elementsPerPage, currentPage);
|
||||
service.dispatchRetrieveQualityAssuranceTopics(elementsPerPage, currentPage);
|
||||
const action = new RetrieveAllTopicsAction(elementsPerPage, currentPage, 'source', 'target');
|
||||
service.dispatchRetrieveQualityAssuranceTopics(elementsPerPage, currentPage, 'source', 'target');
|
||||
expect(serviceAsAny.store.dispatch).toHaveBeenCalledWith(action);
|
||||
});
|
||||
});
|
||||
|
@@ -118,8 +118,8 @@ export class SuggestionNotificationsStateService {
|
||||
* @param currentPage
|
||||
* The number of the current page.
|
||||
*/
|
||||
public dispatchRetrieveQualityAssuranceTopics(elementsPerPage: number, currentPage: number): void {
|
||||
this.store.dispatch(new RetrieveAllTopicsAction(elementsPerPage, currentPage));
|
||||
public dispatchRetrieveQualityAssuranceTopics(elementsPerPage: number, currentPage: number, sourceId: string, targteId?: string): void {
|
||||
this.store.dispatch(new RetrieveAllTopicsAction(elementsPerPage, currentPage, sourceId, targteId));
|
||||
}
|
||||
|
||||
// Quality Assurance source
|
||||
|
@@ -5,7 +5,6 @@ import { SuggestionsPageResolver } from './suggestions-page.resolver';
|
||||
import { SuggestionsPageComponent } from './suggestions-page.component';
|
||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
||||
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||
import { SiteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@@ -892,11 +892,11 @@
|
||||
|
||||
"coar-notify-support.title": "COAR Notify Protocol",
|
||||
|
||||
"coar-notify-support-title.content":"Here, we fully support the COAR Notify protocol, which is designed to enhance the communication between repositories. To learn more about the COAR Notify protocol, you can visit their official website <a href=\"https://notify.coar-repositories.org/\">here</a>.",
|
||||
"coar-notify-support-title.content": "Here, we fully support the COAR Notify protocol, which is designed to enhance the communication between repositories. To learn more about the COAR Notify protocol, you can visit their official website <a href=\"https://notify.coar-repositories.org/\">here</a>.",
|
||||
|
||||
"coar-notify-support.ldn-inbox.title": "LDN InBox",
|
||||
|
||||
"coar-notify-support.ldn-inbox.content": "For your convenience, our LDN (Linked Data Notifications) InBox is easily accessible at <code>{restApiUrl}ldn/inbox</code>. The LDN InBox enables seamless communication and data exchange, ensuring efficient and effective collaboration.",
|
||||
"coar-notify-support.ldn-inbox.content": "For your convenience, our LDN (Linked Data Notifications) InBox is easily accessible at <code>{ldnInboxUrl}ldn/inbox</code>. The LDN InBox enables seamless communication and data exchange, ensuring efficient and effective collaboration.",
|
||||
|
||||
"coar-notify-support.message-moderation.title": "Message Moderation",
|
||||
|
||||
@@ -905,7 +905,7 @@
|
||||
"service.overview.delete.header": "Delete Service",
|
||||
|
||||
"ldn-registered-services.title": "Registered Services",
|
||||
"ldn-registered-services.table.name":"Name",
|
||||
"ldn-registered-services.table.name": "Name",
|
||||
"ldn-registered-services.table.description": "Description",
|
||||
"ldn-registered-services.table.status": "Status",
|
||||
"ldn-registered-services.table.action": "Action",
|
||||
@@ -920,7 +920,7 @@
|
||||
"ldn-edit-registered-service.title": "Edit Service",
|
||||
"ldn-create-service.title": "Create service",
|
||||
"service.overview.create.modal": "Create Service",
|
||||
"service.overview.create.body": "Please confirm the creation of this service",
|
||||
"service.overview.create.body": "Please confirm the creation of this service.",
|
||||
"ldn-service-status": "Status",
|
||||
"service.confirm.create": "Create",
|
||||
"service.refuse.create": "Discard",
|
||||
@@ -929,16 +929,77 @@
|
||||
"ldn-new-service.form.label.name": "Name",
|
||||
"ldn-new-service.form.label.description": "Description",
|
||||
"ldn-new-service.form.label.url": "Service URL",
|
||||
"ldn-new-service.form.label.score": "Level of trust",
|
||||
"ldn-new-service.form.label.ldnUrl": "LDN Inbox URL",
|
||||
"ldn-new-service.form.placeholder.name": "Please provide service name",
|
||||
"ldn-new-service.form.placeholder.description": "Please provide a description regarding your service",
|
||||
"ldn-new-service.form.placeholder.url": "Please input the URL for users to check out more information about the service",
|
||||
"ldn-new-service.form.placeholder.ldnUrl": "Please specify the URL of the LDN Inbox",
|
||||
"ldn-new-service.form.label.inboundPattern": "Inbound Patterns",
|
||||
"ldn-new-service.form.label.placeholder.inboundPattern": "Select an Inbound Pattern",
|
||||
"ldn-new-service.form.placeholder.score": "Please enter a value between 0 and 1. Use the “.” as decimal separator",
|
||||
"ldn-service.form.label.placeholder.default-select": "Select a pattern",
|
||||
|
||||
"ldn-service.form.pattern.ack-accept.label": "Acknowledge and Accept",
|
||||
"ldn-service.form.pattern.ack-accept.description": "This pattern is used to acknowledge and accept a request (offer). It implies an intention to act on the request.",
|
||||
"ldn-service.form.pattern.ack-accept.category": "Acknowledgements",
|
||||
|
||||
"ldn-service.form.pattern.ack-reject.label": "Acknowledge and Reject",
|
||||
"ldn-service.form.pattern.ack-reject.description": "This pattern is used to acknowledge and reject a request (offer). It signifies no further action regarding the request.",
|
||||
"ldn-service.form.pattern.ack-reject.category": "Acknowledgements",
|
||||
|
||||
"ldn-service.form.pattern.ack-tentative-accept.label": "Acknowledge and Tentatively Accept",
|
||||
"ldn-service.form.pattern.ack-tentative-accept.description": "This pattern is used to acknowledge and tentatively accept a request (offer). It implies an intention to act, which may change.",
|
||||
"ldn-service.form.pattern.ack-tentative-accept.category": "Acknowledgements",
|
||||
|
||||
"ldn-service.form.pattern.ack-tentative-reject.label": "Acknowledge and Tentatively Reject",
|
||||
"ldn-service.form.pattern.ack-tentative-reject.description": "This pattern is used to acknowledge and tentatively reject a request (offer). It signifies no further action, subject to change.",
|
||||
"ldn-service.form.pattern.ack-tentative-reject.category": "Acknowledgements",
|
||||
|
||||
"ldn-service.form.pattern.announce-endorsement.label": "Announce Endorsement",
|
||||
"ldn-service.form.pattern.announce-endorsement.description": "This pattern is used to announce the existence of an endorsement, referencing the endorsed resource.",
|
||||
"ldn-service.form.pattern.announce-endorsement.category": "Announcements",
|
||||
|
||||
"ldn-service.form.pattern.announce-ingest.label": "Announce Ingest",
|
||||
"ldn-service.form.pattern.announce-ingest.description": "This pattern is used to announce that a resource has been ingested.",
|
||||
"ldn-service.form.pattern.announce-ingest.category": "Announcements",
|
||||
|
||||
"ldn-service.form.pattern.announce-relationship.label": "Announce Relationship",
|
||||
"ldn-service.form.pattern.announce-relationship.description": "This pattern is used to announce a relationship between two resources.",
|
||||
"ldn-service.form.pattern.announce-relationship.category": "Announcements",
|
||||
|
||||
"ldn-service.form.pattern.announce-review.label": "Announce Review",
|
||||
"ldn-service.form.pattern.announce-review.description": "This pattern is used to announce the existence of a review, referencing the reviewed resource.",
|
||||
"ldn-service.form.pattern.announce-review.category": "Announcements",
|
||||
|
||||
"ldn-service.form.pattern.announce-service-result.label": "Announce Service Result",
|
||||
"ldn-service.form.pattern.announce-service-result.description": "This pattern is used to announce the existence of a 'service result', referencing the relevant resource.",
|
||||
"ldn-service.form.pattern.announce-service-result.category": "Announcements",
|
||||
|
||||
"ldn-service.form.pattern.request-endorsement.label": "Request Endorsement",
|
||||
"ldn-service.form.pattern.request-endorsement.description": "This pattern is used to request endorsement of a resource owned by the origin system.",
|
||||
"ldn-service.form.pattern.request-endorsement.category": "Requests",
|
||||
|
||||
"ldn-service.form.pattern.request-ingest.label": "Request Ingest",
|
||||
"ldn-service.form.pattern.request-ingest.description": "This pattern is used to request that the target system ingest a resource.",
|
||||
"ldn-service.form.pattern.request-ingest.category": "Requests",
|
||||
|
||||
"ldn-service.form.pattern.request-review.label": "Request Review",
|
||||
"ldn-service.form.pattern.request-review.description": "This pattern is used to request a review of a resource owned by the origin system.",
|
||||
"ldn-service.form.pattern.request-review.category": "Requests",
|
||||
|
||||
"ldn-service.form.pattern.undo-offer.label": "Undo Offer",
|
||||
"ldn-service.form.pattern.undo-offer.description": "This pattern is used to undo (retract) an offer previously made.",
|
||||
"ldn-service.form.pattern.undo-offer.category": "Undo",
|
||||
|
||||
"ldn-new-service.form.label.placeholder.selectedItemFilter": "No Item Filter Selected",
|
||||
"ldn-new-service.form.label.ItemFilter": "Item Filter",
|
||||
"ldn-new-service.form.label.automatic": "Automatic",
|
||||
"ldn-new-service.form.error.name": "Name is required",
|
||||
"ldn-new-service.form.error.url": "URL is required",
|
||||
"ldn-new-service.form.error.ldnurl": "LDN URL is required",
|
||||
"ldn-new-service.form.error.patterns": "At least a pattern is required",
|
||||
"ldn-new-service.form.error.score": "Please enter a valid score (between 0 and 1). Use the “.” as decimal separator",
|
||||
|
||||
"ldn-new-service.form.label.inboundPattern": "Inbound Pattern",
|
||||
"ldn-new-service.form.label.outboundPattern": "Outbound Patterns",
|
||||
"ldn-new-service.form.label.placeholder.outboundPattern": "Select an Outbound Pattern",
|
||||
"ldn-new-service.form.label.addPattern": "+ Add more",
|
||||
@@ -951,11 +1012,15 @@
|
||||
"service.detail.return": "Cancel",
|
||||
"service.overview.reset-form.body": "Are you sure you want to discard those changes and leave?",
|
||||
"service.overview.reset-form.modal": "Discard Service Changes",
|
||||
"service.overview.reset-form.reset-confirm":"Discard",
|
||||
"service.overview.reset-form.reset-confirm": "Discard",
|
||||
"admin.registries.services-formats.modify.success.head": "Successful Edit",
|
||||
"admin.registries.services-formats.modify.success.content": "The service has been edited",
|
||||
"admin.registries.services-formats.modify.failure.head": "Failed Edit",
|
||||
"admin.registries.services-formats.modify.failure.content": "The service has not been edited",
|
||||
"ldn-service-notification.created.success.title": "Successful Create",
|
||||
"ldn-service-notification.created.success.body": "The service has been created",
|
||||
"ldn-service-notification.created.failure.title": "Failed Create",
|
||||
"ldn-service-notification.created.failure.body": "The service has not been created",
|
||||
"ldn-enable-service.notification.success.title": "Successful status updated",
|
||||
"ldn-enable-service.notification.success.content": "The service status has been updated",
|
||||
"ldn-service-delete.notification.success.title": "Successful Deletion",
|
||||
@@ -1978,10 +2043,12 @@
|
||||
|
||||
"info.feedback.breadcrumbs": "Feedback",
|
||||
|
||||
"info.coar-notify-support.title":"Notify Support",
|
||||
"info.coar-notify-support.title": "Notify Support",
|
||||
|
||||
"info.coar-notify.breadcrumbs": "Notify Support",
|
||||
|
||||
"submission.sections.notify.info": "The selected service is compatible with the item according to its current status. {{ service.name }}: {{ service.description }}",
|
||||
|
||||
"info.feedback.head": "Feedback",
|
||||
|
||||
"info.feedback.title": "Feedback",
|
||||
@@ -2472,6 +2539,14 @@
|
||||
|
||||
"item.truncatable-part.show-less": "Collapse",
|
||||
|
||||
"item.qa-event-notification.check.notification-info": "There are {{num}} pending review to check",
|
||||
|
||||
"item.qa-event-notification-info.check.button": "Check",
|
||||
|
||||
"mydspace.qa-event-notification.check.notification-info": "There are {{num}} pending review to check",
|
||||
|
||||
"mydspace.qa-event-notification-info.check.button": "Check",
|
||||
|
||||
"workflow-item.search.result.delete-supervision.modal.header": "Delete Supervision Order",
|
||||
|
||||
"workflow-item.search.result.delete-supervision.modal.info": "Are you sure you want to delete Supervision Order",
|
||||
@@ -2494,6 +2569,12 @@
|
||||
|
||||
"item.page.citation": "Citation",
|
||||
|
||||
"item.page.endorsed-by": "Endorsement",
|
||||
|
||||
"item.page.is-reviewed-by": "Review",
|
||||
|
||||
"item.page.is-supplemented-by": "Dataset",
|
||||
|
||||
"item.page.collections": "Collections",
|
||||
|
||||
"item.page.collections.loading": "Loading...",
|
||||
@@ -3210,6 +3291,8 @@
|
||||
|
||||
"quality-assurance.topics.description": "Below you can see all the topics received from the subscriptions to {{source}}.",
|
||||
|
||||
"quality-assurance.topics.description-with-target": "Below you can see all the topics received from the subscriptions to {{source}} in regards to the",
|
||||
|
||||
"quality-assurance.source.description": "Below you can see all the notification's sources.",
|
||||
|
||||
"quality-assurance.topics": "Current Topics",
|
||||
@@ -3236,7 +3319,9 @@
|
||||
|
||||
"quality-assurance.source.error.service.retrieve": "An error occurred while loading the Quality Assurance source",
|
||||
|
||||
"quality-assurance.events.description": "Below the list of all the suggestions for the selected topic.",
|
||||
"quality-assurance.events.description": "Below the list of all the suggestions for the selected topic <b>{{topic}}</b>, related to <b>{{source}}</b>.",
|
||||
|
||||
"quality-assurance.events.description-with-topic-and-target": "Below the list of all the suggestions for the selected topic <b>{{topic}}</b>, related to <b>{{source}}</b> and ",
|
||||
|
||||
"quality-assurance.loading": "Loading ...",
|
||||
|
||||
@@ -3290,6 +3375,10 @@
|
||||
|
||||
"quality-assurance.event.table.more": "Show more",
|
||||
|
||||
"quality-assurance.event.table.event.message.serviceName": "Service Name:",
|
||||
|
||||
"quality-assurance.event.table.event.message.link": "Link:",
|
||||
|
||||
"quality-assurance.event.project.found": "Bound to the local record:",
|
||||
|
||||
"quality-assurance.event.project.notFound": "No local record found",
|
||||
@@ -4760,6 +4849,8 @@
|
||||
|
||||
"submission.sections.submit.progressbar.sherpaPolicies": "Publisher open access policy information",
|
||||
|
||||
"submission.sections.submit.progressbar.coarnotify": "COAR Notify",
|
||||
|
||||
"submission.sections.sherpa-policy.title-empty": "No publisher policy information available. If your work has an associated ISSN, please enter it above to see any related publisher open access policies.",
|
||||
|
||||
"submission.sections.status.errors.title": "Errors",
|
||||
@@ -5006,6 +5097,28 @@
|
||||
|
||||
"submission.workspace.generic.view-help": "Select this option to view the item's metadata.",
|
||||
|
||||
"submission.section.section-coar-notify.control.request-review.label": "You can request a review to one of the following services",
|
||||
|
||||
"submission.section.section-coar-notify.control.request-endorsement.label": "You can request an Endorsement to one of the following overlay journals",
|
||||
|
||||
"submission.section.section-coar-notify.control.request-ingest.label": "You can request to ingest a copy of your submission to one of the following services",
|
||||
|
||||
"submission.section.section-coar-notify.dropdown.no-data": "No data available",
|
||||
|
||||
"submission.section.section-coar-notify.dropdown.select-none": "Select none",
|
||||
|
||||
"submission.section.section-coar-notify.small.notification": "Select a service for {{ pattern }} of this item",
|
||||
|
||||
"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",
|
||||
|
@@ -3723,6 +3723,22 @@
|
||||
// "item.truncatable-part.show-less": "Collapse",
|
||||
"item.truncatable-part.show-less": "Riduci",
|
||||
|
||||
// "item.qa-event-notification.check.notification-info": "There are {{num}} pending review to check",
|
||||
// TODO New key - Add a translation
|
||||
"item.qa-event-notification.check.notification-info": "There are {{num}} pending review to check",
|
||||
|
||||
// "item.qa-event-notification-info.check.button": "Check",
|
||||
// TODO New key - Add a translation
|
||||
"item.qa-event-notification-info.check.button": "Check",
|
||||
|
||||
// "mydspace.qa-event-notification.check.notification-info": "There are {{num}} pending review to check",
|
||||
// TODO New key - Add a translation
|
||||
"mydspace.qa-event-notification.check.notification-info": "There are {{num}} pending review to check",
|
||||
|
||||
// "mydspace.qa-event-notification-info.check.button": "Check",
|
||||
// TODO New key - Add a translation
|
||||
"mydspace.qa-event-notification-info.check.button": "Check",
|
||||
|
||||
// "workflow-item.search.result.delete-supervision.modal.header": "Delete Supervision Order",
|
||||
// TODO New key - Add a translation
|
||||
"workflow-item.search.result.delete-supervision.modal.header": "Delete Supervision Order",
|
||||
@@ -7461,6 +7477,25 @@
|
||||
// "submission.workspace.generic.view-help": "Select this option to view the item's metadata.",
|
||||
"submission.workspace.generic.view-help": "Seleziona questa opzione per vedere i metadata dell'item.",
|
||||
|
||||
// "submission.section.section-coar-notify.dropdown.no-data": "No data available",
|
||||
// TODO New key - a translation
|
||||
"submission.section.section-coar-notify.dropdown.no-data": "No data available",
|
||||
|
||||
// "submission.section.section-coar-notify.dropdown.select-none": "Select none",
|
||||
// TODO New key - a translation
|
||||
"submission.section.section-coar-notify.dropdown.select-none": "Select none",
|
||||
|
||||
// "submission.section.section-coar-notify.small.notification": "Select a service for {{ pattern }} of this item",
|
||||
// TODO New key - a translation
|
||||
"submission.section.section-coar-notify.small.notification": "Select a service for {{ pattern }} of this item",
|
||||
|
||||
// "submission.section.section-coar-notify.selection.description": "Selected service's description:",
|
||||
// TODO New key - a translation
|
||||
"submission.section.section-coar-notify.selection.description": "Selected service's description:",
|
||||
|
||||
// "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.",
|
||||
// TODO New key - a translation
|
||||
"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.",
|
||||
|
||||
// "subscriptions.title": "Subscriptions",
|
||||
"subscriptions.title": "Sottoscrizioni",
|
||||
|
BIN
src/assets/images/qa-coar-notify-logo.png
Normal file
BIN
src/assets/images/qa-coar-notify-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
src/assets/images/qa-openaire-logo.png
Normal file
BIN
src/assets/images/qa-openaire-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user