mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-17 23:13:04 +00:00
Merge branch 'coar-notify-7-part-two' of bitbucket.org:4Science/dspace-angular into coar-notify-7-part-two
This commit is contained in:
@@ -8,11 +8,6 @@ describe('Header', () => {
|
||||
cy.get('ds-header').should('be.visible');
|
||||
|
||||
// Analyze <ds-header> for accessibility
|
||||
testA11y({
|
||||
include: ['ds-header'],
|
||||
exclude: [
|
||||
['#search-navbar-container'] // search in navbar has duplicative ID. Will be fixed in #1174
|
||||
],
|
||||
});
|
||||
testA11y('ds-header');
|
||||
});
|
||||
});
|
||||
|
@@ -121,7 +121,7 @@
|
||||
"ngx-infinite-scroll": "^15.0.0",
|
||||
"ngx-pagination": "6.0.3",
|
||||
"ngx-sortablejs": "^11.1.0",
|
||||
"ngx-ui-switch": "^14.0.3",
|
||||
"ngx-ui-switch": "^14.1.0",
|
||||
"nouislider": "^15.7.1",
|
||||
"pem": "1.14.7",
|
||||
"prop-types": "^15.8.1",
|
||||
|
@@ -1,15 +1,15 @@
|
||||
<ngb-accordion #acc="ngbAccordion" [activeIds]="'browse'">
|
||||
<ngb-panel [id]="'browse'">
|
||||
<ng-template ngbPanelHeader>
|
||||
<div class="w-100 d-flex justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('browse')"
|
||||
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('browse')"
|
||||
data-test="browse">
|
||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()"
|
||||
[attr.aria-expanded]="!acc.isExpanded('browse')"
|
||||
aria-controls="collapsePanels">
|
||||
[attr.aria-expanded]="acc.isExpanded('browse')"
|
||||
aria-controls="bulk-access-browse-panel-content">
|
||||
{{ 'admin.access-control.bulk-access-browse.header' | translate }}
|
||||
</button>
|
||||
<div class="text-right d-flex">
|
||||
<div class="ml-3 d-inline-block">
|
||||
<div class="text-right d-flex gap-2">
|
||||
<div class="d-flex my-auto">
|
||||
<span *ngIf="acc.isExpanded('browse')" class="fas fa-chevron-up fa-fw"></span>
|
||||
<span *ngIf="!acc.isExpanded('browse')" class="fas fa-chevron-down fa-fw"></span>
|
||||
</div>
|
||||
@@ -17,51 +17,53 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="activateId" class="nav-pills">
|
||||
<li [ngbNavItem]="'search'">
|
||||
<a ngbNavLink>{{'admin.access-control.bulk-access-browse.search.header' | translate}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="mx-n3">
|
||||
<ds-themed-search [configuration]="'administrativeBulkAccess'"
|
||||
[selectable]="true"
|
||||
[selectionConfig]="{ repeatable: true, listId: listId }"
|
||||
[showThumbnails]="false"></ds-themed-search>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li [ngbNavItem]="'selected'">
|
||||
<a ngbNavLink>
|
||||
{{'admin.access-control.bulk-access-browse.selected.header' | translate: {number: ((objectsSelected$ | async)?.payload?.totalElements) ? (objectsSelected$ | async)?.payload?.totalElements : '0'} }}
|
||||
</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ds-pagination
|
||||
[paginationOptions]="(paginationOptions$ | async)"
|
||||
[pageInfoState]="(objectsSelected$|async)?.payload.pageInfo"
|
||||
[collectionSize]="(objectsSelected$|async)?.payload?.totalElements"
|
||||
[objects]="(objectsSelected$|async)"
|
||||
[showPaginator]="false"
|
||||
(prev)="pagePrev()"
|
||||
(next)="pageNext()">
|
||||
<ul *ngIf="(objectsSelected$|async)?.hasSucceeded" class="list-unstyled ml-4">
|
||||
<li *ngFor='let object of (objectsSelected$|async)?.payload?.page | paginate: { itemsPerPage: (paginationOptions$ | async).pageSize,
|
||||
currentPage: (paginationOptions$ | async).currentPage, totalItems: (objectsSelected$|async)?.payload?.page.length }; let i = index; let last = last '
|
||||
class="mt-4 mb-4 d-flex"
|
||||
[attr.data-test]="'list-object' | dsBrowserOnly">
|
||||
<ds-selectable-list-item-control [index]="i"
|
||||
[object]="object"
|
||||
[selectionConfig]="{ repeatable: true, listId: listId }"></ds-selectable-list-item-control>
|
||||
<ds-listable-object-component-loader [listID]="listId"
|
||||
[index]="i"
|
||||
[object]="object"
|
||||
[showThumbnails]="false"
|
||||
[viewMode]="'list'"></ds-listable-object-component-loader>
|
||||
</li>
|
||||
</ul>
|
||||
</ds-pagination>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
<div [ngbNavOutlet]="nav" class="mt-5"></div>
|
||||
<div id="bulk-access-browse-panel-content">
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="activateId" class="nav-pills">
|
||||
<li [ngbNavItem]="'search'" role="presentation">
|
||||
<a ngbNavLink>{{'admin.access-control.bulk-access-browse.search.header' | translate}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="mx-n3">
|
||||
<ds-themed-search [configuration]="'administrativeBulkAccess'"
|
||||
[selectable]="true"
|
||||
[selectionConfig]="{ repeatable: true, listId: listId }"
|
||||
[showThumbnails]="false"></ds-themed-search>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li [ngbNavItem]="'selected'" role="presentation">
|
||||
<a ngbNavLink>
|
||||
{{'admin.access-control.bulk-access-browse.selected.header' | translate: {number: ((objectsSelected$ | async)?.payload?.totalElements) ? (objectsSelected$ | async)?.payload?.totalElements : '0'} }}
|
||||
</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ds-pagination
|
||||
[paginationOptions]="(paginationOptions$ | async)"
|
||||
[pageInfoState]="(objectsSelected$|async)?.payload.pageInfo"
|
||||
[collectionSize]="(objectsSelected$|async)?.payload?.totalElements"
|
||||
[objects]="(objectsSelected$|async)"
|
||||
[showPaginator]="false"
|
||||
(prev)="pagePrev()"
|
||||
(next)="pageNext()">
|
||||
<ul *ngIf="(objectsSelected$|async)?.hasSucceeded" class="list-unstyled ml-4">
|
||||
<li *ngFor='let object of (objectsSelected$|async)?.payload?.page | paginate: { itemsPerPage: (paginationOptions$ | async).pageSize,
|
||||
currentPage: (paginationOptions$ | async).currentPage, totalItems: (objectsSelected$|async)?.payload?.page.length }; let i = index; let last = last '
|
||||
class="mt-4 mb-4 d-flex"
|
||||
[attr.data-test]="'list-object' | dsBrowserOnly">
|
||||
<ds-selectable-list-item-control [index]="i"
|
||||
[object]="object"
|
||||
[selectionConfig]="{ repeatable: true, listId: listId }"></ds-selectable-list-item-control>
|
||||
<ds-listable-object-component-loader [listID]="listId"
|
||||
[index]="i"
|
||||
[object]="object"
|
||||
[showThumbnails]="false"
|
||||
[viewMode]="'list'"></ds-listable-object-component-loader>
|
||||
</li>
|
||||
</ul>
|
||||
</ds-pagination>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
<div [ngbNavOutlet]="nav" class="mt-5"></div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
</ngb-accordion>
|
||||
|
@@ -1,13 +1,13 @@
|
||||
<ngb-accordion #acc="ngbAccordion" [activeIds]="'settings'">
|
||||
<ngb-panel [id]="'settings'">
|
||||
<ng-template ngbPanelHeader>
|
||||
<div class="w-100 d-flex justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('settings')" data-test="settings">
|
||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!acc.isExpanded('browse')"
|
||||
aria-controls="collapsePanels">
|
||||
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('settings')" data-test="settings">
|
||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="acc.isExpanded('settings')"
|
||||
aria-controls="bulk-access-settings-panel-content">
|
||||
{{ 'admin.access-control.bulk-access-settings.header' | translate }}
|
||||
</button>
|
||||
<div class="text-right d-flex">
|
||||
<div class="ml-3 d-inline-block">
|
||||
<div class="text-right d-flex gap-2">
|
||||
<div class="d-flex my-auto">
|
||||
<span *ngIf="acc.isExpanded('settings')" class="fas fa-chevron-up fa-fw"></span>
|
||||
<span *ngIf="!acc.isExpanded('settings')" class="fas fa-chevron-down fa-fw"></span>
|
||||
</div>
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<ds-access-control-form-container #dsAccessControlForm [showSubmit]="false"></ds-access-control-form-container>
|
||||
<ds-access-control-form-container id="bulk-access-settings-panel-content" #dsAccessControlForm [showSubmit]="false"></ds-access-control-form-container>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
</ngb-accordion>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="epeople-registry row">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between border-bottom mb-3">
|
||||
<h2 id="header" class="pb-2">{{labelPrefix + 'head' | translate}}</h2>
|
||||
<h1 id="header" class="pb-2">{{labelPrefix + 'head' | translate}}</h1>
|
||||
|
||||
<div>
|
||||
<button class="mr-auto btn btn-success addEPerson-button"
|
||||
@@ -13,9 +13,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 id="search" class="border-bottom pb-2">{{labelPrefix + 'search.head' | translate}}
|
||||
|
||||
</h3>
|
||||
<h2 id="search" class="border-bottom pb-2">
|
||||
{{labelPrefix + 'search.head' | translate}}
|
||||
</h2>
|
||||
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
||||
<div>
|
||||
<select name="scope" id="scope" formControlName="scope" class="form-control" aria-label="Search scope">
|
||||
|
@@ -5,11 +5,11 @@
|
||||
<div *ngIf="epersonService.getActiveEPerson() | async; then editHeader; else createHeader"></div>
|
||||
|
||||
<ng-template #createHeader>
|
||||
<h2 class="border-bottom pb-2">{{messagePrefix + '.create' | translate}}</h2>
|
||||
<h1 class="border-bottom pb-2">{{messagePrefix + '.create' | translate}}</h1>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #editHeader>
|
||||
<h2 class="border-bottom pb-2">{{messagePrefix + '.edit' | translate}}</h2>
|
||||
<h1 class="border-bottom pb-2">{{messagePrefix + '.edit' | translate}}</h1>
|
||||
</ng-template>
|
||||
|
||||
<ds-form [formId]="formId"
|
||||
@@ -45,7 +45,7 @@
|
||||
<ds-themed-loading [showMessage]="false" *ngIf="!formGroup"></ds-themed-loading>
|
||||
|
||||
<div *ngIf="epersonService.getActiveEPerson() | async">
|
||||
<h5>{{messagePrefix + '.groupsEPersonIsMemberOf' | translate}}</h5>
|
||||
<h2>{{messagePrefix + '.groupsEPersonIsMemberOf' | translate}}</h2>
|
||||
|
||||
<ds-themed-loading [showMessage]="false" *ngIf="!(groups | async)"></ds-themed-loading>
|
||||
|
||||
|
@@ -5,11 +5,11 @@
|
||||
<div *ngIf="groupDataService.getActiveGroup() | async; then editHeader; else createHeader"></div>
|
||||
|
||||
<ng-template #createHeader>
|
||||
<h2 class="border-bottom pb-2">{{messagePrefix + '.head.create' | translate}}</h2>
|
||||
<h1 class="border-bottom pb-2">{{messagePrefix + '.head.create' | translate}}</h1>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #editHeader>
|
||||
<h2 class="border-bottom pb-2">
|
||||
<h1 class="border-bottom pb-2">
|
||||
<span
|
||||
*dsContextHelp="{
|
||||
content: 'admin.access-control.groups.form.tooltip.editGroupPage',
|
||||
@@ -20,7 +20,7 @@
|
||||
>
|
||||
{{messagePrefix + '.head.edit' | translate}}
|
||||
</span>
|
||||
</h2>
|
||||
</h1>
|
||||
</ng-template>
|
||||
|
||||
<ds-alert *ngIf="groupBeingEdited?.permanent" [type]="AlertTypeEnum.Warning"
|
||||
@@ -39,9 +39,8 @@
|
||||
<button (click)="onCancel()" type="button"
|
||||
class="btn btn-outline-secondary"><i class="fas fa-arrow-left"></i> {{messagePrefix + '.return' | translate}}</button>
|
||||
</div>
|
||||
<div after *ngIf="(canEdit$ | async) && !groupBeingEdited.permanent" class="btn-group">
|
||||
<button class="btn btn-danger delete-button" [disabled]="!(canEdit$ | async) || groupBeingEdited.permanent"
|
||||
(click)="delete()" type="button">
|
||||
<div after *ngIf="(canEdit$ | async) && !groupBeingEdited?.permanent" class="btn-group">
|
||||
<button (click)="delete()" class="btn btn-danger delete-button" type="button">
|
||||
<i class="fa fa-trash"></i> {{ messagePrefix + '.actions.delete' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<ng-container>
|
||||
<h3 class="border-bottom pb-2">{{messagePrefix + '.head' | translate}}</h3>
|
||||
<h2 class="border-bottom pb-2">{{messagePrefix + '.head' | translate}}</h2>
|
||||
|
||||
<h4>{{messagePrefix + '.headMembers' | translate}}</h4>
|
||||
<h3>{{messagePrefix + '.headMembers' | translate}}</h3>
|
||||
|
||||
<ds-pagination *ngIf="(ePeopleMembersOfGroup | async)?.totalElements > 0"
|
||||
[paginationOptions]="config"
|
||||
@@ -55,7 +55,7 @@
|
||||
{{messagePrefix + '.no-members-yet' | translate}}
|
||||
</div>
|
||||
|
||||
<h4 id="search" class="border-bottom pb-2">
|
||||
<h3 id="search" class="border-bottom pb-2">
|
||||
<span
|
||||
*dsContextHelp="{
|
||||
content: 'admin.access-control.groups.form.tooltip.editGroup.addEpeople',
|
||||
@@ -66,7 +66,7 @@
|
||||
>
|
||||
{{messagePrefix + '.search.head' | translate}}
|
||||
</span>
|
||||
</h4>
|
||||
</h3>
|
||||
|
||||
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
||||
<div class="flex-grow-1 mr-3">
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="groups-registry row">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between border-bottom mb-3">
|
||||
<h2 id="header" class="pb-2">{{messagePrefix + 'head' | translate}}</h2>
|
||||
<h1 id="header" class="pb-2">{{messagePrefix + 'head' | translate}}</h1>
|
||||
<div>
|
||||
<button class="mr-auto btn btn-success"
|
||||
[routerLink]="'create'">
|
||||
@@ -12,7 +12,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 id="search" class="border-bottom pb-2">{{messagePrefix + 'search.head' | translate}}</h3>
|
||||
<h2 id="search" class="border-bottom pb-2">{{messagePrefix + 'search.head' | translate}}</h2>
|
||||
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
||||
<div class="flex-grow-1 mr-3">
|
||||
<div class="form-group input-group">
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<div class="container">
|
||||
<h2>{{'admin.curation-tasks.header' |translate }}</h2>
|
||||
<h1>{{'admin.curation-tasks.header' |translate }}</h1>
|
||||
<ds-curation-form></ds-curation-form>
|
||||
</div>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<div class="container">
|
||||
<h2 id="header">{{'admin.metadata-import.page.header' | translate}}</h2>
|
||||
<h1 id="header">{{'admin.metadata-import.page.header' | translate}}</h1>
|
||||
<p>{{'admin.metadata-import.page.help' | translate}}</p>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
|
@@ -7,6 +7,11 @@ import {typedObject} from '../../../core/cache/builders/build-decorators';
|
||||
import {NotifyServicePattern} from './ldn-service-patterns.model';
|
||||
|
||||
|
||||
export interface LdnServiceByPattern {
|
||||
allowsMultipleRequests: boolean;
|
||||
services: LdnService[];
|
||||
}
|
||||
|
||||
/** An LdnService and its properties. */
|
||||
@typedObject
|
||||
@inheritSerialization(CacheableObject)
|
||||
|
@@ -1,9 +0,0 @@
|
||||
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
||||
import { getNotificationsModuleRoute } from '../admin-routing-paths';
|
||||
|
||||
export const QUALITY_ASSURANCE_EDIT_PATH = 'quality-assurance';
|
||||
export const NOTIFICATIONS_RECITER_SUGGESTION_PATH = 'suggestion-targets';
|
||||
|
||||
export function getQualityAssuranceRoute(id: string) {
|
||||
return new URLCombiner(getNotificationsModuleRoute(), QUALITY_ASSURANCE_EDIT_PATH, id).toString();
|
||||
}
|
@@ -1,115 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { AuthenticatedGuard } from '../../core/auth/authenticated.guard';
|
||||
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||
import { I18nBreadcrumbsService } from '../../core/breadcrumbs/i18n-breadcrumbs.service';
|
||||
import { NOTIFICATIONS_RECITER_SUGGESTION_PATH } from './admin-notifications-routing-paths';
|
||||
import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component';
|
||||
import { AdminNotificationsSuggestionTargetsPageResolver } from './admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page-resolver.service';
|
||||
import { QUALITY_ASSURANCE_EDIT_PATH } from './admin-notifications-routing-paths';
|
||||
import { AdminQualityAssuranceTopicsPageComponent } from './admin-quality-assurance-topics-page/admin-quality-assurance-topics-page.component';
|
||||
import { AdminQualityAssuranceEventsPageComponent } from './admin-quality-assurance-events-page/admin-quality-assurance-events-page.component';
|
||||
import { AdminQualityAssuranceTopicsPageResolver } from './admin-quality-assurance-topics-page/admin-quality-assurance-topics-page-resolver.service';
|
||||
import { AdminQualityAssuranceEventsPageResolver } from './admin-quality-assurance-events-page/admin-quality-assurance-events-page.resolver';
|
||||
import { AdminQualityAssuranceSourcePageComponent } from './admin-quality-assurance-source-page-component/admin-quality-assurance-source-page.component';
|
||||
import { AdminQualityAssuranceSourcePageResolver } from './admin-quality-assurance-source-page-component/admin-quality-assurance-source-page-resolver.service';
|
||||
import { SourceDataResolver } from './admin-quality-assurance-source-page-component/admin-quality-assurance-source-data.reslover';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild([
|
||||
{
|
||||
canActivate: [ AuthenticatedGuard ],
|
||||
path: `${NOTIFICATIONS_RECITER_SUGGESTION_PATH}`,
|
||||
component: AdminNotificationsSuggestionTargetsPageComponent,
|
||||
pathMatch: 'full',
|
||||
resolve: {
|
||||
breadcrumb: I18nBreadcrumbResolver,
|
||||
reciterSuggestionTargetParams: AdminNotificationsSuggestionTargetsPageResolver
|
||||
},
|
||||
data: {
|
||||
title: 'admin.notifications.recitersuggestion.page.title',
|
||||
breadcrumbKey: 'admin.notifications.recitersuggestion',
|
||||
showBreadcrumbsFluid: false
|
||||
}
|
||||
},
|
||||
{
|
||||
canActivate: [ AuthenticatedGuard ],
|
||||
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId`,
|
||||
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}/: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}`,
|
||||
component: AdminQualityAssuranceSourcePageComponent,
|
||||
pathMatch: 'full',
|
||||
resolve: {
|
||||
breadcrumb: I18nBreadcrumbResolver,
|
||||
openaireQualityAssuranceSourceParams: AdminQualityAssuranceSourcePageResolver,
|
||||
sourceData: SourceDataResolver
|
||||
},
|
||||
data: {
|
||||
title: 'admin.notifications.source.breadcrumbs',
|
||||
breadcrumbKey: 'admin.notifications.source',
|
||||
showBreadcrumbsFluid: false
|
||||
}
|
||||
},
|
||||
{
|
||||
canActivate: [ AuthenticatedGuard ],
|
||||
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId/:topicId`,
|
||||
component: AdminQualityAssuranceEventsPageComponent,
|
||||
pathMatch: 'full',
|
||||
resolve: {
|
||||
breadcrumb: I18nBreadcrumbResolver,
|
||||
openaireQualityAssuranceEventsParams: AdminQualityAssuranceEventsPageResolver
|
||||
},
|
||||
data: {
|
||||
title: 'admin.notifications.event.page.title',
|
||||
breadcrumbKey: 'admin.notifications.event',
|
||||
showBreadcrumbsFluid: false
|
||||
}
|
||||
}
|
||||
])
|
||||
],
|
||||
providers: [
|
||||
I18nBreadcrumbResolver,
|
||||
I18nBreadcrumbsService,
|
||||
AdminNotificationsSuggestionTargetsPageResolver,
|
||||
SourceDataResolver,
|
||||
AdminQualityAssuranceSourcePageResolver,
|
||||
AdminQualityAssuranceTopicsPageResolver,
|
||||
AdminQualityAssuranceEventsPageResolver,
|
||||
]
|
||||
})
|
||||
/**
|
||||
* Routing module for the Notifications section of the admin sidebar
|
||||
*/
|
||||
export class AdminNotificationsRoutingModule {
|
||||
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-admin-notifications-reciter-page',
|
||||
templateUrl: './admin-notifications-suggestion-targets-page.component.html',
|
||||
styleUrls: ['./admin-notifications-suggestion-targets-page.component.scss']
|
||||
})
|
||||
export class AdminNotificationsSuggestionTargetsPageComponent {
|
||||
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CoreModule } from '../../core/core.module';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { AdminNotificationsRoutingModule } from './admin-notifications-routing.module';
|
||||
import { AdminNotificationsSuggestionTargetsPageComponent } from './admin-notifications-suggestion-targets-page/admin-notifications-suggestion-targets-page.component';
|
||||
import { AdminQualityAssuranceTopicsPageComponent } from './admin-quality-assurance-topics-page/admin-quality-assurance-topics-page.component';
|
||||
import { AdminQualityAssuranceEventsPageComponent } from './admin-quality-assurance-events-page/admin-quality-assurance-events-page.component';
|
||||
import { AdminQualityAssuranceSourcePageComponent } from './admin-quality-assurance-source-page-component/admin-quality-assurance-source-page.component';
|
||||
import {SuggestionNotificationsModule} from '../../suggestion-notifications/suggestion-notifications.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
CoreModule.forRoot(),
|
||||
AdminNotificationsRoutingModule,
|
||||
SuggestionNotificationsModule
|
||||
],
|
||||
declarations: [
|
||||
AdminNotificationsSuggestionTargetsPageComponent,
|
||||
AdminQualityAssuranceTopicsPageComponent,
|
||||
AdminQualityAssuranceEventsPageComponent,
|
||||
AdminQualityAssuranceSourcePageComponent
|
||||
],
|
||||
entryComponents: []
|
||||
})
|
||||
/**
|
||||
* This module handles all components related to the notifications pages
|
||||
*/
|
||||
export class AdminNotificationsModule {
|
||||
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AdminQualityAssuranceSourcePageComponent } from './admin-quality-assurance-source-page.component';
|
||||
|
||||
describe('AdminQualityAssuranceSourcePageComponent', () => {
|
||||
let component: AdminQualityAssuranceSourcePageComponent;
|
||||
let fixture: ComponentFixture<AdminQualityAssuranceSourcePageComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ AdminQualityAssuranceSourcePageComponent ],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AdminQualityAssuranceSourcePageComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create AdminQualityAssuranceSourcePageComponent', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -1,10 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Component for the page that show the QA sources.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-admin-quality-assurance-source-page-component',
|
||||
templateUrl: './admin-quality-assurance-source-page.component.html',
|
||||
})
|
||||
export class AdminQualityAssuranceSourcePageComponent {}
|
@@ -1,26 +0,0 @@
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { AdminQualityAssuranceTopicsPageComponent } from './admin-quality-assurance-topics-page.component';
|
||||
|
||||
describe('AdminQualityAssuranceTopicsPageComponent', () => {
|
||||
let component: AdminQualityAssuranceTopicsPageComponent;
|
||||
let fixture: ComponentFixture<AdminQualityAssuranceTopicsPageComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ AdminQualityAssuranceTopicsPageComponent ],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AdminQualityAssuranceTopicsPageComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create AdminQualityAssuranceTopicsPageComponent', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -16,6 +16,7 @@ import {
|
||||
AdminNotifySearchResultComponent
|
||||
} from './admin-notify-search-result/admin-notify-search-result.component';
|
||||
import { AdminNotifyMessagesService } from './services/admin-notify-messages.service';
|
||||
import { AdminNotifyLogsResultComponent } from './admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component';
|
||||
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
@@ -28,7 +29,7 @@ const ENTRY_COMPONENTS = [
|
||||
SharedModule,
|
||||
AdminNotifyDashboardRoutingModule,
|
||||
SearchModule,
|
||||
SearchPageModule,
|
||||
SearchPageModule
|
||||
],
|
||||
providers: [
|
||||
AdminNotifyMessagesService,
|
||||
@@ -40,7 +41,9 @@ const ENTRY_COMPONENTS = [
|
||||
AdminNotifyMetricsComponent,
|
||||
AdminNotifyIncomingComponent,
|
||||
AdminNotifyOutgoingComponent,
|
||||
AdminNotifyDetailModalComponent
|
||||
AdminNotifyDetailModalComponent,
|
||||
AdminNotifySearchResultComponent,
|
||||
AdminNotifyLogsResultComponent
|
||||
]
|
||||
})
|
||||
export class AdminNotifyDashboardModule {
|
||||
|
@@ -11,4 +11,12 @@
|
||||
<div class="col text-right">{{'notify-detail-modal.' + notifyMessage[key] | translate: {default: notifyMessage[key] ?? "n/a" } }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end">
|
||||
<button class="btn-primary" (click)="toggleCoarMessage()">
|
||||
{{'notify-message-modal.show-more' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<pre @fadeIn [innerHTML]="notifyMessage.message" class="bg-secondary text-white mt-2 p-2" *ngIf="isCoarMessageVisible"></pre>
|
||||
</div>
|
||||
|
@@ -3,10 +3,14 @@ import { AdminNotifyMessage } from '../models/admin-notify-message.model';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { MissingTranslationHelper } from '../../../shared/translate/missing-translation.helper';
|
||||
import { fadeIn } from '../../../shared/animations/fade';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-admin-notify-detail-modal',
|
||||
templateUrl: './admin-notify-detail-modal.component.html',
|
||||
animations: [
|
||||
fadeIn
|
||||
]
|
||||
})
|
||||
export class AdminNotifyDetailModalComponent {
|
||||
@Input() notifyMessage: AdminNotifyMessage;
|
||||
@@ -18,6 +22,8 @@ export class AdminNotifyDetailModalComponent {
|
||||
@Output()
|
||||
response = new EventEmitter<boolean>();
|
||||
|
||||
public isCoarMessageVisible = false;
|
||||
|
||||
|
||||
constructor(protected activeModal: NgbActiveModal,
|
||||
public translationsService: TranslateService) {
|
||||
@@ -32,4 +38,8 @@ export class AdminNotifyDetailModalComponent {
|
||||
this.activeModal.close();
|
||||
this.response.emit(true);
|
||||
}
|
||||
|
||||
toggleCoarMessage() {
|
||||
this.isCoarMessageVisible = !this.isCoarMessageVisible;
|
||||
}
|
||||
}
|
||||
|
@@ -15,20 +15,7 @@
|
||||
<a class="nav-link" [routerLink]="'../outbound'" [queryParams]="{view: 'table'}">{{'admin.notify.dashboard.outbound-logs' | translate}}</a>
|
||||
</ul>
|
||||
|
||||
<div class="container my-4">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-3 text-left h4">{{'admin.notify.dashboard.inbound' | translate}}</div>
|
||||
<div class="col-md-9">
|
||||
<ds-search-labels [inPlaceSearch]="true"></ds-search-labels>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ds-themed-search
|
||||
[configuration]="defaultConfiguration"
|
||||
[showViewModes]="false"
|
||||
[searchEnabled]="false"
|
||||
[context]="context"
|
||||
></ds-themed-search>
|
||||
<ds-admin-notify-logs-result [defaultConfiguration]="'NOTIFY.incoming'" ></ds-admin-notify-logs-result>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-page.component';
|
||||
import { Context } from '../../../../core/shared/context.model';
|
||||
import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service';
|
||||
|
||||
|
||||
@@ -14,14 +13,7 @@ import { SearchConfigurationService } from '../../../../core/shared/search/searc
|
||||
}
|
||||
]
|
||||
})
|
||||
export class AdminNotifyIncomingComponent implements OnInit{
|
||||
public defaultConfiguration = 'NOTIFY.incoming';
|
||||
protected readonly context = Context.CoarNotify;
|
||||
export class AdminNotifyIncomingComponent {
|
||||
constructor(@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.searchConfigService.getCurrentConfiguration('').subscribe(x => console.log(x));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,25 @@
|
||||
<div class="container my-4">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-3 text-left h4">{{'admin.notify.dashboard.outbound' | translate}}</div>
|
||||
<div class="col-md-9">
|
||||
<ds-search-labels [inPlaceSearch]="true"></ds-search-labels>
|
||||
<div class="h4">
|
||||
<button (click)="resetDefaultConfiguration()" *ngIf="(selectedSearchConfig$ | async) !== defaultConfiguration" class="badge badge-primary mr-1 mb-1">
|
||||
{{'admin.notify.dashboard.configuration' | translate}} {{ selectedSearchConfig$ | async }}
|
||||
<span> ×</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div >
|
||||
<ds-themed-search
|
||||
[configuration]="selectedSearchConfig$ | async"
|
||||
[showViewModes]="false"
|
||||
[searchEnabled]="false"
|
||||
[context]="context"
|
||||
></ds-themed-search>
|
||||
</div>
|
||||
|
@@ -0,0 +1,49 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AdminNotifyLogsResultComponent } from './admin-notify-logs-result.component';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { MockActivatedRoute } from '../../../../shared/mocks/active-router.mock';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
|
||||
import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
|
||||
import { RequestService } from '../../../../core/data/request.service';
|
||||
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { RouterStub } from '../../../../shared/testing/router.stub';
|
||||
import { RouteService } from '../../../../core/services/route.service';
|
||||
import { routeServiceStub } from '../../../../shared/testing/route-service.stub';
|
||||
|
||||
describe('AdminNotifyLogsResultComponent', () => {
|
||||
let component: AdminNotifyLogsResultComponent;
|
||||
let fixture: ComponentFixture<AdminNotifyLogsResultComponent>;
|
||||
let objectCache: ObjectCacheService;
|
||||
let requestService: RequestService;
|
||||
let halService: HALEndpointService;
|
||||
let rdbService: RemoteDataBuildService;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot()],
|
||||
declarations: [ AdminNotifyLogsResultComponent ],
|
||||
providers: [
|
||||
{ provide: RouteService, useValue: routeServiceStub },
|
||||
{ provide: Router, useValue: new RouterStub() },
|
||||
{ provide: ActivatedRoute, useValue: new MockActivatedRoute() },
|
||||
{ provide: HALEndpointService, useValue: halService },
|
||||
{ provide: ObjectCacheService, useValue: objectCache },
|
||||
{ provide: RequestService, useValue: requestService },
|
||||
{ provide: RemoteDataBuildService, useValue: rdbService },
|
||||
provideMockStore({}),
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AdminNotifyLogsResultComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,59 @@
|
||||
import { Component, Inject, Input, OnInit } from '@angular/core';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-page.component';
|
||||
import { Context } from '../../../../core/shared/context.model';
|
||||
import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ActivatedRoute, ActivatedRouteSnapshot, Router } from '@angular/router';
|
||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-admin-notify-logs-result',
|
||||
templateUrl: './admin-notify-logs-result.component.html',
|
||||
providers: [
|
||||
{
|
||||
provide: SEARCH_CONFIG_SERVICE,
|
||||
useClass: SearchConfigurationService
|
||||
}
|
||||
]
|
||||
})
|
||||
export class AdminNotifyLogsResultComponent implements OnInit{
|
||||
|
||||
@Input()
|
||||
defaultConfiguration: string;
|
||||
|
||||
public selectedSearchConfig$: Observable<string>;
|
||||
|
||||
protected readonly context = Context.CoarNotify;
|
||||
constructor(@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// override the route reuse strategy to prevent issue on result loading
|
||||
this.router.routeReuseStrategy.shouldReuseRoute = () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
this.selectedSearchConfig$ = this.searchConfigService.getCurrentConfiguration(this.defaultConfiguration);
|
||||
}
|
||||
|
||||
public resetDefaultConfiguration() {
|
||||
this.router.navigate([this.getResolvedUrl(this.route.snapshot)], {
|
||||
queryParams: {
|
||||
configuration: this.defaultConfiguration,
|
||||
view: ViewMode.Table,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get resolved url from route
|
||||
*
|
||||
* @param route url path
|
||||
* @returns url path
|
||||
*/
|
||||
private getResolvedUrl(route: ActivatedRouteSnapshot): string {
|
||||
return route.pathFromRoot.map(v => v.url.map(segment => segment.toString()).join('/')).join('/');
|
||||
}
|
||||
}
|
@@ -15,22 +15,8 @@
|
||||
<a class="nav-link active">{{'admin.notify.dashboard.outbound-logs' | translate}}</a>
|
||||
</ul>
|
||||
|
||||
<div class="container my-4">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-3 text-left h4">{{'admin.notify.dashboard.outbound' | translate}}</div>
|
||||
<div class="col-md-9">
|
||||
<ds-search-labels [inPlaceSearch]="true"></ds-search-labels>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ds-admin-notify-logs-result [defaultConfiguration]="'NOTIFY.outgoing'" ></ds-admin-notify-logs-result>
|
||||
|
||||
|
||||
<ds-themed-search
|
||||
[configuration]="'NOTIFY.outgoing'"
|
||||
[showViewModes]="false"
|
||||
[searchEnabled]="false"
|
||||
[context]="context"
|
||||
></ds-themed-search>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-page.component';
|
||||
import { Context } from '../../../../core/shared/context.model';
|
||||
import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service';
|
||||
|
||||
|
||||
@@ -15,8 +14,6 @@ import { SearchConfigurationService } from '../../../../core/shared/search/searc
|
||||
]
|
||||
})
|
||||
export class AdminNotifyOutgoingComponent {
|
||||
protected readonly context = Context.CoarNotify;
|
||||
|
||||
constructor(@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService) {
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { AdminNotifyMetricsRow } from './admin-notify-metrics.model';
|
||||
import { Router } from '@angular/router';
|
||||
import { ViewMode } from '../../../core/shared/view-mode.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-admin-notify-metrics',
|
||||
@@ -10,7 +12,38 @@ export class AdminNotifyMetricsComponent {
|
||||
@Input()
|
||||
boxesConfig: AdminNotifyMetricsRow[];
|
||||
|
||||
public navigateToSelectedSearchConfig($event: string) {
|
||||
console.log($event);
|
||||
private incomingConfiguration = 'NOTIFY.incoming';
|
||||
private involvedItemsSuffix = 'involvedItems';
|
||||
private inboundPath = '/inbound';
|
||||
private outboundPath = '/outbound';
|
||||
private adminSearchPath = '/admin/search';
|
||||
|
||||
constructor(private router: Router) {
|
||||
}
|
||||
|
||||
|
||||
public navigateToSelectedSearchConfig(searchConfig: string) {
|
||||
const isRelatedItemsConfig = searchConfig.endsWith(this.involvedItemsSuffix);
|
||||
|
||||
if (isRelatedItemsConfig) {
|
||||
this.router.navigate([this.adminSearchPath], {
|
||||
queryParams: {
|
||||
configuration: searchConfig,
|
||||
view: ViewMode.ListElement
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const isIncomingConfig = searchConfig.startsWith(this.incomingConfiguration);
|
||||
const selectedPath = isIncomingConfig ? this.inboundPath : this.outboundPath;
|
||||
|
||||
this.router.navigate([`${this.router.url}${selectedPath}`], {
|
||||
queryParams: {
|
||||
configuration: searchConfig,
|
||||
view: ViewMode.Table
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +0,0 @@
|
||||
.table-responsive {
|
||||
th, td {
|
||||
padding: 0.5rem !important;
|
||||
}
|
||||
}
|
@@ -19,6 +19,7 @@ import { AdminNotifyDetailModalComponent } from '../admin-notify-detail-modal/ad
|
||||
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
|
||||
|
||||
export const mockAdminNotifyMessages = [
|
||||
@@ -46,7 +47,8 @@ export const mockAdminNotifyMessages = [
|
||||
'item': {},
|
||||
'accessStatus': {},
|
||||
'ldnService': 'NOTIFY inbox - Automatic service',
|
||||
'relatedItem': 'test coar 2 demo'
|
||||
'relatedItem': 'test coar 2 demo',
|
||||
'message': '{"@context":["https://www.w3.org/ns/activitystreams","https://purl.org/coar/notify"],"id":"urn:uuid:668f26e0-2e8d-4118-b0d2-ee713523bc45","type":["Reject","coar-notify:IngestAction"],"actor":{"id":"https://generic-service.com","type":["Service"],"name":"Generic Service"},"context":{"id":"https://dspace-coar.4science.cloud/handle/123456789/28","type":["Document"],"ietf:cite-as":"https://doi.org/10.4598/12123488"},"object":{"id":"https://dspace-coar.4science.cloud/handle/123456789/28","type":["Offer"]},"origin":{"id":"https://generic-service.com/system","type":["Service"],"inbox":"https://notify-inbox.info/inbox7"},"target":{"id":"https://some-organisation.org","type":["Organization"],"inbox":"https://dspace-coar.4science.cloud/server/ldn/inbox"},"inReplyTo":"urn:uuid:d9b4010a-f128-4815-abb2-83707a2ee9cf"}'
|
||||
},
|
||||
{
|
||||
'type': 'message',
|
||||
@@ -72,7 +74,8 @@ export const mockAdminNotifyMessages = [
|
||||
'item': {},
|
||||
'accessStatus': {},
|
||||
'ldnService': 'NOTIFY inbox - Automatic service',
|
||||
'relatedItem': 'test coar demo'
|
||||
'relatedItem': 'test coar demo',
|
||||
'message': '{"@context":["https://www.w3.org/ns/activitystreams","https://purl.org/coar/notify"],"id":"urn:uuid:668f26e0-2e8d-4118-b0d2-ee713523bc45","type":["Reject","coar-notify:IngestAction"],"actor":{"id":"https://generic-service.com","type":["Service"],"name":"Generic Service"},"context":{"id":"https://dspace-coar.4science.cloud/handle/123456789/28","type":["Document"],"ietf:cite-as":"https://doi.org/10.4598/12123488"},"object":{"id":"https://dspace-coar.4science.cloud/handle/123456789/28","type":["Offer"]},"origin":{"id":"https://generic-service.com/system","type":["Service"],"inbox":"https://notify-inbox.info/inbox7"},"target":{"id":"https://some-organisation.org","type":["Organization"],"inbox":"https://dspace-coar.4science.cloud/server/ldn/inbox"},"inReplyTo":"urn:uuid:d9b4010a-f128-4815-abb2-83707a2ee9cf"}'
|
||||
}
|
||||
] as unknown as AdminNotifyMessage[];
|
||||
describe('AdminNotifySearchResultComponent', () => {
|
||||
@@ -131,12 +134,14 @@ describe('AdminNotifySearchResultComponent', () => {
|
||||
{ provide: RemoteDataBuildService, useValue: rdbService },
|
||||
{ provide: SEARCH_CONFIG_SERVICE, useValue: searchConfigService },
|
||||
DatePipe
|
||||
]
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AdminNotifySearchResultComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.searchConfigService = searchConfigService;
|
||||
modalService = (component as any).modalService;
|
||||
spyOn(modalService, 'open').and.returnValue(Object.assign({ componentInstance: Object.assign({ response: observableOf(true) }) }));
|
||||
fixture.detectChanges();
|
||||
|
@@ -22,7 +22,6 @@ import { DatePipe } from '@angular/common';
|
||||
@Component({
|
||||
selector: 'ds-admin-notify-search-result',
|
||||
templateUrl: './admin-notify-search-result.component.html',
|
||||
styleUrls: ['./admin-notify-search-result.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: SEARCH_CONFIG_SERVICE,
|
||||
@@ -50,6 +49,22 @@ export class AdminNotifySearchResultComponent extends TabulatableResultListEleme
|
||||
|
||||
private dateTypeKeys: string[] = ['queueLastStartTime', 'queueTimeout'];
|
||||
|
||||
/**
|
||||
* Keys to be not shown in detail
|
||||
* @private
|
||||
*/
|
||||
private messageKeys: string[] = [
|
||||
'type',
|
||||
'id',
|
||||
'coarNotifyType',
|
||||
'activityStreamType',
|
||||
'inReplyTo',
|
||||
'queueAttempts',
|
||||
'queueLastStartTime',
|
||||
'queueStatusLabel',
|
||||
'queueTimeout'
|
||||
];
|
||||
|
||||
/**
|
||||
* The format for the date values
|
||||
* @private
|
||||
@@ -81,32 +96,22 @@ export class AdminNotifySearchResultComponent extends TabulatableResultListEleme
|
||||
|
||||
/**
|
||||
* Open modal for details visualization
|
||||
* @param message the message to be displayed
|
||||
* @param notifyMessage the message to be displayed
|
||||
*/
|
||||
openDetailModal(message: AdminNotifyMessage) {
|
||||
openDetailModal(notifyMessage: AdminNotifyMessage) {
|
||||
const modalRef = this.modalService.open(AdminNotifyDetailModalComponent);
|
||||
const messageToOpen = {...message};
|
||||
// we delete not necessary or not readable keys
|
||||
delete messageToOpen.target;
|
||||
delete messageToOpen.object;
|
||||
delete messageToOpen.context;
|
||||
delete messageToOpen.origin;
|
||||
delete messageToOpen._links;
|
||||
delete messageToOpen.metadata;
|
||||
delete messageToOpen.thumbnail;
|
||||
delete messageToOpen.item;
|
||||
delete messageToOpen.accessStatus;
|
||||
delete messageToOpen.queueStatus;
|
||||
const messageToOpen = {...notifyMessage};
|
||||
|
||||
const messageKeys = Object.keys(messageToOpen);
|
||||
messageKeys.forEach(key => {
|
||||
this.messageKeys.forEach(key => {
|
||||
if (this.dateTypeKeys.includes(key)) {
|
||||
messageToOpen[key] = this.datePipe.transform(messageToOpen[key], this.dateFormat);
|
||||
}
|
||||
});
|
||||
// format COAR message for technical visualization
|
||||
messageToOpen.message = JSON.stringify(JSON.parse(notifyMessage.message), null, 2);
|
||||
|
||||
modalRef.componentInstance.notifyMessage = messageToOpen;
|
||||
modalRef.componentInstance.notifyMessageKeys = messageKeys;
|
||||
modalRef.componentInstance.notifyMessageKeys = this.messageKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -26,6 +26,18 @@ export class AdminNotifyMessage extends DSpaceObject {
|
||||
@autoserialize
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The id of the notification
|
||||
*/
|
||||
@autoserialize
|
||||
notificationId: string;
|
||||
|
||||
/**
|
||||
* The type of the notification
|
||||
*/
|
||||
@autoserialize
|
||||
notificationType: string;
|
||||
|
||||
/**
|
||||
* The type of the notification
|
||||
*/
|
||||
@@ -68,6 +80,12 @@ export class AdminNotifyMessage extends DSpaceObject {
|
||||
@autoserialize
|
||||
context: string;
|
||||
|
||||
/**
|
||||
* The related COAR message
|
||||
*/
|
||||
@autoserialize
|
||||
message: string;
|
||||
|
||||
/**
|
||||
* The attempts of the queue
|
||||
*/
|
||||
|
@@ -1,11 +1,11 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 mb-4">
|
||||
<h2 id="sub-header"
|
||||
class="border-bottom mb-2">{{ 'admin.registries.bitstream-formats.create.new' | translate }}</h2>
|
||||
<h1 id="sub-header"
|
||||
class="border-bottom pb-2">{{ 'admin.registries.bitstream-formats.create.new' | translate }}</h1>
|
||||
|
||||
<ds-bitstream-format-form (updatedFormat)="createBitstreamFormat($event)"></ds-bitstream-format-form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="bitstream-formats row">
|
||||
<div class="col-12">
|
||||
|
||||
<h2 id="header" class="border-bottom pb-2 ">{{'admin.registries.bitstream-formats.head' | translate}}</h2>
|
||||
<h1 id="header" class="border-bottom pb-2">{{'admin.registries.bitstream-formats.head' | translate}}</h1>
|
||||
|
||||
<p id="description">{{'admin.registries.bitstream-formats.description' | translate}}</p>
|
||||
<p id="create-new" class="mb-2"><a [routerLink]="'add'" class="btn btn-success">{{'admin.registries.bitstream-formats.create.new' | translate}}</a></p>
|
||||
@@ -19,7 +19,7 @@
|
||||
<table id="formats" class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col"></th>
|
||||
<th scope="col" [attr.aria-label]="'admin.registries.bitstream-formats.select' | translate"></th>
|
||||
<th scope="col">{{'admin.registries.bitstream-formats.table.id' | translate}}</th>
|
||||
<th scope="col">{{'admin.registries.bitstream-formats.table.name' | translate}}</th>
|
||||
<th scope="col">{{'admin.registries.bitstream-formats.table.mimetype' | translate}}</th>
|
||||
@@ -31,6 +31,7 @@
|
||||
<td>
|
||||
<label class="mb-0">
|
||||
<input type="checkbox"
|
||||
[attr.aria-label]="'admin.registries.bitstream-formats.select' | translate"
|
||||
[checked]="isSelected(bitstreamFormat) | async"
|
||||
(change)="selectBitStreamFormat(bitstreamFormat, $event)"
|
||||
>
|
||||
|
@@ -1,11 +1,11 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 mb-4">
|
||||
<h2 id="sub-header"
|
||||
class="border-bottom mb-2">{{'admin.registries.bitstream-formats.edit.head' | translate:{format: (bitstreamFormatRD$ | async)?.payload.shortDescription} }}</h2>
|
||||
<h1 id="sub-header"
|
||||
class="border-bottom pb-2">{{'admin.registries.bitstream-formats.edit.head' | translate:{format: (bitstreamFormatRD$ | async)?.payload.shortDescription} }}</h1>
|
||||
|
||||
<ds-bitstream-format-form [bitstreamFormat]="(bitstreamFormatRD$ | async)?.payload" (updatedFormat)="updateFormat($event)"></ds-bitstream-format-form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
||||
import { getAdminModuleRoute } from '../app-routing-paths';
|
||||
import { getQualityAssuranceEditRoute } from '../quality-assurance-notifications-pages/notifications-pages-routing-paths';
|
||||
|
||||
export const REGISTRIES_MODULE_PATH = 'registries';
|
||||
export const NOTIFICATIONS_MODULE_PATH = 'notifications';
|
||||
@@ -12,12 +13,10 @@ export function getRegistriesModuleRoute() {
|
||||
return new URLCombiner(getAdminModuleRoute(), REGISTRIES_MODULE_PATH).toString();
|
||||
}
|
||||
|
||||
export function getNotificationsModuleRoute() {
|
||||
return new URLCombiner(getAdminModuleRoute(), NOTIFICATIONS_MODULE_PATH).toString();
|
||||
}
|
||||
|
||||
export function getLdnServicesModuleRoute() {
|
||||
return new URLCombiner(getAdminModuleRoute(), LDN_PATH).toString();
|
||||
}
|
||||
|
||||
|
||||
export function getNotificatioQualityAssuranceRoute() {
|
||||
return new URLCombiner(`/${NOTIFICATIONS_MODULE_PATH}`, getQualityAssuranceEditRoute()).toString();
|
||||
}
|
||||
|
@@ -6,22 +6,12 @@ import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.reso
|
||||
import { AdminWorkflowPageComponent } from './admin-workflow-page/admin-workflow-page.component';
|
||||
import { I18nBreadcrumbsService } from '../core/breadcrumbs/i18n-breadcrumbs.service';
|
||||
import { AdminCurationTasksComponent } from './admin-curation-tasks/admin-curation-tasks.component';
|
||||
import {
|
||||
LDN_PATH,
|
||||
REGISTRIES_MODULE_PATH,
|
||||
NOTIFICATIONS_MODULE_PATH,
|
||||
NOTIFY_DASHBOARD_MODULE_PATH
|
||||
} from './admin-routing-paths';
|
||||
import { LDN_PATH, NOTIFY_DASHBOARD_MODULE_PATH, REGISTRIES_MODULE_PATH } from './admin-routing-paths';
|
||||
import { BatchImportPageComponent } from './admin-import-batch-page/batch-import-page.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: NOTIFICATIONS_MODULE_PATH,
|
||||
loadChildren: () => import('./admin-notifications/admin-notifications.module')
|
||||
.then((m) => m.AdminNotificationsModule),
|
||||
},
|
||||
{
|
||||
path: REGISTRIES_MODULE_PATH,
|
||||
loadChildren: () => import('./admin-registries/admin-registries.module')
|
||||
@@ -77,7 +67,7 @@ import { BatchImportPageComponent } from './admin-import-batch-page/batch-import
|
||||
{
|
||||
path: NOTIFY_DASHBOARD_MODULE_PATH,
|
||||
loadChildren: () => import('./admin-notify-dashboard/admin-notify-dashboard.module')
|
||||
.then((m) => m.AdminNotifyDashboardModule),
|
||||
.then((m) => m.AdminNotifyDashboardModule),
|
||||
},
|
||||
]),
|
||||
],
|
||||
|
@@ -34,22 +34,21 @@
|
||||
</div>
|
||||
<div class="navbar-nav">
|
||||
<div class="sidebar-section" id="sidebar-collapse-toggle">
|
||||
<a class="nav-item nav-link sidebar-section d-flex flex-row flex-nowrap"
|
||||
href="javascript:void(0);"
|
||||
<button class="nav-item nav-link sidebar-section d-flex flex-row flex-nowrap border-0" type="button"
|
||||
(click)="toggle($event)"
|
||||
(keyup.space)="toggle($event)"
|
||||
>
|
||||
<div class="shortcut-icon">
|
||||
<span class="shortcut-icon">
|
||||
<i *ngIf="(menuCollapsed | async)" class="fas fa-fw fa-angle-double-right"
|
||||
[title]="'menu.section.icon.pin' | translate"></i>
|
||||
<i *ngIf="!(menuCollapsed | async)" class="fas fa-fw fa-angle-double-left"
|
||||
[title]="'menu.section.icon.unpin' | translate"></i>
|
||||
</div>
|
||||
<div class="sidebar-collapsible">
|
||||
</span>
|
||||
<span class="sidebar-collapsible text-left">
|
||||
<span *ngIf="menuCollapsed | async" class="section-header-text">{{'menu.section.pin' | translate }}</span>
|
||||
<span *ngIf="!(menuCollapsed | async)" class="section-header-text">{{'menu.section.unpin' | translate }}</span>
|
||||
</div>
|
||||
</a>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
@@ -143,7 +143,7 @@ describe('AdminSidebarComponent', () => {
|
||||
describe('when the collapse link is clicked', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(menuService, 'toggleMenu');
|
||||
const sidebarToggler = fixture.debugElement.query(By.css('#sidebar-collapse-toggle > a'));
|
||||
const sidebarToggler = fixture.debugElement.query(By.css('#sidebar-collapse-toggle > button'));
|
||||
sidebarToggler.triggerEventHandler('click', {
|
||||
preventDefault: () => {/**/
|
||||
}
|
||||
|
@@ -41,6 +41,7 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard';
|
||||
import { SUGGESTION_MODULE_PATH } from './suggestions-page/suggestions-page-routing-paths';
|
||||
import { MenuResolver } from './menu.resolver';
|
||||
import { ThemedPageErrorComponent } from './page-error/themed-page-error.component';
|
||||
import { NOTIFICATIONS_MODULE_PATH } from './admin/admin-routing-paths';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -157,6 +158,12 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone
|
||||
.then((m) => m.AdminModule),
|
||||
canActivate: [SiteAdministratorGuard, EndUserAgreementCurrentUserGuard]
|
||||
},
|
||||
{
|
||||
path: NOTIFICATIONS_MODULE_PATH,
|
||||
loadChildren: () => import('./quality-assurance-notifications-pages/notifications-pages.module')
|
||||
.then((m) => m.NotificationsPageModule),
|
||||
canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard]
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
loadChildren: () => import('./login-page/login-page.module')
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<div class="container">
|
||||
<h3>{{'bitstream.download.page' | translate:{ bitstream: dsoNameService.getName((bitstream$ | async)) } }}</h3>
|
||||
<h1 class="h2">{{'bitstream.download.page' | translate:{ bitstream: dsoNameService.getName((bitstream$ | async)) } }}</h1>
|
||||
<div class="pt-3">
|
||||
<button (click)="back()" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> {{'bitstream.download.page.back' | translate}}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h3>{{dsoNameService.getName(bitstreamRD?.payload)}} <span class="text-muted">({{bitstreamRD?.payload?.sizeBytes | dsFileSize}})</span></h3>
|
||||
<h1 class="h2">{{dsoNameService.getName(bitstreamRD?.payload)}} <span class="text-muted">({{bitstreamRD?.payload?.sizeBytes | dsFileSize}})</span></h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<div class="container">
|
||||
<h1>{{ ('browse.taxonomy_' + vocabularyName + '.title') | translate }}</h1>
|
||||
<div class="mb-3">
|
||||
<ds-vocabulary-treeview [vocabularyOptions]=vocabularyOptions
|
||||
[multiSelect]="true"
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<p>{{'collection.edit.item-mapper.description' | translate}}</p>
|
||||
|
||||
<ul ngbNav (navChange)="tabChange($event)" [destroyOnHide]="true" #tabs="ngbNav" class="nav-tabs">
|
||||
<li [ngbNavItem]="'browseTab'">
|
||||
<li [ngbNavItem]="'browseTab'" role="presentation">
|
||||
<a ngbNavLink>{{'collection.edit.item-mapper.tabs.browse' | translate}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="mt-2">
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li [ngbNavItem]="'mapTab'">
|
||||
<li [ngbNavItem]="'mapTab'" role="presentation">
|
||||
<a ngbNavLink>{{'collection.edit.item-mapper.tabs.map' | translate}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="row mt-2">
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="row">
|
||||
<ng-container *ngVar="(dsoRD$ | async)?.payload as dso">
|
||||
<div class="col-12 pb-4">
|
||||
<h2 id="header" class="border-bottom pb-2">{{ 'collection.delete.head' | translate}}</h2>
|
||||
<h1 id="header" class="border-bottom pb-2">{{ 'collection.delete.head' | translate}}</h1>
|
||||
<p class="pb-2">{{ 'collection.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
|
||||
<div class="form-group row">
|
||||
<div class="col text-right space-children-mr">
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<div class="container">
|
||||
<h3>{{'collection.curate.header' |translate:{collection: (collectionName$ |async)} }}</h3>
|
||||
<h2>{{'collection.curate.header' |translate:{collection: (collectionName$ |async)} }}</h2>
|
||||
<ds-curation-form
|
||||
[dsoHandle]="(dsoRD$|async)?.payload.handle"
|
||||
></ds-curation-form>
|
||||
|
@@ -18,7 +18,7 @@
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.save-button" | translate}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<h4>{{ 'collection.edit.tabs.source.head' | translate }}</h4>
|
||||
<h2>{{ 'collection.edit.tabs.source.head' | translate }}</h2>
|
||||
<div *ngIf="contentSource" class="form-check mb-4">
|
||||
<input type="checkbox" class="form-check-input" id="externalSourceCheck"
|
||||
[checked]="(contentSource?.harvestType !== harvestTypeNone)" (change)="changeExternalSource()">
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="row">
|
||||
<div class="col-12" *ngVar="(itemRD$ | async) as itemRD">
|
||||
<ng-container *ngIf="itemRD?.hasSucceeded">
|
||||
<h2 class="border-bottom">{{ 'collection.edit.template.head' | translate:{ collection: dsoNameService.getName(collection) } }}</h2>
|
||||
<h1 class="border-bottom">{{ 'collection.edit.template.head' | translate:{ collection: dsoNameService.getName(collection) } }}</h1>
|
||||
<ds-themed-dso-edit-metadata [updateDataService]="itemTemplateService" [dso]="itemRD?.payload"></ds-themed-dso-edit-metadata>
|
||||
<button [routerLink]="getCollectionEditUrl(collection)" class="btn btn-outline-secondary">{{ 'collection.edit.template.cancel' | translate }}</button>
|
||||
</ng-container>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="row">
|
||||
<ng-container *ngVar="(dsoRD$ | async)?.payload as dso">
|
||||
<div class="col-12 pb-4">
|
||||
<h2 id="header" class="border-bottom pb-2">{{ 'community.delete.head' | translate}}</h2>
|
||||
<h1 id="header" class="border-bottom pb-2">{{ 'community.delete.head' | translate}}</h1>
|
||||
<p class="pb-2">{{ 'community.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
|
||||
<div class="form-group row">
|
||||
<div class="col text-right space-children-mr">
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<div class="container">
|
||||
<h3>{{'community.curate.header' |translate:{community: (communityName$ |async)} }}</h3>
|
||||
<h2>{{'community.curate.header' |translate:{community: (communityName$ |async)} }}</h2>
|
||||
<ds-curation-form
|
||||
[dsoHandle]="(dsoRD$|async)?.payload.handle"
|
||||
></ds-curation-form>
|
||||
|
@@ -50,7 +50,7 @@ export class DSONameService {
|
||||
}
|
||||
},
|
||||
OrgUnit: (dso: DSpaceObject): string => {
|
||||
return dso.firstMetadataValue('organization.legalName');
|
||||
return dso.firstMetadataValue('organization.legalName') || this.translateService.instant('dso.name.untitled');
|
||||
},
|
||||
Default: (dso: DSpaceObject): string => {
|
||||
// If object doesn't have dc.title metadata use name property
|
||||
@@ -106,7 +106,7 @@ export class DSONameService {
|
||||
}
|
||||
return `${familyName}, ${givenName}`;
|
||||
} else if (entityType === 'OrgUnit') {
|
||||
return this.firstMetadataValue(object, dso, 'organization.legalName');
|
||||
return this.firstMetadataValue(object, dso, 'organization.legalName') || this.translateService.instant('dso.name.untitled');
|
||||
}
|
||||
return this.firstMetadataValue(object, dso, 'dc.title') || dso.name || this.translateService.instant('dso.name.untitled');
|
||||
}
|
||||
|
@@ -0,0 +1,31 @@
|
||||
import {QualityAssuranceBreadcrumbResolver} from './quality-assurance-breadcrumb.resolver';
|
||||
|
||||
describe('QualityAssuranceBreadcrumbResolver', () => {
|
||||
describe('resolve', () => {
|
||||
let resolver: QualityAssuranceBreadcrumbResolver;
|
||||
let qualityAssuranceBreadcrumbService: any;
|
||||
let route: any;
|
||||
const fullPath = '/test/quality-assurance/';
|
||||
const expectedKey = 'testSourceId:testTopicId';
|
||||
|
||||
beforeEach(() => {
|
||||
route = {
|
||||
paramMap: {
|
||||
get: function (param) {
|
||||
return this[param];
|
||||
},
|
||||
sourceId: 'testSourceId',
|
||||
topicId: 'testTopicId'
|
||||
}
|
||||
};
|
||||
qualityAssuranceBreadcrumbService = {};
|
||||
resolver = new QualityAssuranceBreadcrumbResolver(qualityAssuranceBreadcrumbService);
|
||||
});
|
||||
|
||||
it('should resolve the breadcrumb config', () => {
|
||||
const resolvedConfig = resolver.resolve(route as any, {url: fullPath + 'testSourceId'} as any);
|
||||
const expectedConfig = { provider: qualityAssuranceBreadcrumbService, key: expectedKey, url: fullPath };
|
||||
expect(resolvedConfig).toEqual(expectedConfig);
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,32 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {QualityAssuranceBreadcrumbService} from './quality-assurance-breadcrumb.service';
|
||||
import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router';
|
||||
import {BreadcrumbConfig} from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class QualityAssuranceBreadcrumbResolver implements Resolve<BreadcrumbConfig<string>> {
|
||||
constructor(protected breadcrumbService: QualityAssuranceBreadcrumbService) {}
|
||||
|
||||
/**
|
||||
* Method that resolve QA item into a breadcrumb
|
||||
* The parameter are retrieved by the url since part of the QA route config
|
||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||
* @returns BreadcrumbConfig object
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): BreadcrumbConfig<string> {
|
||||
const sourceId = route.paramMap.get('sourceId');
|
||||
const topicId = route.paramMap.get('topicId');
|
||||
let key = sourceId;
|
||||
|
||||
if (topicId) {
|
||||
key += `:${topicId}`;
|
||||
}
|
||||
const fullPath = state.url;
|
||||
const url = fullPath.substr(0, fullPath.indexOf(sourceId));
|
||||
|
||||
return { provider: this.breadcrumbService, key, url };
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
import { TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model';
|
||||
import { getTestScheduler } from 'jasmine-marbles';
|
||||
import {QualityAssuranceBreadcrumbService} from './quality-assurance-breadcrumb.service';
|
||||
|
||||
describe('QualityAssuranceBreadcrumbService', () => {
|
||||
let service: QualityAssuranceBreadcrumbService;
|
||||
let dataService: any;
|
||||
let translateService: any = {
|
||||
instant: (str) => str,
|
||||
};
|
||||
|
||||
let exampleString;
|
||||
let exampleURL;
|
||||
let exampleQaKey;
|
||||
|
||||
function init() {
|
||||
exampleString = 'sourceId';
|
||||
exampleURL = '/test/quality-assurance/';
|
||||
exampleQaKey = 'admin.quality-assurance.breadcrumbs';
|
||||
}
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
init();
|
||||
TestBed.configureTestingModule({}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
service = new QualityAssuranceBreadcrumbService(dataService,translateService);
|
||||
});
|
||||
|
||||
describe('getBreadcrumbs', () => {
|
||||
it('should return a breadcrumb based on a string', () => {
|
||||
const breadcrumbs = service.getBreadcrumbs(exampleString, exampleURL);
|
||||
getTestScheduler().expectObservable(breadcrumbs).toBe('(a|)', { a: [new Breadcrumb(exampleQaKey, exampleURL),
|
||||
new Breadcrumb(exampleString, exampleURL + exampleString)]
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,53 @@
|
||||
import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model';
|
||||
import { BreadcrumbsProviderService } from './breadcrumbsProviderService';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { getFirstCompletedRemoteData } from '../shared/operators';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { QualityAssuranceTopicDataService } from '../notifications/qa/topics/quality-assurance-topic-data.service';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Service to calculate QA breadcrumbs for a single part of the route
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class QualityAssuranceBreadcrumbService implements BreadcrumbsProviderService<string> {
|
||||
|
||||
private QUALITY_ASSURANCE_BREADCRUMB_KEY = 'admin.quality-assurance.breadcrumbs';
|
||||
constructor(
|
||||
protected qualityAssuranceService: QualityAssuranceTopicDataService,
|
||||
private translationService: TranslateService,
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to calculate the breadcrumbs
|
||||
* @param key The key used to resolve the breadcrumb
|
||||
* @param url The url to use as a link for this breadcrumb
|
||||
*/
|
||||
getBreadcrumbs(key: string, url: string): Observable<Breadcrumb[]> {
|
||||
const sourceId = key.split(':')[0];
|
||||
const topicId = key.split(':')[1];
|
||||
|
||||
if (topicId) {
|
||||
return this.qualityAssuranceService.getTopic(topicId).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
map((topic) => {
|
||||
return [new Breadcrumb(this.translationService.instant(this.QUALITY_ASSURANCE_BREADCRUMB_KEY), url),
|
||||
new Breadcrumb(sourceId, `${url}${sourceId}`),
|
||||
new Breadcrumb(topicId, undefined)];
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return observableOf([new Breadcrumb(this.translationService.instant(this.QUALITY_ASSURANCE_BREADCRUMB_KEY), url),
|
||||
new Breadcrumb(sourceId, `${url}${sourceId}`)]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -157,9 +157,9 @@ import { SequenceService } from './shared/sequence.service';
|
||||
import { CoreState } from './core-state.model';
|
||||
import { GroupDataService } from './eperson/group-data.service';
|
||||
import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model';
|
||||
import { QualityAssuranceTopicObject } from './suggestion-notifications/qa/models/quality-assurance-topic.model';
|
||||
import { QualityAssuranceEventObject } from './suggestion-notifications/qa/models/quality-assurance-event.model';
|
||||
import { QualityAssuranceSourceObject } from './suggestion-notifications/qa/models/quality-assurance-source.model';
|
||||
import { QualityAssuranceTopicObject } from './notifications/qa/models/quality-assurance-topic.model';
|
||||
import { QualityAssuranceEventObject } from './notifications/qa/models/quality-assurance-event.model';
|
||||
import { QualityAssuranceSourceObject } from './notifications/qa/models/quality-assurance-source.model';
|
||||
import { RatingAdvancedWorkflowInfo } from './tasks/models/rating-advanced-workflow-info.model';
|
||||
import { AdvancedWorkflowInfo } from './tasks/models/advanced-workflow-info.model';
|
||||
import { SelectReviewerAdvancedWorkflowInfo } from './tasks/models/select-reviewer-advanced-workflow-info.model';
|
||||
@@ -185,8 +185,8 @@ import { FlatBrowseDefinition } from './shared/flat-browse-definition.model';
|
||||
import { ValueListBrowseDefinition } from './shared/value-list-browse-definition.model';
|
||||
import { NonHierarchicalBrowseDefinition } from './shared/non-hierarchical-browse-definition';
|
||||
import { BulkAccessConditionOptions } from './config/models/bulk-access-condition-options.model';
|
||||
import { SuggestionTarget } from './suggestion-notifications/reciter-suggestions/models/suggestion-target.model';
|
||||
import { SuggestionSource } from './suggestion-notifications/reciter-suggestions/models/suggestion-source.model';
|
||||
import { SuggestionTarget } from './notifications/reciter-suggestions/models/suggestion-target.model';
|
||||
import { SuggestionSource } from './notifications/reciter-suggestions/models/suggestion-source.model';
|
||||
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';
|
||||
|
@@ -34,5 +34,6 @@ export enum FeatureID {
|
||||
CanEditItem = 'canEditItem',
|
||||
CanRegisterDOI = 'canRegisterDOI',
|
||||
CanSubscribe = 'canSubscribeDso',
|
||||
CoarLdnEnabled = 'coarLdnEnabled'
|
||||
CoarLdnEnabled = 'coarLdnEnabled',
|
||||
CanSeeQA = 'canSeeQA'
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { Store } from '@ngrx/store';
|
||||
import {
|
||||
FlushPatchOperationAction,
|
||||
NewPatchAddOperationAction,
|
||||
NewPatchMoveOperationAction,
|
||||
NewPatchRemoveOperationAction,
|
||||
@@ -99,6 +100,20 @@ export class JsonPatchOperationsBuilder {
|
||||
path.path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a new FlushPatchOperationAction
|
||||
*
|
||||
* @param path
|
||||
* a JsonPatchOperationPathObject representing path
|
||||
*/
|
||||
flushOperation(path: JsonPatchOperationPathObject) {
|
||||
this.store.dispatch(
|
||||
new FlushPatchOperationAction(
|
||||
path.rootElement,
|
||||
path.subRootElement,
|
||||
path.path));
|
||||
}
|
||||
|
||||
protected prepareValue(value: any, plain: boolean, first: boolean) {
|
||||
let operationValue: any = null;
|
||||
if (hasValue(value)) {
|
||||
|
@@ -20,6 +20,7 @@ export const JsonPatchOperationsActionTypes = {
|
||||
COMMIT_JSON_PATCH_OPERATIONS: type('dspace/core/patch/COMMIT_JSON_PATCH_OPERATIONS'),
|
||||
ROLLBACK_JSON_PATCH_OPERATIONS: type('dspace/core/patch/ROLLBACK_JSON_PATCH_OPERATIONS'),
|
||||
FLUSH_JSON_PATCH_OPERATIONS: type('dspace/core/patch/FLUSH_JSON_PATCH_OPERATIONS'),
|
||||
FLUSH_JSON_PATCH_OPERATION: type('dspace/core/patch/FLUSH_JSON_PATCH_OPERATION'),
|
||||
START_TRANSACTION_JSON_PATCH_OPERATIONS: type('dspace/core/patch/START_TRANSACTION_JSON_PATCH_OPERATIONS'),
|
||||
DELETE_PENDING_JSON_PATCH_OPERATIONS: type('dspace/core/patch/DELETE_PENDING_JSON_PATCH_OPERATIONS'),
|
||||
};
|
||||
@@ -120,6 +121,32 @@ export class FlushPatchOperationsAction implements Action {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An ngrx action to flush a single operation of the JSON Patch operations
|
||||
*/
|
||||
export class FlushPatchOperationAction implements Action {
|
||||
type = JsonPatchOperationsActionTypes.FLUSH_JSON_PATCH_OPERATION;
|
||||
payload: {
|
||||
resourceType: string;
|
||||
resourceId: string;
|
||||
path: string
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new FlushPatchOperationsAction
|
||||
*
|
||||
* @param resourceType
|
||||
* the resource's type
|
||||
* @param resourceId
|
||||
* the resource's ID
|
||||
* @param path
|
||||
* the path of the operation
|
||||
*/
|
||||
constructor(resourceType: string, resourceId: string, path: string) {
|
||||
this.payload = { resourceType, resourceId, path };
|
||||
}
|
||||
}
|
||||
/**
|
||||
* An ngrx action to Add new HTTP/PATCH ADD operations to state
|
||||
*/
|
||||
@@ -284,4 +311,5 @@ export type PatchOperationsActions
|
||||
| NewPatchReplaceOperationAction
|
||||
| RollbacktPatchOperationsAction
|
||||
| StartTransactionPatchOperationsAction
|
||||
| DeletePendingJsonPatchOperationsAction;
|
||||
| DeletePendingJsonPatchOperationsAction
|
||||
| FlushPatchOperationAction;
|
||||
|
@@ -12,7 +12,7 @@ import {
|
||||
CommitPatchOperationsAction,
|
||||
StartTransactionPatchOperationsAction,
|
||||
RollbacktPatchOperationsAction,
|
||||
DeletePendingJsonPatchOperationsAction
|
||||
DeletePendingJsonPatchOperationsAction, FlushPatchOperationAction
|
||||
} from './json-patch-operations.actions';
|
||||
import { JsonPatchOperationModel, JsonPatchOperationType } from './json-patch.model';
|
||||
|
||||
@@ -71,7 +71,7 @@ export function jsonPatchOperationsReducer(state = initialState, action: PatchOp
|
||||
}
|
||||
|
||||
case JsonPatchOperationsActionTypes.FLUSH_JSON_PATCH_OPERATIONS: {
|
||||
return flushOperation(state, action as FlushPatchOperationsAction);
|
||||
return flushOperations(state, action as FlushPatchOperationsAction);
|
||||
}
|
||||
|
||||
case JsonPatchOperationsActionTypes.NEW_JSON_PATCH_ADD_OPERATION: {
|
||||
@@ -106,6 +106,10 @@ export function jsonPatchOperationsReducer(state = initialState, action: PatchOp
|
||||
return deletePendingOperations(state, action as DeletePendingJsonPatchOperationsAction);
|
||||
}
|
||||
|
||||
case JsonPatchOperationsActionTypes.FLUSH_JSON_PATCH_OPERATION: {
|
||||
return flushOperation(state, action as FlushPatchOperationAction);
|
||||
}
|
||||
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
@@ -197,6 +201,39 @@ function deletePendingOperations(state: JsonPatchOperationsState, action: Delete
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush one operation from JsonPatchOperationsState.
|
||||
*
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* an FlushPatchOperationsAction
|
||||
* @return JsonPatchOperationsState
|
||||
* the new state.
|
||||
*/
|
||||
function flushOperation(state: JsonPatchOperationsState, action: FlushPatchOperationAction): JsonPatchOperationsState {
|
||||
const payload = action.payload;
|
||||
if (state[payload.resourceType] && state[payload.resourceType].children) {
|
||||
const body = state[payload.resourceType].children[payload.resourceId].body;
|
||||
const operation = body.filter(operations => operations.operation.path === payload.path)[0];
|
||||
const operationIndex = body.indexOf(operation);
|
||||
const newBody = [...body];
|
||||
newBody.splice(operationIndex, 1);
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[action.payload.resourceType]: Object.assign({}, {
|
||||
children: {
|
||||
[action.payload.resourceId]: {
|
||||
body: newBody,
|
||||
}
|
||||
},
|
||||
})
|
||||
});
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new JSON patch operation list.
|
||||
*
|
||||
@@ -273,7 +310,7 @@ function hasValidBody(state: JsonPatchOperationsState, resourceType: any, resour
|
||||
* @return SubmissionObjectState
|
||||
* the new state, with the section new validity status.
|
||||
*/
|
||||
function flushOperation(state: JsonPatchOperationsState, action: FlushPatchOperationsAction): JsonPatchOperationsState {
|
||||
function flushOperations(state: JsonPatchOperationsState, action: FlushPatchOperationsAction): JsonPatchOperationsState {
|
||||
if (hasValue(state[ action.payload.resourceType ])) {
|
||||
let newChildren;
|
||||
if (isNotUndefined(action.payload.resourceId)) {
|
||||
|
@@ -22,7 +22,7 @@ export interface QualityAssuranceEventMessageObject {
|
||||
/**
|
||||
* The interface representing the Quality Assurance event message
|
||||
*/
|
||||
export interface OpenaireQualityAssuranceEventMessageObject {
|
||||
export interface SourceQualityAssuranceEventMessageObject {
|
||||
/**
|
||||
* The type of 'value'
|
||||
*/
|
||||
@@ -69,9 +69,9 @@ export interface OpenaireQualityAssuranceEventMessageObject {
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* The OPENAIRE ID.
|
||||
* The Source ID.
|
||||
*/
|
||||
openaireId: string;
|
||||
sourceId: string;
|
||||
|
||||
/**
|
||||
* The PID href.
|
||||
@@ -136,7 +136,7 @@ export class QualityAssuranceEventObject implements CacheableObject {
|
||||
* The suggestion data. Data may vary depending on the source
|
||||
*/
|
||||
@autoserialize
|
||||
message: OpenaireQualityAssuranceEventMessageObject;
|
||||
message: SourceQualityAssuranceEventMessageObject;
|
||||
|
||||
/**
|
||||
* The type of this ConfigObject
|
@@ -4,6 +4,7 @@
|
||||
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex align-items-center" *ngVar="(mdRepresentation$ | async) as mdRepresentation" role="cell">
|
||||
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing && !mdRepresentation">{{ mdValue.newValue.value }}</div>
|
||||
<textarea class="form-control" rows="5" *ngIf="mdValue.editing && !mdRepresentation" [(ngModel)]="mdValue.newValue.value"
|
||||
[attr.aria-label]="(dsoType + '.edit.metadata.edit.value') | translate"
|
||||
[dsDebounce]="300" (onDebounce)="confirm.emit(false)"></textarea>
|
||||
<div class="d-flex" *ngIf="mdRepresentation">
|
||||
<a class="mr-2" target="_blank" [routerLink]="mdRepresentationItemRoute$ | async">{{ mdRepresentationName$ | async }}</a>
|
||||
@@ -13,6 +14,7 @@
|
||||
<div class="ds-flex-cell ds-lang-cell" role="cell">
|
||||
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing">{{ mdValue.newValue.language }}</div>
|
||||
<input class="form-control" type="text" *ngIf="mdValue.editing" [(ngModel)]="mdValue.newValue.language"
|
||||
[attr.aria-label]="(dsoType + '.edit.metadata.edit.language') | translate"
|
||||
[dsDebounce]="300" (onDebounce)="confirm.emit(false)" />
|
||||
</div>
|
||||
<div class="text-center ds-flex-cell ds-edit-cell" role="cell">
|
||||
|
@@ -62,21 +62,22 @@
|
||||
<a class="text-white"
|
||||
href="https://www.lyrasis.org/">{{ 'footer.link.lyrasis' | translate}}</a>
|
||||
</p>
|
||||
<ul class="footer-info list-unstyled small d-flex justify-content-center mb-0">
|
||||
<ul class="footer-info list-unstyled d-flex justify-content-center mb-0">
|
||||
<li>
|
||||
<a class="text-white" href="javascript:void(0);"
|
||||
(click)="showCookieSettings()">{{ 'footer.link.cookies' | translate}}</a>
|
||||
<button class="btn btn-link text-white" type="button" (click)="showCookieSettings()">
|
||||
{{ 'footer.link.cookies' | translate}}
|
||||
</button>
|
||||
</li>
|
||||
<li *ngIf="showPrivacyPolicy">
|
||||
<a class="text-white"
|
||||
<a class="btn text-white"
|
||||
routerLink="info/privacy">{{ 'footer.link.privacy-policy' | translate}}</a>
|
||||
</li>
|
||||
<li *ngIf="showEndUserAgreement">
|
||||
<a class="text-white"
|
||||
<a class="btn text-white"
|
||||
routerLink="info/end-user-agreement">{{ 'footer.link.end-user-agreement' | translate}}</a>
|
||||
</li>
|
||||
<li *ngIf="showSendFeedback$ | async">
|
||||
<a class="text-white"
|
||||
<a class="btn text-white"
|
||||
routerLink="info/feedback">{{ 'footer.link.feedback' | translate}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@@ -43,9 +43,19 @@
|
||||
li {
|
||||
display: inline-flex;
|
||||
|
||||
a {
|
||||
a, .btn-link {
|
||||
padding: 0 calc(var(--bs-spacer) / 2);
|
||||
color: inherit
|
||||
color: inherit;
|
||||
font-size: .875em;
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
|
@@ -1,16 +1,16 @@
|
||||
<div *ngFor="let entry of healthInfoComponent | dsObjNgFor" data-test="collapse">
|
||||
<div *ngIf="entry && !isPlainProperty(entry.value)" class="mb-3 border-bottom">
|
||||
<div class="w-100 d-flex justify-content-between collapse-toggle" (click)="collapse.toggle()">
|
||||
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="collapse.toggle()">
|
||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!collapse.collapsed"
|
||||
aria-controls="collapseExample">
|
||||
[attr.aria-controls]="'health-info-component-' + entry.key + '-content'">
|
||||
{{ entry.key | titlecase }}
|
||||
</button>
|
||||
<div class="d-inline-block">
|
||||
<div class="d-flex my-auto">
|
||||
<span *ngIf="collapse.collapsed" class="fas fa-chevron-down"></span>
|
||||
<span *ngIf="!collapse.collapsed" class="fas fa-chevron-up"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div #collapse="ngbCollapse" [ngbCollapse]="isCollapsed">
|
||||
<div #collapse="ngbCollapse" [id]="'health-info-component-' + entry.key + '-content'" [ngbCollapse]="isCollapsed">
|
||||
<div class="card border-0">
|
||||
<div class="card-body">
|
||||
<ds-health-info-component [healthInfoComponent]="entry.value"
|
||||
|
@@ -2,14 +2,14 @@
|
||||
<ngb-accordion #acc="ngbAccordion" [activeIds]="activeId">
|
||||
<ngb-panel [id]="entry.key" *ngFor="let entry of healthInfoResponse | dsObjNgFor">
|
||||
<ng-template ngbPanelHeader>
|
||||
<div class="w-100 d-flex justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle(entry.key)" data-test="info-component">
|
||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!acc.isExpanded(entry.key)"
|
||||
aria-controls="collapsePanels">
|
||||
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle(entry.key)" data-test="info-component">
|
||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="acc.isExpanded(entry.key)"
|
||||
[attr.aria-controls]="'health-info-' + entry.key + '-content'">
|
||||
{{ getPanelLabel(entry.key) | titlecase }}
|
||||
</button>
|
||||
<div class="text-right d-flex">
|
||||
<div class="text-right d-flex gap-2">
|
||||
<ds-health-status [status]="entry.value?.status"></ds-health-status>
|
||||
<div class="ml-3 d-inline-block">
|
||||
<div class="d-flex my-auto">
|
||||
<span *ngIf="acc.isExpanded(entry.key)" class="fas fa-chevron-up fa-fw"></span>
|
||||
<span *ngIf="!acc.isExpanded(entry.key)" class="fas fa-chevron-down fa-fw"></span>
|
||||
</div>
|
||||
@@ -17,8 +17,9 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<ds-health-info-component [healthInfoComponentName]="entry.key"
|
||||
[healthInfoComponent]="entry.value"></ds-health-info-component>
|
||||
<ds-health-info-component [healthInfoComponentName]="entry.key" [healthInfoComponent]="entry.value"
|
||||
[id]="'health-info-' + entry.key + '-content'">
|
||||
</ds-health-info-component>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
</ngb-accordion>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<h2>{{'health-page.heading' | translate}}</h2>
|
||||
<div *ngIf="(healthResponse | async) && (healthInfoResponse | async)">
|
||||
<ul ngbNav #nav="ngbNav" [activeId]="'status'" class="nav-tabs">
|
||||
<li [ngbNavItem]="'status'">
|
||||
<li [ngbNavItem]="'status'" role="presentation">
|
||||
<a ngbNavLink>{{'health-page.status-tab' | translate}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div id="status">
|
||||
@@ -10,7 +10,7 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li [ngbNavItem]="'info'">
|
||||
<li [ngbNavItem]="'info'" role="presentation">
|
||||
<a ngbNavLink>{{'health-page.info-tab' | translate}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div id="info">
|
||||
@@ -21,7 +21,7 @@
|
||||
</ul>
|
||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
</div>
|
||||
<ds-alert *ngIf="!(healthResponse | async) || !(healthInfoResponse | async)" [type]="'alert-danger'" [content]="'health-page.error.msg'"></ds-alert>
|
||||
<ds-alert *ngIf="!(healthResponse | async) || !(healthInfoResponse | async)" [type]="AlertType.Error" [content]="'health-page.error.msg'"></ds-alert>
|
||||
</div>
|
||||
|
||||
|
||||
|
@@ -5,6 +5,7 @@ import { take } from 'rxjs/operators';
|
||||
|
||||
import { HealthService } from './health.service';
|
||||
import { HealthInfoResponse, HealthResponse } from './models/health-component.model';
|
||||
import { AlertType } from '../shared/alert/alert-type';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-health-page',
|
||||
@@ -33,6 +34,8 @@ export class HealthPageComponent implements OnInit {
|
||||
*/
|
||||
healthInfoResponseInitialised: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
readonly AlertType = AlertType;
|
||||
|
||||
constructor(private healthDataService: HealthService) {
|
||||
}
|
||||
|
||||
|
@@ -1,16 +1,16 @@
|
||||
<ng-container *ngIf="healthComponent?.components">
|
||||
<div *ngFor="let entry of healthComponent?.components | dsObjNgFor" class="mb-3 border-bottom" data-test="collapse">
|
||||
<div class="w-100 d-flex justify-content-between collapse-toggle" (click)="collapse.toggle()">
|
||||
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="collapse.toggle()">
|
||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!collapse.collapsed"
|
||||
aria-controls="collapseExample">
|
||||
[attr.aria-controls]="'health-component-' + entry.key + 'content'">
|
||||
{{ entry.key | titlecase }}
|
||||
</button>
|
||||
<div class="d-inline-block">
|
||||
<div class="d-flex my-auto">
|
||||
<span *ngIf="collapse.collapsed" class="fas fa-chevron-down"></span>
|
||||
<span *ngIf="!collapse.collapsed" class="fas fa-chevron-up"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div #collapse="ngbCollapse" [ngbCollapse]="isCollapsed">
|
||||
<div #collapse="ngbCollapse" [id]="'health-component-' + entry.key + 'content'" [ngbCollapse]="isCollapsed">
|
||||
<div class="card border-0">
|
||||
<div class="card-body">
|
||||
<ds-health-component [healthComponent]="entry.value"
|
||||
|
@@ -1,15 +1,18 @@
|
||||
<p class="h4">{{'health-page.status' | translate}} : <ds-health-status [status]="healthResponse.status"></ds-health-status></p>
|
||||
<p class="h4">
|
||||
{{'health-page.status' | translate}}:
|
||||
<ds-health-status [status]="healthResponse.status" class="d-inline-flex"></ds-health-status>
|
||||
</p>
|
||||
<ngb-accordion #acc="ngbAccordion" [activeIds]="activeId">
|
||||
<ngb-panel [id]="entry.key" *ngFor="let entry of healthResponse.components | dsObjNgFor">
|
||||
<ng-template ngbPanelHeader>
|
||||
<div class="w-100 d-flex justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle(entry.key)" data-test="component">
|
||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!acc.isExpanded(entry.key)"
|
||||
aria-controls="collapsePanels">
|
||||
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle(entry.key)" data-test="component">
|
||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="acc.isExpanded(entry.key)"
|
||||
[attr.aria-controls]="'health-panel-' + entry.key + '-content'">
|
||||
{{ getPanelLabel(entry.key) | titlecase }}
|
||||
</button>
|
||||
<div class="text-right d-flex">
|
||||
<div class="text-right d-flex gap-2">
|
||||
<ds-health-status [status]="entry.value?.status"></ds-health-status>
|
||||
<div class="ml-3 d-inline-block">
|
||||
<div class="d-flex my-auto">
|
||||
<span *ngIf="acc.isExpanded(entry.key)" class="fas fa-chevron-up fa-fw"></span>
|
||||
<span *ngIf="!acc.isExpanded(entry.key)" class="fas fa-chevron-down fa-fw"></span>
|
||||
</div>
|
||||
@@ -17,9 +20,9 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<ds-health-component [healthComponent]="entry.value" [healthComponentName]="entry.key"></ds-health-component>
|
||||
<ds-health-component [healthComponent]="entry.value" [healthComponentName]="entry.key"
|
||||
[id]="'health-panel-' + entry.key + '-content'" role="presentation">
|
||||
</ds-health-component>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
</ngb-accordion>
|
||||
|
||||
|
||||
|
@@ -1,12 +1,12 @@
|
||||
<ng-container [ngSwitch]="status">
|
||||
<i *ngSwitchCase="HealthStatus.UP"
|
||||
class="fa fa-check-circle text-success ml-2 mt-1"
|
||||
class="fa fa-check-circle text-success my-auto"
|
||||
ngbTooltip="{{'health-page.status.ok.info' | translate}}" container="body" ></i>
|
||||
<i *ngSwitchCase="HealthStatus.UP_WITH_ISSUES"
|
||||
class="fa fa-exclamation-triangle text-warning ml-2 mt-1"
|
||||
class="fa fa-exclamation-triangle text-warning my-auto"
|
||||
ngbTooltip="{{'health-page.status.warning.info' | translate}}" container="body"></i>
|
||||
<i *ngSwitchCase="HealthStatus.DOWN"
|
||||
class="fa fa-times-circle text-danger ml-2 mt-1"
|
||||
class="fa fa-times-circle text-danger my-auto"
|
||||
ngbTooltip="{{'health-page.status.error.info' | translate}}" container="body"></i>
|
||||
|
||||
</ng-container>
|
||||
|
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
display: flex;
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ import { RecentItemListComponent } from './recent-item-list/recent-item-list.com
|
||||
import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module';
|
||||
import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module';
|
||||
import { ThemedTopLevelCommunityListComponent } from './top-level-community-list/themed-top-level-community-list.component';
|
||||
import { SuggestionNotificationsModule } from '../suggestion-notifications/suggestion-notifications.module';
|
||||
import { NotificationsModule } from '../notifications/notifications.module';
|
||||
|
||||
const DECLARATIONS = [
|
||||
HomePageComponent,
|
||||
@@ -33,7 +33,7 @@ const DECLARATIONS = [
|
||||
ResearchEntitiesModule.withEntryComponents(),
|
||||
HomePageRoutingModule,
|
||||
StatisticsModule.forRoot(),
|
||||
SuggestionNotificationsModule
|
||||
NotificationsModule
|
||||
],
|
||||
declarations: [
|
||||
...DECLARATIONS,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<div class="row row-offcanvas row-offcanvas-right">
|
||||
<div class="col-xs-12 col-sm-12 col-md-9">
|
||||
<form class="primary" [formGroup]="feedbackForm" (ngSubmit)="createFeedback()">
|
||||
<h2>{{ 'info.feedback.head' | translate }}</h2>
|
||||
<h1>{{ 'info.feedback.head' | translate }}</h1>
|
||||
<p>{{ 'info.feedback.info' | translate }}</p>
|
||||
<fieldset class="col p-0">
|
||||
<div class="row">
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<div class="container">
|
||||
<h3 class="mb-4">{{'bitstream-request-a-copy.header' | translate}}</h3>
|
||||
<h1 class="mb-4">{{'bitstream-request-a-copy.header' | translate}}</h1>
|
||||
<div *ngIf="canDownload$|async" class="alert alert-success">
|
||||
<span>{{'bitstream-request-a-copy.alert.canDownload1' | translate}}</span>
|
||||
<a [routerLink]="getBitstreamLink()">{{'bitstream-request-a-copy.alert.canDownload2'| translate}}</a>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<ng-container *ngIf="bundles">
|
||||
<div class="row">
|
||||
<div class="col-12 mb-2">
|
||||
<h2>{{'item.bitstreams.upload.title' | translate}}</h2>
|
||||
<h1>{{'item.bitstreams.upload.title' | translate}}</h1>
|
||||
<ng-container *ngVar="(itemRD$ | async)?.payload as item">
|
||||
<div *ngIf="item">
|
||||
<span class="font-weight-bold">{{'item.bitstreams.upload.item' | translate}}</span>
|
||||
|
@@ -1,14 +1,16 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h2 class="border-bottom">{{'item.edit.head' | translate}}</h2>
|
||||
<h1 class="border-bottom">{{'item.edit.head' | translate}}</h1>
|
||||
<div class="pt-2">
|
||||
<ul class="nav nav-tabs justify-content-start" role="tablist">
|
||||
<li *ngFor="let page of pages" class="nav-item" [attr.aria-selected]="page.page === currentPage" role="tab">
|
||||
<li *ngFor="let page of pages" class="nav-item" role="presentation">
|
||||
<a *ngIf="(page.enabled | async)"
|
||||
[attr.aria-selected]="page.page === currentPage"
|
||||
class="nav-link"
|
||||
[ngClass]="{'active' : page.page === currentPage}"
|
||||
[routerLink]="['./' + page.page]">
|
||||
[routerLink]="['./' + page.page]"
|
||||
role="tab">
|
||||
{{'item.edit.tabs.' + page.page + '.head' | translate}}
|
||||
</a>
|
||||
<span [ngbTooltip]="'item.edit.tabs.disabled.tooltip' | translate">
|
||||
|
@@ -1,21 +1,25 @@
|
||||
<div class="item-bitstreams" *ngVar="(bundles$ | async) as bundles">
|
||||
<div class="button-row top d-flex mt-2 space-children-mr">
|
||||
<button class="mr-auto btn btn-success"
|
||||
[attr.aria-label]="'item.edit.bitstreams.upload-button' | translate"
|
||||
[routerLink]="[itemPageRoute, 'bitstreams', 'new']"><i
|
||||
class="fas fa-upload"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.upload-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
|
||||
[attr.aria-label]="'item.edit.bitstreams.reinstate-button' | translate"
|
||||
(click)="reinstate()"><i
|
||||
class="fas fa-undo-alt"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.reinstate-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-primary" [disabled]="!(hasChanges() | async) || submitting"
|
||||
[attr.aria-label]="'item.edit.bitstreams.save-button' | translate"
|
||||
(click)="submit()"><i
|
||||
class="fas fa-save"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.save-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
||||
[attr.aria-label]="'item.edit.bitstreams.discard-button' | translate"
|
||||
[disabled]="!(hasChanges() | async) || submitting"
|
||||
(click)="discard()"><i
|
||||
class="fas fa-times"></i>
|
||||
@@ -49,16 +53,19 @@
|
||||
<div class="button-row bottom">
|
||||
<div class="mt-4 float-right space-children-mr ml-gap">
|
||||
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
|
||||
[attr.aria-label]="'item.edit.bitstreams.reinstate-button' | translate"
|
||||
(click)="reinstate()"><i
|
||||
class="fas fa-undo-alt"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.reinstate-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-primary" [disabled]="!(hasChanges() | async) || submitting"
|
||||
[attr.aria-label]="'item.edit.bitstreams.save-button' | translate"
|
||||
(click)="submit()"><i
|
||||
class="fas fa-save"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.save-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
||||
[attr.aria-label]="'item.edit.bitstreams.discard-button' | translate"
|
||||
[disabled]="!(hasChanges() | async) || submitting"
|
||||
(click)="discard()"><i
|
||||
class="fas fa-times"></i>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user