[DURACOM-307] apply migration script to conflicted files resolved in favor of Angular 18 update

This commit is contained in:
FrancescoMolinaro
2025-03-10 18:11:22 +01:00
parent 1aa9faeb2f
commit 51c95de793
193 changed files with 7962 additions and 6485 deletions

View File

@@ -2,10 +2,10 @@
<ngb-panel [id]="'browse'"> <ngb-panel [id]="'browse'">
<ng-template ngbPanelTitle> <ng-template ngbPanelTitle>
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="acc.toggle('browse')" <div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="acc.toggle('browse')"
data-test="browse"> data-test="browse">
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" <button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()"
[attr.aria-expanded]="acc.isExpanded('browse')" [attr.aria-expanded]="acc.isExpanded('browse')"
aria-controls="bulk-access-browse-panel-content"> aria-controls="bulk-access-browse-panel-content">
{{ 'admin.access-control.bulk-access-browse.header' | translate }} {{ 'admin.access-control.bulk-access-browse.header' | translate }}
</button> </button>
</div> </div>
@@ -18,9 +18,9 @@
<ng-template ngbNavContent> <ng-template ngbNavContent>
<div class="bulk-access-search"> <div class="bulk-access-search">
<ds-search [configuration]="'administrativeBulkAccess'" <ds-search [configuration]="'administrativeBulkAccess'"
[selectable]="true" [selectable]="true"
[selectionConfig]="{ repeatable: true, listId: listId }" [selectionConfig]="{ repeatable: true, listId: listId }"
[showThumbnails]="false"></ds-search> [showThumbnails]="false"></ds-search>
</div> </div>
</ng-template> </ng-template>
</li> </li>
@@ -36,21 +36,25 @@
[showPaginator]="false" [showPaginator]="false"
(prev)="pagePrev()" (prev)="pagePrev()"
(next)="pageNext()"> (next)="pageNext()">
<ul *ngIf="(objectsSelected$|async)?.hasSucceeded" class="list-unstyled ms-4"> @if ((objectsSelected$|async)?.hasSucceeded) {
<li *ngFor='let object of (objectsSelected$|async)?.payload?.page | paginate: { itemsPerPage: (paginationOptions$ | async).pageSize, <ul class="list-unstyled ms-4">
currentPage: (paginationOptions$ | async).currentPage, totalItems: (objectsSelected$|async)?.payload?.page.length }; let i = index; let last = last ' @for (object of (objectsSelected$|async)?.payload?.page | paginate: { itemsPerPage: (paginationOptions$ | async).pageSize,
class="mt-4 mb-4 d-flex" currentPage: (paginationOptions$ | async).currentPage, totalItems: (objectsSelected$|async)?.payload?.page.length }; track object; let i = $index; let last = $last) {
[attr.data-test]="'list-object' | dsBrowserOnly"> <li
<ds-selectable-list-item-control [index]="i" class="mt-4 mb-4 d-flex"
[object]="object" [attr.data-test]="'list-object' | dsBrowserOnly">
[selectionConfig]="{ repeatable: true, listId: listId }"></ds-selectable-list-item-control> <ds-selectable-list-item-control [index]="i"
<ds-listable-object-component-loader [listID]="listId" [object]="object"
[index]="i" [selectionConfig]="{ repeatable: true, listId: listId }"></ds-selectable-list-item-control>
[object]="object" <ds-listable-object-component-loader [listID]="listId"
[showThumbnails]="false" [index]="i"
[viewMode]="'list'"></ds-listable-object-component-loader> [object]="object"
</li> [showThumbnails]="false"
</ul> [viewMode]="'list'"></ds-listable-object-component-loader>
</li>
}
</ul>
}
</ds-pagination> </ds-pagination>
</ng-template> </ng-template>
</li> </li>

View File

@@ -6,7 +6,7 @@
<div> <div>
<button class="me-auto btn btn-success addEPerson-button" <button class="me-auto btn btn-success addEPerson-button"
[routerLink]="'create'"> [routerLink]="'create'">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
<span class="d-none d-sm-inline ms-1">{{labelPrefix + 'button.add' | translate}}</span> <span class="d-none d-sm-inline ms-1">{{labelPrefix + 'button.add' | translate}}</span>
</button> </button>
@@ -18,77 +18,84 @@
</h2> </h2>
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between"> <form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
<div> <div>
<select name="scope" id="scope" formControlName="scope" class="form-select" aria-label="Search scope"> <select name="scope" id="scope" formControlName="scope" class="form-select" aria-label="Search scope">
<option value="metadata">{{labelPrefix + 'search.scope.metadata' | translate}}</option> <option value="metadata">{{labelPrefix + 'search.scope.metadata' | translate}}</option>
<option value="email">{{labelPrefix + 'search.scope.email' | translate}}</option> <option value="email">{{labelPrefix + 'search.scope.email' | translate}}</option>
</select> </select>
</div> </div>
<div class="flex-grow-1 me-3 ms-3"> <div class="flex-grow-1 me-3 ms-3">
<div class="mb-3 input-group"> <div class="mb-3 input-group">
<input type="text" name="query" id="query" formControlName="query" <input type="text" name="query" id="query" formControlName="query"
class="form-control" [attr.aria-label]="labelPrefix + 'search.placeholder' | translate" class="form-control" [attr.aria-label]="labelPrefix + 'search.placeholder' | translate"
[placeholder]="(labelPrefix + 'search.placeholder' | translate)"> [placeholder]="(labelPrefix + 'search.placeholder' | translate)">
<span class="input-group-append"> <span class="input-group-append">
<button type="submit" class="search-button btn btn-primary"> <button type="submit" class="search-button btn btn-primary">
<i class="fas fa-search"></i> {{ labelPrefix + 'search.button' | translate }} <i class="fas fa-search"></i> {{ labelPrefix + 'search.button' | translate }}
</button> </button>
</span> </span>
</div> </div>
</div> </div>
<div> <div>
<button (click)="clearFormAndResetResult();" <button (click)="clearFormAndResetResult();"
class="search-button btn btn-secondary">{{labelPrefix + 'button.see-all' | translate}}</button> class="search-button btn btn-secondary">{{labelPrefix + 'button.see-all' | translate}}</button>
</div> </div>
</form> </form>
<ds-loading *ngIf="searching$ | async"></ds-loading> @if (searching$ | async) {
<ds-pagination <ds-loading></ds-loading>
*ngIf="(pageInfoState$ | async)?.totalElements > 0 && (searching$ | async) !== true" }
[paginationOptions]="config" @if ((pageInfoState$ | async)?.totalElements > 0 && (searching$ | async) !== true) {
[collectionSize]="(pageInfoState$ | async)?.totalElements" <ds-pagination
[hideGear]="true" [paginationOptions]="config"
[hidePagerWhenSinglePage]="true"> [collectionSize]="(pageInfoState$ | async)?.totalElements"
[hideGear]="true"
<div class="table-responsive"> [hidePagerWhenSinglePage]="true">
<table id="epeople" class="table table-striped table-hover table-bordered"> <div class="table-responsive">
<thead> <table id="epeople" class="table table-striped table-hover table-bordered">
<tr> <thead>
<th scope="col">{{labelPrefix + 'table.id' | translate}}</th> <tr>
<th scope="col">{{labelPrefix + 'table.name' | translate}}</th> <th scope="col">{{labelPrefix + 'table.id' | translate}}</th>
<th scope="col">{{labelPrefix + 'table.email' | translate}}</th> <th scope="col">{{labelPrefix + 'table.name' | translate}}</th>
<th>{{labelPrefix + 'table.edit' | translate}}</th> <th scope="col">{{labelPrefix + 'table.email' | translate}}</th>
</tr> <th>{{labelPrefix + 'table.edit' | translate}}</th>
</thead> </tr>
<tbody> </thead>
<tr *ngFor="let epersonDto of (ePeopleDto$ | async)?.page" <tbody>
[ngClass]="{'table-primary' : (activeEPerson$ | async) === epersonDto.eperson}"> @for (epersonDto of (ePeopleDto$ | async)?.page; track epersonDto) {
<td>{{epersonDto.eperson.id}}</td> <tr
<td>{{ dsoNameService.getName(epersonDto.eperson) }}</td> [ngClass]="{'table-primary' : (activeEPerson$ | async) === epersonDto.eperson}">
<td>{{epersonDto.eperson.email}}</td> <td>{{epersonDto.eperson.id}}</td>
<td> <td>{{ dsoNameService.getName(epersonDto.eperson) }}</td>
<div class="btn-group edit-field"> <td>{{epersonDto.eperson.email}}</td>
<button [routerLink]="getEditEPeoplePage(epersonDto.eperson.id)" <td>
<div class="btn-group edit-field">
<button [routerLink]="getEditEPeoplePage(epersonDto.eperson.id)"
class="btn btn-outline-primary btn-sm access-control-editEPersonButton" class="btn btn-outline-primary btn-sm access-control-editEPersonButton"
title="{{labelPrefix + 'table.edit.buttons.edit' | translate: { name: dsoNameService.getName(epersonDto.eperson) } }}"> title="{{labelPrefix + 'table.edit.buttons.edit' | translate: { name: dsoNameService.getName(epersonDto.eperson) } }}">
<i class="fas fa-edit fa-fw"></i> <i class="fas fa-edit fa-fw"></i>
</button> </button>
<button *ngIf="epersonDto.ableToDelete" (click)="deleteEPerson(epersonDto.eperson)" @if (epersonDto.ableToDelete) {
class="delete-button btn btn-outline-danger btn-sm access-control-deleteEPersonButton" <button (click)="deleteEPerson(epersonDto.eperson)"
title="{{labelPrefix + 'table.edit.buttons.remove' | translate: { name: dsoNameService.getName(epersonDto.eperson) } }}"> class="delete-button btn btn-outline-danger btn-sm access-control-deleteEPersonButton"
<i class="fas fa-trash-alt fa-fw"></i> title="{{labelPrefix + 'table.edit.buttons.remove' | translate: { name: dsoNameService.getName(epersonDto.eperson) } }}">
</button> <i class="fas fa-trash-alt fa-fw"></i>
</div> </button>
</td> }
</tr> </div>
</tbody> </td>
</table> </tr>
}
</tbody>
</table>
</div>
</ds-pagination>
}
@if ((pageInfoState$ | async)?.totalElements === 0) {
<div class="alert alert-info w-100 mb-2" role="alert">
{{labelPrefix + 'no-items' | translate}}
</div> </div>
}
</ds-pagination>
<div *ngIf="(pageInfoState$ | async)?.totalElements === 0" class="alert alert-info w-100 mb-2" role="alert">
{{labelPrefix + 'no-items' | translate}}
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -2,97 +2,111 @@
<div class="group-form row"> <div class="group-form row">
<div class="col-12"> <div class="col-12">
<div *ngIf="activeEPerson$ | async; then editHeader; else createHeader"></div> @if (activeEPerson$ | async) {
<ng-template #createHeader>
<h1 class="border-bottom pb-2">{{messagePrefix + '.create' | translate}}</h1>
</ng-template>
<ng-template #editHeader>
<h1 class="border-bottom pb-2">{{messagePrefix + '.edit' | translate}}</h1> <h1 class="border-bottom pb-2">{{messagePrefix + '.edit' | translate}}</h1>
</ng-template> } @else {
<h1 class="border-bottom pb-2">{{messagePrefix + '.create' | translate}}</h1>
}
<ds-form [formId]="formId" <ds-form [formId]="formId"
[formModel]="formModel" [formModel]="formModel"
[formGroup]="formGroup" [formGroup]="formGroup"
[formLayout]="formLayout" [formLayout]="formLayout"
[displayCancel]="false" [displayCancel]="false"
[submitLabel]="submitLabel" [submitLabel]="submitLabel"
(submitForm)="onSubmit()"> (submitForm)="onSubmit()">
<div before class="btn-group"> <div before class="btn-group">
<button (click)="onCancel()" type="button" class="btn btn-outline-secondary"> <button (click)="onCancel()" type="button" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left"></i> {{messagePrefix + '.return' | translate}} <i class="fas fa-arrow-left"></i> {{messagePrefix + '.return' | translate}}
</button> </button>
</div> </div>
<div *ngIf="displayResetPassword" between class="btn-group"> @if (displayResetPassword) {
<button class="btn btn-primary" [dsBtnDisabled]="(canReset$ | async) !== true" type="button" (click)="resetPassword()"> <div between class="btn-group">
<i class="fa fa-key"></i> {{'admin.access-control.epeople.actions.reset' | translate}} <button class="btn btn-primary" [dsBtnDisabled]="(canReset$ | async) !== true" type="button" (click)="resetPassword()">
<i class="fa fa-key"></i> {{'admin.access-control.epeople.actions.reset' | translate}}
</button>
</div>
}
@if (canImpersonate$ | async) {
<div between class="btn-group ms-1">
@if (!isImpersonated) {
<button class="btn btn-primary" type="button" (click)="impersonate()">
<i class="fa fa-user-secret"></i> {{'admin.access-control.epeople.actions.impersonate' | translate}}
</button>
}
@if (isImpersonated) {
<button class="btn btn-primary" type="button" (click)="stopImpersonating()">
<i class="fa fa-user-secret"></i> {{'admin.access-control.epeople.actions.stop-impersonating' | translate}}
</button>
}
</div>
}
@if (canDelete$ | async) {
<button after class="btn btn-danger delete-button" type="button" (click)="delete()">
<i class="fas fa-trash"></i> {{'admin.access-control.epeople.actions.delete' | translate}}
</button> </button>
</div> }
<div *ngIf="canImpersonate$ | async" between class="btn-group ms-1">
<button *ngIf="!isImpersonated" class="btn btn-primary" type="button" (click)="impersonate()">
<i class="fa fa-user-secret"></i> {{'admin.access-control.epeople.actions.impersonate' | translate}}
</button>
<button *ngIf="isImpersonated" class="btn btn-primary" type="button" (click)="stopImpersonating()">
<i class="fa fa-user-secret"></i> {{'admin.access-control.epeople.actions.stop-impersonating' | translate}}
</button>
</div>
<button *ngIf="canDelete$ | async" after class="btn btn-danger delete-button" type="button" (click)="delete()">
<i class="fas fa-trash"></i> {{'admin.access-control.epeople.actions.delete' | translate}}
</button>
</ds-form> </ds-form>
<ds-loading [showMessage]="false" *ngIf="!formGroup"></ds-loading> @if (!formGroup) {
<ds-loading [showMessage]="false"></ds-loading>
}
<div *ngIf="activeEPerson$ | async"> @if (activeEPerson$ | async) {
<h2>{{messagePrefix + '.groupsEPersonIsMemberOf' | translate}}</h2> <div>
<h2>{{messagePrefix + '.groupsEPersonIsMemberOf' | translate}}</h2>
<ds-loading [showMessage]="false" *ngIf="groups$ | async | dsHasNoValue"></ds-loading> @if (groups$ | async | dsHasNoValue) {
<ds-loading [showMessage]="false"></ds-loading>
<ds-pagination }
*ngIf="(groups$ | async)?.payload?.totalElements > 0" @if ((groups$ | async)?.payload?.totalElements > 0) {
[paginationOptions]="config" <ds-pagination
[collectionSize]="(groups$ | async)?.payload?.totalElements" [paginationOptions]="config"
[hideGear]="true" [collectionSize]="(groups$ | async)?.payload?.totalElements"
[hidePagerWhenSinglePage]="true" [hideGear]="true"
(pageChange)="onPageChange($event)"> [hidePagerWhenSinglePage]="true"
(pageChange)="onPageChange($event)">
<div class="table-responsive"> <div class="table-responsive">
<table id="groups" class="table table-striped table-hover table-bordered"> <table id="groups" class="table table-striped table-hover table-bordered">
<thead> <thead>
<tr> <tr>
<th scope="col" class="align-middle">{{messagePrefix + '.table.id' | translate}}</th> <th scope="col" class="align-middle">{{messagePrefix + '.table.id' | translate}}</th>
<th scope="col" class="align-middle">{{messagePrefix + '.table.name' | translate}}</th> <th scope="col" class="align-middle">{{messagePrefix + '.table.name' | translate}}</th>
<th scope="col" class="align-middle">{{messagePrefix + '.table.collectionOrCommunity' | translate}}</th> <th scope="col" class="align-middle">{{messagePrefix + '.table.collectionOrCommunity' | translate}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let group of (groups$ | async)?.payload?.page"> @for (group of (groups$ | async)?.payload?.page; track group) {
<td class="align-middle">{{group.id}}</td> <tr>
<td class="align-middle"> <td class="align-middle">{{group.id}}</td>
<a (click)="groupsDataService.startEditingNewGroup(group)" <td class="align-middle">
[routerLink]="[groupsDataService.getGroupEditPageRouterLink(group)]"> <a (click)="groupsDataService.startEditingNewGroup(group)"
{{ dsoNameService.getName(group) }} [routerLink]="[groupsDataService.getGroupEditPageRouterLink(group)]">
</a> {{ dsoNameService.getName(group) }}
</td> </a>
<td class="align-middle"> </td>
{{ dsoNameService.getName((group.object | async)?.payload) }} <td class="align-middle">
</td> {{ dsoNameService.getName((group.object | async)?.payload) }}
</tr> </td>
</tbody> </tr>
</table> }
</div> </tbody>
</table>
</ds-pagination> </div>
</ds-pagination>
<div *ngIf="(groups$ | async)?.payload?.totalElements === 0" class="alert alert-info w-100 mb-2" role="alert"> }
<div>{{messagePrefix + '.memberOfNoGroups' | translate}}</div> @if ((groups$ | async)?.payload?.totalElements === 0) {
<div> <div class="alert alert-info w-100 mb-2" role="alert">
<button [routerLink]="[groupsDataService.getGroupRegistryRouterLink()]" <div>{{messagePrefix + '.memberOfNoGroups' | translate}}</div>
class="btn btn-primary">{{messagePrefix + '.goToGroups' | translate}}</button> <div>
</div> <button [routerLink]="[groupsDataService.getGroupRegistryRouterLink()]"
class="btn btn-primary">{{messagePrefix + '.goToGroups' | translate}}</button>
</div>
</div>
}
</div> </div>
</div> }
</div> </div>
</div> </div>
</div> </div>

View File

@@ -5,7 +5,7 @@
<h1 id="header" class="pb-2">{{messagePrefix + 'head' | translate}}</h1> <h1 id="header" class="pb-2">{{messagePrefix + 'head' | translate}}</h1>
<div> <div>
<button class="me-auto btn btn-success" <button class="me-auto btn btn-success"
[routerLink]="'create'"> [routerLink]="'create'">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
<span class="d-none d-sm-inline ms-1">{{messagePrefix + 'button.add' | translate}}</span> <span class="d-none d-sm-inline ms-1">{{messagePrefix + 'button.add' | translate}}</span>
</button> </button>
@@ -17,8 +17,8 @@
<div class="flex-grow-1 me-3"> <div class="flex-grow-1 me-3">
<div class="mb-3 input-group"> <div class="mb-3 input-group">
<input type="text" name="query" id="query" formControlName="query" <input type="text" name="query" id="query" formControlName="query"
class="form-control" [attr.aria-label]="messagePrefix + 'search.placeholder' | translate" class="form-control" [attr.aria-label]="messagePrefix + 'search.placeholder' | translate"
[placeholder]="(messagePrefix + 'search.placeholder' | translate)" > [placeholder]="(messagePrefix + 'search.placeholder' | translate)" >
<span class="input-group-append"> <span class="input-group-append">
<button type="submit" class="search-button btn btn-primary"> <button type="submit" class="search-button btn btn-primary">
<i class="fas fa-search"></i> {{ messagePrefix + 'search.button' | translate }} <i class="fas fa-search"></i> {{ messagePrefix + 'search.button' | translate }}
@@ -33,66 +33,78 @@
</div> </div>
</form> </form>
<ds-loading *ngIf="loading$ | async"></ds-loading> @if (loading$ | async) {
<ds-pagination <ds-loading></ds-loading>
*ngIf="(pageInfoState$ | async)?.totalElements > 0 && (loading$ | async) !== true" }
[paginationOptions]="config" @if ((pageInfoState$ | async)?.totalElements > 0 && (loading$ | async) !== true) {
[collectionSize]="(pageInfoState$ | async)?.totalElements" <ds-pagination
[hideGear]="true" [paginationOptions]="config"
[hidePagerWhenSinglePage]="true"> [collectionSize]="(pageInfoState$ | async)?.totalElements"
[hideGear]="true"
[hidePagerWhenSinglePage]="true">
<div class="table-responsive">
<table id="groups" class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th scope="col">{{messagePrefix + 'table.id' | translate}}</th>
<th scope="col">{{messagePrefix + 'table.name' | translate}}</th>
<th scope="col">{{messagePrefix + 'table.collectionOrCommunity' | translate}}</th>
<th scope="col">{{messagePrefix + 'table.members' | translate}}</th>
<th>{{messagePrefix + 'table.edit' | translate}}</th>
</tr>
</thead>
<tbody>
@for (groupDto of (groupsDto$ | async)?.page; track groupDto) {
<tr>
<td>{{groupDto.group.id}}</td>
<td>{{ dsoNameService.getName(groupDto.group) }}</td>
<td>{{ dsoNameService.getName((groupDto.group.object | async)?.payload) }}</td>
<td>{{groupDto.epersons?.totalElements + groupDto.subgroups?.totalElements}}</td>
<td>
<div class="btn-group edit-field">
@switch (groupDto.ableToEdit) {
@case (true) {
<button
[routerLink]="groupService.getGroupEditPageRouterLink(groupDto.group)"
class="btn btn-outline-primary btn-sm btn-edit"
title="{{messagePrefix + 'table.edit.buttons.edit' | translate: {name: dsoNameService.getName(groupDto.group) } }}"
>
<i class="fas fa-edit fa-fw"></i>
</button>
}
@case (false) {
<button
[dsBtnDisabled]="true"
class="btn btn-outline-primary btn-sm btn-edit"
placement="left"
[ngbTooltip]="'admin.access-control.epeople.table.edit.buttons.edit-disabled' | translate"
>
<i class="fas fa-edit fa-fw"></i>
</button>
}
}
@if (!groupDto.group?.permanent && groupDto.ableToDelete) {
<button
(click)="deleteGroup(groupDto)" class="btn btn-outline-danger btn-sm btn-delete"
title="{{messagePrefix + 'table.edit.buttons.remove' | translate: {name: dsoNameService.getName(groupDto.group) } }}">
<i class="fas fa-trash-alt fa-fw"></i>
</button>
}
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
</ds-pagination>
}
<div class="table-responsive"> @if ((pageInfoState$ | async)?.totalElements === 0) {
<table id="groups" class="table table-striped table-hover table-bordered"> <div class="alert alert-info w-100 mb-2" role="alert">
<thead> {{messagePrefix + 'no-items' | translate}}
<tr>
<th scope="col">{{messagePrefix + 'table.id' | translate}}</th>
<th scope="col">{{messagePrefix + 'table.name' | translate}}</th>
<th scope="col">{{messagePrefix + 'table.collectionOrCommunity' | translate}}</th>
<th scope="col">{{messagePrefix + 'table.members' | translate}}</th>
<th>{{messagePrefix + 'table.edit' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let groupDto of (groupsDto$ | async)?.page">
<td>{{groupDto.group.id}}</td>
<td>{{ dsoNameService.getName(groupDto.group) }}</td>
<td>{{ dsoNameService.getName((groupDto.group.object | async)?.payload) }}</td>
<td>{{groupDto.epersons?.totalElements + groupDto.subgroups?.totalElements}}</td>
<td>
<div class="btn-group edit-field">
<ng-container [ngSwitch]="groupDto.ableToEdit">
<button *ngSwitchCase="true"
[routerLink]="groupService.getGroupEditPageRouterLink(groupDto.group)"
class="btn btn-outline-primary btn-sm btn-edit"
title="{{messagePrefix + 'table.edit.buttons.edit' | translate: {name: dsoNameService.getName(groupDto.group) } }}"
>
<i class="fas fa-edit fa-fw"></i>
</button>
<button *ngSwitchCase="false"
[dsBtnDisabled]="true"
class="btn btn-outline-primary btn-sm btn-edit"
placement="left"
[ngbTooltip]="'admin.access-control.epeople.table.edit.buttons.edit-disabled' | translate"
>
<i class="fas fa-edit fa-fw"></i>
</button>
</ng-container>
<button *ngIf="!groupDto.group?.permanent && groupDto.ableToDelete"
(click)="deleteGroup(groupDto)" class="btn btn-outline-danger btn-sm btn-delete"
title="{{messagePrefix + 'table.edit.buttons.remove' | translate: {name: dsoNameService.getName(groupDto.group) } }}">
<i class="fas fa-trash-alt fa-fw"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div> </div>
</ds-pagination> }
<div *ngIf="(pageInfoState$ | async)?.totalElements === 0" class="alert alert-info w-100 mb-2" role="alert">
{{messagePrefix + 'no-items' | translate}}
</div>
</div> </div>
</div> </div>

View File

@@ -1,10 +1,12 @@
<div class="container"> <div class="container">
<h1 id="header">{{'admin.batch-import.page.header' | translate}}</h1> <h1 id="header">{{'admin.batch-import.page.header' | translate}}</h1>
<p>{{'admin.batch-import.page.help' | translate}}</p> <p>{{'admin.batch-import.page.help' | translate}}</p>
<p *ngIf="dso"> @if (dso) {
selected collection: <b>{{getDspaceObjectName()}}</b>&nbsp; <p>
<a href="javascript:void(0)" (click)="removeDspaceObject()">{{'admin.batch-import.page.remove' | translate}}</a> selected collection: <b>{{getDspaceObjectName()}}</b>&nbsp;
</p> <a href="javascript:void(0)" (click)="removeDspaceObject()">{{'admin.batch-import.page.remove' | translate}}</a>
</p>
}
<p> <p>
<button class="btn btn-primary" (click)="this.selectCollection();">{{'admin.metadata-import.page.button.select-collection' | translate}}</button> <button class="btn btn-primary" (click)="this.selectCollection();">{{'admin.metadata-import.page.button.select-collection' | translate}}</button>
</p> </p>
@@ -21,32 +23,35 @@
</div> </div>
<ui-switch color="#ebebeb" <ui-switch color="#ebebeb"
[checkedLabel]="'admin.metadata-import.page.toggle.upload' | translate" [checkedLabel]="'admin.metadata-import.page.toggle.upload' | translate"
[uncheckedLabel]="'admin.metadata-import.page.toggle.url' | translate" [uncheckedLabel]="'admin.metadata-import.page.toggle.url' | translate"
[checked]="isUpload" [checked]="isUpload"
(change)="toggleUpload()" ></ui-switch> (change)="toggleUpload()" ></ui-switch>
<small class="form-text text-muted d-block"> <small class="form-text text-muted d-block">
{{'admin.batch-import.page.toggle.help' | translate}} {{'admin.batch-import.page.toggle.help' | translate}}
</small> </small>
<ds-file-dropzone-no-uploader @if (isUpload) {
*ngIf="isUpload" <ds-file-dropzone-no-uploader
data-test="file-dropzone" data-test="file-dropzone"
(onFileAdded)="setFile($event)" (onFileAdded)="setFile($event)"
[dropMessageLabel]="'admin.batch-import.page.dropMsg'" [dropMessageLabel]="'admin.batch-import.page.dropMsg'"
[dropMessageLabelReplacement]="'admin.batch-import.page.dropMsgReplace'"> [dropMessageLabelReplacement]="'admin.batch-import.page.dropMsgReplace'">
</ds-file-dropzone-no-uploader> </ds-file-dropzone-no-uploader>
}
<div class="mb-3 mt-2" *ngIf="!isUpload"> @if (!isUpload) {
<input class="form-control" type="text" placeholder="{{'admin.metadata-import.page.urlMsg' | translate}}" <div class="mb-3 mt-2">
data-test="file-url-input" [(ngModel)]="fileURL"> <input class="form-control" type="text" placeholder="{{'admin.metadata-import.page.urlMsg' | translate}}"
</div> data-test="file-url-input" [(ngModel)]="fileURL">
</div>
}
<div class="space-children-mr"> <div class="space-children-mr">
<button class="btn btn-secondary" id="backButton" <button class="btn btn-secondary" id="backButton"
(click)="this.onReturn();">{{'admin.metadata-import.page.button.return' | translate}}</button> (click)="this.onReturn();">{{'admin.metadata-import.page.button.return' | translate}}</button>
<button class="btn btn-primary" id="proceedButton" <button class="btn btn-primary" id="proceedButton"
(click)="this.importMetadata();">{{'admin.metadata-import.page.button.proceed' | translate}}</button> (click)="this.importMetadata();">{{'admin.metadata-import.page.button.proceed' | translate}}</button>
</div> </div>
</div> </div>

View File

@@ -4,34 +4,38 @@
<h1 class="flex-grow-1">{{ isNewService ? ('ldn-create-service.title' | translate) : ('ldn-edit-registered-service.title' | translate) }}</h1> <h1 class="flex-grow-1">{{ isNewService ? ('ldn-create-service.title' | translate) : ('ldn-edit-registered-service.title' | translate) }}</h1>
</div> </div>
<!-- In the toggle section --> <!-- In the toggle section -->
<div class="toggle-switch-container" *ngIf="!isNewService"> @if (!isNewService) {
<label class="status-label font-weight-bold" for="enabled">{{ 'ldn-service-status' | translate }}</label> <div class="toggle-switch-container">
<div> <label class="status-label font-weight-bold" for="enabled">{{ 'ldn-service-status' | translate }}</label>
<input formControlName="enabled" hidden id="enabled" name="enabled" type="checkbox"> <div>
<div (click)="toggleEnabled()" [class.checked]="formModel.get('enabled').value" class="toggle-switch"> <input formControlName="enabled" hidden id="enabled" name="enabled" type="checkbox">
<div class="slider"></div> <div (click)="toggleEnabled()" [class.checked]="formModel.get('enabled').value" class="toggle-switch">
<div class="slider"></div>
</div>
</div> </div>
</div> </div>
</div> }
<!-- In the Name section --> <!-- In the Name section -->
<div class="mb-5"> <div class="mb-5">
<label for="name" class="font-weight-bold">{{ 'ldn-new-service.form.label.name' | translate }}</label> <label for="name" class="font-weight-bold">{{ 'ldn-new-service.form.label.name' | translate }}</label>
<input [class.invalid-field]="formModel.get('name').invalid && formModel.get('name').touched" <input [class.invalid-field]="formModel.get('name').invalid && formModel.get('name').touched"
[placeholder]="'ldn-new-service.form.placeholder.name' | translate" class="form-control" [placeholder]="'ldn-new-service.form.placeholder.name' | translate" class="form-control"
formControlName="name" formControlName="name"
id="name" id="name"
name="name" name="name"
type="text"> type="text">
<div *ngIf="formModel.get('name').invalid && formModel.get('name').touched" class="error-text"> @if (formModel.get('name').invalid && formModel.get('name').touched) {
{{ 'ldn-new-service.form.error.name' | translate }} <div class="error-text">
</div> {{ 'ldn-new-service.form.error.name' | translate }}
</div>
}
</div> </div>
<!-- In the description section --> <!-- In the description section -->
<div class="mb-5 mt-5 d-flex flex-column"> <div class="mb-5 mt-5 d-flex flex-column">
<label for="description" class="font-weight-bold">{{ 'ldn-new-service.form.label.description' | translate }}</label> <label for="description" class="font-weight-bold">{{ 'ldn-new-service.form.label.description' | translate }}</label>
<textarea [placeholder]="'ldn-new-service.form.placeholder.description' | translate" <textarea [placeholder]="'ldn-new-service.form.placeholder.description' | translate"
class="form-control" formControlName="description" id="description" name="description"></textarea> class="form-control" formControlName="description" id="description" name="description"></textarea>
</div> </div>
<div class="mb-5 mt-5"> <div class="mb-5 mt-5">
@@ -40,30 +44,34 @@
<div class="d-flex flex-column w-50 me-2"> <div class="d-flex flex-column w-50 me-2">
<label for="url" class="font-weight-bold">{{ 'ldn-new-service.form.label.url' | translate }}</label> <label for="url" class="font-weight-bold">{{ 'ldn-new-service.form.label.url' | translate }}</label>
<input [class.invalid-field]="formModel.get('url').invalid && formModel.get('url').touched" <input [class.invalid-field]="formModel.get('url').invalid && formModel.get('url').touched"
[placeholder]="'ldn-new-service.form.placeholder.url' | translate" class="form-control" [placeholder]="'ldn-new-service.form.placeholder.url' | translate" class="form-control"
formControlName="url" formControlName="url"
id="url" id="url"
name="url" name="url"
type="text"> type="text">
<div *ngIf="formModel.get('url').invalid && formModel.get('url').touched" class="error-text"> @if (formModel.get('url').invalid && formModel.get('url').touched) {
{{ 'ldn-new-service.form.error.url' | translate }} <div class="error-text">
</div> {{ 'ldn-new-service.form.error.url' | translate }}
</div>
}
</div> </div>
<div class="d-flex flex-column w-50"> <div class="d-flex flex-column w-50">
<label for="score" class="font-weight-bold">{{ 'ldn-new-service.form.label.score' | translate }}</label> <label for="score" class="font-weight-bold">{{ 'ldn-new-service.form.label.score' | translate }}</label>
<input [class.invalid-field]="formModel.get('score').invalid && formModel.get('score').touched" <input [class.invalid-field]="formModel.get('score').invalid && formModel.get('score').touched"
[placeholder]="'ldn-new-service.form.placeholder.score' | translate" formControlName="score" [placeholder]="'ldn-new-service.form.placeholder.score' | translate" formControlName="score"
id="score" id="score"
name="score" name="score"
min="0" min="0"
max="1" max="1"
step=".01" step=".01"
class="form-control" class="form-control"
type="number"> type="number">
<div *ngIf="formModel.get('score').invalid && formModel.get('score').touched" class="error-text"> @if (formModel.get('score').invalid && formModel.get('score').touched) {
{{ 'ldn-new-service.form.error.score' | translate }} <div class="error-text">
</div> {{ 'ldn-new-service.form.error.score' | translate }}
</div>
}
</div> </div>
</div> </div>
</div> </div>
@@ -73,21 +81,23 @@
<label for="lowerIp" class="font-weight-bold">{{ 'ldn-new-service.form.label.ip-range' | translate }}</label> <label for="lowerIp" class="font-weight-bold">{{ 'ldn-new-service.form.label.ip-range' | translate }}</label>
<div class="d-flex"> <div class="d-flex">
<input [class.invalid-field]="formModel.get('lowerIp').invalid && formModel.get('lowerIp').touched" <input [class.invalid-field]="formModel.get('lowerIp').invalid && formModel.get('lowerIp').touched"
[placeholder]="'ldn-new-service.form.placeholder.lowerIp' | translate" class="form-control me-2" [placeholder]="'ldn-new-service.form.placeholder.lowerIp' | translate" class="form-control me-2"
formControlName="lowerIp" formControlName="lowerIp"
id="lowerIp" id="lowerIp"
name="lowerIp" name="lowerIp"
type="text"> type="text">
<input [class.invalid-field]="formModel.get('upperIp').invalid && formModel.get('upperIp').touched" <input [class.invalid-field]="formModel.get('upperIp').invalid && formModel.get('upperIp').touched"
[placeholder]="'ldn-new-service.form.placeholder.upperIp' | translate" class="form-control" [placeholder]="'ldn-new-service.form.placeholder.upperIp' | translate" class="form-control"
formControlName="upperIp" formControlName="upperIp"
id="upperIp" id="upperIp"
name="upperIp" name="upperIp"
type="text"> type="text">
</div>
<div *ngIf="(formModel.get('lowerIp').invalid && formModel.get('lowerIp').touched) || (formModel.get('upperIp').invalid && formModel.get('upperIp').touched)" class="error-text">
{{ 'ldn-new-service.form.error.ipRange' | translate }}
</div> </div>
@if ((formModel.get('lowerIp').invalid && formModel.get('lowerIp').touched) || (formModel.get('upperIp').invalid && formModel.get('upperIp').touched)) {
<div class="error-text">
{{ 'ldn-new-service.form.error.ipRange' | translate }}
</div>
}
<div class="text-muted"> <div class="text-muted">
{{ 'ldn-new-service.form.hint.ipRange' | translate }} {{ 'ldn-new-service.form.hint.ipRange' | translate }}
</div> </div>
@@ -97,223 +107,242 @@
<div class="mb-5 mt-5"> <div class="mb-5 mt-5">
<label for="ldnUrl" class="font-weight-bold">{{ 'ldn-new-service.form.label.ldnUrl' | translate }}</label> <label for="ldnUrl" class="font-weight-bold">{{ 'ldn-new-service.form.label.ldnUrl' | translate }}</label>
<input [class.invalid-field]="formModel.get('ldnUrl').invalid && formModel.get('ldnUrl').touched" <input [class.invalid-field]="formModel.get('ldnUrl').invalid && formModel.get('ldnUrl').touched"
[placeholder]="'ldn-new-service.form.placeholder.ldnUrl' | translate" class="form-control" [placeholder]="'ldn-new-service.form.placeholder.ldnUrl' | translate" class="form-control"
formControlName="ldnUrl" formControlName="ldnUrl"
id="ldnUrl" id="ldnUrl"
name="ldnUrl" name="ldnUrl"
type="text"> type="text">
<div *ngIf="formModel.get('ldnUrl').invalid && formModel.get('ldnUrl').touched" > @if (formModel.get('ldnUrl').invalid && formModel.get('ldnUrl').touched) {
<div *ngIf="formModel.get('ldnUrl').errors['required']" class="error-text"> <div >
{{ 'ldn-new-service.form.error.ldnurl' | translate }} @if (formModel.get('ldnUrl').errors['required']) {
<div class="error-text">
{{ 'ldn-new-service.form.error.ldnurl' | translate }}
</div>
}
@if (formModel.get('ldnUrl').errors['ldnUrlAlreadyAssociated']) {
<div class="error-text">
{{ 'ldn-new-service.form.error.ldnurl.ldnUrlAlreadyAssociated' | translate }}
</div>
}
</div> </div>
<div *ngIf="formModel.get('ldnUrl').errors['ldnUrlAlreadyAssociated']" class="error-text"> }
{{ 'ldn-new-service.form.error.ldnurl.ldnUrlAlreadyAssociated' | translate }}
</div>
</div>
</div> </div>
<!-- In the Inbound Patterns Labels section --> <!-- In the Inbound Patterns Labels section -->
<div class="row mb-1 mt-5" *ngIf="areControlsInitialized"> @if (areControlsInitialized) {
<div class="col"> <div class="row mb-1 mt-5">
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.inboundPattern' | translate }} </label>
</div>
<ng-container *ngIf="formModel.get('notifyServiceInboundPatterns')['controls'][0]?.value?.pattern">
<div class="col"> <div class="col">
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.ItemFilter' | translate }}</label> <label class="font-weight-bold">{{ 'ldn-new-service.form.label.inboundPattern' | translate }} </label>
</div> </div>
<div class="col-sm-1"> @if (formModel.get('notifyServiceInboundPatterns')['controls'][0]?.value?.pattern) {
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.automatic' | translate }}</label> <div class="col">
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.ItemFilter' | translate }}</label>
</div>
<div class="col-sm-1">
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.automatic' | translate }}</label>
</div>
}
<div class="col-sm-2">
</div> </div>
</ng-container>
<div class="col-sm-2">
</div> </div>
</div> }
<!-- In the Inbound Patterns section --> <!-- In the Inbound Patterns section -->
<div *ngIf="areControlsInitialized"> @if (areControlsInitialized) {
<div *ngFor="let patternGroup of formModel.get('notifyServiceInboundPatterns')['controls']; let i = index" <div>
[class.marked-for-deletion]="markedForDeletionInboundPattern.includes(i)" @for (patternGroup of formModel.get('notifyServiceInboundPatterns')['controls']; track patternGroup; let i = $index) {
formGroupName="notifyServiceInboundPatterns"> <div
[class.marked-for-deletion]="markedForDeletionInboundPattern.includes(i)"
<ng-container [formGroupName]="i"> formGroupName="notifyServiceInboundPatterns">
<ng-container [formGroupName]="i">
<div class="row mb-1 align-items-center">
<div class="row mb-1 align-items-center"> <div class="col">
<div class="col"> <div #inboundPatternDropdown="ngbDropdown" class="w-80" display="dynamic"
<div #inboundPatternDropdown="ngbDropdown" class="w-80" display="dynamic" id="additionalInboundPattern{{i}}"
id="additionalInboundPattern{{i}}" ngbDropdown placement="top-start">
ngbDropdown placement="top-start"> <div class="position-relative right-addon" role="combobox" aria-expanded="false" aria-controls="inboundPatternDropdownButton">
<div class="position-relative right-addon" role="combobox" aria-expanded="false" aria-controls="inboundPatternDropdownButton"> <i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle" ngbDropdownToggle></i>
ngbDropdownToggle></i> <input
<input (click)="inboundPatternDropdown.open();"
(click)="inboundPatternDropdown.open();" [readonly]="true"
[readonly]="true" [value]="selectedInboundPatterns"
[value]="selectedInboundPatterns" class="form-control w-80 scrollable-dropdown-input"
class="form-control w-80 scrollable-dropdown-input" formControlName="patternLabel"
formControlName="patternLabel" id="inboundPatternDropdownButton"
id="inboundPatternDropdownButton" ngbDropdownAnchor
ngbDropdownAnchor type="text"
type="text" [attr.aria-label]="'ldn-service-input-inbound-pattern-dropdown' | translate"
[attr.aria-label]="'ldn-service-input-inbound-pattern-dropdown' | translate" />
/> <div aria-labelledby="inboundPatternDropdownButton"
<div aria-labelledby="inboundPatternDropdownButton" class="dropdown-menu dropdown-menu-top w-100 "
class="dropdown-menu dropdown-menu-top w-100 " ngbDropdownMenu>
ngbDropdownMenu> <div class="scrollable-menu" role="listbox">
<div class="scrollable-menu" role="listbox"> @for (pattern of inboundPatterns; track pattern; let internalIndex = $index) {
<button (click)="selectInboundPattern(pattern, i); $event.stopPropagation()" <button (click)="selectInboundPattern(pattern, i); $event.stopPropagation()"
*ngFor="let pattern of inboundPatterns; let internalIndex = index"
[title]="'ldn-service.form.pattern.' + pattern + '.description' | translate" [title]="'ldn-service.form.pattern.' + pattern + '.description' | translate"
class="dropdown-item collection-item text-truncate w-100" class="dropdown-item collection-item text-truncate w-100"
ngbDropdownItem ngbDropdownItem
type="button"> type="button">
<div>{{ 'ldn-service.form.pattern.' + pattern + '.label' | translate }}</div> <div>{{ 'ldn-service.form.pattern.' + pattern + '.label' | translate }}</div>
</button> </button>
</div> }
</div> </div>
</div>
</div>
</div>
<div class="col">
<ng-container
*ngIf="formModel.get('notifyServiceInboundPatterns')['controls'][i].value.pattern">
<div #inboundItemfilterDropdown="ngbDropdown" class="w-100" id="constraint{{i}}" ngbDropdown
placement="top-start">
<div class="position-relative right-addon" aria-expanded="false" aria-controls="inboundItemfilterDropdown" role="combobox">
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
ngbDropdownToggle></i>
<input
[readonly]="true"
class="form-control d-none w-100 scrollable-dropdown-input"
formControlName="constraint"
id="inboundItemfilterDropdown"
ngbDropdownAnchor
type="text"
[attr.aria-label]="'ldn-service-input-inbound-item-filter-dropdown' | translate"
/>
<input
(click)="inboundItemfilterDropdown.open();"
[readonly]="true"
class="form-control w-100 scrollable-dropdown-input"
formControlName="constraintFormatted"
id="inboundItemfilterDropdownPrettified"
ngbDropdownAnchor
type="text"
[attr.aria-label]="'ldn-service-input-inbound-item-filter-dropdown' | translate"
/>
<div aria-labelledby="inboundItemfilterDropdownButton"
class="dropdown-menu scrollable-dropdown-menu w-100 "
ngbDropdownMenu>
<div class="scrollable-menu" role="listbox">
<button (click)="selectInboundItemFilter('', i); $event.stopPropagation()"
class="dropdown-item collection-item text-truncate w-100" ngbDropdownItem type="button">
<span> {{'ldn-service.control-constaint-select-none' | translate}} </span>
</button>
<button (click)="selectInboundItemFilter(constraint.id, i); $event.stopPropagation()"
*ngFor="let constraint of (itemFiltersRD$ | async)?.payload?.page; let internalIndex = index"
class="dropdown-item collection-item text-truncate w-100"
ngbDropdownItem
type="button">
<div>{{ constraint.id + '.label' | translate }}</div>
</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</ng-container> <div class="col">
</div> @if (formModel.get('notifyServiceInboundPatterns')['controls'][i].value.pattern) {
<div #inboundItemfilterDropdown="ngbDropdown" class="w-100" id="constraint{{i}}" ngbDropdown
<div placement="top-start">
[style.visibility]="formModel.get('notifyServiceInboundPatterns')['controls'][i].value.pattern ? 'visible' : 'hidden'" <div class="position-relative right-addon" aria-expanded="false" aria-controls="inboundItemfilterDropdown" role="combobox">
class="col-sm-1"> <i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
<input formControlName="automatic" hidden id="automatic{{i}}" name="automatic{{i}}" ngbDropdownToggle></i>
type="checkbox"> <input
<div (click)="toggleAutomatic(i)" [readonly]="true"
[class.checked]="formModel.get('notifyServiceInboundPatterns.' + i + '.automatic').value" class="form-control d-none w-100 scrollable-dropdown-input"
class="toggle-switch"> formControlName="constraint"
<div class="slider"></div> id="inboundItemfilterDropdown"
</div> ngbDropdownAnchor
</div> type="text"
[attr.aria-label]="'ldn-service-input-inbound-item-filter-dropdown' | translate"
/>
<div class="col-sm-2"> <input
<div class="btn-group"> (click)="inboundItemfilterDropdown.open();"
<button (click)="markForInboundPatternDeletion(i)" class="btn btn-outline-dark trash-button" [readonly]="true"
[title]="'ldn-service-button-mark-inbound-deletion' | translate" class="form-control w-100 scrollable-dropdown-input"
type="button"> formControlName="constraintFormatted"
<i class="fas fa-trash"></i> id="inboundItemfilterDropdownPrettified"
</button> ngbDropdownAnchor
type="text"
[attr.aria-label]="'ldn-service-input-inbound-item-filter-dropdown' | translate"
<button (click)="unmarkForInboundPatternDeletion(i)" />
*ngIf="markedForDeletionInboundPattern.includes(i)" <div aria-labelledby="inboundItemfilterDropdownButton"
class="dropdown-menu scrollable-dropdown-menu w-100 "
ngbDropdownMenu>
<div class="scrollable-menu" role="listbox">
<button (click)="selectInboundItemFilter('', i); $event.stopPropagation()"
class="dropdown-item collection-item text-truncate w-100" ngbDropdownItem type="button">
<span> {{'ldn-service.control-constaint-select-none' | translate}} </span>
</button>
@for (constraint of (itemFiltersRD$ | async)?.payload?.page; track constraint; let internalIndex = $index) {
<button (click)="selectInboundItemFilter(constraint.id, i); $event.stopPropagation()"
class="dropdown-item collection-item text-truncate w-100"
ngbDropdownItem
type="button">
<div>{{ constraint.id + '.label' | translate }}</div>
</button>
}
</div>
</div>
</div>
</div>
}
</div>
<div
[style.visibility]="formModel.get('notifyServiceInboundPatterns')['controls'][i].value.pattern ? 'visible' : 'hidden'"
class="col-sm-1">
<input formControlName="automatic" hidden id="automatic{{i}}" name="automatic{{i}}"
type="checkbox">
<div (click)="toggleAutomatic(i)"
[class.checked]="formModel.get('notifyServiceInboundPatterns.' + i + '.automatic').value"
class="toggle-switch">
<div class="slider"></div>
</div>
</div>
<div class="col-sm-2">
<div class="btn-group">
<button (click)="markForInboundPatternDeletion(i)" class="btn btn-outline-dark trash-button"
[title]="'ldn-service-button-mark-inbound-deletion' | translate"
type="button">
<i class="fas fa-trash"></i>
</button>
@if (markedForDeletionInboundPattern.includes(i)) {
<button (click)="unmarkForInboundPatternDeletion(i)"
[title]="'ldn-service-button-unmark-inbound-deletion' | translate" [title]="'ldn-service-button-unmark-inbound-deletion' | translate"
class="btn btn-warning " class="btn btn-warning "
type="button"> type="button">
<i class="fas fa-undo"></i> <i class="fas fa-undo"></i>
</button> </button>
}
</div>
</div>
</div> </div>
</div> </ng-container>
</div> </div>
</ng-container> }
</div> </div>
</div> }
<span (click)="addInboundPattern()" <span (click)="addInboundPattern()"
class="add-pattern-link mb-2">{{ 'ldn-new-service.form.label.addPattern' | translate }}</span> class="add-pattern-link mb-2">{{ 'ldn-new-service.form.label.addPattern' | translate }}</span>
<hr> <hr>
<div class="form-group row"> <div class="form-group row">
<div class="col text-right space-children-mr"> <div class="col text-right space-children-mr">
<ng-content select="[before]"></ng-content> <ng-content select="[before]"></ng-content>
<button (click)="resetFormAndLeave()" class="btn btn-outline-secondary" type="button"> <button (click)="resetFormAndLeave()" class="btn btn-outline-secondary" type="button">
<span>&nbsp;{{ 'submission.general.back.submit' | translate }}</span> <span>&nbsp;{{ 'submission.general.back.submit' | translate }}</span>
</button> </button>
<button class="btn btn-primary" type="submit"> <button class="btn btn-primary" type="submit">
<span><i class="fas fa-save"></i>&nbsp;{{ 'ldn-new-service.form.label.submit' | translate }}</span> <span><i class="fas fa-save"></i>&nbsp;{{ 'ldn-new-service.form.label.submit' | translate }}</span>
</button> </button>
</div>
</div> </div>
</div> </form>
</form> </div>
</div> <ng-template #confirmModal>
<ng-template #confirmModal>
<div class="modal-header"> <div class="modal-header">
<h4 *ngIf="!isNewService">{{'service.overview.edit.modal' | translate }}</h4> @if (!isNewService) {
<h4 *ngIf="isNewService">{{'service.overview.create.modal' | translate }}</h4> <h4>{{'service.overview.edit.modal' | translate }}</h4>
}
@if (isNewService) {
<h4>{{'service.overview.create.modal' | translate }}</h4>
}
<button (click)="closeModal()" aria-label="Close" <button (click)="closeModal()" aria-label="Close"
class="close" type="button"> class="close" type="button">
<span aria-hidden="true">×</span> <span aria-hidden="true">×</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div *ngIf="!isNewService"> @if (!isNewService) {
{{ 'service.overview.edit.body' | translate }} <div>
</div> {{ 'service.overview.edit.body' | translate }}
<span *ngIf="isNewService">
{{ 'service.overview.create.body' | translate }}
</span>
</div>
<div class="modal-footer">
<div *ngIf="!isNewService">
<button (click)="closeModal()" class="btn btn-outline-secondary me-2"
id="delete-confirm-edit">{{ 'service.detail.return' | translate }}
</button>
<button *ngIf="!isNewService" (click)="patchService()"
class="btn btn-primary">{{ 'service.detail.update' | translate }}
</button>
</div> </div>
<div *ngIf="isNewService"> }
@if (isNewService) {
<span>
{{ 'service.overview.create.body' | translate }}
</span>
}
</div>
<div class="modal-footer">
@if (!isNewService) {
<div>
<button (click)="closeModal()" class="btn btn-outline-secondary me-2"
id="delete-confirm-edit">{{ 'service.detail.return' | translate }}
</button>
@if (!isNewService) {
<button (click)="patchService()"
class="btn btn-primary">{{ 'service.detail.update' | translate }}
</button>
}
</div>
}
@if (isNewService) {
<div>
<button (click)="closeModal()" class="btn btn-outline-secondary me-2 " <button (click)="closeModal()" class="btn btn-outline-secondary me-2 "
id="delete-confirm-new">{{ 'service.refuse.create' | translate }} id="delete-confirm-new">{{ 'service.refuse.create' | translate }}
</button> </button>
<button (click)="createService()" <button (click)="createService()"
class="btn btn-primary">{{ 'service.confirm.create' | translate }} class="btn btn-primary">{{ 'service.confirm.create' | translate }}
</button> </button>
</div> </div>
</div> }
</ng-template> </div>
</ng-template>

View File

@@ -4,63 +4,67 @@
</div> </div>
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<button class="btn btn-success" routerLink="/admin/ldn/services/new"><i <button class="btn btn-success" routerLink="/admin/ldn/services/new"><i
class="fas fa-plus pe-2"></i>{{ 'process.overview.new' | translate }}</button> class="fas fa-plus pe-2"></i>{{ 'process.overview.new' | translate }}</button>
</div> </div>
<ds-pagination *ngIf="(ldnServicesRD$ | async)?.payload?.totalElements > 0" @if ((ldnServicesRD$ | async)?.payload?.totalElements > 0) {
[collectionSize]="(ldnServicesRD$ | async)?.payload?.totalElements" <ds-pagination
[hideGear]="true" [collectionSize]="(ldnServicesRD$ | async)?.payload?.totalElements"
[hidePagerWhenSinglePage]="true" [hideGear]="true"
[paginationOptions]="pageConfig"> [hidePagerWhenSinglePage]="true"
<div class="table-responsive"> [paginationOptions]="pageConfig">
<table class="table table-striped table-hover"> <div class="table-responsive">
<thead> <table class="table table-striped table-hover">
<tr> <thead>
<th scope="col">{{ 'service.overview.table.name' | translate }}</th> <tr>
<th scope="col">{{ 'service.overview.table.description' | translate }}</th> <th scope="col">{{ 'service.overview.table.name' | translate }}</th>
<th scope="col">{{ 'service.overview.table.status' | translate }}</th> <th scope="col">{{ 'service.overview.table.description' | translate }}</th>
<th scope="col">{{ 'service.overview.table.actions' | translate }}</th> <th scope="col">{{ 'service.overview.table.status' | translate }}</th>
</tr> <th scope="col">{{ 'service.overview.table.actions' | translate }}</th>
</thead> </tr>
<tbody> </thead>
<tr *ngFor="let ldnService of (ldnServicesRD$ | async)?.payload?.page"> <tbody>
<td class="col-3">{{ ldnService.name }}</td> @for (ldnService of (ldnServicesRD$ | async)?.payload?.page; track ldnService) {
<td> <tr>
<ds-truncatable [id]="ldnService.id"> <td class="col-3">{{ ldnService.name }}</td>
<ds-truncatable-part [id]="ldnService.id" [minLines]="2"> <td>
<div> <ds-truncatable [id]="ldnService.id">
{{ ldnService.description }} <ds-truncatable-part [id]="ldnService.id" [minLines]="2">
</div> <div>
</ds-truncatable-part> {{ ldnService.description }}
</ds-truncatable> </div>
</td> </ds-truncatable-part>
<td> </ds-truncatable>
<span (click)="toggleStatus(ldnService, ldnServicesService)" </td>
[ngClass]="{ 'status-enabled': ldnService.enabled, 'status-disabled': !ldnService.enabled }" <td>
[title]="ldnService.enabled ? ('ldn-service.overview.table.clickToDisable' | translate) : ('ldn-service.overview.table.clickToEnable' | translate)" <span (click)="toggleStatus(ldnService, ldnServicesService)"
class="status-indicator"> [ngClass]="{ 'status-enabled': ldnService.enabled, 'status-disabled': !ldnService.enabled }"
{{ ldnService.enabled ? ('ldn-service.overview.table.enabled' | translate) : ('ldn-service.overview.table.disabled' | translate) }} [title]="ldnService.enabled ? ('ldn-service.overview.table.clickToDisable' | translate) : ('ldn-service.overview.table.clickToEnable' | translate)"
</span> class="status-indicator">
</td> {{ ldnService.enabled ? ('ldn-service.overview.table.enabled' | translate) : ('ldn-service.overview.table.disabled' | translate) }}
<td> </span>
<div class="btn-group"> </td>
<button <td>
(click)="selectServiceToDelete(ldnService.id)" <div class="btn-group">
[attr.aria-label]="'ldn-service-overview-select-delete' | translate" <button
class="btn btn-outline-danger"> (click)="selectServiceToDelete(ldnService.id)"
<i class="fas fa-trash"></i> [attr.aria-label]="'ldn-service-overview-select-delete' | translate"
</button> class="btn btn-outline-danger">
<button [routerLink]="['/admin/ldn/services/edit/', ldnService.id]" <i class="fas fa-trash"></i>
</button>
<button [routerLink]="['/admin/ldn/services/edit/', ldnService.id]"
[attr.aria-label]="'ldn-service-overview-select-edit' | translate" [attr.aria-label]="'ldn-service-overview-select-edit' | translate"
class="btn btn-outline-dark"> class="btn btn-outline-dark">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</button> </button>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> }
</table> </tbody>
</div> </table>
</ds-pagination> </div>
</ds-pagination>
}
</div> </div>
<ng-template #deleteModal> <ng-template #deleteModal>
@@ -72,8 +76,8 @@
<h4>{{'service.overview.delete.header' | translate }}</h4> <h4>{{'service.overview.delete.header' | translate }}</h4>
</div> </div>
<button (click)="closeModal()" aria-label="Close" <button (click)="closeModal()" aria-label="Close"
[attr.aria-label]="'ldn-service-overview-close-modal' | translate" [attr.aria-label]="'ldn-service-overview-close-modal' | translate"
class="close" type="button"> class="close" type="button">
<span aria-hidden="true">×</span> <span aria-hidden="true">×</span>
</button> </button>
</div> </div>
@@ -84,12 +88,12 @@
</div> </div>
<div class="mt-4 text-right"> <div class="mt-4 text-right">
<button (click)="closeModal()" <button (click)="closeModal()"
[attr.aria-label]="'ldn-service-overview-close-modal' | translate" [attr.aria-label]="'ldn-service-overview-close-modal' | translate"
class="btn btn-outline-secondary me-2">{{ 'service.detail.delete.cancel' | translate }}</button> class="btn btn-outline-secondary me-2">{{ 'service.detail.delete.cancel' | translate }}</button>
<button (click)="deleteSelected(this.selectedServiceId.toString(), ldnServicesService)" <button (click)="deleteSelected(this.selectedServiceId.toString(), ldnServicesService)"
class="btn btn-danger" class="btn btn-danger"
[attr.aria-label]="'ldn-service-overview-select-delete' | translate" [attr.aria-label]="'ldn-service-overview-select-delete' | translate"
id="delete-confirm">{{ 'service.overview.delete' | translate }} id="delete-confirm">{{ 'service.overview.delete' | translate }}
</button> </button>
</div> </div>
</div> </div>

View File

@@ -3,10 +3,12 @@
<div class="col-12 col-md-3 text-left h4">{{((isInbound$ | async) ? 'admin.notify.dashboard.inbound' : 'admin.notify.dashboard.outbound') | translate}}</div> <div class="col-12 col-md-3 text-left h4">{{((isInbound$ | async) ? 'admin.notify.dashboard.inbound' : 'admin.notify.dashboard.outbound') | translate}}</div>
<div class="col-md-9"> <div class="col-md-9">
<div class="h4"> <div class="h4">
<button (click)="resetDefaultConfiguration()" *ngIf="(selectedSearchConfig$ | async) !== defaultConfiguration" class="badge bg-primary me-1 mb-1"> @if ((selectedSearchConfig$ | async) !== defaultConfiguration) {
{{ 'admin-notify-logs.' + (selectedSearchConfig$ | async) | translate}} <button (click)="resetDefaultConfiguration()" class="badge bg-primary me-1 mb-1">
<span> ×</span> {{ 'admin-notify-logs.' + (selectedSearchConfig$ | async) | translate}}
</button> <span> ×</span>
</button>
}
</div> </div>
<ds-search-labels [inPlaceSearch]="true"></ds-search-labels> <ds-search-labels [inPlaceSearch]="true"></ds-search-labels>
</div> </div>

View File

@@ -8,51 +8,62 @@
<p id="create-new" class="mb-2"><a [routerLink]="'add'" class="btn btn-success">{{'admin.registries.bitstream-formats.create.new' | translate}}</a></p> <p id="create-new" class="mb-2"><a [routerLink]="'add'" class="btn btn-success">{{'admin.registries.bitstream-formats.create.new' | translate}}</a></p>
<ds-pagination @if ((bitstreamFormats$ | async)?.payload?.totalElements > 0) {
*ngIf="(bitstreamFormats$ | async)?.payload?.totalElements > 0" <ds-pagination
[paginationOptions]="pageConfig" [paginationOptions]="pageConfig"
[collectionSize]="(bitstreamFormats$ | async)?.payload?.totalElements" [collectionSize]="(bitstreamFormats$ | async)?.payload?.totalElements"
[hideGear]="false" [hideGear]="false"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">
<div class="table-responsive"> <div class="table-responsive">
<table id="formats" class="table table-striped table-hover"> <table id="formats" class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col"><span class="sr-only">{{'admin.registries.bitstream-formats.table.selected' | translate}}</span></th> <th scope="col"><span class="sr-only">{{'admin.registries.bitstream-formats.table.selected' | translate}}</span></th>
<th scope="col">{{'admin.registries.bitstream-formats.table.id' | 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.name' | translate}}</th>
<th scope="col">{{'admin.registries.bitstream-formats.table.mimetype' | translate}}</th> <th scope="col">{{'admin.registries.bitstream-formats.table.mimetype' | translate}}</th>
<th scope="col">{{'admin.registries.bitstream-formats.table.supportLevel.head' | translate}}</th> <th scope="col">{{'admin.registries.bitstream-formats.table.supportLevel.head' | translate}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let bitstreamFormat of (bitstreamFormats$ | async)?.payload?.page"> @for (bitstreamFormat of (bitstreamFormats$ | async)?.payload?.page; track bitstreamFormat) {
<td> <tr>
<label class="form-label mb-0"> <td>
<input type="checkbox" <label class="form-label mb-0">
[attr.aria-label]="'admin.registries.bitstream-formats.select' | translate" <input type="checkbox"
[checked]="(selectedBitstreamFormatIDs$ | async)?.includes(bitstreamFormat.id)" [attr.aria-label]="'admin.registries.bitstream-formats.select' | translate"
(change)="selectBitStreamFormat(bitstreamFormat, $event)" [checked]="(selectedBitstreamFormatIDs$ | async)?.includes(bitstreamFormat.id)"
> (change)="selectBitStreamFormat(bitstreamFormat, $event)"
<span class="sr-only">{{'admin.registries.bitstream-formats.select' | translate}}&#125;</span> >
</label> <span class="sr-only">{{'admin.registries.bitstream-formats.select' | translate}}&#125;</span>
</td> </label>
<td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{bitstreamFormat.id}}</a></td> </td>
<td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{bitstreamFormat.shortDescription}}</a></td> <td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{bitstreamFormat.id}}</a></td>
<td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{bitstreamFormat.mimetype}} <span *ngIf="bitstreamFormat.internal">({{'admin.registries.bitstream-formats.table.internal' | translate}})</span></a></td> <td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{bitstreamFormat.shortDescription}}</a></td>
<td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{'admin.registries.bitstream-formats.table.supportLevel.'+bitstreamFormat.supportLevel | translate}}</a></td> <td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{bitstreamFormat.mimetype}} @if (bitstreamFormat.internal) {
</tr> <span>({{'admin.registries.bitstream-formats.table.internal' | translate}})</span>
</tbody> }</a></td>
</table> <td><a [routerLink]="['/admin/registries/bitstream-formats', bitstreamFormat.id, 'edit']">{{'admin.registries.bitstream-formats.table.supportLevel.'+bitstreamFormat.supportLevel | translate}}</a></td>
</tr>
}
</tbody>
</table>
</div>
</ds-pagination>
}
@if ((bitstreamFormats$ | async)?.payload?.totalElements === 0) {
<div class="alert alert-info" role="alert">
{{'admin.registries.bitstream-formats.no-items' | translate}}
</div> </div>
</ds-pagination> }
<div *ngIf="(bitstreamFormats$ | async)?.payload?.totalElements === 0" class="alert alert-info" role="alert">
{{'admin.registries.bitstream-formats.no-items' | translate}}
</div>
<div> <div>
<button *ngIf="(bitstreamFormats$ | async)?.payload?.page?.length > 0" class="btn btn-primary deselect" (click)="deselectAll()">{{'admin.registries.bitstream-formats.table.deselect-all' | translate}}</button> @if ((bitstreamFormats$ | async)?.payload?.page?.length > 0) {
<button *ngIf="(bitstreamFormats$ | async)?.payload?.page?.length > 0" type="submit" class="btn btn-danger float-end" (click)="deleteFormats()">{{'admin.registries.bitstream-formats.table.delete' | translate}}</button> <button class="btn btn-primary deselect" (click)="deselectAll()">{{'admin.registries.bitstream-formats.table.deselect-all' | translate}}</button>
}
@if ((bitstreamFormats$ | async)?.payload?.page?.length > 0) {
<button type="submit" class="btn btn-danger float-end" (click)="deleteFormats()">{{'admin.registries.bitstream-formats.table.delete' | translate}}</button>
}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,60 +1,65 @@
<div class="container"> <div class="container">
<div class="metadata-registry row"> <div class="metadata-registry row">
<div class="col-12"> <div class="col-12">
<h1 id="header" class="border-bottom pb-2">{{'admin.registries.metadata.head' | translate}}</h1> <h1 id="header" class="border-bottom pb-2">{{'admin.registries.metadata.head' | translate}}</h1>
<p id="description" class="pb-2">{{'admin.registries.metadata.description' | translate}}</p> <p id="description" class="pb-2">{{'admin.registries.metadata.description' | translate}}</p>
<ds-metadata-schema-form (submitForm)="forceUpdateSchemas()"></ds-metadata-schema-form> <ds-metadata-schema-form (submitForm)="forceUpdateSchemas()"></ds-metadata-schema-form>
<ds-pagination @if ((metadataSchemas | async)?.payload?.totalElements > 0) {
*ngIf="(metadataSchemas | async)?.payload?.totalElements > 0" <ds-pagination
[paginationOptions]="config" [paginationOptions]="config"
[collectionSize]="(metadataSchemas | async)?.payload?.totalElements" [collectionSize]="(metadataSchemas | async)?.payload?.totalElements"
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">
<div class="table-responsive">
<div class="table-responsive"> <table id="metadata-schemas" class="table table-striped table-hover">
<table id="metadata-schemas" class="table table-striped table-hover"> <thead>
<thead> <tr>
<tr> <th scope="col"><span class="sr-only">{{'admin.registries.metadata.schemas.table.selected' | translate}}</span></th>
<th scope="col"><span class="sr-only">{{'admin.registries.metadata.schemas.table.selected' | translate}}</span></th> <th scope="col">{{'admin.registries.metadata.schemas.table.id' | translate}}</th>
<th scope="col">{{'admin.registries.metadata.schemas.table.id' | translate}}</th> <th scope="col">{{'admin.registries.metadata.schemas.table.namespace' | translate}}</th>
<th scope="col">{{'admin.registries.metadata.schemas.table.namespace' | translate}}</th> <th scope="col">{{'admin.registries.metadata.schemas.table.name' | translate}}</th>
<th scope="col">{{'admin.registries.metadata.schemas.table.name' | translate}}</th> </tr>
</tr> </thead>
</thead> <tbody>
<tbody> @for (schema of (metadataSchemas | async)?.payload?.page; track schema) {
<tr *ngFor="let schema of (metadataSchemas | async)?.payload?.page" <tr
[ngClass]="{'table-primary' : (activeMetadataSchema$ | async)?.id === schema.id}"> [ngClass]="{'table-primary' : (activeMetadataSchema$ | async)?.id === schema.id}">
<td> <td>
<label class="form-label mb-0"> <label class="form-label mb-0">
<input type="checkbox" <input type="checkbox"
[checked]="(selectedMetadataSchemaIDs$ | async)?.includes(schema.id)" [checked]="(selectedMetadataSchemaIDs$ | async)?.includes(schema.id)"
(change)="selectMetadataSchema(schema, $event)" (change)="selectMetadataSchema(schema, $event)"
> >
<span class="sr-only">{{(((selectedMetadataSchemaIDs$ | async)?.includes(schema.id)) ? 'admin.registries.metadata.schemas.deselect' : 'admin.registries.metadata.schemas.select') | translate}}</span> <span class="sr-only">{{(((selectedMetadataSchemaIDs$ | async)?.includes(schema.id)) ? 'admin.registries.metadata.schemas.deselect' : 'admin.registries.metadata.schemas.select') | translate}}</span>
</label> </label>
</td> </td>
<td class="selectable-row" (click)="editSchema(schema)"><a [routerLink]="[schema.prefix]">{{schema.id}}</a></td> <td class="selectable-row" (click)="editSchema(schema)"><a [routerLink]="[schema.prefix]">{{schema.id}}</a></td>
<td class="selectable-row" (click)="editSchema(schema)"><a [routerLink]="[schema.prefix]">{{schema.namespace}}</a></td> <td class="selectable-row" (click)="editSchema(schema)"><a [routerLink]="[schema.prefix]">{{schema.namespace}}</a></td>
<td class="selectable-row" (click)="editSchema(schema)"><a [routerLink]="[schema.prefix]">{{schema.prefix}}</a></td> <td class="selectable-row" (click)="editSchema(schema)"><a [routerLink]="[schema.prefix]">{{schema.prefix}}</a></td>
</tr> </tr>
</tbody> }
</table> </tbody>
</div> </table>
</div>
</ds-pagination> </ds-pagination>
}
<div *ngIf="(metadataSchemas | async)?.payload?.totalElements === 0" class="alert alert-info w-100 mb-2" role="alert">
{{'admin.registries.metadata.schemas.no-items' | translate}}
</div>
<div>
<button *ngIf="(metadataSchemas | async)?.payload?.page?.length > 0" type="submit" class="btn btn-danger float-end" (click)="deleteSchemas()">{{'admin.registries.metadata.schemas.table.delete' | translate}}</button>
</div>
@if ((metadataSchemas | async)?.payload?.totalElements === 0) {
<div class="alert alert-info w-100 mb-2" role="alert">
{{'admin.registries.metadata.schemas.no-items' | translate}}
</div> </div>
}
<div>
@if ((metadataSchemas | async)?.payload?.page?.length > 0) {
<button type="submit" class="btn btn-danger float-end" (click)="deleteSchemas()">{{'admin.registries.metadata.schemas.table.delete' | translate}}</button>
}
</div>
</div> </div>
</div>
</div> </div>

View File

@@ -8,52 +8,59 @@
<ds-metadata-field-form <ds-metadata-field-form
[metadataSchema]="schema" [metadataSchema]="schema"
(submitForm)="forceUpdateFields()"></ds-metadata-field-form> (submitForm)="forceUpdateFields()"></ds-metadata-field-form>
<h2>{{'admin.registries.schema.fields.head' | translate}}</h2> <h2>{{'admin.registries.schema.fields.head' | translate}}</h2>
<ng-container *ngVar="(metadataFields$ | async)?.payload as fields"> <ng-container *ngVar="(metadataFields$ | async)?.payload as fields">
<ds-pagination @if (fields?.totalElements > 0) {
*ngIf="fields?.totalElements > 0" <ds-pagination
[paginationOptions]="config" [paginationOptions]="config"
[collectionSize]="fields?.totalElements" [collectionSize]="fields?.totalElements"
[hideGear]="false" [hideGear]="false"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">
<div class="table-responsive"> <div class="table-responsive">
<table id="metadata-fields" class="table table-striped table-hover"> <table id="metadata-fields" class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th><span class="sr-only">{{'admin.registries.schema.fields.table.selected' | translate}}</span></th> <th><span class="sr-only">{{'admin.registries.schema.fields.table.selected' | translate}}</span></th>
<th scope="col">{{'admin.registries.schema.fields.table.id' | translate}}</th> <th scope="col">{{'admin.registries.schema.fields.table.id' | translate}}</th>
<th scope="col">{{'admin.registries.schema.fields.table.field' | translate}}</th> <th scope="col">{{'admin.registries.schema.fields.table.field' | translate}}</th>
<th scope="col">{{'admin.registries.schema.fields.table.scopenote' | translate}}</th> <th scope="col">{{'admin.registries.schema.fields.table.scopenote' | translate}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let field of fields?.page" @for (field of fields?.page; track field) {
[ngClass]="{'table-primary' : (activeField$ | async)?.id === field.id}"> <tr
<td *ngVar="(selectedMetadataFieldIDs$ | async)?.includes(field.id) as selected"> [ngClass]="{'table-primary' : (activeField$ | async)?.id === field.id}">
<input type="checkbox" <td *ngVar="(selectedMetadataFieldIDs$ | async)?.includes(field.id) as selected">
[attr.aria-label]="(selected ? 'admin.registries.schema.fields.deselect' : 'admin.registries.schema.fields.select') | translate" <input type="checkbox"
[checked]="selected" [attr.aria-label]="(selected ? 'admin.registries.schema.fields.deselect' : 'admin.registries.schema.fields.select') | translate"
(change)="selectMetadataField(field, $event)"> [checked]="selected"
</td> (change)="selectMetadataField(field, $event)">
<td class="selectable-row" (click)="editField(field)">{{field.id}}</td> </td>
<td class="selectable-row" (click)="editField(field)">{{schema?.prefix}}.{{field.element}}{{field.qualifier ? '.' + field.qualifier : ''}}</td> <td class="selectable-row" (click)="editField(field)">{{field.id}}</td>
<td class="selectable-row" (click)="editField(field)">{{field.scopeNote}}</td> <td class="selectable-row" (click)="editField(field)">{{schema?.prefix}}.{{field.element}}{{field.qualifier ? '.' + field.qualifier : ''}}</td>
</tr> <td class="selectable-row" (click)="editField(field)">{{field.scopeNote}}</td>
</tbody> </tr>
</table> }
</div> </tbody>
</ds-pagination> </table>
</div>
</ds-pagination>
}
<div *ngIf="fields?.totalElements === 0" class="alert alert-info w-100 mb-2" role="alert"> @if (fields?.totalElements === 0) {
{{'admin.registries.schema.fields.no-items' | translate}} <div class="alert alert-info w-100 mb-2" role="alert">
</div> {{'admin.registries.schema.fields.no-items' | translate}}
</div>
}
<div> <div>
<button [routerLink]="['/admin/registries/metadata']" class="btn btn-primary">{{'admin.registries.schema.return' | translate}}</button> <button [routerLink]="['/admin/registries/metadata']" class="btn btn-primary">{{'admin.registries.schema.return' | translate}}</button>
<button *ngIf="fields?.page?.length > 0" type="submit" class="btn btn-danger float-end" (click)="deleteFields()">{{'admin.registries.schema.fields.table.delete' | translate}}</button> @if (fields?.page?.length > 0) {
<button type="submit" class="btn btn-danger float-end" (click)="deleteFields()">{{'admin.registries.schema.fields.table.delete' | translate}}</button>
}
</div> </div>
</ng-container> </ng-container>

View File

@@ -1,19 +1,23 @@
<fieldset class="row row-cols-2"> <fieldset class="row row-cols-2">
<ng-container> <ng-container>
<span class="col-12">&nbsp;</span> <span class="col-12">&nbsp;</span>
<span class="col-1">&nbsp;</span> <span class="col-1">&nbsp;</span>
<button id="btnSelectAllFilters" class="btn btn-light mt-1 col-4" (click)="selectAll()">{{"admin.reports.commons.filters.select_all" | translate}}</button> <button id="btnSelectAllFilters" class="btn btn-light mt-1 col-4" (click)="selectAll()">{{"admin.reports.commons.filters.select_all" | translate}}</button>
<span class="col-2">&nbsp;</span> <span class="col-2">&nbsp;</span>
<button id="btnDeselectAllFilters" class="btn btn-light mt-1 col-4" (click)="deselectAll()">{{"admin.reports.commons.filters.deselect_all" | translate}}</button> <button id="btnDeselectAllFilters" class="btn btn-light mt-1 col-4" (click)="deselectAll()">{{"admin.reports.commons.filters.deselect_all" | translate}}</button>
<span class="col-1">&nbsp;</span> <span class="col-1">&nbsp;</span>
<span class="col-12">&nbsp;</span> <span class="col-12">&nbsp;</span>
</ng-container> </ng-container>
</fieldset> </fieldset>
<fieldset class="row row-cols-2" *ngFor="let group of allFilters()"> @for (group of allFilters(); track group) {
<fieldset class="row row-cols-2">
<legend>{{group.key | translate}}</legend> <legend>{{group.key | translate}}</legend>
<ng-container [formGroup]="filtersForm"> <ng-container [formGroup]="filtersForm">
<div *ngFor="let filter of group.filters" class="col-6"> @for (filter of group.filters; track filter) {
<input type="checkbox" id="flt-{{filter.id}}" value="{{filter.id}}" class="form-check-input col-1" formControlName="{{filter.id}}"><label for="flt-{{filter.id}}" class="col-11 align-middle" title="{{filter.tooltipKey | translate}}">{{filter.key | translate}}</label> <div class="col-6">
<input type="checkbox" id="flt-{{filter.id}}" value="{{filter.id}}" class="form-check-input col-1" formControlName="{{filter.id}}"><label for="flt-{{filter.id}}" class="col-11 align-middle" title="{{filter.tooltipKey | translate}}">{{filter.key | translate}}</label>
</div> </div>
}
</ng-container> </ng-container>
</fieldset> </fieldset>
}

View File

@@ -1,43 +1,47 @@
<div> <div>
<div class="modal-header">{{'supervision-group-selector.header' | translate}} <div class="modal-header">{{'supervision-group-selector.header' | translate}}
<button type="button" class="btn-close" (click)="close()" aria-label="Close"> <button type="button" class="btn-close" (click)="close()" aria-label="Close">
</button> </button>
</div>
<div class="modal-body">
<div class="row">
<div class="control-group col-sm-12">
<label for="supervisionOrder" class="form-label">{{'supervision-group-selector.select.type-of-order.label' | translate}}</label>
<select name="supervisionOrder" id="supervisionOrder" class="form-select"
[(ngModel)]="selectedOrderType"
attr.aria-label="{{'supervision-group-selector.select.type-of-order.label' | translate}}">
<option value="EDITOR">{{'supervision-group-selector.select.type-of-order.option.editor' | translate}}</option>
<option value="OBSERVER">{{'supervision-group-selector.select.type-of-order.option.observer' | translate}}</option>
</select>
@if (isSubmitted && (!selectedOrderType || selectedOrderType === '')) {
<ds-error
message="{{'supervision-group-selector.select.type-of-order.error' | translate}}"></ds-error>
}
</div>
</div> </div>
<div class="modal-body"> <div class="row">
<div class="row"> <div class="control-group col-sm-12">
<div class="control-group col-sm-12"> <label for="supervisionGroup" class="form-label">{{'supervision-group-selector.select.group.label' | translate}}</label>
<label for="supervisionOrder" class="form-label">{{'supervision-group-selector.select.type-of-order.label' | translate}}</label> <ng-container class="mb-3">
<select name="supervisionOrder" id="supervisionOrder" class="form-select" <input id="supervisionGroup" class="form-control" type="text" [value]="selectedGroup ? dsoNameService.getName(selectedGroup) : ''" disabled>
[(ngModel)]="selectedOrderType" @if (isSubmitted && !selectedGroup) {
attr.aria-label="{{'supervision-group-selector.select.type-of-order.label' | translate}}"> <ds-error message="{{'supervision-group-selector.select.group.error' | translate}}"></ds-error>
<option value="EDITOR">{{'supervision-group-selector.select.type-of-order.option.editor' | translate}}</option> }
<option value="OBSERVER">{{'supervision-group-selector.select.type-of-order.option.observer' | translate}}</option> </ng-container>
</select> <ds-eperson-group-list [isListOfEPerson]="false"
<ds-error *ngIf="isSubmitted && (!selectedOrderType || selectedOrderType === '')" (select)="updateGroupObjectSelected($event)"></ds-eperson-group-list>
message="{{'supervision-group-selector.select.type-of-order.error' | translate}}"></ds-error> </div>
</div>
</div>
<div class="row">
<div class="control-group col-sm-12">
<label for="supervisionGroup" class="form-label">{{'supervision-group-selector.select.group.label' | translate}}</label>
<ng-container class="mb-3">
<input id="supervisionGroup" class="form-control" type="text" [value]="selectedGroup ? dsoNameService.getName(selectedGroup) : ''" disabled>
<ds-error *ngIf="isSubmitted && !selectedGroup" message="{{'supervision-group-selector.select.group.error' | translate}}"></ds-error>
</ng-container>
<ds-eperson-group-list [isListOfEPerson]="false"
(select)="updateGroupObjectSelected($event)"></ds-eperson-group-list>
</div>
</div>
<!-- <div class="d-flex flex-row-reverse m-2"> -->
<div class="modal-footer">
<button class="btn btn-outline-secondary"
(click)="close()">
<i class="fas fa-times"></i> {{"supervision-group-selector.button.cancel" | translate}}
</button>
<button class="btn btn-primary save"
(click)="save()">
<i class="fas fa-save"></i> {{"supervision-group-selector.button.save" | translate}}
</button>
</div>
</div> </div>
<!-- <div class="d-flex flex-row-reverse m-2"> -->
<div class="modal-footer">
<button class="btn btn-outline-secondary"
(click)="close()">
<i class="fas fa-times"></i> {{"supervision-group-selector.button.cancel" | translate}}
</button>
<button class="btn btn-primary save"
(click)="save()">
<i class="fas fa-save"></i> {{"supervision-group-selector.button.save" | translate}}
</button>
</div>
</div>
</div> </div>

View File

@@ -1,15 +1,19 @@
<ng-container *ngVar="(supervisionOrderEntries$ | async) as supervisionOrders"> <ng-container *ngVar="(supervisionOrderEntries$ | async) as supervisionOrders">
<div class="item-list-supervision" *ngIf="supervisionOrders?.length > 0"> @if (supervisionOrders?.length > 0) {
<div> <div class="item-list-supervision">
<span>{{'workflow-item.search.result.list.element.supervised-by' | translate}} </span> <div>
<span>{{'workflow-item.search.result.list.element.supervised-by' | translate}} </span>
</div>
<div>
@for (supervisionOrder of supervisionOrders; track supervisionOrder) {
<a class="badge bg-primary me-1 mb-1 text-capitalize mw-100 text-truncate" data-test="soBadge"
[ngbTooltip]="'workflow-item.search.result.list.element.supervised.remove-tooltip' | translate"
(click)="$event.preventDefault(); $event.stopImmediatePropagation(); deleteSupervisionOrder(supervisionOrder)" aria-label="Close">
{{ dsoNameService.getName(supervisionOrder.group) }}
<span aria-hidden="true"> ×</span>
</a>
}
</div>
</div> </div>
<div> }
<a class="badge bg-primary me-1 mb-1 text-capitalize mw-100 text-truncate" *ngFor="let supervisionOrder of supervisionOrders" data-test="soBadge"
[ngbTooltip]="'workflow-item.search.result.list.element.supervised.remove-tooltip' | translate"
(click)="$event.preventDefault(); $event.stopImmediatePropagation(); deleteSupervisionOrder(supervisionOrder)" aria-label="Close">
{{ dsoNameService.getName(supervisionOrder.group) }}
<span aria-hidden="true"> ×</span>
</a>
</div>
</div>
</ng-container> </ng-container>

View File

@@ -1,12 +1,14 @@
<ng-template dsDynamicComponentLoader> <ng-template dsDynamicComponentLoader>
</ng-template> </ng-template>
<div #badges class="position-absolute ms-1"> <div #badges class="position-absolute ms-1">
<div class="workflow-badge"> <div class="workflow-badge">
<span class="badge bg-info">{{ "admin.workflow.item.workflow" | translate }}</span> <span class="badge bg-info">{{ "admin.workflow.item.workflow" | translate }}</span>
</div> </div>
</div> </div>
<ul #buttons class="list-group list-group-flush"> <ul #buttons class="list-group list-group-flush">
<li class="list-group-item"> <li class="list-group-item">
<ds-workflow-item-admin-workflow-actions-element *ngIf="object" class="d-flex justify-content-between" [wfi]="dso" [small]="true"></ds-workflow-item-admin-workflow-actions-element> @if (object) {
</li> <ds-workflow-item-admin-workflow-actions-element class="d-flex justify-content-between" [wfi]="dso" [small]="true"></ds-workflow-item-admin-workflow-actions-element>
}
</li>
</ul> </ul>

View File

@@ -1,10 +1,12 @@
<div class="workflow-badge"> <div class="workflow-badge">
<span class="badge bg-info">{{ "admin.workflow.item.workflow" | translate }}</span> <span class="badge bg-info">{{ "admin.workflow.item.workflow" | translate }}</span>
</div> </div>
<ds-listable-object-component-loader *ngIf="item$ | async" @if (item$ | async) {
[object]="item$ | async" <ds-listable-object-component-loader
[viewMode]="viewModes.ListElement" [object]="item$ | async"
[index]="index" [viewMode]="viewModes.ListElement"
[linkType]="linkType" [index]="index"
[listID]="listID"></ds-listable-object-component-loader> [linkType]="linkType"
[listID]="listID"></ds-listable-object-component-loader>
}
<ds-workflow-item-admin-workflow-actions-element [wfi]="dso" [small]="false"></ds-workflow-item-admin-workflow-actions-element> <ds-workflow-item-admin-workflow-actions-element [wfi]="dso" [small]="false"></ds-workflow-item-admin-workflow-actions-element>

View File

@@ -1,13 +1,15 @@
<span class="badge bg-info">{{ "admin.workflow.item.workspace" | translate }}</span> <span class="badge bg-info">{{ "admin.workflow.item.workspace" | translate }}</span>
<ds-listable-object-component-loader *ngIf="item$ | async" @if (item$ | async) {
[object]="item$ | async" <ds-listable-object-component-loader
[viewMode]="viewModes.ListElement" [object]="item$ | async"
[index]="index" [viewMode]="viewModes.ListElement"
[linkType]="linkType" [index]="index"
[listID]="listID"></ds-listable-object-component-loader> [linkType]="linkType"
[listID]="listID"></ds-listable-object-component-loader>
}
<ds-workspace-item-admin-workflow-actions-element [small]="false" <ds-workspace-item-admin-workflow-actions-element [small]="false"
[supervisionOrderList]="supervisionOrder$ | async" [supervisionOrderList]="supervisionOrder$ | async"
[wsi]="dso" [wsi]="dso"
(create)="reloadObject($event)" (create)="reloadObject($event)"
(delete)="reloadObject($event)"></ds-workspace-item-admin-workflow-actions-element> (delete)="reloadObject($event)"></ds-workspace-item-admin-workflow-actions-element>

View File

@@ -1,61 +1,71 @@
<div class="container"> <div class="container">
<div class="collection-page" <div class="collection-page"
*ngVar="(collectionRD$ | async) as collectionRD"> *ngVar="(collectionRD$ | async) as collectionRD">
<div *ngIf="collectionRD?.hasSucceeded" @fadeInOut> @if (collectionRD?.hasSucceeded) {
<div *ngIf="collectionRD?.payload as collection"> <div @fadeInOut>
<ds-view-tracker [object]="collection"></ds-view-tracker> @if (collectionRD?.payload; as collection) {
<div class="d-flex flex-row border-bottom mb-4 pb-4"> <div>
<header class="comcol-header me-auto"> <ds-view-tracker [object]="collection"></ds-view-tracker>
<!-- Collection Name --> <div class="d-flex flex-row border-bottom mb-4 pb-4">
<ds-comcol-page-header <header class="comcol-header me-auto">
[name]="dsoNameService.getName(collection)"> <!-- Collection Name -->
</ds-comcol-page-header> <ds-comcol-page-header
<!-- Collection logo --> [name]="dsoNameService.getName(collection)">
<ds-comcol-page-logo *ngIf="logoRD$" </ds-comcol-page-header>
[logo]="(logoRD$ | async)?.payload" <!-- Collection logo -->
[alternateText]="'collection.logo' | translate"> @if (logoRD$) {
</ds-comcol-page-logo> <ds-comcol-page-logo
[logo]="(logoRD$ | async)?.payload"
<!-- Handle --> [alternateText]="'collection.logo' | translate">
<ds-comcol-page-handle </ds-comcol-page-logo>
[content]="collection.handle" }
[title]="'collection.page.handle'"> <!-- Handle -->
</ds-comcol-page-handle> <ds-comcol-page-handle
<!-- Introductory text --> [content]="collection.handle"
<ds-comcol-page-content [title]="'collection.page.handle'">
[content]="collection.introductoryText" </ds-comcol-page-handle>
[hasInnerHtml]="true"> <!-- Introductory text -->
</ds-comcol-page-content> <ds-comcol-page-content
<!-- News --> [content]="collection.introductoryText"
<ds-comcol-page-content [hasInnerHtml]="true">
[content]="collection.sidebarText" </ds-comcol-page-content>
[hasInnerHtml]="true" <!-- News -->
[title]="'collection.page.news'"> <ds-comcol-page-content
</ds-comcol-page-content> [content]="collection.sidebarText"
</header> [hasInnerHtml]="true"
<ds-dso-edit-menu></ds-dso-edit-menu> [title]="'collection.page.news'">
</div> </ds-comcol-page-content>
<section class="comcol-page-browse-section"> </header>
<!-- Browse-By Links --> <ds-dso-edit-menu></ds-dso-edit-menu>
<ds-comcol-page-browse-by </div>
[id]="collection.id" <section class="comcol-page-browse-section">
[contentType]="collection.type"> <!-- Browse-By Links -->
</ds-comcol-page-browse-by> <ds-comcol-page-browse-by
[id]="collection.id"
<router-outlet></router-outlet> [contentType]="collection.type">
</section> </ds-comcol-page-browse-by>
<footer *ngIf="collection.copyrightText" class="border-top my-5 pt-4"> <router-outlet></router-outlet>
<!-- Copyright --> </section>
<ds-comcol-page-content @if (collection.copyrightText) {
[content]="collection.copyrightText" <footer class="border-top my-5 pt-4">
[hasInnerHtml]="true"> <!-- Copyright -->
</ds-comcol-page-content> <ds-comcol-page-content
</footer> [content]="collection.copyrightText"
[hasInnerHtml]="true">
</ds-comcol-page-content>
</footer>
}
</div>
}
</div> </div>
</div> }
<ds-error *ngIf="collectionRD?.hasFailed" @if (collectionRD?.hasFailed) {
message="{{'error.collection' | translate}}"></ds-error> <ds-error
<ds-loading *ngIf="collectionRD?.isLoading" message="{{'error.collection' | translate}}"></ds-error>
message="{{'loading.collection' | translate}}"></ds-loading> }
@if (collectionRD?.isLoading) {
<ds-loading
message="{{'loading.collection' | translate}}"></ds-loading>
}
</div> </div>
</div> </div>

View File

@@ -1,23 +1,27 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<ng-container *ngVar="(dsoRD$ | async)?.payload as dso"> <ng-container *ngVar="(dsoRD$ | async)?.payload as dso">
<div class="col-12 pb-4"> <div class="col-12 pb-4">
<h1 id="header" class="border-bottom pb-2">{{ 'collection.delete.head' | translate}}</h1> <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> <p class="pb-2">{{ 'collection.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
<div class="mb-3 row"> <div class="mb-3 row">
<div class="col text-end space-children-mr"> <div class="col text-end space-children-mr">
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [dsBtnDisabled]="(processing$ | async)"> <button class="btn btn-outline-secondary" (click)="onCancel(dso)" [dsBtnDisabled]="(processing$ | async)">
<i class="fas fa-times"></i> {{'collection.delete.cancel' | translate}} <i class="fas fa-times"></i> {{'collection.delete.cancel' | translate}}
</button> </button>
<button class="btn btn-danger" (click)="onConfirm(dso)" [dsBtnDisabled]="(processing$ | async)"> <button class="btn btn-danger" (click)="onConfirm(dso)" [dsBtnDisabled]="(processing$ | async)">
<span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin'></i> {{'collection.delete.processing' | translate}}</span> @if (processing$ | async) {
<span *ngIf="(processing$ | async) !== true"><i class="fas fa-trash"></i> {{'collection.delete.confirm' | translate}}</span> <span><i class='fas fa-circle-notch fa-spin'></i> {{'collection.delete.processing' | translate}}</span>
</button> }
</div> @if ((processing$ | async) !== true) {
</div> <span><i class="fas fa-trash"></i> {{'collection.delete.confirm' | translate}}</span>
</div> }
</ng-container> </button>
</div>
</div>
</div>
</ng-container>
</div> </div>
</div> </div>

View File

@@ -1,8 +1,9 @@
<div class="custom-alignment"> <div class="custom-alignment">
<ds-comcol-role @for (comcolRole of comcolRoles$ | async; track comcolRole) {
*ngFor="let comcolRole of comcolRoles$ | async" <ds-comcol-role
[dso]="collection$ | async" [dso]="collection$ | async"
[comcolRole]="comcolRole" [comcolRole]="comcolRole"
> >
</ds-comcol-role> </ds-comcol-role>
}
</div> </div>

View File

@@ -1,54 +1,65 @@
<div *ngVar="(contentSource$ |async) as contentSource"> <div *ngVar="(contentSource$ |async) as contentSource">
<div class="container-fluid space-children-mr" *ngIf="shouldShow"> @if (shouldShow) {
<h3>{{ 'collection.source.controls.head' | translate }}</h3> <div class="container-fluid space-children-mr">
<div> <h3>{{ 'collection.source.controls.head' | translate }}</h3>
<span class="fw-bold">{{'collection.source.controls.harvest.status' | translate}}</span> <div>
<span>{{contentSource?.harvestStatus}}</span> <span class="fw-bold">{{'collection.source.controls.harvest.status' | translate}}</span>
</div> <span>{{contentSource?.harvestStatus}}</span>
<div> </div>
<span class="fw-bold">{{'collection.source.controls.harvest.start' | translate}}</span> <div>
<span>{{contentSource?.harvestStartTime ? contentSource?.harvestStartTime : 'collection.source.controls.harvest.no-information'|translate }}</span> <span class="fw-bold">{{'collection.source.controls.harvest.start' | translate}}</span>
</div> <span>{{contentSource?.harvestStartTime ? contentSource?.harvestStartTime : 'collection.source.controls.harvest.no-information'|translate }}</span>
<div> </div>
<span class="fw-bold">{{'collection.source.controls.harvest.last' | translate}}</span> <div>
<span>{{contentSource?.lastHarvested ? contentSource?.lastHarvested : 'collection.source.controls.harvest.no-information'|translate }}</span> <span class="fw-bold">{{'collection.source.controls.harvest.last' | translate}}</span>
</div> <span>{{contentSource?.lastHarvested ? contentSource?.lastHarvested : 'collection.source.controls.harvest.no-information'|translate }}</span>
<div> </div>
<span class="fw-bold">{{'collection.source.controls.harvest.message' | translate}}</span> <div>
<span>{{contentSource?.message ? contentSource?.message: 'collection.source.controls.harvest.no-information'|translate }}</span> <span class="fw-bold">{{'collection.source.controls.harvest.message' | translate}}</span>
</div> <span>{{contentSource?.message ? contentSource?.message: 'collection.source.controls.harvest.no-information'|translate }}</span>
</div>
<button *ngIf="(testConfigRunning$ |async) !== true" class="btn btn-secondary" @if ((testConfigRunning$ |async) !== true) {
[dsBtnDisabled]="!(isEnabled)" <button class="btn btn-secondary"
(click)="testConfiguration(contentSource)"> [dsBtnDisabled]="!(isEnabled)"
<span>{{'collection.source.controls.test.submit' | translate}}</span> (click)="testConfiguration(contentSource)">
<span>{{'collection.source.controls.test.submit' | translate}}</span>
</button> </button>
<button *ngIf="(testConfigRunning$ |async)" class="btn btn-secondary" }
[dsBtnDisabled]="true"> @if ((testConfigRunning$ |async)) {
<span class="spinner-border spinner-border-sm spinner-button" role="status" aria-hidden="true"></span> <button class="btn btn-secondary"
<span>{{'collection.source.controls.test.running' | translate}}</span> [dsBtnDisabled]="true">
<span class="spinner-border spinner-border-sm spinner-button" role="status" aria-hidden="true"></span>
<span>{{'collection.source.controls.test.running' | translate}}</span>
</button> </button>
<button *ngIf="(importRunning$ |async) !== true" class="btn btn-primary" }
[dsBtnDisabled]="!(isEnabled)" @if ((importRunning$ |async) !== true) {
(click)="importNow()"> <button class="btn btn-primary"
<span class="d-none d-sm-inline">{{'collection.source.controls.import.submit' | translate}}</span> [dsBtnDisabled]="!(isEnabled)"
(click)="importNow()">
<span class="d-none d-sm-inline">{{'collection.source.controls.import.submit' | translate}}</span>
</button> </button>
<button *ngIf="(importRunning$ |async)" class="btn btn-primary" }
[dsBtnDisabled]="true"> @if ((importRunning$ |async)) {
<span class="spinner-border spinner-border-sm spinner-button" role="status" aria-hidden="true"></span> <button class="btn btn-primary"
<span class="d-none d-sm-inline">{{'collection.source.controls.import.running' | translate}}</span> [dsBtnDisabled]="true">
<span class="spinner-border spinner-border-sm spinner-button" role="status" aria-hidden="true"></span>
<span class="d-none d-sm-inline">{{'collection.source.controls.import.running' | translate}}</span>
</button> </button>
<button *ngIf="(reImportRunning$ |async) !== true" class="btn btn-primary" }
[dsBtnDisabled]="!(isEnabled)" @if ((reImportRunning$ |async) !== true) {
(click)="resetAndReimport()"> <button class="btn btn-primary"
<span class="d-none d-sm-inline">&nbsp;{{'collection.source.controls.reset.submit' | translate}}</span> [dsBtnDisabled]="!(isEnabled)"
(click)="resetAndReimport()">
<span class="d-none d-sm-inline">&nbsp;{{'collection.source.controls.reset.submit' | translate}}</span>
</button> </button>
<button *ngIf="(reImportRunning$ |async)" class="btn btn-primary" }
[dsBtnDisabled]="true"> @if ((reImportRunning$ |async)) {
<span class="spinner-border spinner-border-sm spinner-button" role="status" aria-hidden="true"></span> <button class="btn btn-primary"
<span class="d-none d-sm-inline">&nbsp;{{'collection.source.controls.reset.running' | translate}}</span> [dsBtnDisabled]="true">
<span class="spinner-border spinner-border-sm spinner-button" role="status" aria-hidden="true"></span>
<span class="d-none d-sm-inline">&nbsp;{{'collection.source.controls.reset.running' | translate}}</span>
</button> </button>
}
</div> </div>
}
</div> </div>

View File

@@ -1,74 +1,92 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="d-inline-block float-end space-children-mr"> <div class="d-inline-block float-end space-children-mr">
<button class=" btn btn-danger" *ngIf="(isReinstatable$ | async) !== true" @if ((isReinstatable$ | async) !== true) {
[dsBtnDisabled]="(hasChanges$ | async) !== true" <button class=" btn btn-danger"
(click)="discard()"><i [dsBtnDisabled]="(hasChanges$ | async) !== true"
class="fas fa-times"></i> (click)="discard()"><i
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.discard-button" | translate}}</span> class="fas fa-times"></i>
</button> <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.discard-button" | translate}}</span>
<button class="btn btn-warning" *ngIf="isReinstatable$ | async" </button>
(click)="reinstate()"><i }
class="fas fa-undo-alt"></i> @if (isReinstatable$ | async) {
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.reinstate-button" | translate}}</span> <button class="btn btn-warning"
</button> (click)="reinstate()"><i
<button class="btn btn-primary" class="fas fa-undo-alt"></i>
[dsBtnDisabled]="(hasChanges$ | async) !== true || !isValid() || (initialHarvestType === harvestTypeNone && contentSource.harvestType === initialHarvestType)" <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.reinstate-button" | translate}}</span>
(click)="onSubmit()"><i </button>
class="fas fa-save"></i> }
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.save-button" | translate}}</span> <button class="btn btn-primary"
</button> [dsBtnDisabled]="(hasChanges$ | async) !== true || !isValid() || (initialHarvestType === harvestTypeNone && contentSource.harvestType === initialHarvestType)"
</div> (click)="onSubmit()"><i
<h2>{{ 'collection.edit.tabs.source.head' | translate }}</h2> class="fas fa-save"></i>
<div *ngIf="contentSource" class="form-check mb-4"> <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.save-button" | translate}}</span>
<input type="checkbox" class="form-check-input" id="externalSourceCheck" </button>
[checked]="(contentSource?.harvestType !== harvestTypeNone)" (change)="changeExternalSource()"> </div>
<label class="form-check-label" <h2>{{ 'collection.edit.tabs.source.head' | translate }}</h2>
for="externalSourceCheck">{{ 'collection.edit.tabs.source.external' | translate }}</label> @if (contentSource) {
</div> <div class="form-check mb-4">
<ds-loading *ngIf="!contentSource" [message]="'loading.content-source' | translate"></ds-loading> <input type="checkbox" class="form-check-input" id="externalSourceCheck"
<h3 *ngIf="contentSource && (contentSource?.harvestType !== harvestTypeNone)">{{ 'collection.edit.tabs.source.form.head' | translate }}</h3> [checked]="(contentSource?.harvestType !== harvestTypeNone)" (change)="changeExternalSource()">
<label class="form-check-label"
for="externalSourceCheck">{{ 'collection.edit.tabs.source.external' | translate }}</label>
</div>
}
@if (!contentSource) {
<ds-loading [message]="'loading.content-source' | translate"></ds-loading>
}
@if (contentSource && (contentSource?.harvestType !== harvestTypeNone)) {
<h3>{{ 'collection.edit.tabs.source.form.head' | translate }}</h3>
}
</div> </div>
<div class="row"> <div class="row">
<ds-form *ngIf="formGroup && contentSource && (contentSource?.harvestType !== harvestTypeNone)" @if (formGroup && contentSource && (contentSource?.harvestType !== harvestTypeNone)) {
[formId]="'collection-source-form-id'" <ds-form
[formGroup]="formGroup" [formId]="'collection-source-form-id'"
[formModel]="formModel" [formGroup]="formGroup"
[formLayout]="formLayout" [formModel]="formModel"
[displaySubmit]="false" [formLayout]="formLayout"
[displayCancel]="false" [displaySubmit]="false"
(dfChange)="onChange($event)" [displayCancel]="false"
(submitForm)="onSubmit()" (dfChange)="onChange($event)"
(cancel)="onCancel()"></ds-form> (submitForm)="onSubmit()"
(cancel)="onCancel()"></ds-form>
}
</div> </div>
<div class="container mt-2" *ngIf="(contentSource?.harvestType !== harvestTypeNone)"> @if ((contentSource?.harvestType !== harvestTypeNone)) {
<div class="container mt-2">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="d-inline-block float-end ms-1 space-children-mr"> <div class="d-inline-block float-end ms-1 space-children-mr">
<button class=" btn btn-danger" *ngIf="(isReinstatable$ | async) !== true" @if ((isReinstatable$ | async) !== true) {
[dsBtnDisabled]="(hasChanges$ | async) !== true" <button class=" btn btn-danger"
(click)="discard()"><i [dsBtnDisabled]="(hasChanges$ | async) !== true"
class="fas fa-times"></i> (click)="discard()"><i
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.discard-button" | translate}}</span> class="fas fa-times"></i>
</button> <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.discard-button" | translate}}</span>
<button class="btn btn-warning" *ngIf="isReinstatable$ | async" </button>
(click)="reinstate()"><i }
class="fas fa-undo-alt"></i> @if (isReinstatable$ | async) {
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.reinstate-button" | translate}}</span> <button class="btn btn-warning"
</button> (click)="reinstate()"><i
<button class="btn btn-primary" class="fas fa-undo-alt"></i>
[dsBtnDisabled]="(hasChanges$ | async) !== true || !isValid() || (initialHarvestType === harvestTypeNone && contentSource.harvestType === initialHarvestType)" <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.reinstate-button" | translate}}</span>
(click)="onSubmit()"><i </button>
class="fas fa-save"></i> }
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.save-button" | translate}}</span> <button class="btn btn-primary"
</button> [dsBtnDisabled]="(hasChanges$ | async) !== true || !isValid() || (initialHarvestType === harvestTypeNone && contentSource.harvestType === initialHarvestType)"
</div> (click)="onSubmit()"><i
</div> class="fas fa-save"></i>
</div> <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.save-button" | translate}}</span>
</button>
</div>
</div> </div>
</div>
</div>
}
<ds-collection-source-controls <ds-collection-source-controls
[isEnabled]="(hasChanges$ | async) !== true" [isEnabled]="(hasChanges$ | async) !== true"
[shouldShow]="contentSource?.harvestType !== harvestTypeNone" [shouldShow]="contentSource?.harvestType !== harvestTypeNone"
[collection]="(collectionRD$ |async)?.payload" [collection]="(collectionRD$ |async)?.payload"
> >
</ds-collection-source-controls> </ds-collection-source-controls>

View File

@@ -1,18 +1,24 @@
<ds-loading *ngIf="(dataSource.loading$ | async) && !loadingNode" class="ds-themed-loading"></ds-loading> @if ((dataSource.loading$ | async) && !loadingNode) {
<ds-loading class="ds-themed-loading"></ds-loading>
}
<cdk-tree [dataSource]="dataSource" [treeControl]="treeControl" [trackBy]="trackBy"> <cdk-tree [dataSource]="dataSource" [treeControl]="treeControl" [trackBy]="trackBy">
<!-- This is the tree node template for show more node --> <!-- This is the tree node template for show more node -->
<cdk-tree-node *cdkTreeNodeDef="let node; when: isShowMore" cdkTreeNodePadding <cdk-tree-node *cdkTreeNodeDef="let node; when: isShowMore" cdkTreeNodePadding
class="example-tree-node show-more-node"> class="example-tree-node show-more-node">
<div class="btn-group"> <div class="btn-group">
<span aria-hidden="true" class="btn btn-default invisible" cdkTreeNodeToggle> <span aria-hidden="true" class="btn btn-default invisible" cdkTreeNodeToggle>
<span class="fa fa-chevron-right"></span> <span class="fa fa-chevron-right"></span>
</span> </span>
<div class="align-middle my-auto"> <div class="align-middle my-auto">
<button *ngIf="(dataSource.loading$ | async) !== true" (click)="getNextPage(node)" @if ((dataSource.loading$ | async) !== true) {
class="btn btn-outline-primary btn-sm" role="button"> <button (click)="getNextPage(node)"
<i class="fas fa-angle-down"></i> {{ 'communityList.showMore' | translate }} class="btn btn-outline-primary btn-sm" role="button">
</button> <i class="fas fa-angle-down"></i> {{ 'communityList.showMore' | translate }}
<ds-loading *ngIf="node===loadingNode && dataSource.loading$ | async" class="ds-themed-loading"></ds-loading> </button>
}
@if (node===loadingNode && dataSource.loading$ | async) {
<ds-loading class="ds-themed-loading"></ds-loading>
}
</div> </div>
</div> </div>
<div class="text-muted" cdkTreeNodePadding> <div class="text-muted" cdkTreeNodePadding>
@@ -22,51 +28,61 @@
</cdk-tree-node> </cdk-tree-node>
<!-- This is the tree node template for expandable nodes (coms and subcoms with children) --> <!-- This is the tree node template for expandable nodes (coms and subcoms with children) -->
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding <cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding
class="example-tree-node expandable-node"> class="example-tree-node expandable-node">
<div class="btn-group"> <div class="btn-group">
<button *ngIf="hasChild(null, node) | async" type="button" class="btn btn-default btn-transparent" cdkTreeNodeToggle @if (hasChild(null, node) | async) {
[attr.aria-label]="(node.isExpanded ? 'communityList.collapse' : 'communityList.expand') | translate:{ name: dsoNameService.getName(node.payload) }" <button type="button" class="btn btn-default btn-transparent" cdkTreeNodeToggle
(click)="toggleExpanded(node)" [attr.aria-label]="(node.isExpanded ? 'communityList.collapse' : 'communityList.expand') | translate:{ name: dsoNameService.getName(node.payload) }"
data-test="expand-button"> (click)="toggleExpanded(node)"
<span class="{{node.isExpanded ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}" data-test="expand-button">
aria-hidden="true"></span> <span class="{{node.isExpanded ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}"
<span class="sr-only">{{ (node.isExpanded ? 'communityList.collapse' : 'communityList.expand') | translate:{ name: dsoNameService.getName(node.payload) } }}</span> aria-hidden="true"></span>
</button> <span class="sr-only">{{ (node.isExpanded ? 'communityList.collapse' : 'communityList.expand') | translate:{ name: dsoNameService.getName(node.payload) } }}</span>
</button>
}
<!--Don't render the button when non-expandable otherwise it's still accessible, instead render this placeholder--> <!--Don't render the button when non-expandable otherwise it's still accessible, instead render this placeholder-->
<span *ngIf="(hasChild(null, node) | async) !== true" aria-hidden="true" class="btn btn-default invisible"> @if ((hasChild(null, node) | async) !== true) {
<span class="fa fa-chevron-right"></span> <span aria-hidden="true" class="btn btn-default invisible">
</span> <span class="fa fa-chevron-right"></span>
</span>
}
<div class="d-flex flex-row"> <div class="d-flex flex-row">
<span class="d-flex align-middle my-auto"> <span class="d-flex align-middle my-auto">
<a [routerLink]="node.route" class="lead">{{ dsoNameService.getName(node.payload) }}</a> <a [routerLink]="node.route" class="lead">{{ dsoNameService.getName(node.payload) }}</a>
<span class="pe-2">&nbsp;</span> <span class="pe-2">&nbsp;</span>
<span *ngIf="node.payload.archivedItemsCount >= 0" class="badge rounded-pill bg-secondary align-top archived-items-lead my-auto ps-2 pe-2">{{node.payload.archivedItemsCount}}</span> @if (node.payload.archivedItemsCount >= 0) {
<span class="badge rounded-pill bg-secondary align-top archived-items-lead my-auto ps-2 pe-2">{{node.payload.archivedItemsCount}}</span>
}
</span> </span>
</div> </div>
</div> </div>
<ds-truncatable [id]="node.id"> <ds-truncatable [id]="node.id">
<div class="text-muted" cdkTreeNodePadding> <div class="text-muted" cdkTreeNodePadding>
<div class="d-flex" *ngIf="node.payload.shortDescription"> @if (node.payload.shortDescription) {
<span aria-hidden="true" class="btn btn-default invisible"> <div class="d-flex">
<span class="fa fa-chevron-right"></span> <span aria-hidden="true" class="btn btn-default invisible">
</span> <span class="fa fa-chevron-right"></span>
<ds-truncatable-part [id]="node.id" [minLines]="3"> </span>
<span>{{node.payload.shortDescription}}</span> <ds-truncatable-part [id]="node.id" [minLines]="3">
</ds-truncatable-part> <span>{{node.payload.shortDescription}}</span>
</div> </ds-truncatable-part>
</div>
}
</div> </div>
</ds-truncatable> </ds-truncatable>
<div class="d-flex" *ngIf="node===loadingNode && dataSource.loading$ | async" @if (node===loadingNode && dataSource.loading$ | async) {
cdkTreeNodePadding> <div class="d-flex"
<span aria-hidden="true" class="btn btn-default invisible"> cdkTreeNodePadding>
<span class="fa fa-chevron-right"></span> <span aria-hidden="true" class="btn btn-default invisible">
</span> <span class="fa fa-chevron-right"></span>
<ds-loading class="ds-themed-loading"></ds-loading> </span>
</div> <ds-loading class="ds-themed-loading"></ds-loading>
</div>
}
</cdk-tree-node> </cdk-tree-node>
<!-- This is the tree node template for leaf nodes (collections and (sub)coms without children) --> <!-- This is the tree node template for leaf nodes (collections and (sub)coms without children) -->
<cdk-tree-node *cdkTreeNodeDef="let node; when: !(hasChild && isShowMore)" cdkTreeNodePadding <cdk-tree-node *cdkTreeNodeDef="let node; when: !(hasChild && isShowMore)" cdkTreeNodePadding
class="example-tree-node childless-node"> class="example-tree-node childless-node">
<div class="btn-group"> <div class="btn-group">
<span aria-hidden="true" class="btn btn-default invisible" cdkTreeNodeToggle> <span aria-hidden="true" class="btn btn-default invisible" cdkTreeNodeToggle>
<span class="fa fa-chevron-right"></span> <span class="fa fa-chevron-right"></span>
@@ -77,14 +93,16 @@
</div> </div>
<ds-truncatable [id]="node.id"> <ds-truncatable [id]="node.id">
<div class="text-muted" cdkTreeNodePadding> <div class="text-muted" cdkTreeNodePadding>
<div class="d-flex" *ngIf="node.payload.shortDescription"> @if (node.payload.shortDescription) {
<span aria-hidden="true" class="btn btn-default invisible"> <div class="d-flex">
<span class="fa fa-chevron-right"></span> <span aria-hidden="true" class="btn btn-default invisible">
</span> <span class="fa fa-chevron-right"></span>
<ds-truncatable-part [id]="node.id" [minLines]="3"> </span>
<span>{{node.payload.shortDescription}}</span> <ds-truncatable-part [id]="node.id" [minLines]="3">
</ds-truncatable-part> <span>{{node.payload.shortDescription}}</span>
</div> </ds-truncatable-part>
</div>
}
</div> </div>
</ds-truncatable> </ds-truncatable>
</cdk-tree-node> </cdk-tree-node>

View File

@@ -1,43 +1,53 @@
<div class="container" *ngVar="(communityRD$ | async) as communityRD"> <div class="container" *ngVar="(communityRD$ | async) as communityRD">
<div class="community-page" *ngIf="communityRD?.hasSucceeded" @fadeInOut> @if (communityRD?.hasSucceeded) {
<div *ngIf="communityRD?.payload; let communityPayload"> <div class="community-page" @fadeInOut>
<ds-view-tracker [object]="communityPayload"></ds-view-tracker> @if (communityRD?.payload; as communityPayload) {
<div class="d-flex flex-row border-bottom mb-4 pb-4"> <div>
<header class="comcol-header me-auto"> <ds-view-tracker [object]="communityPayload"></ds-view-tracker>
<!-- Community name --> <div class="d-flex flex-row border-bottom mb-4 pb-4">
<ds-comcol-page-header [name]="dsoNameService.getName(communityPayload)"></ds-comcol-page-header> <header class="comcol-header me-auto">
<!-- Community logo --> <!-- Community name -->
<ds-comcol-page-logo *ngIf="logoRD$" [logo]="(logoRD$ | async)?.payload" [alternateText]="'community.logo' | translate"> <ds-comcol-page-header [name]="dsoNameService.getName(communityPayload)"></ds-comcol-page-header>
</ds-comcol-page-logo> <!-- Community logo -->
<!-- Handle --> @if (logoRD$) {
<ds-comcol-page-handle [content]="communityPayload.handle" [title]="'community.page.handle'"> <ds-comcol-page-logo [logo]="(logoRD$ | async)?.payload" [alternateText]="'community.logo' | translate">
</ds-comcol-page-handle> </ds-comcol-page-logo>
<!-- Introductory text --> }
<ds-comcol-page-content [content]="communityPayload.introductoryText" [hasInnerHtml]="true"> <!-- Handle -->
</ds-comcol-page-content> <ds-comcol-page-handle [content]="communityPayload.handle" [title]="'community.page.handle'">
<!-- News --> </ds-comcol-page-handle>
<ds-comcol-page-content [content]="communityPayload.sidebarText" [hasInnerHtml]="true" <!-- Introductory text -->
[title]="'community.page.news'"> <ds-comcol-page-content [content]="communityPayload.introductoryText" [hasInnerHtml]="true">
</ds-comcol-page-content> </ds-comcol-page-content>
</header> <!-- News -->
<ds-dso-edit-menu></ds-dso-edit-menu> <ds-comcol-page-content [content]="communityPayload.sidebarText" [hasInnerHtml]="true"
</div> [title]="'community.page.news'">
<section class="comcol-page-browse-section"> </ds-comcol-page-content>
</header>
<!-- Browse-By Links --> <ds-dso-edit-menu></ds-dso-edit-menu>
<ds-comcol-page-browse-by [id]="communityPayload.id" [contentType]="communityPayload.type"> </div>
</ds-comcol-page-browse-by> <section class="comcol-page-browse-section">
<!-- Browse-By Links -->
<router-outlet></router-outlet> <ds-comcol-page-browse-by [id]="communityPayload.id" [contentType]="communityPayload.type">
</section> </ds-comcol-page-browse-by>
<footer *ngIf="communityPayload.copyrightText" class="border-top my-5 pt-4"> <router-outlet></router-outlet>
<!-- Copyright --> </section>
<ds-comcol-page-content [content]="communityPayload.copyrightText" [hasInnerHtml]="true"> @if (communityPayload.copyrightText) {
</ds-comcol-page-content> <footer class="border-top my-5 pt-4">
</footer> <!-- Copyright -->
<ds-comcol-page-content [content]="communityPayload.copyrightText" [hasInnerHtml]="true">
</ds-comcol-page-content>
</footer>
}
</div>
}
</div> </div>
</div> }
<ds-error *ngIf="communityRD?.hasFailed" message="{{'error.community' | translate}}"></ds-error> @if (communityRD?.hasFailed) {
<ds-loading *ngIf="communityRD?.isLoading" message="{{'loading.community' | translate}}"></ds-loading> <ds-error message="{{'error.community' | translate}}"></ds-error>
}
@if (communityRD?.isLoading) {
<ds-loading message="{{'loading.community' | translate}}"></ds-loading>
}
</div> </div>

View File

@@ -1,22 +1,26 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<ng-container *ngVar="(dsoRD$ | async)?.payload as dso"> <ng-container *ngVar="(dsoRD$ | async)?.payload as dso">
<div class="col-12 pb-4"> <div class="col-12 pb-4">
<h1 id="header" class="border-bottom pb-2">{{ 'community.delete.head' | translate}}</h1> <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> <p class="pb-2">{{ 'community.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
<div class="mb-3 row"> <div class="mb-3 row">
<div class="col text-end space-children-mr"> <div class="col text-end space-children-mr">
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [dsBtnDisabled]="(processing$ | async)"> <button class="btn btn-outline-secondary" (click)="onCancel(dso)" [dsBtnDisabled]="(processing$ | async)">
<i class="fas fa-times" aria-hidden="true"></i> {{'community.delete.cancel' | translate}} <i class="fas fa-times" aria-hidden="true"></i> {{'community.delete.cancel' | translate}}
</button> </button>
<button class="btn btn-danger" (click)="onConfirm(dso)" [dsBtnDisabled]="(processing$ | async)"> <button class="btn btn-danger" (click)="onConfirm(dso)" [dsBtnDisabled]="(processing$ | async)">
<span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin' aria-hidden="true"></i> {{'community.delete.processing' | translate}}</span> @if (processing$ | async) {
<span *ngIf="(processing$ | async) !== true"><i class="fas fa-trash" aria-hidden="true"></i> {{'community.delete.confirm' | translate}}</span> <span><i class='fas fa-circle-notch fa-spin' aria-hidden="true"></i> {{'community.delete.processing' | translate}}</span>
</button> }
</div> @if ((processing$ | async) !== true) {
</div> <span><i class="fas fa-trash" aria-hidden="true"></i> {{'community.delete.confirm' | translate}}</span>
</div> }
</ng-container> </button>
</div> </div>
</div>
</div>
</ng-container>
</div>
</div> </div>

View File

@@ -1,8 +1,9 @@
<div class="custom-alignment"> <div class="custom-alignment">
<ds-comcol-role @for (comcolRole of comcolRoles$ | async; track comcolRole) {
*ngFor="let comcolRole of comcolRoles$ | async" <ds-comcol-role
[dso]="community$ | async" [dso]="community$ | async"
[comcolRole]="comcolRole" [comcolRole]="comcolRole"
> >
</ds-comcol-role> </ds-comcol-role>
}
</div> </div>

View File

@@ -1,20 +1,24 @@
<form [formGroup]="form" (ngSubmit)="submit()"> <form [formGroup]="form" (ngSubmit)="submit()">
<div class="mb-3"> <div class="mb-3">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-12 col-sm-6"> <div class="col-12 col-sm-6">
<label class="fw-bold form-label" for="task">{{'curation.form.task-select.label' |translate }}</label> <label class="fw-bold form-label" for="task">{{'curation.form.task-select.label' |translate }}</label>
<select id="task" formControlName="task" class="form-select"> <select id="task" formControlName="task" class="form-select">
<option *ngFor="let task of tasks" [value]="task"> @for (task of tasks; track task) {
{{ 'curation-task.task.' + task + '.label' | translate }} <option [value]="task">
</option> {{ 'curation-task.task.' + task + '.label' | translate }}
</select> </option>
</div> }
<div *ngIf="!hasHandleValue()" class="col-12 col-sm-6"> </select>
<label class="fw-bold form-label" for="handle">{{'curation.form.handle.label' |translate }}</label> </div>
<input id="handle" class="form-control" formControlName="handle"> @if (!hasHandleValue()) {
<small class="text-muted">{{'curation.form.handle.hint' |translate }}</small> <div class="col-12 col-sm-6">
</div> <label class="fw-bold form-label" for="handle">{{'curation.form.handle.label' |translate }}</label>
<input id="handle" class="form-control" formControlName="handle">
<small class="text-muted">{{'curation.form.handle.hint' |translate }}</small>
</div> </div>
<button class="btn btn-default btn-primary" type="submit">{{'curation.form.submit' |translate }}</button> }
</div> </div>
<button class="btn btn-default btn-primary" type="submit">{{'curation.form.submit' |translate }}</button>
</div>
</form> </form>

View File

@@ -1,107 +1,135 @@
<div class="d-flex flex-row ds-value-row" *ngVar="metadataService.isVirtual(mdValue.newValue) as isVirtual" role="row" <div class="d-flex flex-row ds-value-row" *ngVar="metadataService.isVirtual(mdValue.newValue) as isVirtual" role="row"
cdkDrag (cdkDragStarted)="dragging.emit(true)" (cdkDragEnded)="dragging.emit(false)" cdkDrag (cdkDragStarted)="dragging.emit(true)" (cdkDragEnded)="dragging.emit(false)"
[ngClass]="{ 'ds-warning': mdValue.reordered || mdValue.change === DsoEditMetadataChangeTypeEnum.UPDATE, 'ds-danger': mdValue.change === DsoEditMetadataChangeTypeEnum.REMOVE, 'ds-success': mdValue.change === DsoEditMetadataChangeTypeEnum.ADD, 'h-100': isOnlyValue }"> [ngClass]="{ 'ds-warning': mdValue.reordered || mdValue.change === DsoEditMetadataChangeTypeEnum.UPDATE, 'ds-danger': mdValue.change === DsoEditMetadataChangeTypeEnum.REMOVE, 'ds-success': mdValue.change === DsoEditMetadataChangeTypeEnum.ADD, 'h-100': isOnlyValue }">
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex flex-column" *ngVar="(mdRepresentation$ | async) as mdRepresentation" role="cell"> <div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex flex-column" *ngVar="(mdRepresentation$ | async) as mdRepresentation" role="cell">
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing && !mdRepresentation">{{ mdValue.newValue.value }}</div> @if (!mdValue.editing && !mdRepresentation) {
<textarea class="form-control" rows="5" *ngIf="mdValue.editing && !mdRepresentation && ((isAuthorityControlled() | async) !== true || (enabledFreeTextEditing && (isSuggesterVocabulary() | async) !== true))" [(ngModel)]="mdValue.newValue.value" <div class="dont-break-out preserve-line-breaks">{{ mdValue.newValue.value }}</div>
[attr.aria-label]="(dsoType + '.edit.metadata.edit.value') | translate" }
[dsDebounce]="300" (onDebounce)="confirm.emit(false)"></textarea> @if (mdValue.editing && !mdRepresentation && ((isAuthorityControlled() | async) !== true || (enabledFreeTextEditing && (isSuggesterVocabulary() | async) !== true))) {
<ds-dynamic-scrollable-dropdown *ngIf="mdValue.editing && (isScrollableVocabulary() | async) && !enabledFreeTextEditing" <textarea class="form-control" rows="5" [(ngModel)]="mdValue.newValue.value"
[bindId]="mdField" [attr.aria-label]="(dsoType + '.edit.metadata.edit.value') | translate"
[group]="group" [dsDebounce]="300" (onDebounce)="confirm.emit(false)"></textarea>
[model]="getModel()" }
(change)="onChangeAuthorityField($event)"> @if (mdValue.editing && (isScrollableVocabulary() | async) && !enabledFreeTextEditing) {
</ds-dynamic-scrollable-dropdown> <ds-dynamic-scrollable-dropdown
<ds-dynamic-onebox *ngIf="mdValue.editing && (((isHierarchicalVocabulary() | async) && !enabledFreeTextEditing) || (isSuggesterVocabulary() | async))" [bindId]="mdField"
[group]="group" [group]="group"
[model]="getModel()" [model]="getModel()"
(change)="onChangeAuthorityField($event)"> (change)="onChangeAuthorityField($event)">
</ds-dynamic-onebox> </ds-dynamic-scrollable-dropdown>
<button class="btn btn-secondary mt-2" *ngIf="mdValue.editing && ((isScrollableVocabulary() | async) || (isHierarchicalVocabulary() | async))" }
@if (mdValue.editing && (((isHierarchicalVocabulary() | async) && !enabledFreeTextEditing) || (isSuggesterVocabulary() | async))) {
<ds-dynamic-onebox
[group]="group"
[model]="getModel()"
(change)="onChangeAuthorityField($event)">
</ds-dynamic-onebox>
}
@if (mdValue.editing && ((isScrollableVocabulary() | async) || (isHierarchicalVocabulary() | async))) {
<button class="btn btn-secondary mt-2"
[title]="enabledFreeTextEditing ? dsoType + '.edit.metadata.edit.buttons.disable-free-text-editing' : dsoType + '.edit.metadata.edit.buttons.enable-free-text-editing' | translate" [title]="enabledFreeTextEditing ? dsoType + '.edit.metadata.edit.buttons.disable-free-text-editing' : dsoType + '.edit.metadata.edit.buttons.enable-free-text-editing' | translate"
(click)="toggleFreeTextEdition()"> (click)="toggleFreeTextEdition()">
<i class="fas fa-fw" [ngClass]="enabledFreeTextEditing ? 'fa-lock' : 'fa-unlock'"></i> <i class="fas fa-fw" [ngClass]="enabledFreeTextEditing ? 'fa-lock' : 'fa-unlock'"></i>
{{ (enabledFreeTextEditing ? dsoType + '.edit.metadata.edit.buttons.disable-free-text-editing' : dsoType + '.edit.metadata.edit.buttons.enable-free-text-editing') | translate }} {{ (enabledFreeTextEditing ? dsoType + '.edit.metadata.edit.buttons.disable-free-text-editing' : dsoType + '.edit.metadata.edit.buttons.enable-free-text-editing') | translate }}
</button> </button>
<div *ngIf="!isVirtual && !mdValue.editing && mdValue.newValue.authority && mdValue.newValue.confidence !== ConfidenceTypeEnum.CF_UNSET && mdValue.newValue.confidence !== ConfidenceTypeEnum.CF_NOVALUE"> }
<span class="badge bg-light border" > @if (!isVirtual && !mdValue.editing && mdValue.newValue.authority && mdValue.newValue.confidence !== ConfidenceTypeEnum.CF_UNSET && mdValue.newValue.confidence !== ConfidenceTypeEnum.CF_NOVALUE) {
<i dsAuthorityConfidenceState <div>
class="fas fa-fw p-0" <span class="badge bg-light border" >
aria-hidden="true" <i dsAuthorityConfidenceState
[authorityValue]="mdValue.newValue" class="fas fa-fw p-0"
[iconMode]="true" aria-hidden="true"
></i> [authorityValue]="mdValue.newValue"
{{ dsoType + '.edit.metadata.authority.label' | translate }} {{ mdValue.newValue.authority }} [iconMode]="true"
</span> ></i>
</div> {{ dsoType + '.edit.metadata.authority.label' | translate }} {{ mdValue.newValue.authority }}
<div class="mt-2" *ngIf=" mdValue.editing && (isAuthorityControlled() | async) && (isSuggesterVocabulary() | async)"> </span>
<div class="btn-group w-75">
<i dsAuthorityConfidenceState
class="fas fa-fw p-0 me-1 mt-auto mb-auto"
aria-hidden="true"
[authorityValue]="mdValue.newValue.confidence"
[iconMode]="true"
></i>
<input class="form-control form-outline" data-test="authority-input" [(ngModel)]="mdValue.newValue.authority" [disabled]="!editingAuthority"
[attr.aria-label]="(dsoType + '.edit.metadata.edit.authority.key') | translate"
(change)="onChangeAuthorityKey()" />
<button class="btn btn-outline-secondary btn-sm ng-star-inserted" id="metadata-confirm-btn" *ngIf="!editingAuthority"
[title]="dsoType + '.edit.metadata.edit.buttons.open-authority-edition' | translate"
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.open-authority-edition' | translate }}"
(click)="onChangeEditingAuthorityStatus(true)">
<i class="fas fa-lock fa-fw"></i>
</button>
<button class="btn btn-outline-success btn-sm ng-star-inserted" id="metadata-confirm-btn" *ngIf="editingAuthority"
[title]="dsoType + '.edit.metadata.edit.buttons.close-authority-edition' | translate"
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.close-authority-edition' | translate }}"
(click)="onChangeEditingAuthorityStatus(false)">
<i class="fas fa-lock-open fa-fw"></i>
</button>
</div> </div>
</div> }
<div class="d-flex" *ngIf="mdRepresentation"> @if ( mdValue.editing && (isAuthorityControlled() | async) && (isSuggesterVocabulary() | async)) {
<a class="me-2" target="_blank" [routerLink]="mdRepresentationItemRoute$ | async">{{ mdRepresentationName$ | async }}</a> <div class="mt-2">
<ds-type-badge [object]="mdRepresentation"></ds-type-badge> <div class="btn-group w-75">
</div> <i dsAuthorityConfidenceState
class="fas fa-fw p-0 me-1 mt-auto mb-auto"
aria-hidden="true"
[authorityValue]="mdValue.newValue.confidence"
[iconMode]="true"
></i>
<input class="form-control form-outline" data-test="authority-input" [(ngModel)]="mdValue.newValue.authority" [disabled]="!editingAuthority"
[attr.aria-label]="(dsoType + '.edit.metadata.edit.authority.key') | translate"
(change)="onChangeAuthorityKey()" />
@if (!editingAuthority) {
<button class="btn btn-outline-secondary btn-sm ng-star-inserted" id="metadata-confirm-btn"
[title]="dsoType + '.edit.metadata.edit.buttons.open-authority-edition' | translate"
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.open-authority-edition' | translate }}"
(click)="onChangeEditingAuthorityStatus(true)">
<i class="fas fa-lock fa-fw"></i>
</button>
}
@if (editingAuthority) {
<button class="btn btn-outline-success btn-sm ng-star-inserted" id="metadata-confirm-btn"
[title]="dsoType + '.edit.metadata.edit.buttons.close-authority-edition' | translate"
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.close-authority-edition' | translate }}"
(click)="onChangeEditingAuthorityStatus(false)">
<i class="fas fa-lock-open fa-fw"></i>
</button>
}
</div>
</div>
}
@if (mdRepresentation) {
<div class="d-flex">
<a class="me-2" target="_blank" [routerLink]="mdRepresentationItemRoute$ | async">{{ mdRepresentationName$ | async }}</a>
<ds-type-badge [object]="mdRepresentation"></ds-type-badge>
</div>
}
</div> </div>
<div class="ds-flex-cell ds-lang-cell" role="cell"> <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> @if (!mdValue.editing) {
<input class="form-control" type="text" *ngIf="mdValue.editing" [(ngModel)]="mdValue.newValue.language" <div class="dont-break-out preserve-line-breaks">{{ mdValue.newValue.language }}</div>
[attr.aria-label]="(dsoType + '.edit.metadata.edit.language') | translate" }
[dsDebounce]="300" (onDebounce)="confirm.emit(false)" /> @if (mdValue.editing) {
<input class="form-control" type="text" [(ngModel)]="mdValue.newValue.language"
[attr.aria-label]="(dsoType + '.edit.metadata.edit.language') | translate"
[dsDebounce]="300" (onDebounce)="confirm.emit(false)" />
}
</div> </div>
<div class="text-center ds-flex-cell ds-edit-cell" role="cell"> <div class="text-center ds-flex-cell ds-edit-cell" role="cell">
<div class="btn-group"> <div class="btn-group">
<div class="edit-field"> <div class="edit-field">
<div class="btn-group edit-buttons" [ngbTooltip]="isVirtual ? (dsoType + '.edit.metadata.edit.buttons.virtual' | translate) : null"> <div class="btn-group edit-buttons" [ngbTooltip]="isVirtual ? (dsoType + '.edit.metadata.edit.buttons.virtual' | translate) : null">
<button class="btn btn-outline-primary btn-sm ng-star-inserted" data-test="metadata-edit-btn" *ngIf="!mdValue.editing" @if (!mdValue.editing) {
[title]="dsoType + '.edit.metadata.edit.buttons.edit' | translate" <button class="btn btn-outline-primary btn-sm ng-star-inserted" data-test="metadata-edit-btn"
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.edit' | translate }}" [title]="dsoType + '.edit.metadata.edit.buttons.edit' | translate"
[dsBtnDisabled]="isVirtual || mdValue.change === DsoEditMetadataChangeTypeEnum.REMOVE || (saving$ | async)" (click)="edit.emit()"> ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.edit' | translate }}"
<i class="fas fa-edit fa-fw"></i> [dsBtnDisabled]="isVirtual || mdValue.change === DsoEditMetadataChangeTypeEnum.REMOVE || (saving$ | async)" (click)="edit.emit()">
</button> <i class="fas fa-edit fa-fw"></i>
<button class="btn btn-outline-success btn-sm ng-star-inserted" data-test="metadata-confirm-btn" *ngIf="mdValue.editing" </button>
[title]="dsoType + '.edit.metadata.edit.buttons.confirm' | translate" }
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.confirm' | translate }}" @if (mdValue.editing) {
[dsBtnDisabled]="isVirtual || (saving$ | async)" (click)="confirm.emit(true)"> <button class="btn btn-outline-success btn-sm ng-star-inserted" data-test="metadata-confirm-btn"
<i class="fas fa-check fa-fw"></i> [title]="dsoType + '.edit.metadata.edit.buttons.confirm' | translate"
</button> ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.confirm' | translate }}"
[dsBtnDisabled]="isVirtual || (saving$ | async)" (click)="confirm.emit(true)">
<i class="fas fa-check fa-fw"></i>
</button>
}
<button class="btn btn-outline-danger btn-sm" data-test="metadata-remove-btn" <button class="btn btn-outline-danger btn-sm" data-test="metadata-remove-btn"
[title]="dsoType + '.edit.metadata.edit.buttons.remove' | translate" [title]="dsoType + '.edit.metadata.edit.buttons.remove' | translate"
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.remove' | translate }}" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.remove' | translate }}"
[dsBtnDisabled]="isVirtual || (mdValue.change && mdValue.change !== DsoEditMetadataChangeTypeEnum.ADD) || mdValue.editing || (saving$ | async)" (click)="remove.emit()"> [dsBtnDisabled]="isVirtual || (mdValue.change && mdValue.change !== DsoEditMetadataChangeTypeEnum.ADD) || mdValue.editing || (saving$ | async)" (click)="remove.emit()">
<i class="fas fa-trash-alt fa-fw"></i> <i class="fas fa-trash-alt fa-fw"></i>
</button> </button>
<button class="btn btn-outline-warning btn-sm" data-test="metadata-undo-btn" <button class="btn btn-outline-warning btn-sm" data-test="metadata-undo-btn"
[title]="dsoType + '.edit.metadata.edit.buttons.undo' | translate" [title]="dsoType + '.edit.metadata.edit.buttons.undo' | translate"
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.undo' | translate }}" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.undo' | translate }}"
[dsBtnDisabled]="isVirtual || (!mdValue.change && mdValue.reordered) || (!mdValue.change && !mdValue.editing) || (saving$ | async)" (click)="undo.emit()"> [dsBtnDisabled]="isVirtual || (!mdValue.change && mdValue.reordered) || (!mdValue.change && !mdValue.editing) || (saving$ | async)" (click)="undo.emit()">
<i class="fas fa-undo-alt fa-fw"></i> <i class="fas fa-undo-alt fa-fw"></i>
</button> </button>
<button class="btn btn-outline-secondary ds-drag-handle btn-sm" data-test="metadata-drag-btn" *ngVar="(isOnlyValue || (saving$ | async)) as disabled" <button class="btn btn-outline-secondary ds-drag-handle btn-sm" data-test="metadata-drag-btn" *ngVar="(isOnlyValue || (saving$ | async)) as disabled"
cdkDragHandle [cdkDragHandleDisabled]="disabled" [ngClass]="{'disabled': disabled}" [dsBtnDisabled]="disabled" cdkDragHandle [cdkDragHandleDisabled]="disabled" [ngClass]="{'disabled': disabled}" [dsBtnDisabled]="disabled"
[title]="dsoType + '.edit.metadata.edit.buttons.drag' | translate" [title]="dsoType + '.edit.metadata.edit.buttons.drag' | translate"
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.drag' | translate }}"> ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.drag' | translate }}">
<i class="fas fa-grip-vertical fa-fw"></i> <i class="fas fa-grip-vertical fa-fw"></i>
</button> </button>
</div> </div>

View File

@@ -1,102 +1,120 @@
<div class="item-metadata" *ngIf="form"> @if (form) {
<div class="button-row top d-flex my-2 space-children-mr ms-gap"> <div class="item-metadata">
<button class="me-auto btn btn-success" id="dso-add-btn" [dsBtnDisabled]="form.newValue || (saving$ | async)" <div class="button-row top d-flex my-2 space-children-mr ms-gap">
[attr.aria-label]="dsoType + '.edit.metadata.add-button' | translate" <button class="me-auto btn btn-success" id="dso-add-btn" [dsBtnDisabled]="form.newValue || (saving$ | async)"
[title]="dsoType + '.edit.metadata.add-button' | translate" [attr.aria-label]="dsoType + '.edit.metadata.add-button' | translate"
(click)="add()"><i class="fas fa-plus" aria-hidden="true"></i> [title]="dsoType + '.edit.metadata.add-button' | translate"
<span class="d-none d-sm-inline">&nbsp;{{ dsoType + '.edit.metadata.add-button' | translate }}</span> (click)="add()"><i class="fas fa-plus" aria-hidden="true"></i>
</button> <span class="d-none d-sm-inline">&nbsp;{{ dsoType + '.edit.metadata.add-button' | translate }}</span>
<button class="btn btn-warning ms-1" id="dso-reinstate-btn" *ngIf="isReinstatable" [dsBtnDisabled]="(saving$ | async)" </button>
@if (isReinstatable) {
<button class="btn btn-warning ms-1" id="dso-reinstate-btn" [dsBtnDisabled]="(saving$ | async)"
[attr.aria-label]="dsoType + '.edit.metadata.reinstate-button' | translate"
[title]="dsoType + '.edit.metadata.reinstate-button' | translate"
(click)="reinstate()"><i class="fas fa-undo-alt" aria-hidden="true"></i>
<span class="d-none d-sm-inline">&nbsp;{{ dsoType + '.edit.metadata.reinstate-button' | translate }}</span>
</button>
}
<button class="btn btn-primary ms-1" id="dso-save-btn" [dsBtnDisabled]="!hasChanges || (saving$ | async)"
[attr.aria-label]="dsoType + '.edit.metadata.save-button' | translate"
[title]="dsoType + '.edit.metadata.save-button' | translate"
(click)="submit()"><i class="fas fa-save" aria-hidden="true"></i>
<span class="d-none d-sm-inline">&nbsp;{{ dsoType + '.edit.metadata.save-button' | translate }}</span>
</button>
@if (!isReinstatable) {
<button class="btn btn-danger ms-1" id="dso-discard-btn"
[attr.aria-label]="dsoType + '.edit.metadata.discard-button' | translate"
[title]="dsoType + '.edit.metadata.discard-button' | translate"
[dsBtnDisabled]="!hasChanges || (saving$ | async)"
(click)="discard()"><i class="fas fa-times" aria-hidden="true"></i>
<span class="d-none d-sm-inline">&nbsp;{{ dsoType + '.edit.metadata.discard-button' | translate }}</span>
</button>
}
</div>
<div role="table" [attr.aria-label]="'item.edit.head' | translate">
<ds-dso-edit-metadata-headers [dsoType]="dsoType"></ds-dso-edit-metadata-headers>
@if (form.newValue) {
<div class="d-flex flex-row ds-field-row" role="row">
<div class="lbl-cell ds-success" role="rowheader">
<ds-metadata-field-selector [dsoType]="dsoType"
[(mdField)]="newMdField"
[autofocus]="true">
</ds-metadata-field-selector>
</div>
<div class="flex-grow-1 ds-drop-list" role="cell">
<div role="table">
<ds-dso-edit-metadata-value-headers role="presentation" [dsoType]="dsoType"></ds-dso-edit-metadata-value-headers>
<ds-dso-edit-metadata-value [dso]="dso"
[mdValue]="form.newValue"
[dsoType]="dsoType"
[saving$]="savingOrLoadingFieldValidation$"
[isOnlyValue]="true"
[mdField]="newMdField"
(confirm)="confirmNewValue($event)"
(remove)="form.newValue = undefined"
(undo)="form.newValue = undefined">
</ds-dso-edit-metadata-value>
</div>
</div>
</div>
}
@for (mdField of form.fieldKeys; track mdField) {
<div class="d-flex flex-row ds-field-row" role="row">
<div class="lbl-cell" role="rowheader">
<span class="dont-break-out preserve-line-breaks">{{ mdField }}</span>
@if (form.hasOrderChanges(mdField)) {
<div class="btn btn-warning reset-order-button mt-2 w-100"
(click)="form.resetOrder(mdField); onValueSaved()">
{{ dsoType + '.edit.metadata.reset-order-button' | translate }}
</div>
}
</div>
<ds-dso-edit-metadata-field-values class="flex-grow-1" role="cell"
[dso]="dso"
[form]="form"
[dsoType]="dsoType"
[saving$]="saving$"
[draggingMdField$]="draggingMdField$"
[mdField]="mdField"
(valueSaved)="onValueSaved()">
</ds-dso-edit-metadata-field-values>
</div>
}
</div>
@if (isEmpty && !form.newValue) {
<div>
<ds-alert [content]="dsoType + '.edit.metadata.empty'" [type]="AlertTypeEnum.Info"></ds-alert>
</div>
}
<div class="button-row bottom d-inline-block w-100">
<div class="mt-2 float-end space-children-mr ms-gap">
@if (isReinstatable) {
<button class="btn btn-warning" [dsBtnDisabled]="(saving$ | async)"
[attr.aria-label]="dsoType + '.edit.metadata.reinstate-button' | translate" [attr.aria-label]="dsoType + '.edit.metadata.reinstate-button' | translate"
[title]="dsoType + '.edit.metadata.reinstate-button' | translate" [title]="dsoType + '.edit.metadata.reinstate-button' | translate"
(click)="reinstate()"><i class="fas fa-undo-alt" aria-hidden="true"></i> (click)="reinstate()">
<span class="d-none d-sm-inline">&nbsp;{{ dsoType + '.edit.metadata.reinstate-button' | translate }}</span> <i class="fas fa-undo-alt" aria-hidden="true"></i> {{ dsoType + '.edit.metadata.reinstate-button' | translate }}
</button> </button>
<button class="btn btn-primary ms-1" id="dso-save-btn" [dsBtnDisabled]="!hasChanges || (saving$ | async)" }
[attr.aria-label]="dsoType + '.edit.metadata.save-button' | translate" <button class="btn btn-primary" [dsBtnDisabled]="!hasChanges || (saving$ | async)"
[title]="dsoType + '.edit.metadata.save-button' | translate" [attr.aria-label]="dsoType + '.edit.metadata.save-button' | translate"
(click)="submit()"><i class="fas fa-save" aria-hidden="true"></i> [title]="dsoType + '.edit.metadata.save-button' | translate"
<span class="d-none d-sm-inline">&nbsp;{{ dsoType + '.edit.metadata.save-button' | translate }}</span> (click)="submit()">
</button> <i class="fas fa-save" aria-hidden="true"></i> {{ dsoType + '.edit.metadata.save-button' | translate }}
<button class="btn btn-danger ms-1" id="dso-discard-btn" *ngIf="!isReinstatable" </button>
@if (!isReinstatable) {
<button class="btn btn-danger"
[attr.aria-label]="dsoType + '.edit.metadata.discard-button' | translate" [attr.aria-label]="dsoType + '.edit.metadata.discard-button' | translate"
[title]="dsoType + '.edit.metadata.discard-button' | translate" [title]="dsoType + '.edit.metadata.discard-button' | translate"
[dsBtnDisabled]="!hasChanges || (saving$ | async)" [dsBtnDisabled]="!hasChanges || (saving$ | async)"
(click)="discard()"><i class="fas fa-times" aria-hidden="true"></i> (click)="discard()">
<span class="d-none d-sm-inline">&nbsp;{{ dsoType + '.edit.metadata.discard-button' | translate }}</span> <i class="fas fa-times" aria-hidden="true"></i> {{ dsoType + '.edit.metadata.discard-button' | translate }}
</button> </button>
</div> }
<div role="table" [attr.aria-label]="'item.edit.head' | translate">
<ds-dso-edit-metadata-headers [dsoType]="dsoType"></ds-dso-edit-metadata-headers>
<div class="d-flex flex-row ds-field-row" role="row" *ngIf="form.newValue">
<div class="lbl-cell ds-success" role="rowheader">
<ds-metadata-field-selector [dsoType]="dsoType"
[(mdField)]="newMdField"
[autofocus]="true">
</ds-metadata-field-selector>
</div>
<div class="flex-grow-1 ds-drop-list" role="cell">
<div role="table">
<ds-dso-edit-metadata-value-headers role="presentation" [dsoType]="dsoType"></ds-dso-edit-metadata-value-headers>
<ds-dso-edit-metadata-value [dso]="dso"
[mdValue]="form.newValue"
[dsoType]="dsoType"
[saving$]="savingOrLoadingFieldValidation$"
[isOnlyValue]="true"
[mdField]="newMdField"
(confirm)="confirmNewValue($event)"
(remove)="form.newValue = undefined"
(undo)="form.newValue = undefined">
</ds-dso-edit-metadata-value>
</div>
</div> </div>
</div> </div>
<div class="d-flex flex-row ds-field-row" role="row" *ngFor="let mdField of form.fieldKeys">
<div class="lbl-cell" role="rowheader">
<span class="dont-break-out preserve-line-breaks">{{ mdField }}</span>
<div class="btn btn-warning reset-order-button mt-2 w-100" *ngIf="form.hasOrderChanges(mdField)"
(click)="form.resetOrder(mdField); onValueSaved()">
{{ dsoType + '.edit.metadata.reset-order-button' | translate }}
</div>
</div>
<ds-dso-edit-metadata-field-values class="flex-grow-1" role="cell"
[dso]="dso"
[form]="form"
[dsoType]="dsoType"
[saving$]="saving$"
[draggingMdField$]="draggingMdField$"
[mdField]="mdField"
(valueSaved)="onValueSaved()">
</ds-dso-edit-metadata-field-values>
</div>
</div> </div>
}
<div *ngIf="isEmpty && !form.newValue"> @if (!form) {
<ds-alert [content]="dsoType + '.edit.metadata.empty'" [type]="AlertTypeEnum.Info"></ds-alert> <ds-loading></ds-loading>
</div> }
<div class="button-row bottom d-inline-block w-100">
<div class="mt-2 float-end space-children-mr ms-gap">
<button class="btn btn-warning" *ngIf="isReinstatable" [dsBtnDisabled]="(saving$ | async)"
[attr.aria-label]="dsoType + '.edit.metadata.reinstate-button' | translate"
[title]="dsoType + '.edit.metadata.reinstate-button' | translate"
(click)="reinstate()">
<i class="fas fa-undo-alt" aria-hidden="true"></i> {{ dsoType + '.edit.metadata.reinstate-button' | translate }}
</button>
<button class="btn btn-primary" [dsBtnDisabled]="!hasChanges || (saving$ | async)"
[attr.aria-label]="dsoType + '.edit.metadata.save-button' | translate"
[title]="dsoType + '.edit.metadata.save-button' | translate"
(click)="submit()">
<i class="fas fa-save" aria-hidden="true"></i> {{ dsoType + '.edit.metadata.save-button' | translate }}
</button>
<button class="btn btn-danger" *ngIf="!isReinstatable"
[attr.aria-label]="dsoType + '.edit.metadata.discard-button' | translate"
[title]="dsoType + '.edit.metadata.discard-button' | translate"
[dsBtnDisabled]="!hasChanges || (saving$ | async)"
(click)="discard()">
<i class="fas fa-times" aria-hidden="true"></i> {{ dsoType + '.edit.metadata.discard-button' | translate }}
</button>
</div>
</div>
</div>
<ds-loading *ngIf="!form"></ds-loading>

View File

@@ -1,22 +1,32 @@
<div class="d-flex"> <div class="d-flex">
<!-- <div class="person-thumbnail pe-2">--> <!-- <div class="person-thumbnail pe-2">-->
<!-- <ds-themed-thumbnail [thumbnail]="dso?.thumbnail | async" [defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-themed-thumbnail>--> <!-- <ds-themed-thumbnail [thumbnail]="dso?.thumbnail | async" [defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-themed-thumbnail>-->
<!-- </div>--> <!-- </div>-->
<div class="flex-grow-1"> <div class="flex-grow-1">
<ds-org-unit-input-suggestions *ngIf="useNameVariants" [suggestions]="allSuggestions" [(ngModel)]="selectedName" (clickSuggestion)="select($event)" @if (useNameVariants) {
(submitSuggestion)="selectCustom($event)"></ds-org-unit-input-suggestions> <ds-org-unit-input-suggestions [suggestions]="allSuggestions" [(ngModel)]="selectedName" (clickSuggestion)="select($event)"
(submitSuggestion)="selectCustom($event)"></ds-org-unit-input-suggestions>
}
<div *ngIf="!useNameVariants" class="lead" [innerHTML]="dsoTitle"></div> @if (!useNameVariants) {
<div class="lead" [innerHTML]="dsoTitle"></div>
}
<span class="text-muted"> <span class="text-muted">
<span *ngIf="dso.allMetadata('organization.address.addressLocality').length > 0" @if (dso.allMetadata('organization.address.addressLocality').length > 0) {
class="item-list-address-locality"> <span
<span [innerHTML]="firstMetadataValue(['organization.address.addressLocality'])"></span><span *ngIf="dso.allMetadata('organization.address.addressCountry').length > 0">, </span> class="item-list-address-locality">
</span> <span [innerHTML]="firstMetadataValue(['organization.address.addressLocality'])"></span>@if (dso.allMetadata('organization.address.addressCountry').length > 0) {
<span *ngIf="dso.allMetadata('organization.address.addressCountry').length > 0" <span>, </span>
class="item-list-address-country"> }
<span [innerHTML]="firstMetadataValue(['organization.address.addressCountry'])"></span> </span>
</span> }
</span> @if (dso.allMetadata('organization.address.addressCountry').length > 0) {
</div> <span
class="item-list-address-country">
<span [innerHTML]="firstMetadataValue(['organization.address.addressCountry'])"></span>
</span>
}
</span>
</div>
</div> </div>

View File

@@ -1,55 +1,51 @@
<footer> <footer>
<div *ngIf="showTopFooter" class="top-footer"> @if (showTopFooter) {
<!-- Grid container --> <div class="top-footer">
<div class=" container p-4"> <!-- Grid container -->
<!--Grid row--> <div class=" container p-4">
<div class="row"> <!--Grid row-->
<div class="row">
<!--Grid column--> <!--Grid column-->
<div class="col-lg-4 col-md-6 mb-4 mb-lg-0"> <div class="col-lg-4 col-md-6 mb-4 mb-lg-0">
<h5 class="text-uppercase">Footer Content</h5> <h5 class="text-uppercase">Footer Content</h5>
<ul class="list-unstyled mb-0">
<ul class="list-unstyled mb-0"> <li>
<li> <a routerLink="./" class="">Lorem ipsum</a>
<a routerLink="./" class="">Lorem ipsum</a> </li>
</li> <li>
<li> <a routerLink="./" class="">Ut facilisis</a>
<a routerLink="./" class="">Ut facilisis</a> </li>
</li> <li>
<li> <a routerLink="./" class="">Aenean sit</a>
<a routerLink="./" class="">Aenean sit</a> </li>
</li> </ul>
</ul> </div>
<!--Grid column-->
<!--Grid column-->
<div class="col-lg-4 col-md-6 mb-4 mb-lg-0">
<h5 class="text-uppercase">Footer Content</h5>
<ul class="list-unstyled mb-0">
<li>
<a routerLink="./" class="">Suspendisse potenti</a>
</li>
</ul>
</div>
<!--Grid column-->
<!--Grid column-->
<div class="col-lg-4 col-md-12 mb-4 mb-md-0">
<h5 class="text-uppercase">Footer Content</h5>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Iste atque ea quis
molestias. Fugiat pariatur maxime quis culpa corporis vitae repudiandae aliquam
voluptatem veniam, est atque cumque eum delectus sint!
</p>
</div>
<!--Grid column-->
</div> </div>
<!--Grid column--> <!--Grid row-->
<!--Grid column-->
<div class="col-lg-4 col-md-6 mb-4 mb-lg-0">
<h5 class="text-uppercase">Footer Content</h5>
<ul class="list-unstyled mb-0">
<li>
<a routerLink="./" class="">Suspendisse potenti</a>
</li>
</ul>
</div>
<!--Grid column-->
<!--Grid column-->
<div class="col-lg-4 col-md-12 mb-4 mb-md-0">
<h5 class="text-uppercase">Footer Content</h5>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Iste atque ea quis
molestias. Fugiat pariatur maxime quis culpa corporis vitae repudiandae aliquam
voluptatem veniam, est atque cumque eum delectus sint!
</p>
</div>
<!--Grid column-->
</div> </div>
<!--Grid row-->
</div> </div>
</div> }
<!-- Grid container --> <!-- Grid container -->
<!-- Copyright --> <!-- Copyright -->
@@ -57,10 +53,10 @@
<div class="content-container"> <div class="content-container">
<p class="m-0"> <p class="m-0">
<a class="text-white" <a class="text-white"
href="http://www.dspace.org/">{{ 'footer.link.dspace' | translate}}</a> href="http://www.dspace.org/">{{ 'footer.link.dspace' | translate}}</a>
{{ 'footer.copyright' | translate:{year: dateObj | date:'y'} }} {{ 'footer.copyright' | translate:{year: dateObj | date:'y'} }}
<a class="text-white" <a class="text-white"
href="https://www.lyrasis.org/">{{ 'footer.link.lyrasis' | translate}}</a> href="https://www.lyrasis.org/">{{ 'footer.link.lyrasis' | translate}}</a>
</p> </p>
<ul class="footer-info list-unstyled d-flex justify-content-center mb-0"> <ul class="footer-info list-unstyled d-flex justify-content-center mb-0">
<li> <li>
@@ -68,26 +64,34 @@
{{ 'footer.link.cookies' | translate}} {{ 'footer.link.cookies' | translate}}
</button> </button>
</li> </li>
<li *ngIf="showPrivacyPolicy"> @if (showPrivacyPolicy) {
<a class="btn text-white" <li>
routerLink="info/privacy">{{ 'footer.link.privacy-policy' | translate}}</a> <a class="btn text-white"
</li> routerLink="info/privacy">{{ 'footer.link.privacy-policy' | translate}}</a>
<li *ngIf="showEndUserAgreement"> </li>
<a class="btn text-white" }
routerLink="info/end-user-agreement">{{ 'footer.link.end-user-agreement' | translate}}</a> @if (showEndUserAgreement) {
</li> <li>
<li *ngIf="showSendFeedback$ | async"> <a class="btn text-white"
<a class="btn text-white" routerLink="info/end-user-agreement">{{ 'footer.link.end-user-agreement' | translate}}</a>
routerLink="info/feedback">{{ 'footer.link.feedback' | translate}}</a> </li>
</li> }
@if (showSendFeedback$ | async) {
<li>
<a class="btn text-white"
routerLink="info/feedback">{{ 'footer.link.feedback' | translate}}</a>
</li>
}
</ul> </ul>
</div> </div>
<div *ngIf="coarLdnEnabled$ | async" class="notify-enabled text-white"> @if (coarLdnEnabled$ | async) {
<a class="coar-notify-support-route" routerLink="info/coar-notify-support"> <div class="notify-enabled text-white">
<img class="n-coar" src="assets/images/n-coar.svg" [attr.alt]="'menu.header.image.logo' | translate" /> <a class="coar-notify-support-route" routerLink="info/coar-notify-support">
{{ 'footer.link.coar-notify-support' | translate }} <img class="n-coar" src="assets/images/n-coar.svg" [attr.alt]="'menu.header.image.logo' | translate" />
</a> {{ 'footer.link.coar-notify-support' | translate }}
</div> </a>
</div>
}
</div> </div>
<!-- Copyright --> <!-- Copyright -->
</footer> </footer>

View File

@@ -1,36 +1,35 @@
<div class="container" *ngIf="(registration$ |async)"> @if ((registration$ |async)) {
<div class="container">
<h1 class="mb-4">{{'forgot-password.form.head' | translate}}</h1> <h1 class="mb-4">{{'forgot-password.form.head' | translate}}</h1>
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header">{{'forgot-password.form.identification.header' | translate}}</div> <div class="card-header">{{'forgot-password.form.identification.header' | translate}}</div>
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<span class="fw-bold form-label">{{'forgot-password.form.identification.email' | translate}} </span> <span class="fw-bold form-label">{{'forgot-password.form.identification.email' | translate}} </span>
<span [attr.data-test]="'email' | dsBrowserOnly">{{(registration$ |async).email}}</span> <span [attr.data-test]="'email' | dsBrowserOnly">{{(registration$ |async).email}}</span>
</div> </div>
</div>
</div> </div>
</div>
</div> </div>
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header">{{'forgot-password.form.card.security' | translate}}</div> <div class="card-header">{{'forgot-password.form.card.security' | translate}}</div>
<div class="card-body"> <div class="card-body">
<ds-profile-page-security-form
<ds-profile-page-security-form [passwordCanBeEmpty]="false"
[passwordCanBeEmpty]="false" [FORM_PREFIX]="'forgot-password.form.'"
[FORM_PREFIX]="'forgot-password.form.'" (isInvalid)="setInValid($event)"
(isInvalid)="setInValid($event)" (passwordValue)="setPasswordValue($event)"
(passwordValue)="setPasswordValue($event)" ></ds-profile-page-security-form>
></ds-profile-page-security-form> </div>
</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<button <button
[dsBtnDisabled]="isInValid" [dsBtnDisabled]="isInValid"
class="btn btn-default btn-primary" class="btn btn-default btn-primary"
(click)="submit()">{{'forgot-password.form.submit' | translate}}</button> (click)="submit()">{{'forgot-password.form.submit' | translate}}</button>
</div> </div>
</div> </div>
</div> </div>
}

View File

@@ -11,13 +11,15 @@
<ds-context-help-toggle></ds-context-help-toggle> <ds-context-help-toggle></ds-context-help-toggle>
<ds-auth-nav-menu></ds-auth-nav-menu> <ds-auth-nav-menu></ds-auth-nav-menu>
<ds-impersonate-navbar></ds-impersonate-navbar> <ds-impersonate-navbar></ds-impersonate-navbar>
<div *ngIf="isMobile$ | async" class="ps-2"> @if (isMobile$ | async) {
<button class="navbar-toggler px-0" type="button" (click)="toggleNavbar()" <div class="ps-2">
aria-controls="collapsingNav" <button class="navbar-toggler px-0" type="button" (click)="toggleNavbar()"
aria-expanded="false" [attr.aria-label]="'nav.toggle' | translate"> aria-controls="collapsingNav"
<span class="fas fa-bars fa-fw toggler-icon" aria-hidden="true"></span> aria-expanded="false" [attr.aria-label]="'nav.toggle' | translate">
</button> <span class="fas fa-bars fa-fw toggler-icon" aria-hidden="true"></span>
</div> </button>
</div>
}
</nav> </nav>
</div> </div>
</div> </div>

View File

@@ -1,27 +1,35 @@
<div *ngFor="let entry of healthInfoComponent | dsObjNgFor" data-test="collapse"> @for (entry of healthInfoComponent | dsObjNgFor; track entry) {
<div *ngIf="entry && !isPlainProperty(entry.value)" class="mb-3 border-bottom"> <div data-test="collapse">
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="collapse.toggle()"> @if (entry && !isPlainProperty(entry.value)) {
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!collapse.collapsed" <div class="mb-3 border-bottom">
[attr.aria-controls]="'health-info-component-' + entry.key + '-content'"> <div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="collapse.toggle()">
{{ entry.key | titlecase }} <button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!collapse.collapsed"
</button> [attr.aria-controls]="'health-info-component-' + entry.key + '-content'">
<div class="d-flex my-auto"> {{ entry.key | titlecase }}
<span *ngIf="collapse.collapsed" class="fas fa-chevron-down"></span> </button>
<span *ngIf="!collapse.collapsed" class="fas fa-chevron-up"></span> <div class="d-flex my-auto">
</div> @if (collapse.collapsed) {
</div> <span class="fas fa-chevron-down"></span>
<div #collapse="ngbCollapse" [id]="'health-info-component-' + entry.key + '-content'" [ngbCollapse]="isCollapsed"> }
<div class="card border-0"> @if (!collapse.collapsed) {
<div class="card-body"> <span class="fas fa-chevron-up"></span>
<ds-health-info-component [healthInfoComponent]="entry.value" }
[healthInfoComponentName]="entry.key" </div>
[isNested]="true" </div>
data-test="info-component"></ds-health-info-component> <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"
[healthInfoComponentName]="entry.key"
[isNested]="true"
data-test="info-component"></ds-health-info-component>
</div>
</div>
</div> </div>
</div> </div>
</div> }
@if (entry && isPlainProperty(entry.value)) {
<p data-test="property"> <span class="fw-bold">{{ getPropertyLabel(entry.key) | titlecase }}</span> : {{entry.value}}</p>
}
</div> </div>
<ng-container *ngIf="entry && isPlainProperty(entry.value)"> }
<p data-test="property"> <span class="fw-bold">{{ getPropertyLabel(entry.key) | titlecase }}</span> : {{entry.value}}</p>
</ng-container>
</div>

View File

@@ -1,22 +1,24 @@
<ng-container *ngIf="healthInfoResponse"> @if (healthInfoResponse) {
<ngb-accordion #acc="ngbAccordion" [activeIds]="activeId"> <ngb-accordion #acc="ngbAccordion" [activeIds]="activeId">
<ngb-panel [id]="entry.key" *ngFor="let entry of healthInfoResponse | dsObjNgFor"> @for (entry of healthInfoResponse | dsObjNgFor; track entry) {
<ng-template ngbPanelTitle> <ngb-panel [id]="entry.key">
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="acc.toggle(entry.key)" data-test="info-component"> <ng-template ngbPanelTitle>
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="acc.isExpanded(entry.key)" <div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="acc.toggle(entry.key)" data-test="info-component">
[attr.aria-controls]="'health-info-' + entry.key + '-content'"> <button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="acc.isExpanded(entry.key)"
{{ getPanelLabel(entry.key) | titlecase }} [attr.aria-controls]="'health-info-' + entry.key + '-content'">
</button> {{ getPanelLabel(entry.key) | titlecase }}
<div class="text-end d-flex gap-2"> </button>
<ds-health-status class="me-2" [status]="entry.value?.status"></ds-health-status> <div class="text-end d-flex gap-2">
<ds-health-status class="me-2" [status]="entry.value?.status"></ds-health-status>
</div>
</div> </div>
</div> </ng-template>
</ng-template> <ng-template ngbPanelContent>
<ng-template ngbPanelContent> <ds-health-info-component [healthInfoComponentName]="entry.key" [healthInfoComponent]="entry.value"
<ds-health-info-component [healthInfoComponentName]="entry.key" [healthInfoComponent]="entry.value" [id]="'health-info-' + entry.key + '-content'">
[id]="'health-info-' + entry.key + '-content'"> </ds-health-info-component>
</ds-health-info-component> </ng-template>
</ng-template> </ngb-panel>
</ngb-panel> }
</ngb-accordion> </ngb-accordion>
</ng-container> }

View File

@@ -1,30 +1,38 @@
<ng-container *ngIf="healthComponent?.components"> @if (healthComponent?.components) {
<div *ngFor="let entry of healthComponent?.components | dsObjNgFor" class="mb-3 border-bottom" data-test="collapse"> @for (entry of healthComponent?.components | dsObjNgFor; track entry) {
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="collapse.toggle()"> <div class="mb-3 border-bottom" data-test="collapse">
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!collapse.collapsed" <div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="collapse.toggle()">
[attr.aria-controls]="'health-component-' + entry.key + 'content'"> <button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!collapse.collapsed"
{{ entry.key | titlecase }} [attr.aria-controls]="'health-component-' + entry.key + 'content'">
</button> {{ entry.key | titlecase }}
<div class="d-flex my-auto"> </button>
<span *ngIf="collapse.collapsed" class="fas fa-chevron-down"></span> <div class="d-flex my-auto">
<span *ngIf="!collapse.collapsed" class="fas fa-chevron-up"></span> @if (collapse.collapsed) {
<span class="fas fa-chevron-down"></span>
}
@if (!collapse.collapsed) {
<span class="fas fa-chevron-up"></span>
}
</div>
</div> </div>
</div> <div #collapse="ngbCollapse" [id]="'health-component-' + entry.key + 'content'" [ngbCollapse]="isCollapsed">
<div #collapse="ngbCollapse" [id]="'health-component-' + entry.key + 'content'" [ngbCollapse]="isCollapsed"> <div class="card border-0">
<div class="card border-0"> <div class="card-body">
<div class="card-body"> <ds-health-component [healthComponent]="entry.value"
<ds-health-component [healthComponent]="entry.value" [healthComponentName]="entry.key"></ds-health-component>
[healthComponentName]="entry.key"></ds-health-component> </div>
</div> </div>
</div> </div>
</div> </div>
</div> }
</ng-container> }
<ng-container *ngIf="healthComponent?.details"> @if (healthComponent?.details) {
<div *ngFor="let item of healthComponent?.details | dsObjNgFor" data-test="details"> @for (item of healthComponent?.details | dsObjNgFor; track item) {
<p data-test="property"><span class="fw-bold">{{ getPropertyLabel(item.key) | titlecase }}</span> : {{item.value}}</p> <div data-test="details">
</div> <p data-test="property"><span class="fw-bold">{{ getPropertyLabel(item.key) | titlecase }}</span> : {{item.value}}</p>
</ng-container> </div>
<ng-container *ngIf="!healthComponent?.details && !healthComponent?.components"> }
}
@if (!healthComponent?.details && !healthComponent?.components) {
<ds-alert [content]="'health-page.section.no-issues'" [type]="AlertTypeEnum.Info"></ds-alert> <ds-alert [content]="'health-page.section.no-issues'" [type]="AlertTypeEnum.Info"></ds-alert>
</ng-container> }

View File

@@ -3,22 +3,24 @@
<ds-health-status [status]="healthResponse.status" class="d-inline-flex"></ds-health-status> <ds-health-status [status]="healthResponse.status" class="d-inline-flex"></ds-health-status>
</p> </p>
<ngb-accordion #acc="ngbAccordion" [activeIds]="activeId"> <ngb-accordion #acc="ngbAccordion" [activeIds]="activeId">
<ngb-panel [id]="entry.key" *ngFor="let entry of healthResponse.components | dsObjNgFor"> @for (entry of healthResponse.components | dsObjNgFor; track entry) {
<ng-template ngbPanelTitle> <ngb-panel [id]="entry.key">
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="acc.toggle(entry.key)" data-test="component"> <ng-template ngbPanelTitle>
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="acc.isExpanded(entry.key)" <div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="acc.toggle(entry.key)" data-test="component">
[attr.aria-controls]="'health-panel-' + entry.key + '-content'"> <button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="acc.isExpanded(entry.key)"
{{ getPanelLabel(entry.key) | titlecase }} [attr.aria-controls]="'health-panel-' + entry.key + '-content'">
</button> {{ getPanelLabel(entry.key) | titlecase }}
<div class="text-end d-flex gap-2"> </button>
<ds-health-status class="me-2" [status]="entry.value?.status"></ds-health-status> <div class="text-end d-flex gap-2">
<ds-health-status class="me-2" [status]="entry.value?.status"></ds-health-status>
</div>
</div> </div>
</div> </ng-template>
</ng-template> <ng-template ngbPanelContent>
<ng-template ngbPanelContent> <ds-health-component [healthComponent]="entry.value" [healthComponentName]="entry.key"
<ds-health-component [healthComponent]="entry.value" [healthComponentName]="entry.key" [id]="'health-panel-' + entry.key + '-content'" role="presentation">
[id]="'health-panel-' + entry.key + '-content'" role="presentation"> </ds-health-component>
</ds-health-component> </ng-template>
</ng-template> </ngb-panel>
</ngb-panel> }
</ngb-accordion> </ngb-accordion>

View File

@@ -1,12 +1,18 @@
<ng-container [ngSwitch]="status">
<i *ngSwitchCase="HealthStatus.UP"
class="fa fa-check-circle text-success ms-2 mt-1"
ngbTooltip="{{'health-page.status.ok.info' | translate}}" container="body" ></i>
<i *ngSwitchCase="HealthStatus.UP_WITH_ISSUES"
class="fa fa-exclamation-triangle text-warning ms-2 mt-1"
ngbTooltip="{{'health-page.status.warning.info' | translate}}" container="body"></i>
<i *ngSwitchCase="HealthStatus.DOWN"
class="fa fa-times-circle text-danger ms-2 mt-1"
ngbTooltip="{{'health-page.status.error.info' | translate}}" container="body"></i>
</ng-container> @switch (status) {
@case (HealthStatus.UP) {
<i
class="fa fa-check-circle text-success ms-2 mt-1"
ngbTooltip="{{'health-page.status.ok.info' | translate}}" container="body" ></i>
}
@case (HealthStatus.UP_WITH_ISSUES) {
<i
class="fa fa-exclamation-triangle text-warning ms-2 mt-1"
ngbTooltip="{{'health-page.status.warning.info' | translate}}" container="body"></i>
}
@case (HealthStatus.DOWN) {
<i
class="fa fa-times-circle text-danger ms-2 mt-1"
ngbTooltip="{{'health-page.status.error.info' | translate}}" container="body"></i>
}
}

View File

@@ -1,14 +1,22 @@
<ng-container *ngVar="(itemRD$ | async) as itemRD"> <ng-container *ngVar="(itemRD$ | async) as itemRD">
<div class="mt-4" [ngClass]="placeholderFontClass" *ngIf="itemRD?.hasSucceeded && itemRD?.payload?.page.length > 0" @fadeIn> @if (itemRD?.hasSucceeded && itemRD?.payload?.page.length > 0) {
<div class="d-flex flex-row border-bottom mb-4 pb-4"></div> <div class="mt-4" [ngClass]="placeholderFontClass" @fadeIn>
<h2> {{'home.recent-submissions.head' | translate}}</h2> <div class="d-flex flex-row border-bottom mb-4 pb-4"></div>
<div class="my-4" *ngFor="let item of itemRD?.payload?.page"> <h2> {{'home.recent-submissions.head' | translate}}</h2>
<ds-listable-object-component-loader [object]="item" [viewMode]="viewMode" class="pb-4"> @for (item of itemRD?.payload?.page; track item) {
</ds-listable-object-component-loader> <div class="my-4">
</div> <ds-listable-object-component-loader [object]="item" [viewMode]="viewMode" class="pb-4">
<button (click)="onLoadMore()" class="btn btn-primary search-button mt-4 float-start ng-tns-c290-40"> {{'vocabulary-treeview.load-more' | translate }} ...</button> </ds-listable-object-component-loader>
</div> </div>
<ds-error *ngIf="itemRD?.hasFailed" message="{{'error.recent-submissions' | translate}}"></ds-error> }
<ds-loading *ngIf="!itemRD || itemRD.isLoading" message="{{'loading.recent-submissions' | translate}}"> <button (click)="onLoadMore()" class="btn btn-primary search-button mt-4 float-start ng-tns-c290-40"> {{'vocabulary-treeview.load-more' | translate }} ...</button>
</ds-loading> </div>
}
@if (itemRD?.hasFailed) {
<ds-error message="{{'error.recent-submissions' | translate}}"></ds-error>
}
@if (!itemRD || itemRD.isLoading) {
<ds-loading message="{{'loading.recent-submissions' | translate}}">
</ds-loading>
}
</ng-container> </ng-container>

View File

@@ -1,87 +1,101 @@
<div class="container"> <div class="container">
<h1 class="mb-4">{{'bitstream-request-a-copy.header' | translate}}</h1> <h1 class="mb-4">{{'bitstream-request-a-copy.header' | translate}}</h1>
<div *ngIf="canDownload$|async" class="alert alert-success"> @if (canDownload$|async) {
<span>{{'bitstream-request-a-copy.alert.canDownload1' | translate}}</span> <div class="alert alert-success">
<a [routerLink]="getBitstreamLink()">{{'bitstream-request-a-copy.alert.canDownload2'| translate}}</a> <span>{{'bitstream-request-a-copy.alert.canDownload1' | translate}}</span>
<a [routerLink]="getBitstreamLink()">{{'bitstream-request-a-copy.alert.canDownload2'| translate}}</a>
</div> </div>
<div> }
<p>{{'bitstream-request-a-copy.intro' | translate}} <a [routerLink]="getItemPath()">{{itemName}}</a></p> <div>
<p *ngIf="bitstream !== undefined && allfiles.value === 'false'">{{'bitstream-request-a-copy.intro.bitstream.one' | translate}} {{bitstreamName}}</p> <p>{{'bitstream-request-a-copy.intro' | translate}} <a [routerLink]="getItemPath()">{{itemName}}</a></p>
<p *ngIf="allfiles.value === 'true'">{{'bitstream-request-a-copy.intro.bitstream.all' | translate}}</p> @if (bitstream !== undefined && allfiles.value === 'false') {
</div> <p>{{'bitstream-request-a-copy.intro.bitstream.one' | translate}} {{bitstreamName}}</p>
<form [class]="'ng-invalid'" [formGroup]="requestCopyForm" (ngSubmit)="onSubmit()"> }
@if (allfiles.value === 'true') {
<p>{{'bitstream-request-a-copy.intro.bitstream.all' | translate}}</p>
}
</div>
<form [class]="'ng-invalid'" [formGroup]="requestCopyForm" (ngSubmit)="onSubmit()">
<div class="mb-3"> <div class="mb-3">
<div class="row mb-4"> <div class="row mb-4">
<div class="col-12"> <div class="col-12">
<label for="name" class="form-label">{{'bitstream-request-a-copy.name.label' | translate}}</label> <label for="name" class="form-label">{{'bitstream-request-a-copy.name.label' | translate}}</label>
<input [className]="(name.invalid) && (name.dirty || name.touched) ? 'form-control is-invalid' :'form-control'" <input [className]="(name.invalid) && (name.dirty || name.touched) ? 'form-control is-invalid' :'form-control'"
type="text" id="name" formControlName="name"/> type="text" id="name" formControlName="name"/>
<div *ngIf="name.invalid && (name.dirty || name.touched)" @if (name.invalid && (name.dirty || name.touched)) {
class="invalid-feedback show-feedback"> <div
<span *ngIf="name.errors && name.errors.required"> class="invalid-feedback show-feedback">
{{ 'bitstream-request-a-copy.name.error' | translate }} @if (name.errors && name.errors.required) {
</span> <span>
</div> {{ 'bitstream-request-a-copy.name.error' | translate }}
</div> </span>
</div> }
<div class="row mb-4">
<div class="col-12">
<label
for="email" class="form-label">{{'bitstream-request-a-copy.email.label' | translate}}</label>
<input
[className]="(email.invalid) && (email.dirty || email.touched) ? 'form-control is-invalid' :'form-control'"
id="email" formControlName="email">
<div *ngIf="email.invalid && (email.dirty || email.touched)"
class="invalid-feedback show-feedback">
<span *ngIf="email.errors">
{{ 'bitstream-request-a-copy.email.error' | translate }}
</span>
</div>
<small class="text-muted ds-hint">{{'bitstream-request-a-copy.email.hint' |translate}}</small>
</div>
</div>
<div class="row mb-4">
<div class="col-12">
<div>{{'bitstream-request-a-copy.allfiles.label' |translate}}</div>
<div class="ms-4 form-check">
<input [className]="'form-check-input'" type="radio"
id="allfiles-true" formControlName="allfiles" value="true">
<label class="form-check-label"
for="allfiles-true">{{'bitstream-request-a-copy.files-all-true.label' | translate}}</label>
</div>
<div class="ms-4">
<input [className]="'form-check-input'" type="radio"
id="allfiles-false" formControlName="allfiles" value="false" [attr.disabled]="bitstream === undefined ? true : null ">
<label class="form-check-label"
for="allfiles-false">{{'bitstream-request-a-copy.files-all-false.label' | translate}}</label>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-12">
<label
for="message" class="form-label">{{'bitstream-request-a-copy.message.label' | translate}}</label>
<textarea rows="5"
[className]="'form-control'"
id="message" formControlName="message"></textarea>
</div>
</div> </div>
}
</div> </div>
</form> </div>
<div class="row mb-4">
<div class="col-12">
<label
for="email" class="form-label">{{'bitstream-request-a-copy.email.label' | translate}}</label>
<input
[className]="(email.invalid) && (email.dirty || email.touched) ? 'form-control is-invalid' :'form-control'"
id="email" formControlName="email">
@if (email.invalid && (email.dirty || email.touched)) {
<div
class="invalid-feedback show-feedback">
@if (email.errors) {
<span>
{{ 'bitstream-request-a-copy.email.error' | translate }}
</span>
}
</div>
}
<small class="text-muted ds-hint">{{'bitstream-request-a-copy.email.hint' |translate}}</small>
</div>
</div>
<div class="row mb-4">
<div class="col-12">
<div>{{'bitstream-request-a-copy.allfiles.label' |translate}}</div>
<div class="ms-4 form-check">
<input [className]="'form-check-input'" type="radio"
id="allfiles-true" formControlName="allfiles" value="true">
<label class="form-check-label"
for="allfiles-true">{{'bitstream-request-a-copy.files-all-true.label' | translate}}</label>
</div>
<div class="ms-4">
<input [className]="'form-check-input'" type="radio"
id="allfiles-false" formControlName="allfiles" value="false" [attr.disabled]="bitstream === undefined ? true : null ">
<label class="form-check-label"
for="allfiles-false">{{'bitstream-request-a-copy.files-all-false.label' | translate}}</label>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-12">
<label
for="message" class="form-label">{{'bitstream-request-a-copy.message.label' | translate}}</label>
<textarea rows="5"
[className]="'form-control'"
id="message" formControlName="message"></textarea>
</div>
</div>
</div>
</form>
<hr> <hr>
<div class="row"> <div class="row">
<div class="col-12 text-end"> <div class="col-12 text-end">
<a (click)="navigateBack()" role="button" class="btn btn-outline-secondary me-1"> <a (click)="navigateBack()" role="button" class="btn btn-outline-secondary me-1">
<i class="fas fa-arrow-left"></i> {{'bitstream-request-a-copy.return' | translate}} <i class="fas fa-arrow-left"></i> {{'bitstream-request-a-copy.return' | translate}}
</a> </a>
<button <button
[dsBtnDisabled]="requestCopyForm.invalid" [dsBtnDisabled]="requestCopyForm.invalid"
class="btn btn-default btn-primary" class="btn btn-default btn-primary"
(click)="onSubmit()">{{'bitstream-request-a-copy.submit' | translate}}</button> (click)="onSubmit()">{{'bitstream-request-a-copy.submit' | translate}}</button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,41 +1,47 @@
<div class="container"> <div class="container">
<ng-container *ngIf="bundles"> @if (bundles) {
<div class="row"> <div class="row">
<div class="col-12 mb-2"> <div class="col-12 mb-2">
<h1>{{'item.bitstreams.upload.title' | translate}}</h1> <h1>{{'item.bitstreams.upload.title' | translate}}</h1>
<ng-container *ngVar="(itemRD$ | async)?.payload as item"> <ng-container *ngVar="(itemRD$ | async)?.payload as item">
<div *ngIf="item"> @if (item) {
<span class="fw-bold">{{'item.bitstreams.upload.item' | translate}}</span> <div>
<span>{{ dsoNameService.getName(item) }}</span> <span class="fw-bold">{{'item.bitstreams.upload.item' | translate}}</span>
</div> <span>{{ dsoNameService.getName(item) }}</span>
</div>
}
</ng-container> </ng-container>
</div> </div>
<div class="col-12"> <div class="col-12">
<label class="fw-bold form-label">{{'item.bitstreams.upload.bundle' | translate}}</label> <label class="fw-bold form-label">{{'item.bitstreams.upload.bundle' | translate}}</label>
<ds-dso-input-suggestions #f id="search-form" <ds-dso-input-suggestions #f id="search-form"
[suggestions]="bundles" [suggestions]="bundles"
[placeholder]="'item.bitstreams.upload.bundle.placeholder' | translate" [placeholder]="'item.bitstreams.upload.bundle.placeholder' | translate"
[action]="getCurrentUrl()" [action]="getCurrentUrl()"
[name]="'bundle-select'" [name]="'bundle-select'"
[debounceTime]="50" [debounceTime]="50"
[(ngModel)]="selectedBundleName" [(ngModel)]="selectedBundleName"
(typeSuggestion)="bundleNameChange()" (typeSuggestion)="bundleNameChange()"
(clickSuggestion)="onClick($event)" (clickSuggestion)="onClick($event)"
(click)="f.open()" (click)="f.open()"
ngDefaultControl> ngDefaultControl>
</ds-dso-input-suggestions> </ds-dso-input-suggestions>
<button *ngIf="!selectedBundleId && selectedBundleName?.length > 0" class="btn btn-success me-2" (click)="createBundle()"> @if (!selectedBundleId && selectedBundleName?.length > 0) {
<i class="fa fa-plus"></i> {{ 'item.bitstreams.upload.bundle.new' | translate }} <button class="btn btn-success me-2" (click)="createBundle()">
</button> <i class="fa fa-plus"></i> {{ 'item.bitstreams.upload.bundle.new' | translate }}
<ds-uploader class="w-100" *ngIf="selectedBundleId" </button>
[dropMsg]="'item.bitstreams.upload.drop-message'" }
[dropOverDocumentMsg]="'item.bitstreams.upload.drop-message'" @if (selectedBundleId) {
[enableDragOverDocument]="true" <ds-uploader class="w-100"
[uploadFilesOptions]="uploadFilesOptions" [dropMsg]="'item.bitstreams.upload.drop-message'"
(onCompleteItem)="onCompleteItem($event)" [dropOverDocumentMsg]="'item.bitstreams.upload.drop-message'"
(onUploadError)="onUploadError()"></ds-uploader> [enableDragOverDocument]="true"
[uploadFilesOptions]="uploadFilesOptions"
(onCompleteItem)="onCompleteItem($event)"
(onUploadError)="onUploadError()"></ds-uploader>
}
<button class="btn btn-outline-secondary" (click)="onCancel()">{{'item.bitstreams.upload.cancel' | translate}}</button> <button class="btn btn-outline-secondary" (click)="onCancel()">{{'item.bitstreams.upload.cancel' | translate}}</button>
</div> </div>
</div> </div>
</ng-container> }
</div> </div>

View File

@@ -1,38 +1,46 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<h1 class="border-bottom">{{'item.edit.head' | translate}}</h1> <h1 class="border-bottom">{{'item.edit.head' | translate}}</h1>
<div class="pt-2"> <div class="pt-2">
<ul *ngIf="pages.length > 0" class="nav nav-tabs justify-content-start" role="tablist"> @if (pages.length > 0) {
<li *ngFor="let page of pages" class="nav-item" role="presentation"> <ul class="nav nav-tabs justify-content-start" role="tablist">
<a *ngIf="(page.enabled | async)" @for (page of pages; track page) {
[attr.aria-selected]="page.page === currentPage" <li class="nav-item" role="presentation">
class="nav-link" @if ((page.enabled | async)) {
[ngClass]="{'active' : page.page === currentPage}" <a
[routerLink]="['./' + page.page]" [attr.aria-selected]="page.page === currentPage"
[attr.data-test]="page.page" class="nav-link"
role="tab"> [ngClass]="{'active' : page.page === currentPage}"
{{'item.edit.tabs.' + page.page + '.head' | translate}} [routerLink]="['./' + page.page]"
</a> [attr.data-test]="page.page"
<span [ngbTooltip]="'item.edit.tabs.disabled.tooltip' | translate"> role="tab">
<button *ngIf="(page.enabled | async) !== true" {{'item.edit.tabs.' + page.page + '.head' | translate}}
class="nav-link disabled"> </a>
{{'item.edit.tabs.' + page.page + '.head' | translate}} }
</button> <span [ngbTooltip]="'item.edit.tabs.disabled.tooltip' | translate">
</span> @if ((page.enabled | async) !== true) {
</li> <button
</ul> class="nav-link disabled">
<div class="tab-pane active"> {{'item.edit.tabs.' + page.page + '.head' | translate}}
<div class="mb-4"> </button>
<router-outlet></router-outlet> }
</div> </span>
<div class="button-row bottom"> </li>
<div class="text-end"> }
<a [routerLink]="getItemPage((itemRD$ | async)?.payload)" role="button" class="btn btn-outline-secondary"><i class="fas fa-arrow-left"></i> {{'item.edit.return' | translate}}</a> </ul>
</div> }
</div> <div class="tab-pane active">
</div> <div class="mb-4">
<router-outlet></router-outlet>
</div>
<div class="button-row bottom">
<div class="text-end">
<a [routerLink]="getItemPage((itemRD$ | async)?.payload)" role="button" class="btn btn-outline-secondary"><i class="fas fa-arrow-left"></i> {{'item.edit.return' | translate}}</a>
</div> </div>
</div>
</div> </div>
</div>
</div> </div>
</div>
</div> </div>

View File

@@ -5,73 +5,93 @@
<div class="button-row top d-flex mt-2 space-children-mr"> <div class="button-row top d-flex mt-2 space-children-mr">
<button class="me-auto btn btn-success" <button class="me-auto btn btn-success"
[attr.aria-label]="'item.edit.bitstreams.upload-button' | translate" [attr.aria-label]="'item.edit.bitstreams.upload-button' | translate"
[routerLink]="[itemPageRoute, 'bitstreams', 'new']"><i [routerLink]="[itemPageRoute, 'bitstreams', 'new']"><i
class="fas fa-upload"></i> class="fas fa-upload"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.upload-button" | translate}}</span> <span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.upload-button" | translate}}</span>
</button> </button>
<button class="btn btn-warning" *ngIf="isReinstatable$ | async" @if (isReinstatable$ | async) {
[attr.aria-label]="'item.edit.bitstreams.reinstate-button' | translate" <button class="btn btn-warning"
(click)="reinstate()"><i [attr.aria-label]="'item.edit.bitstreams.reinstate-button' | translate"
class="fas fa-undo-alt"></i> (click)="reinstate()"><i
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.reinstate-button" | translate}}</span> class="fas fa-undo-alt"></i>
</button> <span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.reinstate-button" | translate}}</span>
<button class="btn btn-primary" [dsBtnDisabled]="(hasChanges$ | async) !== true || submitting" </button>
[attr.aria-label]="'item.edit.bitstreams.save-button' | translate" }
(click)="submit()"><i <button class="btn btn-primary" [dsBtnDisabled]="(hasChanges$ | async) !== true || submitting"
class="fas fa-save"></i> [attr.aria-label]="'item.edit.bitstreams.save-button' | translate"
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.save-button" | translate}}</span> (click)="submit()"><i
</button> class="fas fa-save"></i>
<button class="btn btn-danger" *ngIf="(isReinstatable$ | async) !== true" <span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.save-button" | translate}}</span>
[attr.aria-label]="'item.edit.bitstreams.discard-button' | translate" </button>
[dsBtnDisabled]="(hasChanges$ | async) !== true || submitting" @if ((isReinstatable$ | async) !== true) {
(click)="discard()"><i <button class="btn btn-danger"
class="fas fa-times"></i> [attr.aria-label]="'item.edit.bitstreams.discard-button' | translate"
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.discard-button" | translate}}</span> [dsBtnDisabled]="(hasChanges$ | async) !== true || submitting"
</button> (click)="discard()"><i
</div> class="fas fa-times"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.discard-button" | translate}}</span>
<div *ngIf="item && bundles?.length > 0" class="mt-4 table-border scrollable-table" [ngClass]="{'disabled-overlay': (isProcessingMoveRequest | async)}"> </button>
<ds-item-edit-bitstream-bundle *ngFor="let bundle of bundles; first as isFirst" }
[bundle]="bundle"
[item]="item"
[columnSizes]="columnSizes"
[isFirstTable]="isFirst"
aria-describedby="reorder-description">
</ds-item-edit-bitstream-bundle>
<div class="d-flex justify-content-center" *ngIf="showLoadMoreLink$ | async">
<button class="btn btn-link my-3" (click)="loadBundles()"> {{'item.edit.bitstreams.load-more.link' | translate}}</button>
</div>
</div>
<div *ngIf="bundles?.length === 0"
class="alert alert-info w-100 d-inline-block mt-4" role="alert">
{{'item.edit.bitstreams.empty' | translate}}
</div>
<ds-loading *ngIf="!bundles" message="{{'loading.bitstreams' | translate}}"></ds-loading>
<div class="button-row bottom">
<div class="mt-4 float-end space-children-mr ms-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">&nbsp;{{"item.edit.bitstreams.reinstate-button" | translate}}</span>
</button>
<button class="btn btn-primary" [dsBtnDisabled]="(hasChanges$ | async) !== true || 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">&nbsp;{{"item.edit.bitstreams.save-button" | translate}}</span>
</button>
<button class="btn btn-danger" *ngIf="(isReinstatable$ | async) !== true"
[attr.aria-label]="'item.edit.bitstreams.discard-button' | translate"
[dsBtnDisabled]="(hasChanges$ | async) !== true || submitting"
(click)="discard()"><i
class="fas fa-times"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.discard-button" | translate}}</span>
</button>
</div>
</div>
</div> </div>
<ds-loading *ngIf="isProcessingMoveRequest | async" class="loading-overlay"></ds-loading> @if (item && bundles?.length > 0) {
<div class="mt-4 table-border scrollable-table" [ngClass]="{'disabled-overlay': (isProcessingMoveRequest | async)}">
@for (bundle of bundles; track bundle; let isFirst = $first) {
<ds-item-edit-bitstream-bundle
[bundle]="bundle"
[item]="item"
[columnSizes]="columnSizes"
[isFirstTable]="isFirst"
aria-describedby="reorder-description">
</ds-item-edit-bitstream-bundle>
}
@if (showLoadMoreLink$ | async) {
<div class="d-flex justify-content-center">
<button class="btn btn-link my-3" (click)="loadBundles()"> {{'item.edit.bitstreams.load-more.link' | translate}}</button>
</div>
}
</div>
}
@if (bundles?.length === 0) {
<div
class="alert alert-info w-100 d-inline-block mt-4" role="alert">
{{'item.edit.bitstreams.empty' | translate}}
</div>
}
@if (!bundles) {
<ds-loading message="{{'loading.bitstreams' | translate}}"></ds-loading>
}
<div class="button-row bottom">
<div class="mt-4 float-end space-children-mr ms-gap">
@if (isReinstatable$ | async) {
<button class="btn btn-warning"
[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">&nbsp;{{"item.edit.bitstreams.reinstate-button" | translate}}</span>
</button>
}
<button class="btn btn-primary" [dsBtnDisabled]="(hasChanges$ | async) !== true || 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">&nbsp;{{"item.edit.bitstreams.save-button" | translate}}</span>
</button>
@if ((isReinstatable$ | async) !== true) {
<button class="btn btn-danger"
[attr.aria-label]="'item.edit.bitstreams.discard-button' | translate"
[dsBtnDisabled]="(hasChanges$ | async) !== true || submitting"
(click)="discard()"><i
class="fas fa-times"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.discard-button" | translate}}</span>
</button>
}
</div>
</div>
</div>
@if (isProcessingMoveRequest | async) {
<ds-loading class="loading-overlay"></ds-loading>
}

View File

@@ -1,140 +1,137 @@
<ng-template #bundleView> <ng-template #bundleView>
<ds-pagination *ngIf="(bitstreamsRD$ | async)?.payload as bitstreamsList" @if ((bitstreamsRD$ | async)?.payload; as bitstreamsList) {
[hideGear]="true" <ds-pagination
[hidePagerWhenSinglePage]="true" [hideGear]="true"
[hidePaginationDetail]="true" [hidePagerWhenSinglePage]="true"
[paginationOptions]="paginationOptions" [hidePaginationDetail]="true"
[collectionSize]="bitstreamsList.totalElements" [paginationOptions]="paginationOptions"
[retainScrollPosition]="true" [collectionSize]="bitstreamsList.totalElements"
[ngbTooltip]="'item.edit.bitstreams.bundle.tooltip' | translate" placement="bottom" [retainScrollPosition]="true"
[autoClose]="false" triggers="manual" #dragTooltip="ngbTooltip"> [ngbTooltip]="'item.edit.bitstreams.bundle.tooltip' | translate" placement="bottom"
<ng-container *ngIf="(updates$ | async) as updates"> [autoClose]="false" triggers="manual" #dragTooltip="ngbTooltip">
@if ((updates$ | async); as updates) {
<table class="table" [class.mt-n1]="!isFirstTable" <table class="table" [class.mt-n1]="!isFirstTable"
[attr.aria-label]="'item.edit.bitstreams.bundle.table.aria-label' | translate: { bundle: bundleName } "> [attr.aria-label]="'item.edit.bitstreams.bundle.table.aria-label' | translate: { bundle: bundleName } ">
<thead [class.visually-hidden]="!isFirstTable"> <thead [class.visually-hidden]="!isFirstTable">
<tr class="header-row font-weight-bold"> <tr class="header-row font-weight-bold">
<th id="name" scope="col" class="{{ columnSizes.columns[0].buildClasses() }}"> <th id="name" scope="col" class="{{ columnSizes.columns[0].buildClasses() }}">
{{'item.edit.bitstreams.headers.name' | translate}} {{'item.edit.bitstreams.headers.name' | translate}}
</th> </th>
<th id="description" scope="col" class="{{ columnSizes.columns[1].buildClasses() }}"> <th id="description" scope="col" class="{{ columnSizes.columns[1].buildClasses() }}">
{{'item.edit.bitstreams.headers.description' | translate}} {{'item.edit.bitstreams.headers.description' | translate}}
</th> </th>
<th id="format" scope="col" class="{{ columnSizes.columns[2].buildClasses() }}"> <th id="format" scope="col" class="{{ columnSizes.columns[2].buildClasses() }}">
{{'item.edit.bitstreams.headers.format' | translate}} {{'item.edit.bitstreams.headers.format' | translate}}
</th> </th>
<th id="actions" scope="col" class="{{ columnSizes.columns[3].buildClasses() }} text-center"> <th id="actions" scope="col" class="{{ columnSizes.columns[3].buildClasses() }} text-center">
{{'item.edit.bitstreams.headers.actions' | translate}} {{'item.edit.bitstreams.headers.actions' | translate}}
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody cdkDropList (cdkDropListDropped)="drop($event)">
<tbody cdkDropList (cdkDropListDropped)="drop($event)"> <tr class="bundle-row">
<tr class="bundle-row"> <th id="{{ bundleName }}" class="span" colspan="3" scope="colgroup">
<th id="{{ bundleName }}" class="span" colspan="3" scope="colgroup"> {{'item.edit.bitstreams.bundle.name' | translate:{ name: bundleName } }}
{{'item.edit.bitstreams.bundle.name' | translate:{ name: bundleName } }} </th>
</th> <td class="text-center">
<td class="text-center"> <div class="btn-group">
<button [routerLink]="[itemPageRoute, 'bitstreams', 'new']"
<div class="btn-group"> [queryParams]="{bundle: bundle.id}"
<button [routerLink]="[itemPageRoute, 'bitstreams', 'new']" [attr.aria-label]="'item.edit.bitstreams.bundle.edit.buttons.upload' | translate"
[queryParams]="{bundle: bundle.id}" class="btn btn-outline-success btn-sm"
[attr.aria-label]="'item.edit.bitstreams.bundle.edit.buttons.upload' | translate" title="{{'item.edit.bitstreams.bundle.edit.buttons.upload' | translate}}">
class="btn btn-outline-success btn-sm" <i class="fas fa-upload fa-fw"></i>
title="{{'item.edit.bitstreams.bundle.edit.buttons.upload' | translate}}"> </button>
<i class="fas fa-upload fa-fw"></i> <div ngbDropdown #paginationControls="ngbDropdown" class="btn-group float-right btn-sm p-0"
</button> placement="bottom-right">
<div ngbDropdown #paginationControls="ngbDropdown" class="btn-group float-right btn-sm p-0" <button class="btn btn-outline-secondary" id="paginationControls" ngbDropdownToggle
placement="bottom-right"> [title]="'pagination.options.description' | translate"
<button class="btn btn-outline-secondary" id="paginationControls" ngbDropdownToggle [attr.aria-label]="'pagination.options.description' | translate" aria-haspopup="true"
[title]="'pagination.options.description' | translate" aria-expanded="false">
[attr.aria-label]="'pagination.options.description' | translate" aria-haspopup="true" <i class="fas fa-cog" aria-hidden="true"></i>
aria-expanded="false"> </button>
<i class="fas fa-cog" aria-hidden="true"></i> <ul id="paginationControlsDropdownMenu" aria-labelledby="paginationControls" role="menu"
</button> ngbDropdownMenu>
<li role="menuitem">
<ul id="paginationControlsDropdownMenu" aria-labelledby="paginationControls" role="menu" <span class="dropdown-header" id="pagination-control_results-per-page"
ngbDropdownMenu> role="heading">{{ 'pagination.results-per-page' | translate}}</span>
<li role="menuitem"> <ul aria-labelledby="pagination-control_results-per-page" class="list-unstyled" role="listbox">
<span class="dropdown-header" id="pagination-control_results-per-page" @for (size of paginationOptions.pageSizeOptions; track size) {
role="heading">{{ 'pagination.results-per-page' | translate}}</span> <li role="option"
<ul aria-labelledby="pagination-control_results-per-page" class="list-unstyled" role="listbox"> [attr.aria-selected]="size === (pageSize$ | async)">
<li *ngFor="let size of paginationOptions.pageSizeOptions" role="option" <button (click)="doPageSizeChange(size)" class="dropdown-item">
[attr.aria-selected]="size === (pageSize$ | async)"> <i [ngClass]="{'invisible': size !== (pageSize$ | async) }" class="fas fa-check"
<button (click)="doPageSizeChange(size)" class="dropdown-item"> aria-hidden="true"></i> {{size}}
<i [ngClass]="{'invisible': size !== (pageSize$ | async) }" class="fas fa-check" </button>
aria-hidden="true"></i> {{size}} </li>
</button> }
</ul>
</li> </li>
</ul> </ul>
</li> </div>
</ul> </div>
</div> </td>
</div> </tr>
@for (entry of (tableEntries$ | async); track entry) {
</td> @if (updates[entry.id]; as update) {
</tr> <tr [ngClass]="getRowClass(update, entry)" class="bitstream-row" cdkDrag
(cdkDragStarted)="dragStart()" (cdkDragEnded)="dragEnd()">
<ng-container *ngFor="let entry of (tableEntries$ | async)"> <th class="bitstream-name row-element {{ columnSizes.columns[0].buildClasses() }}"
<tr *ngIf="updates[entry.id] as update" [ngClass]="getRowClass(update, entry)" class="bitstream-row" cdkDrag scope="row" id="{{ entry.nameStripped }}" headers="{{ bundleName }} name">
(cdkDragStarted)="dragStart()" (cdkDragEnded)="dragEnd()"> <div class="drag-handle text-muted float-left p-1 mr-2 d-inline" tabindex="0" cdkDragHandle
(keydown.enter)="select($event, entry)" (keydown.space)="select($event, entry)" (click)="select($event, entry)">
<th class="bitstream-name row-element {{ columnSizes.columns[0].buildClasses() }}" <i class="fas fa-grip-vertical fa-fw"
scope="row" id="{{ entry.nameStripped }}" headers="{{ bundleName }} name"> [title]="'item.edit.bitstreams.edit.buttons.drag' | translate"></i>
<div class="drag-handle text-muted float-left p-1 mr-2 d-inline" tabindex="0" cdkDragHandle </div>
(keydown.enter)="select($event, entry)" (keydown.space)="select($event, entry)" (click)="select($event, entry)"> {{ entry.name }}
<i class="fas fa-grip-vertical fa-fw" </th>
[title]="'item.edit.bitstreams.edit.buttons.drag' | translate"></i> <td class="row-element {{ columnSizes.columns[1].buildClasses() }}"
</div> headers="{{ entry.nameStripped }} {{ bundleName }} description">
{{ entry.name }} {{ entry.description }}
</th> </td>
<td class="row-element {{ columnSizes.columns[1].buildClasses() }}" <td class="row-element {{ columnSizes.columns[2].buildClasses() }}"
headers="{{ entry.nameStripped }} {{ bundleName }} description"> headers="{{ entry.nameStripped }} {{ bundleName }} format">
{{ entry.description }} {{ (entry.format | async)?.shortDescription }}
</td> </td>
<td class="row-element {{ columnSizes.columns[2].buildClasses() }}" <td class="row-element {{ columnSizes.columns[3].buildClasses() }}"
headers="{{ entry.nameStripped }} {{ bundleName }} format"> headers="{{ entry.nameStripped }} {{ bundleName }} actions">
{{ (entry.format | async)?.shortDescription }} <div class="text-center w-100">
</td> <div class="btn-group relationship-action-buttons">
<td class="row-element {{ columnSizes.columns[3].buildClasses() }}" <a [href]="entry.downloadUrl"
headers="{{ entry.nameStripped }} {{ bundleName }} actions"> [attr.aria-label]="'item.edit.bitstreams.edit.buttons.download' | translate"
<div class="text-center w-100"> class="btn btn-outline-primary btn-sm"
<div class="btn-group relationship-action-buttons"> title="{{'item.edit.bitstreams.edit.buttons.download' | translate}}"
<a [href]="entry.downloadUrl" [attr.data-test]="'download-button' | dsBrowserOnly">
[attr.aria-label]="'item.edit.bitstreams.edit.buttons.download' | translate" <i class="fas fa-download fa-fw"></i>
class="btn btn-outline-primary btn-sm" </a>
title="{{'item.edit.bitstreams.edit.buttons.download' | translate}}" <button [routerLink]="['/bitstreams/', entry.id, 'edit']" class="btn btn-outline-primary btn-sm"
[attr.data-test]="'download-button' | dsBrowserOnly">
<i class="fas fa-download fa-fw"></i>
</a>
<button [routerLink]="['/bitstreams/', entry.id, 'edit']" class="btn btn-outline-primary btn-sm"
[attr.aria-label]="'item.edit.bitstreams.edit.buttons.edit' | translate" [attr.aria-label]="'item.edit.bitstreams.edit.buttons.edit' | translate"
title="{{'item.edit.bitstreams.edit.buttons.edit' | translate}}"> title="{{'item.edit.bitstreams.edit.buttons.edit' | translate}}">
<i class="fas fa-edit fa-fw"></i> <i class="fas fa-edit fa-fw"></i>
</button> </button>
<button [dsBtnDisabled]="!canRemove(update)" (click)="remove(entry.bitstream)" <button [dsBtnDisabled]="!canRemove(update)" (click)="remove(entry.bitstream)"
[attr.aria-label]=" 'item. edit bitstreams.edit.buttons.remove' | translate" [attr.aria-label]=" 'item. edit bitstreams.edit.buttons.remove' | translate"
class="btn btn-outline-danger btn-sm" class="btn btn-outline-danger btn-sm"
title="{{'item.edit.bitstreams.edit.buttons.remove' | translate}}"> title="{{'item.edit.bitstreams.edit.buttons.remove' | translate}}">
<i class="fas fa-trash-alt fa-fw"></i> <i class="fas fa-trash-alt fa-fw"></i>
</button> </button>
<button [dsBtnDisabled]="!canUndo(update)" (click)="undo(entry.bitstream)" <button [dsBtnDisabled]="!canUndo(update)" (click)="undo(entry.bitstream)"
[attr.aria-label]="'item.edit.bitstreams.edit.buttons.undo' | translate" [attr.aria-label]="'item.edit.bitstreams.edit.buttons.undo' | translate"
class="btn btn-outline-warning btn-sm" class="btn btn-outline-warning btn-sm"
title="{{'item.edit.bitstreams.edit.buttons.undo' | translate}}"> title="{{'item.edit.bitstreams.edit.buttons.undo' | translate}}">
<i class="fas fa-undo-alt fa-fw"></i> <i class="fas fa-undo-alt fa-fw"></i>
</button> </button>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
</ng-container> }
}
</tbody> </tbody>
</table> </table>
}
</ng-container> </ds-pagination>
</ds-pagination> }
</ng-template> </ng-template>

View File

@@ -1,99 +1,99 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<h1>{{headerMessage | translate: {id: item.handle} }}</h1> <h1>{{headerMessage | translate: {id: item.handle} }}</h1>
<p>{{descriptionMessage | translate}}</p> <p>{{descriptionMessage | translate}}</p>
<ds-modify-item-overview [item]="item"></ds-modify-item-overview> <ds-modify-item-overview [item]="item"></ds-modify-item-overview>
<ng-container *ngVar="(typeDTOs$ | async) as types"> <ng-container>
<div *ngIf="types && types.length > 0" class="mb-4"> @if ((typeDTOs$ | async) && (typeDTOs$ | async).length > 0) {
<div class="mb-4">
{{'virtual-metadata.delete-item.info' | translate}} {{'virtual-metadata.delete-item.info' | translate}}
@for (typeDto of (typeDTOs$ | async); track typeDto) {
<div *ngFor="let typeDto of types" class="mb-4"> <div class="mb-4">
<div *ngVar="(typeDto.isSelected$ | async) as selected" <div *ngVar="(typeDto.isSelected$ | async) as selected"
class="d-flex flex-row"> class="d-flex flex-row">
<div class="m-2" (click)="setSelected(typeDto.relationshipType, !selected)">
<div class="m-2" (click)="setSelected(typeDto.relationshipType, !selected)"> <label>
<label> <input type="checkbox" [checked]="selected" [disabled]="isDeleting$ | async">
<input type="checkbox" [checked]="selected" [disabled]="isDeleting$ | async"> </label>
</label> </div>
<div class="flex-column flex-grow-1">
<h5 (click)="setSelected(typeDto.relationshipType, !selected)">
{{getRelationshipMessageKey(typeDto.label$ | async) | translate}}
</h5>
@for (relationshipDto of (typeDto.relationshipDTOs$ | async); track relationshipDto.relationship.id) {
<div
class="d-flex flex-row">
<ng-container *ngVar="(relationshipDto.relatedItem$ | async) as relatedItem">
@if (relatedItem) {
<ds-listable-object-component-loader
[object]="relatedItem"
[viewMode]="viewMode">
</ds-listable-object-component-loader>
}
<div class="ms-auto">
<div class="btn-group">
<button class="btn btn-outline-info btn-sm"
(click)="openVirtualMetadataModal(virtualMetadataModal)">
<i class="fas fa-info fa-fw"></i>
</button>
</div> </div>
</div>
<div class="flex-column flex-grow-1"> <ng-template #virtualMetadataModal>
<h5 (click)="setSelected(typeDto.relationshipType, !selected)"> <div class="thumb-font-1">
{{getRelationshipMessageKey(typeDto.label$ | async) | translate}} <div class="modal-header">
</h5> {{'virtual-metadata.delete-item.modal-head' | translate}}
<div *ngFor="let relationshipDto of (typeDto.relationshipDTOs$ | async)" <button type="button" class="btn-close"
class="d-flex flex-row"> (click)="closeVirtualMetadataModal()" aria-label="Close">
<ng-container *ngVar="(relationshipDto.relatedItem$ | async) as relatedItem"> </button>
</div>
<ds-listable-object-component-loader <div class="modal-body">
*ngIf="relatedItem" @if (relatedItem) {
[object]="relatedItem" <ds-listable-object-component-loader
[viewMode]="viewMode"> [object]="relatedItem"
</ds-listable-object-component-loader> [viewMode]="viewMode">
<div class="ms-auto"> </ds-listable-object-component-loader>
<div class="btn-group"> }
<button class="btn btn-outline-info btn-sm" @for (metadata of (relationshipDto.virtualMetadata$ | async); track metadata) {
(click)="openVirtualMetadataModal(virtualMetadataModal)"> <div>
<i class="fas fa-info fa-fw"></i> <div>
</button> <div class="fw-bold">
</div> {{metadata.metadataField}}
</div> </div>
<div>
<ng-template #virtualMetadataModal> {{metadata.metadataValue.value}}
<div class="thumb-font-1"> </div>
<div class="modal-header"> </div>
{{'virtual-metadata.delete-item.modal-head' | translate}} </div>
<button type="button" class="btn-close" }
(click)="closeVirtualMetadataModal()" aria-label="Close"> </div>
</button>
</div>
<div class="modal-body">
<ds-listable-object-component-loader
*ngIf="relatedItem"
[object]="relatedItem"
[viewMode]="viewMode">
</ds-listable-object-component-loader>
<div *ngFor="let metadata of (relationshipDto.virtualMetadata$ | async)">
<div>
<div class="fw-bold">
{{metadata.metadataField}}
</div>
<div>
{{metadata.metadataValue.value}}
</div>
</div>
</div>
</div>
</div>
</ng-template>
</ng-container>
</div>
</div> </div>
</ng-template>
</div> </ng-container>
</div>
</div> }
</div>
</div> </div>
</div>
}
</div>
}
</ng-container> </ng-container>
<div class="space-children-mr"> <div class="space-children-mr">
<button [dsBtnDisabled]="isDeleting$ | async" (click)="performAction()" <button [dsBtnDisabled]="isDeleting$ | async" (click)="performAction()"
class="btn btn-outline-secondary perform-action">{{confirmMessage | translate}} class="btn btn-outline-secondary perform-action">{{confirmMessage | translate}}
</button> </button>
<button [dsBtnDisabled]="isDeleting$ | async" [routerLink]="[itemPageRoute, 'edit']" <button [dsBtnDisabled]="isDeleting$ | async" [routerLink]="[itemPageRoute, 'edit']"
class="btn btn-outline-secondary cancel"> class="btn btn-outline-secondary cancel">
{{cancelMessage| translate}} {{cancelMessage| translate}}
</button> </button>
</div> </div>
</div>
</div> </div>
</div>
</div> </div>

View File

@@ -1,58 +1,62 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-12">
<h1>{{'item.edit.move.head' | translate: {id: (itemRD$ | async)?.payload?.handle} }}</h1>
<p>{{'item.edit.move.description' | translate}}</p>
<div class="row">
<div class="col-12"> <div class="col-12">
<h1>{{'item.edit.move.head' | translate: {id: (itemRD$ | async)?.payload?.handle} }}</h1> <div class="card mb-3">
<p>{{'item.edit.move.description' | translate}}</p> <div class="card-header">{{'dso-selector.placeholder' | translate: { type: 'dso-selector.placeholder.type.collection' | translate } }}</div>
<div class="row"> <div class="card-body">
<div class="col-12"> <ds-authorized-collection-selector [types]="COLLECTIONS"
<div class="card mb-3"> [currentDSOId]="selectedCollection ? selectedCollection.id : originalCollection.id"
<div class="card-header">{{'dso-selector.placeholder' | translate: { type: 'dso-selector.placeholder.type.collection' | translate } }}</div> (onSelect)="selectDso($event)">
<div class="card-body"> </ds-authorized-collection-selector>
<ds-authorized-collection-selector [types]="COLLECTIONS"
[currentDSOId]="selectedCollection ? selectedCollection.id : originalCollection.id"
(onSelect)="selectDso($event)">
</ds-authorized-collection-selector>
</div>
<div></div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<p>
<label class="form-label" for="inheritPoliciesCheckbox">
<ng-template #tooltipContent>
{{ 'item.edit.move.inheritpolicies.tooltip' | translate }}
</ng-template>
<input type="checkbox" name="tc" [(ngModel)]="inheritPolicies" id="inheritPoliciesCheckbox" [ngbTooltip]="tooltipContent"
>
{{'item.edit.move.inheritpolicies.checkbox' |translate}}
</label>
</p>
<p>
{{'item.edit.move.inheritpolicies.description' | translate}}
</p>
</div>
</div>
<div class="button-row bottom">
<div class="float-end space-children-mr">
<button [routerLink]="[(itemPageRoute$ | async), 'edit']" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left"></i> {{'item.edit.move.cancel' | translate}}
</button>
<button class="btn btn-primary" [dsBtnDisabled]="!canMove" (click)="moveToCollection()">
<span *ngIf="!processing">
<i class="fas fa-save"></i> {{'item.edit.move.save-button' | translate}}
</span>
<span *ngIf="processing">
<i class="fas fa-circle-notch fa-spin"></i> {{'item.edit.move.processing' | translate}}
</span>
</button>
<button class="btn btn-danger" [dsBtnDisabled]="!canSubmit" (click)="discard()">
<i class="fas fa-times"></i> {{"item.edit.move.discard-button" | translate}}
</button>
</div> </div>
<div></div>
</div> </div>
</div> </div>
</div>
<div class="row">
<div class="col-12">
<p>
<label class="form-label" for="inheritPoliciesCheckbox">
<ng-template #tooltipContent>
{{ 'item.edit.move.inheritpolicies.tooltip' | translate }}
</ng-template>
<input type="checkbox" name="tc" [(ngModel)]="inheritPolicies" id="inheritPoliciesCheckbox" [ngbTooltip]="tooltipContent"
>
{{'item.edit.move.inheritpolicies.checkbox' |translate}}
</label>
</p>
<p>
{{'item.edit.move.inheritpolicies.description' | translate}}
</p>
</div>
</div>
<div class="button-row bottom">
<div class="float-end space-children-mr">
<button [routerLink]="[(itemPageRoute$ | async), 'edit']" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left"></i> {{'item.edit.move.cancel' | translate}}
</button>
<button class="btn btn-primary" [dsBtnDisabled]="!canMove" (click)="moveToCollection()">
@if (!processing) {
<span>
<i class="fas fa-save"></i> {{'item.edit.move.save-button' | translate}}
</span>
}
@if (processing) {
<span>
<i class="fas fa-circle-notch fa-spin"></i> {{'item.edit.move.processing' | translate}}
</span>
}
</button>
<button class="btn btn-danger" [dsBtnDisabled]="!canSubmit" (click)="discard()">
<i class="fas fa-times"></i> {{"item.edit.move.discard-button" | translate}}
</button>
</div>
</div>
</div> </div>
</div>
</div> </div>

View File

@@ -1,17 +1,21 @@
<div class="col-12 col-md-3 h-auto float-start d-flex action-label"> <div class="col-12 col-md-3 h-auto float-start d-flex action-label">
<span class="justify-content-center align-self-center font-weight-bold"> <span class="justify-content-center align-self-center font-weight-bold">
{{'item.edit.tabs.status.buttons.' + operation.operationKey + '.label' | translate}} {{'item.edit.tabs.status.buttons.' + operation.operationKey + '.label' | translate}}
</span> </span>
</div> </div>
<div class="col-12 col-md-9 float-start action-button"> <div class="col-12 col-md-9 float-start action-button">
<span *ngIf="operation.authorized"> @if (operation.authorized) {
<button class="btn btn-outline-primary" [dsBtnDisabled]="operation.disabled" [routerLink]="operation.operationUrl" [attr.aria-label]="'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate"> <span>
{{'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate}} <button class="btn btn-outline-primary" [dsBtnDisabled]="operation.disabled" [routerLink]="operation.operationUrl" [attr.aria-label]="'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate">
</button> {{'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate}}
</span> </button>
<span *ngIf="!operation.authorized" [ngbTooltip]="'item.edit.tabs.status.buttons.unauthorized' | translate"> </span>
<button class="btn btn-outline-primary" [dsBtnDisabled]="true" [attr.aria-label]="'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate"> }
{{'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate}} @if (!operation.authorized) {
</button> <span [ngbTooltip]="'item.edit.tabs.status.buttons.unauthorized' | translate">
</span> <button class="btn btn-outline-primary" [dsBtnDisabled]="true" [attr.aria-label]="'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate">
{{'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate}}
</button>
</span>
}
</div> </div>

View File

@@ -3,13 +3,17 @@
<div class="col-12"> <div class="col-12">
<h2>{{headerMessage | translate: {id: item.handle} }}</h2> <h2>{{headerMessage | translate: {id: item.handle} }}</h2>
<p>{{descriptionMessage | translate}}</p> <p>{{descriptionMessage | translate}}</p>
<div *ngFor="let identifier of (identifiers$ | async)" class="w-100 p"> @for (identifier of (identifiers$ | async); track identifier) {
<div *ngIf="(identifier.identifierType === 'doi')"> <div class="w-100 p">
<p class="float-start">{{doiToUpdateMessage | translate}}: {{identifier.value}} @if ((identifier.identifierType === 'doi')) {
({{"item.edit.identifiers.doi.status."+identifier.identifierStatus|translate}}) <div>
</p> <p class="float-start">{{doiToUpdateMessage | translate}}: {{identifier.value}}
({{"item.edit.identifiers.doi.status."+identifier.identifierStatus|translate}})
</p>
</div>
}
</div> </div>
</div> }
<ds-modify-item-overview [item]="item"></ds-modify-item-overview> <ds-modify-item-overview [item]="item"></ds-modify-item-overview>
<div class="space-children-mr"> <div class="space-children-mr">
<button (click)="performAction()" class="btn btn-outline-secondary perform-action">{{confirmMessage | translate}} <button (click)="performAction()" class="btn btn-outline-secondary perform-action">{{confirmMessage | translate}}

View File

@@ -1,34 +1,40 @@
<h2 class="h4"> <h2 class="h4">
{{relationshipMessageKey$ | async | translate}} {{relationshipMessageKey$ | async | translate}}
<button class="ms-2 btn btn-success" [dsBtnDisabled]="(hasChanges | async)" (click)="openLookup()"> <button class="ms-2 btn btn-success" [dsBtnDisabled]="(hasChanges | async)" (click)="openLookup()">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.relationships.edit.buttons.add" | translate}}</span> <span class="d-none d-sm-inline">&nbsp;{{"item.edit.relationships.edit.buttons.add" | translate}}</span>
</button> </button>
</h2> </h2>
<ng-container *ngVar="updates$ | async as updates"> <ng-container *ngVar="updates$ | async as updates">
<ng-container *ngIf="updates && (loading$ | async) !== true"> @if (updates && (loading$ | async) !== true) {
<ng-container *ngVar="updates | dsObjectValues as updateValues"> <ng-container *ngVar="updates | dsObjectValues as updateValues">
<ds-pagination <ds-pagination
[paginationOptions]="paginationConfig" [paginationOptions]="paginationConfig"
[collectionSize]="(relationshipsRd$ | async)?.payload?.totalElements + (this.nbAddedFields$ | async)" [collectionSize]="(relationshipsRd$ | async)?.payload?.totalElements + (this.nbAddedFields$ | async)"
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">
<div class="my-2"> <div class="my-2">
<ds-edit-relationship *ngFor="let updateValue of updateValues; trackBy: trackUpdate" @for (updateValue of updateValues; track trackUpdate($index, updateValue)) {
class="relationship-row d-block alert" <ds-edit-relationship
[fieldUpdate]="updateValue || {}" class="relationship-row d-block alert"
[url]="url" [fieldUpdate]="updateValue || {}"
[editItem]="item" [url]="url"
[editItem]="item"
[ngClass]="{ [ngClass]="{
'alert-success': updateValue.changeType === 1, 'alert-success': updateValue.changeType === 1,
'alert-warning': updateValue.changeType === 0, 'alert-warning': updateValue.changeType === 0,
'alert-danger': updateValue.changeType === 2 'alert-danger': updateValue.changeType === 2
}"> }">
</ds-edit-relationship> </ds-edit-relationship>
</div> }
</ds-pagination> </div>
<div *ngIf="updateValues.length === 0">{{"item.edit.relationships.no-relationships" | translate}}</div> </ds-pagination>
</ng-container> @if (updateValues.length === 0) {
<div>{{"item.edit.relationships.no-relationships" | translate}}</div>
}
</ng-container> </ng-container>
<ds-loading *ngIf="loading$ | async"></ds-loading> }
@if (loading$ | async) {
<ds-loading></ds-loading>
}
</ng-container> </ng-container>

View File

@@ -1,35 +1,37 @@
<div class="row" *ngIf="relatedItem$ | async"> @if (relatedItem$ | async) {
<div class="col-10 relationship"> <div class="row">
<ds-listable-object-component-loader <div class="col-10 relationship">
[object]="relatedItem$ | async" <ds-listable-object-component-loader
[viewMode]="viewMode" [object]="relatedItem$ | async"
[value]="nameVariant" [viewMode]="viewMode"
> [value]="nameVariant"
</ds-listable-object-component-loader> >
</div> </ds-listable-object-component-loader>
<div class="col-2 position-relative"> </div>
<div class="btn-group relationship-action-buttons"> <div class="col-2 position-relative">
<button [dsBtnDisabled]="!canRemove()" (click)="openVirtualMetadataModal(virtualMetadataModal)" <div class="btn-group relationship-action-buttons">
class="btn btn-outline-danger btn-sm" <button [dsBtnDisabled]="!canRemove()" (click)="openVirtualMetadataModal(virtualMetadataModal)"
title="{{'item.edit.metadata.edit.buttons.remove' | translate}}"> class="btn btn-outline-danger btn-sm"
<i class="fas fa-trash-alt fa-fw"></i> title="{{'item.edit.metadata.edit.buttons.remove' | translate}}">
</button> <i class="fas fa-trash-alt fa-fw"></i>
<button [dsBtnDisabled]="!canUndo()" (click)="undo()" </button>
class="btn btn-outline-warning btn-sm" <button [dsBtnDisabled]="!canUndo()" (click)="undo()"
title="{{'item.edit.metadata.edit.buttons.undo' | translate}}"> class="btn btn-outline-warning btn-sm"
<i class="fas fa-undo-alt fa-fw"></i> title="{{'item.edit.metadata.edit.buttons.undo' | translate}}">
</button> <i class="fas fa-undo-alt fa-fw"></i>
</button>
</div>
</div> </div>
</div> </div>
</div> }
<ng-template #virtualMetadataModal> <ng-template #virtualMetadataModal>
<ds-virtual-metadata <ds-virtual-metadata
[relationshipId]="relationship.id" [relationshipId]="relationship.id"
[leftItem]="leftItem$ | async" [leftItem]="leftItem$ | async"
[rightItem]="rightItem$ | async" [rightItem]="rightItem$ | async"
[url]="url" [url]="url"
(close)="closeVirtualMetadataModal()" (close)="closeVirtualMetadataModal()"
(save)="remove()" (save)="remove()"
> >
</ds-virtual-metadata> </ds-virtual-metadata>
</ng-template> </ng-template>

View File

@@ -1,23 +1,29 @@
<p class="mt-2">{{'item.edit.tabs.status.description' | translate}}</p> <p class="mt-2">{{'item.edit.tabs.status.description' | translate}}</p>
<div class="row"> <div class="row">
<div *ngFor="let statusKey of statusDataKeys" class="w-100 pt-1"> @for (statusKey of statusDataKeys; track statusKey) {
<div class="col-12 col-md-3 float-start status-label font-weight-bold"> <div class="w-100 pt-1">
{{'item.edit.tabs.status.labels.' + statusKey | translate}}:
</div>
<div class="col-12 col-md-9 float-start status-data" id="status-{{statusKey}}">
{{statusData[statusKey]}}
</div>
</div>
<div *ngFor="let identifier of (identifiers$ | async)" class="w-100 pt-1">
<div *ngIf="(identifier.identifierType==='doi')">
<div class="col-12 col-md-3 float-start status-label font-weight-bold"> <div class="col-12 col-md-3 float-start status-label font-weight-bold">
{{identifier.identifierType.toLocaleUpperCase()}} {{'item.edit.tabs.status.labels.' + statusKey | translate}}:
</div>
<div class="col-12 col-md-9 float-start status-data" id="status-{{statusKey}}">
{{statusData[statusKey]}}
</div> </div>
<div class="col-12 col-md-9 float-start status-label font-weight-bold">{{identifier.value}}
({{"item.edit.identifiers.doi.status."+identifier.identifierStatus|translate}})</div>
</div> </div>
</div> }
@for (identifier of (identifiers$ | async); track identifier) {
<div class="w-100 pt-1">
@if ((identifier.identifierType==='doi')) {
<div>
<div class="col-12 col-md-3 float-start status-label font-weight-bold">
{{identifier.identifierType.toLocaleUpperCase()}}
</div>
<div class="col-12 col-md-9 float-start status-label font-weight-bold">{{identifier.value}}
({{"item.edit.identifiers.doi.status."+identifier.identifierStatus|translate}})</div>
</div>
}
</div>
}
<div class="col-12 col-md-3 float-start status-label font-weight-bold"> <div class="col-12 col-md-3 float-start status-label font-weight-bold">
{{'item.edit.tabs.status.labels.itemPage' | translate}}: {{'item.edit.tabs.status.labels.itemPage' | translate}}:
@@ -26,8 +32,10 @@
<a [routerLink]="itemPageRoute$ | async">{{itemPageRoute$ | async}}</a> <a [routerLink]="itemPageRoute$ | async">{{itemPageRoute$ | async}}</a>
</div> </div>
<div *ngFor="let operation of (operations$ | async)" class="w-100" [ngClass]="{'pt-3': operation}"> @for (operation of (operations$ | async); track operation) {
<ds-item-operation [operation]="operation"></ds-item-operation> <div class="w-100" [ngClass]="{'pt-3': operation}">
</div> <ds-item-operation [operation]="operation"></ds-item-operation>
</div>
}
</div> </div>

View File

@@ -1,37 +1,39 @@
<div [ngClass]="showThumbnails ? 'hide-modal-thumbnail-column' : ''"> <div [ngClass]="showThumbnails ? 'hide-modal-thumbnail-column' : ''">
<div class="modal-header">{{'virtual-metadata.delete-relationship.modal-head' | translate}} <div class="modal-header">{{'virtual-metadata.delete-relationship.modal-head' | translate}}
<button type="button" class="btn-close" (click)="close.emit()" aria-label="Close"> <button type="button" class="btn-close" (click)="close.emit()" aria-label="Close">
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<ng-container *ngFor="let itemDTO of itemDTOs$ | async; trackBy: trackItemDTO"> @for (itemDTO of itemDTOs$ | async; track trackItemDTO($index, itemDTO)) {
<div *ngVar="(itemDTO.isSelectedVirtualMetadataItem$ | async) as selected" <div *ngVar="(itemDTO.isSelectedVirtualMetadataItem$ | async) as selected"
(click)="setSelectedVirtualMetadataItem(itemDTO.item, !selected)" (click)="setSelectedVirtualMetadataItem(itemDTO.item, !selected)"
class="item d-flex flex-row"> class="item d-flex flex-row">
<div class="m-2"> <div class="m-2">
<label> <label>
<input class="select" type="checkbox" [checked]="selected"> <input class="select" type="checkbox" [checked]="selected">
</label> </label>
</div>
<div class="flex-column">
<ds-listable-object-component-loader [object]="itemDTO.item">
</ds-listable-object-component-loader>
<div *ngFor="let metadata of virtualMetadata.get(itemDTO.item.uuid)">
<div class="fw-bold">
{{metadata.metadataField}}
</div>
<div>
{{metadata.metadataValue.value}}
</div>
</div>
</div>
</div>
</ng-container>
<div class="d-flex flex-row-reverse m-2">
<button class="btn btn-primary save"
(click)="save.emit()">
<i aria-hidden="true" class="fas fa-save"></i> {{ 'item.edit.metadata.save-button' | translate }}
</button>
</div> </div>
<div class="flex-column">
<ds-listable-object-component-loader [object]="itemDTO.item">
</ds-listable-object-component-loader>
@for (metadata of virtualMetadata.get(itemDTO.item.uuid); track metadata) {
<div>
<div class="fw-bold">
{{metadata.metadataField}}
</div>
<div>
{{metadata.metadataValue.value}}
</div>
</div>
}
</div>
</div>
}
<div class="d-flex flex-row-reverse m-2">
<button class="btn btn-primary save"
(click)="save.emit()">
<i aria-hidden="true" class="fas fa-save"></i> {{ 'item.edit.metadata.save-button' | translate }}
</button>
</div> </div>
</div>
</div> </div>

View File

@@ -1,44 +1,60 @@
<div class="container" *ngVar="(itemRD$ | async) as itemRD"> <div class="container" *ngVar="(itemRD$ | async) as itemRD">
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut> @if (itemRD?.hasSucceeded) {
<div *ngIf="itemRD?.payload as item"> <div class="item-page" @fadeInOut>
<ds-item-alerts [item]="item"></ds-item-alerts> @if (itemRD?.payload; as item) {
<ds-item-versions-notice [item]="item"></ds-item-versions-notice> <div>
<ds-view-tracker [object]="item"></ds-view-tracker> <ds-item-alerts [item]="item"></ds-item-alerts>
<div *ngIf="!item.isWithdrawn || (isAdmin$|async)" class="full-item-info"> <ds-item-versions-notice [item]="item"></ds-item-versions-notice>
<div class="d-flex flex-row"> <ds-view-tracker [object]="item"></ds-view-tracker>
<ds-item-page-title-field class="me-auto" [item]="item"></ds-item-page-title-field> @if (!item.isWithdrawn || (isAdmin$|async)) {
<ds-dso-edit-menu></ds-dso-edit-menu> <div class="full-item-info">
</div> <div class="d-flex flex-row">
<div class="simple-view-link my-3" *ngIf="!fromSubmissionObject"> <ds-item-page-title-field class="me-auto" [item]="item"></ds-item-page-title-field>
<a class="btn btn-outline-primary" [routerLink]="[(itemPageRoute$ | async)]"> <ds-dso-edit-menu></ds-dso-edit-menu>
{{"item.page.link.simple" | translate}} </div>
</a> @if (!fromSubmissionObject) {
</div> <div class="simple-view-link my-3">
<div class="table-responsive"> <a class="btn btn-outline-primary" [routerLink]="[(itemPageRoute$ | async)]">
<table class="table table-striped"> {{"item.page.link.simple" | translate}}
<tbody> </a>
<ng-container *ngFor="let mdEntry of (metadata$ | async) | keyvalue"> </div>
<tr *ngFor="let mdValue of mdEntry.value"> }
<td>{{mdEntry.key}}</td> <div class="table-responsive">
<td>{{mdValue.value}}</td> <table class="table table-striped">
<td>{{mdValue.language}}</td> <tbody>
</tr> @for (mdEntry of (metadata$ | async) | keyvalue; track mdEntry) {
</ng-container> @for (mdValue of mdEntry.value; track mdValue) {
</tbody> <tr>
</table> <td>{{mdEntry.key}}</td>
</div> <td>{{mdValue.value}}</td>
<ds-item-page-full-file-section [item]="item"></ds-item-page-full-file-section> <td>{{mdValue.language}}</td>
<ds-item-page-collections [item]="item"></ds-item-page-collections> </tr>
<ds-item-versions class="mt-2" [item]="item"></ds-item-versions> }
<div class="button-row bottom" *ngIf="fromSubmissionObject"> }
<div class="text-end"> </tbody>
<button class="btn btn-outline-secondary me-1" (click)="back()"><i </table>
</div>
<ds-item-page-full-file-section [item]="item"></ds-item-page-full-file-section>
<ds-item-page-collections [item]="item"></ds-item-page-collections>
<ds-item-versions class="mt-2" [item]="item"></ds-item-versions>
@if (fromSubmissionObject) {
<div class="button-row bottom">
<div class="text-end">
<button class="btn btn-outline-secondary me-1" (click)="back()"><i
class="fas fa-arrow-left"></i> {{'item.page.return' | translate}}</button> class="fas fa-arrow-left"></i> {{'item.page.return' | translate}}</button>
</div> </div>
</div>
}
</div>
}
</div> </div>
</div> }
</div> </div>
</div> }
<ds-error *ngIf="itemRD?.hasFailed" message="{{'error.item' | translate}}"></ds-error> @if (itemRD?.hasFailed) {
<ds-loading *ngIf="itemRD?.isLoading" message="{{'loading.item' | translate}}"></ds-loading> <ds-error message="{{'error.item' | translate}}"></ds-error>
}
@if (itemRD?.isLoading) {
<ds-loading message="{{'loading.item' | translate}}"></ds-loading>
}
</div> </div>

View File

@@ -1,65 +1,180 @@
<div class="container mb-5"> <div class="container mb-5">
<h1>{{'person.orcid.registry.auth' | translate}}</h1> <h1>{{'person.orcid.registry.auth' | translate}}</h1>
<ng-container *ngIf="(isLinkedToOrcid() | async); then orcidLinked; else orcidNotLinked"></ng-container> @if ((isLinkedToOrcid() | async)) {
<div data-test="orcidLinked">
<div class="row">
@if ((hasOrcidAuthorizations() | async)) {
<div class="col-sm-6 mb-3" data-test="hasOrcidAuthorizations">
<div class="card h-100">
<div class="card-header">{{ 'person.page.orcid.granted-authorizations'| translate }}</div>
<div class="card-body">
<div class="container p-0">
<ul>
@for (auth of (getOrcidAuthorizations() | async); track auth) {
<li data-test="orcidAuthorization">
{{getAuthorizationDescription(auth) | translate}}
</li>
}
</ul>
</div>
</div>
</div>
</div>
}
<div class="col-sm-6 mb-3">
<div class="card h-100">
<div class="card-header">{{ 'person.page.orcid.missing-authorizations'| translate }}</div>
<div class="card-body">
<div class="container">
@if ((hasMissingOrcidAuthorizations() | async) !== true) {
<ds-alert [type]="'alert-success'" data-test="noMissingOrcidAuthorizations">
{{'person.page.orcid.no-missing-authorizations-message' | translate}}
</ds-alert>
}
@if ((hasMissingOrcidAuthorizations() | async)) {
<ds-alert [type]="'alert-warning'" data-test="missingOrcidAuthorizations">
{{'person.page.orcid.missing-authorizations-message' | translate}}
<ul>
@for (auth of (getMissingOrcidAuthorizations() | async); track auth) {
<li data-test="missingOrcidAuthorization">
{{getAuthorizationDescription(auth) | translate }}
</li>
}
</ul>
</ds-alert>
}
</div>
</div>
</div>
</div>
</div>
@if ((onlyAdminCanDisconnectProfileFromOrcid() | async) && (ownerCanDisconnectProfileFromOrcid() | async) !== true) {
<ds-alert
[type]="'alert-warning'" data-test="unlinkOnlyAdmin">
{{ 'person.page.orcid.remove-orcid-message' | translate}}
</ds-alert>
}
@if ((ownerCanDisconnectProfileFromOrcid() | async)) {
<div class="row" data-test="unlinkOwner">
<div class="col">
<button type="submit" class="btn btn-danger float-end" (click)="unlinkOrcid()"
[dsBtnDisabled]="(unlinkProcessing | async)">
@if ((unlinkProcessing | async) !== true) {
<span><i
class="fas fa-unlink"></i> {{ 'person.page.orcid.unlink' | translate }}</span>
}
@if ((unlinkProcessing | async)) {
<span><i
class='fas fa-circle-notch fa-spin'></i> {{'person.page.orcid.unlink.processing' | translate}}</span>
}
</button>
@if ((hasMissingOrcidAuthorizations() | async)) {
<button type="submit"
class="btn btn-primary float-end" (click)="linkOrcid()">
<span><i class="fas fa-check"></i> {{ 'person.page.orcid.grant-authorizations' | translate }}</span>
</button>
}
</div>
</div>
}
</div>
} @else {
<div data-test="orcidNotLinked">
<div class="row">
<div class="col-2"><img alt="orcid-logo" src="../../../../assets/images/orcid.logo.icon.svg"/></div>
<div class="col">
<ds-alert [type]="'alert-info'">{{ getOrcidNotLinkedMessage() | async }}</ds-alert>
</div>
</div>
<div class="row">
<div class="col">
<button class="btn btn-primary float-end" (click)="linkOrcid()">
<i class="fas fa-link"></i>
{{'person.page.orcid.link' | translate}}
</button>
</div>
</div>
</div>
}
</div> </div>
<ng-template #orcidLinked> <ng-template #orcidLinked>
<div data-test="orcidLinked"> <div data-test="orcidLinked">
<div class="row"> <div class="row">
<div *ngIf="(hasOrcidAuthorizations() | async)" class="col-sm-6 mb-3" data-test="hasOrcidAuthorizations"> @if ((hasOrcidAuthorizations() | async)) {
<div class="card h-100"> <div class="col-sm-6 mb-3" data-test="hasOrcidAuthorizations">
<div class="card-header">{{ 'person.page.orcid.granted-authorizations'| translate }}</div> <div class="card h-100">
<div class="card-body"> <div class="card-header">{{ 'person.page.orcid.granted-authorizations'| translate }}</div>
<div class="container p-0"> <div class="card-body">
<ul> <div class="container p-0">
<li *ngFor="let auth of (getOrcidAuthorizations() | async)" data-test="orcidAuthorization"> <ul>
{{getAuthorizationDescription(auth) | translate}} @for (auth of (getOrcidAuthorizations() | async); track auth) {
</li> <li data-test="orcidAuthorization">
</ul> {{getAuthorizationDescription(auth) | translate}}
</li>
}
</ul>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> }
<div class="col-sm-6 mb-3"> <div class="col-sm-6 mb-3">
<div class="card h-100"> <div class="card h-100">
<div class="card-header">{{ 'person.page.orcid.missing-authorizations'| translate }}</div> <div class="card-header">{{ 'person.page.orcid.missing-authorizations'| translate }}</div>
<div class="card-body"> <div class="card-body">
<div class="container"> <div class="container">
<ds-alert *ngIf="(hasMissingOrcidAuthorizations() | async) !== true" [type]="'alert-success'" data-test="noMissingOrcidAuthorizations"> @if ((hasMissingOrcidAuthorizations() | async) !== true) {
{{'person.page.orcid.no-missing-authorizations-message' | translate}} <ds-alert [type]="'alert-success'" data-test="noMissingOrcidAuthorizations">
</ds-alert> {{'person.page.orcid.no-missing-authorizations-message' | translate}}
<ds-alert *ngIf="(hasMissingOrcidAuthorizations() | async)" [type]="'alert-warning'" data-test="missingOrcidAuthorizations"> </ds-alert>
{{'person.page.orcid.missing-authorizations-message' | translate}} }
<ul> @if ((hasMissingOrcidAuthorizations() | async)) {
<li *ngFor="let auth of (getMissingOrcidAuthorizations() | async)" data-test="missingOrcidAuthorization"> <ds-alert [type]="'alert-warning'" data-test="missingOrcidAuthorizations">
{{getAuthorizationDescription(auth) | translate }} {{'person.page.orcid.missing-authorizations-message' | translate}}
</li> <ul>
</ul> @for (auth of (getMissingOrcidAuthorizations() | async); track auth) {
</ds-alert> <li data-test="missingOrcidAuthorization">
{{getAuthorizationDescription(auth) | translate }}
</li>
}
</ul>
</ds-alert>
}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<ds-alert *ngIf="(onlyAdminCanDisconnectProfileFromOrcid() | async) && (ownerCanDisconnectProfileFromOrcid() | async) !== true" @if ((onlyAdminCanDisconnectProfileFromOrcid() | async) && (ownerCanDisconnectProfileFromOrcid() | async) !== true) {
[type]="'alert-warning'" data-test="unlinkOnlyAdmin"> <ds-alert
{{ 'person.page.orcid.remove-orcid-message' | translate}} [type]="'alert-warning'" data-test="unlinkOnlyAdmin">
</ds-alert> {{ 'person.page.orcid.remove-orcid-message' | translate}}
<div class="row" *ngIf="(ownerCanDisconnectProfileFromOrcid() | async)" data-test="unlinkOwner"> </ds-alert>
<div class="col"> }
<button type="submit" class="btn btn-danger float-end" (click)="unlinkOrcid()" @if ((ownerCanDisconnectProfileFromOrcid() | async)) {
[dsBtnDisabled]="(unlinkProcessing | async)"> <div class="row" data-test="unlinkOwner">
<span *ngIf="(unlinkProcessing | async) !== true"><i <div class="col">
class="fas fa-unlink"></i> {{ 'person.page.orcid.unlink' | translate }}</span> <button type="submit" class="btn btn-danger float-end" (click)="unlinkOrcid()"
<span *ngIf="(unlinkProcessing | async)"><i [dsBtnDisabled]="(unlinkProcessing | async)">
class='fas fa-circle-notch fa-spin'></i> {{'person.page.orcid.unlink.processing' | translate}}</span> @if ((unlinkProcessing | async) !== true) {
</button> <span><i
<button *ngIf="(hasMissingOrcidAuthorizations() | async)" type="submit" class="fas fa-unlink"></i> {{ 'person.page.orcid.unlink' | translate }}</span>
class="btn btn-primary float-end" (click)="linkOrcid()"> }
<span><i class="fas fa-check"></i> {{ 'person.page.orcid.grant-authorizations' | translate }}</span> @if ((unlinkProcessing | async)) {
</button> <span><i
class='fas fa-circle-notch fa-spin'></i> {{'person.page.orcid.unlink.processing' | translate}}</span>
}
</button>
@if ((hasMissingOrcidAuthorizations() | async)) {
<button type="submit"
class="btn btn-primary float-end" (click)="linkOrcid()">
<span><i class="fas fa-check"></i> {{ 'person.page.orcid.grant-authorizations' | translate }}</span>
</button>
}
</div>
</div> </div>
</div> }
</div> </div>
</ng-template> </ng-template>

View File

@@ -1,19 +1,29 @@
<ds-loading *ngIf="(processingConnection | async)" [message]="'person.page.orcid.link.processing' | translate"></ds-loading> @if ((processingConnection | async)) {
<div class="container" *ngIf="(processingConnection | async) !== true && (connectionStatus | async) !== true" data-test="error-box"> <ds-loading [message]="'person.page.orcid.link.processing' | translate"></ds-loading>
<ds-alert [type]="AlertType.Error">{{'person.page.orcid.link.error.message' | translate}}</ds-alert> }
</div> @if ((processingConnection | async) !== true && (connectionStatus | async) !== true) {
<ng-container *ngIf="(processingConnection | async) !== true && (item | async) && (connectionStatus | async)" > <div class="container" data-test="error-box">
<ds-alert [type]="AlertType.Error">{{'person.page.orcid.link.error.message' | translate}}</ds-alert>
</div>
}
@if ((processingConnection | async) !== true && (item | async) && (connectionStatus | async)) {
<ds-orcid-auth [item]="(item | async)" (unlink)="updateItem()" data-test="orcid-auth"></ds-orcid-auth> <ds-orcid-auth [item]="(item | async)" (unlink)="updateItem()" data-test="orcid-auth"></ds-orcid-auth>
<ds-orcid-sync-setting *ngIf="isLinkedToOrcid()" [item]="(item | async)" (settingsUpdated)="updateItem()" data-test="orcid-sync-setting"></ds-orcid-sync-setting> @if (isLinkedToOrcid()) {
<ds-orcid-queue *ngIf="isLinkedToOrcid()" [item]="(item | async)"></ds-orcid-queue> <ds-orcid-sync-setting [item]="(item | async)" (settingsUpdated)="updateItem()" data-test="orcid-sync-setting"></ds-orcid-sync-setting>
</ng-container> }
@if (isLinkedToOrcid()) {
<ds-orcid-queue [item]="(item | async)"></ds-orcid-queue>
}
}
<div *ngIf="(processingConnection | async) !== true && (item | async)" class="container"> @if ((processingConnection | async) !== true && (item | async)) {
<div class="button-row bottom mb-3"> <div class="container">
<div class="text-end"> <div class="button-row bottom mb-3">
<a [routerLink]="getItemPage()" role="button" class="btn btn-outline-secondary" data-test="back-button"> <div class="text-end">
<i class="fas fa-arrow-left"></i> {{'item.orcid.return' | translate}} <a [routerLink]="getItemPage()" role="button" class="btn btn-outline-secondary" data-test="back-button">
</a> <i class="fas fa-arrow-left"></i> {{'item.orcid.return' | translate}}
</a>
</div>
</div> </div>
</div> </div>
</div> }

View File

@@ -15,9 +15,11 @@
<div class="mb-3 row"> <div class="mb-3 row">
<label class="form-label" for="syncMode">{{ 'person.page.orcid.synchronization-mode.label'| translate }}</label> <label class="form-label" for="syncMode">{{ 'person.page.orcid.synchronization-mode.label'| translate }}</label>
<select class="form-select" [(ngModel)]="currentSyncMode" name="syncMode" id="syncMode" <select class="form-select" [(ngModel)]="currentSyncMode" name="syncMode" id="syncMode"
required> required>
<option *ngFor="let syncMode of syncModes" @for (syncMode of syncModes; track syncMode) {
[value]="syncMode.value">{{ syncMode.label | translate }}</option> <option
[value]="syncMode.value">{{ syncMode.label | translate }}</option>
}
</select> </select>
</div> </div>
</div> </div>
@@ -37,13 +39,15 @@
</ds-alert> </ds-alert>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<div *ngFor="let option of syncPublicationOptions" class="row form-check"> @for (option of syncPublicationOptions; track option) {
<input type="radio" [(ngModel)]="currentSyncPublications" <div class="row form-check">
name="syncPublications" id="publicationOption_{{option.value}}" [value]="option.value" <input type="radio" [(ngModel)]="currentSyncPublications"
required> name="syncPublications" id="publicationOption_{{option.value}}" [value]="option.value"
<label for="publicationOption_{{option.value}}" required>
class="ms-2 form-label">{{option.label | translate}}</label> <label for="publicationOption_{{option.value}}"
</div> class="ms-2 form-label">{{option.label | translate}}</label>
</div>
}
</div> </div>
</div> </div>
</div> </div>
@@ -60,12 +64,14 @@
</ds-alert> </ds-alert>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<div *ngFor="let option of syncFundingOptions" class="row form-check"> @for (option of syncFundingOptions; track option) {
<input type="radio" [(ngModel)]="currentSyncFunding" <div class="row form-check">
name="syncFundings" id="fundingOption_{{option.value}}" [value]="option.value" <input type="radio" [(ngModel)]="currentSyncFunding"
required> name="syncFundings" id="fundingOption_{{option.value}}" [value]="option.value"
<label for="fundingOption_{{option.value}}" class="ms-2 form-label">{{option.label | translate}}</label> required>
</div> <label for="fundingOption_{{option.value}}" class="ms-2 form-label">{{option.label | translate}}</label>
</div>
}
</div> </div>
</div> </div>
</div> </div>
@@ -82,12 +88,14 @@
</ds-alert> </ds-alert>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<div *ngFor="let option of syncProfileOptions" class="row form-check"> @for (option of syncProfileOptions; track option) {
<input type="checkbox" [(ngModel)]="option.checked" <div class="row form-check">
name="syncProfile_{{option.value}}" id="profileOption_{{option.value}}" <input type="checkbox" [(ngModel)]="option.checked"
[value]="option.value"> name="syncProfile_{{option.value}}" id="profileOption_{{option.value}}"
<label for="profileOption_{{option.value}}" class="ms-2 form-label">{{option.label | translate}}</label> [value]="option.value">
</div> <label for="profileOption_{{option.value}}" class="ms-2 form-label">{{option.label | translate}}</label>
</div>
}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,21 +1,35 @@
<ng-container *ngVar="(bitstreams$ | async) as bitstreams"> <ng-container *ngVar="(bitstreams$ | async) as bitstreams">
<ds-metadata-field-wrapper *ngIf="bitstreams?.length > 0" [label]="label | translate"> @if (bitstreams?.length > 0) {
<div class="file-section"> <ds-metadata-field-wrapper [label]="label | translate">
<ds-file-download-link *ngFor="let file of bitstreams; let last=last;" [bitstream]="file" [item]="item"> <div class="file-section">
<span> @for (file of bitstreams; track file; let last = $last) {
<span *ngIf="primaryBitsreamId === file.id" class="badge bg-primary">{{ 'item.page.bitstreams.primary' | translate }}</span> <ds-file-download-link [bitstream]="file" [item]="item">
{{ dsoNameService.getName(file) }} <span>
</span> @if (primaryBitsreamId === file.id) {
<span> ({{(file?.sizeBytes) | dsFileSize }})</span> <span class="badge bg-primary">{{ 'item.page.bitstreams.primary' | translate }}</span>
<span *ngIf="!last" innerHTML="{{separator}}"></span> }
</ds-file-download-link> {{ dsoNameService.getName(file) }}
<ds-loading *ngIf="isLoading" message="{{'loading.default' | translate}}" [showMessage]="false"></ds-loading> </span>
<div *ngIf="!isLastPage" class="mt-1" id="view-more"> <span> ({{(file?.sizeBytes) | dsFileSize }})</span>
<button class="bitstream-view-more btn btn-outline-secondary btn-sm" (click)="getNextPage()">{{'item.page.bitstreams.view-more' | translate}}</button> @if (!last) {
<span innerHTML="{{separator}}"></span>
}
</ds-file-download-link>
}
@if (isLoading) {
<ds-loading message="{{'loading.default' | translate}}" [showMessage]="false"></ds-loading>
}
@if (!isLastPage) {
<div class="mt-1" id="view-more">
<button class="bitstream-view-more btn btn-outline-secondary btn-sm" (click)="getNextPage()">{{'item.page.bitstreams.view-more' | translate}}</button>
</div>
}
@if (isLastPage && currentPage !== 1) {
<div class="mt-1" id="collapse">
<button class="bitstream-collapse btn btn-outline-secondary btn-sm" (click)="currentPage = undefined; getNextPage();">{{'item.page.bitstreams.collapse' | translate}}</button>
</div>
}
</div> </div>
<div *ngIf="isLastPage && currentPage !== 1" class="mt-1" id="collapse"> </ds-metadata-field-wrapper>
<button class="bitstream-collapse btn btn-outline-secondary btn-sm" (click)="currentPage = undefined; getNextPage();">{{'item.page.bitstreams.collapse' | translate}}</button> }
</div>
</div>
</ds-metadata-field-wrapper>
</ng-container> </ng-container>

View File

@@ -1,20 +1,30 @@
<ds-metadata-field-wrapper [label]="label"> <ds-metadata-field-wrapper [label]="label">
<ng-container *ngFor="let objectPage of objects; let i = index"> @for (objectPage of objects; track objectPage; let i = $index) {
<ng-container *ngVar="(objectPage | async) as representations"> <ng-container *ngVar="(objectPage | async) as representations">
<ds-metadata-representation-loader *ngFor="let rep of representations" @for (rep of representations; track rep) {
[mdRepresentation]="rep"> <ds-metadata-representation-loader
</ds-metadata-representation-loader> [mdRepresentation]="rep">
<ds-loading *ngIf="(i + 1) === objects.length && (i > 0) && (!representations || representations?.length === 0)" message="{{'loading.default' | translate}}"></ds-loading> </ds-metadata-representation-loader>
<div class="d-inline-block w-100 mt-2" *ngIf="(i + 1) === objects.length && representations?.length > 0"> }
<div *ngIf="(objects.length * incrementBy) < total" class="float-start"> @if ((i + 1) === objects.length && (i > 0) && (!representations || representations?.length === 0)) {
<button class="btn btn-link btn-link-inline" (click)="increase()">{{'item.page.related-items.view-more' | <ds-loading message="{{'loading.default' | translate}}"></ds-loading>
translate:{ amount: (total - (objects.length * incrementBy) < incrementBy) ? total - (objects.length * incrementBy) : incrementBy } }}</button> }
@if ((i + 1) === objects.length && representations?.length > 0) {
<div class="d-inline-block w-100 mt-2">
@if ((objects.length * incrementBy) < total) {
<div class="float-start">
<button class="btn btn-link btn-link-inline" (click)="increase()">{{'item.page.related-items.view-more' |
translate:{ amount: (total - (objects.length * incrementBy) < incrementBy) ? total - (objects.length * incrementBy) : incrementBy } }}</button>
</div>
}
@if (objects.length > 1) {
<div class="float-end">
<button class="btn btn-link btn-link-inline" (click)="decrease()">{{'item.page.related-items.view-less' |
translate:{ amount: representations?.length } }}</button>
</div>
}
</div> </div>
<div *ngIf="objects.length > 1" class="float-end"> }
<button class="btn btn-link btn-link-inline" (click)="decrease()">{{'item.page.related-items.view-less' |
translate:{ amount: representations?.length } }}</button>
</div>
</div>
</ng-container> </ng-container>
</ng-container> }
</ds-metadata-field-wrapper> </ds-metadata-field-wrapper>

View File

@@ -1,22 +1,24 @@
<ng-container *ngIf="(sources$ | async) as sources"> @if ((sources$ | async); as sources) {
<ng-container *ngFor="let source of sources"> @for (source of sources; track source) {
<div class="alert alert-info d-flex flex-row" *ngIf="source.totalEvents > 0"> @if (source.totalEvents > 0) {
<div class="source-logo-container"> <div class="alert alert-info d-flex flex-row">
<img class="source-logo" <div class="source-logo-container">
src="assets/images/qa-{{(source.id | dsSplit: ':')[0]}}-logo.png" <img class="source-logo"
alt="{{source.id}} logo" src="assets/images/qa-{{(source.id | dsSplit: ':')[0]}}-logo.png"
onerror="this.src='assets/images/dspace-logo.svg'"> alt="{{source.id}} logo"
</div> onerror="this.src='assets/images/dspace-logo.svg'">
<div class="w-100 d-flex justify-content-between"> </div>
<div class="ps-4 align-self-center"> <div class="w-100 d-flex justify-content-between">
{{'item.qa-event-notification.check.notification-info' | translate : {num: source.totalEvents } }} <div class="ps-4 align-self-center">
{{'item.qa-event-notification.check.notification-info' | translate : {num: source.totalEvents } }}
</div>
<button [routerLink]="[ getQualityAssuranceRoute(), (source.id | dsSplit: ':')[0], 'target', item.id]"
[queryParams]="{ forward: true }"
class="btn btn-primary align-self-center">
{{'item.qa-event-notification-info.check.button' | translate}}
</button>
</div> </div>
<button [routerLink]="[ getQualityAssuranceRoute(), (source.id | dsSplit: ':')[0], 'target', item.id]"
[queryParams]="{ forward: true }"
class="btn btn-primary align-self-center">
{{'item.qa-event-notification-info.check.button' | translate}}
</button>
</div> </div>
</div> }
</ng-container> }
</ng-container> }

View File

@@ -1,20 +1,30 @@
<ds-metadata-field-wrapper [label]="label" [ngClass]="placeholderFontClass"> <ds-metadata-field-wrapper [label]="label" [ngClass]="placeholderFontClass">
<ng-container *ngFor="let objectPage of objects; let i = index"> @for (objectPage of objects; track objectPage; let i = $index) {
<ng-container *ngVar="(objectPage | async) as itemsRD"> <ng-container *ngVar="(objectPage | async) as itemsRD">
<ds-listable-object-component-loader *ngFor="let item of itemsRD?.payload?.page" @for (item of itemsRD?.payload?.page; track item) {
[object]="item" [viewMode]="viewMode"> <ds-listable-object-component-loader
</ds-listable-object-component-loader> [object]="item" [viewMode]="viewMode">
<ds-loading *ngIf="(i + 1) === objects.length && (itemsRD || i > 0) && !(itemsRD?.hasSucceeded && itemsRD?.payload && itemsRD?.payload?.page?.length > 0)" message="{{'loading.default' | translate}}"></ds-loading> </ds-listable-object-component-loader>
<div class="d-inline-block w-100 mt-2" *ngIf="(i + 1) === objects.length && itemsRD?.payload?.page?.length > 0"> }
<div *ngIf="itemsRD?.payload?.totalPages > objects.length" class="float-start" id="view-more"> @if ((i + 1) === objects.length && (itemsRD || i > 0) && !(itemsRD?.hasSucceeded && itemsRD?.payload && itemsRD?.payload?.page?.length > 0)) {
<button class="btn btn-link btn-link-inline text-capitalize" (click)="increase()">{{'item.page.related-items.view-more' | <ds-loading message="{{'loading.default' | translate}}"></ds-loading>
translate:{ amount: (itemsRD?.payload?.totalElements - (incrementBy * objects.length) < incrementBy) ? itemsRD?.payload?.totalElements - (incrementBy * objects.length) : incrementBy } }} {{label}}</button> }
@if ((i + 1) === objects.length && itemsRD?.payload?.page?.length > 0) {
<div class="d-inline-block w-100 mt-2">
@if (itemsRD?.payload?.totalPages > objects.length) {
<div class="float-start" id="view-more">
<button class="btn btn-link btn-link-inline text-capitalize" (click)="increase()">{{'item.page.related-items.view-more' |
translate:{ amount: (itemsRD?.payload?.totalElements - (incrementBy * objects.length) < incrementBy) ? itemsRD?.payload?.totalElements - (incrementBy * objects.length) : incrementBy } }} {{label}}</button>
</div>
}
@if (objects.length > 1) {
<div class="float-end" id="view-less">
<button class="btn btn-link btn-link-inline text-capitalize" (click)="decrease()">{{'item.page.related-items.view-less' |
translate:{ amount: itemsRD?.payload?.page?.length } }} {{label}}</button>
</div>
}
</div> </div>
<div *ngIf="objects.length > 1" class="float-end" id="view-less"> }
<button class="btn btn-link btn-link-inline text-capitalize" (click)="decrease()">{{'item.page.related-items.view-less' |
translate:{ amount: itemsRD?.payload?.page?.length } }} {{label}}</button>
</div>
</div>
</ng-container> </ng-container>
</ng-container> }
</ds-metadata-field-wrapper> </ds-metadata-field-wrapper>

View File

@@ -1,53 +1,60 @@
<div class="left-column"> <div class="left-column">
<span *ngIf="(workspaceId$ | async) || (workflowId$ | async); then versionNumberWithoutLink else versionNumberWithLink"></span> @if ((workspaceId$ | async) || (workflowId$ | async)) {
<ng-template #versionNumberWithLink> {{version.version}}
<a [routerLink]="getVersionRoute(version.id)">{{version.version}}</a> } @else {
</ng-template> <a [routerLink]="getVersionRoute(version.id)">{{version.version}}</a>
<ng-template #versionNumberWithoutLink> }
{{version.version}} @if (version?.id === itemVersion?.id) {
</ng-template> <span>*</span>
<span *ngIf="version?.id === itemVersion?.id">*</span> }
<span *ngIf="workspaceId$ | async" class="text-light badge bg-primary ms-3"> @if (workspaceId$ | async) {
{{ "item.version.history.table.workspaceItem" | translate }} <span class="text-light badge bg-primary ms-3">
</span> {{ "item.version.history.table.workspaceItem" | translate }}
</span>
}
<span *ngIf="workflowId$ | async" class="text-light badge bg-info ms-3"> @if (workflowId$ | async) {
{{ "item.version.history.table.workflowItem" | translate }} <span class="text-light badge bg-info ms-3">
</span> {{ "item.version.history.table.workflowItem" | translate }}
</span>
}
</div> </div>
<div class="right-column"> <div class="right-column">
<div class="btn-group edit-field space-children-mr" *ngIf="displayActions"> @if (displayActions) {
<!--EDIT WORKSPACE ITEM--> <div class="btn-group edit-field space-children-mr">
<!--EDIT WORKSPACE ITEM-->
@if (workspaceId$ | async) {
<button class="btn btn-outline-primary btn-sm version-row-element-edit" <button class="btn btn-outline-primary btn-sm version-row-element-edit"
*ngIf="workspaceId$ | async" (click)="editWorkspaceItem(workspaceId$)"
(click)="editWorkspaceItem(workspaceId$)" title="{{'item.version.history.table.action.editWorkspaceItem' | translate }}">
title="{{'item.version.history.table.action.editWorkspaceItem' | translate }}">
<i class="fas fa-pencil-alt fa-fw"></i> <i class="fas fa-pencil-alt fa-fw"></i>
</button> </button>
<!--CREATE--> }
<ng-container *ngIf="canCreateVersion$ | async"> <!--CREATE-->
<button class="btn btn-outline-primary btn-sm version-row-element-create" @if (canCreateVersion$ | async) {
[dsBtnDisabled]="isAnyBeingEdited() || hasDraftVersion" <button class="btn btn-outline-primary btn-sm version-row-element-create"
(click)="createNewVersion(version)" [dsBtnDisabled]="isAnyBeingEdited() || hasDraftVersion"
title="{{createVersionTitle | translate }}"> (click)="createNewVersion(version)"
<i class="fas fa-code-branch fa-fw"></i> title="{{createVersionTitle | translate }}">
</button> <i class="fas fa-code-branch fa-fw"></i>
</ng-container> </button>
<!--DELETE--> }
<ng-container *ngIf="canDeleteVersion$ | async"> <!--DELETE-->
<button class="btn btn-sm version-row-element-delete" @if (canDeleteVersion$ | async) {
[ngClass]="isAnyBeingEdited() ? 'btn-outline-primary' : 'btn-outline-danger'" <button class="btn btn-sm version-row-element-delete"
[dsBtnDisabled]="isAnyBeingEdited()" [ngClass]="isAnyBeingEdited() ? 'btn-outline-primary' : 'btn-outline-danger'"
(click)="deleteVersion(version, version.id === itemVersion.id)" [dsBtnDisabled]="isAnyBeingEdited()"
title="{{'item.version.history.table.action.deleteVersion' | translate}}"> (click)="deleteVersion(version, version.id === itemVersion.id)"
<i class="fas fa-trash fa-fw"></i> title="{{'item.version.history.table.action.deleteVersion' | translate}}">
</button> <i class="fas fa-trash fa-fw"></i>
</ng-container> </button>
</div> }
</div> </div>
}
</div>

View File

@@ -1,40 +1,42 @@
<div *ngIf="(this.submitted$ | async) !== true; else waiting"> @if ((this.submitted$ | async) !== true) {
<div class="modal-header">{{'item.version.create.modal.header' | translate}} <div>
<button type="button" class="btn-close" (click)="onModalClose()" aria-label="Close"> <div class="modal-header">{{'item.version.create.modal.header' | translate}}
</button> <button type="button" class="btn-close" (click)="onModalClose()" aria-label="Close">
</div> </button>
<div class="modal-body">
<p class="pb-2">
{{ "item.version.create.modal.text" | translate }}
<span *ngIf="!firstVersion">
{{ "item.version.create.modal.text.startingFrom" | translate : {version: versionNumber} }}
</span>
</p>
<div class="mb-3">
<label for="summary" class="form-label">{{'item.version.create.modal.form.summary.label' | translate }}:</label>
<input type="text" id="summary" class="form-control" [(ngModel)]="newVersionSummary"
(keyup.enter)="onModalSubmit()"
placeholder="{{'item.version.create.modal.form.summary.placeholder' | translate }}"/>
<!-- (keyup.enter)="$event.preventDefault(); $event.stopImmediatePropagation()"-->
</div> </div>
</div> <div class="modal-body">
<div class="modal-footer space-children-mr"> <p class="pb-2">
{{ "item.version.create.modal.text" | translate }}
@if (!firstVersion) {
<span>
{{ "item.version.create.modal.text.startingFrom" | translate : {version: versionNumber} }}
</span>
}
</p>
<div class="mb-3">
<label for="summary" class="form-label">{{'item.version.create.modal.form.summary.label' | translate }}:</label>
<input type="text" id="summary" class="form-control" [(ngModel)]="newVersionSummary"
(keyup.enter)="onModalSubmit()"
placeholder="{{'item.version.create.modal.form.summary.placeholder' | translate }}"/>
<!-- (keyup.enter)="$event.preventDefault(); $event.stopImmediatePropagation()"-->
</div>
</div>
<div class="modal-footer space-children-mr">
<button class="btn btn-outline-secondary btn-sm ms-0" <button class="btn btn-outline-secondary btn-sm ms-0"
type="button" type="button"
(click)="onModalClose()" (click)="onModalClose()"
title="{{'item.version.create.modal.button.cancel.tooltip' | translate}}"> title="{{'item.version.create.modal.button.cancel.tooltip' | translate}}">
<i class="fas fa-times fa-fw"></i> {{'item.version.create.modal.button.cancel' | translate}} <i class="fas fa-times fa-fw"></i> {{'item.version.create.modal.button.cancel' | translate}}
</button> </button>
<button class="btn btn-success btn-sm ms-0" <button class="btn btn-success btn-sm ms-0"
type="submit" type="submit"
(click)="onModalSubmit()" (click)="onModalSubmit()"
title="{{'item.version.create.modal.button.confirm.tooltip' | translate}}"> title="{{'item.version.create.modal.button.confirm.tooltip' | translate}}">
<i class="fas fa-check fa-fw"></i> {{'item.version.create.modal.button.confirm' | translate}} <i class="fas fa-check fa-fw"></i> {{'item.version.create.modal.button.confirm' | translate}}
</button> </button>
</div>
</div> </div>
</div> } @else {
<ng-template #waiting>
<div class="modal-header">{{'item.version.create.modal.submitted.header' | translate}}</div> <div class="modal-header">{{'item.version.create.modal.submitted.header' | translate}}</div>
<div class="modal-body"> <div class="modal-body">
<p>{{'item.version.create.modal.submitted.text' | translate}}</p> <p>{{'item.version.create.modal.submitted.text' | translate}}</p>
@@ -42,4 +44,5 @@
<ds-loading [showMessage]="false"></ds-loading> <ds-loading [showMessage]="false"></ds-loading>
</div> </div>
</div> </div>
</ng-template> }

View File

@@ -1,92 +1,114 @@
<div *ngIf="(versionsDTO$ | async) as versionsDTO; else noItemVersion"> @if ((versionsDTO$ | async); as versionsDTO) {
<div *ngIf="(versionRD$ | async)?.payload as itemVersion"> <div>
<div class="mb-2" *ngIf="versionsDTO.versionDTOs.length > 0 || displayWhenEmpty"> @if ((versionRD$ | async)?.payload; as itemVersion) {
<h2 *ngIf="displayTitle" class="h4">{{"item.version.history.head" | translate}}</h2> <div>
<ds-alert [type]="AlertTypeEnum.Info"> @if (versionsDTO.versionDTOs.length > 0 || displayWhenEmpty) {
{{ "item.version.history.selected.alert" | translate : {version: itemVersion.version} }} <div class="mb-2">
</ds-alert> @if (displayTitle) {
<ds-pagination *ngIf="versionsDTO.versionDTOs.length > 0" <h2 class="h4">{{"item.version.history.head" | translate}}</h2>
(paginationChange)="onPageChange()" }
[hideGear]="true" <ds-alert [type]="AlertTypeEnum.Info">
[hidePagerWhenSinglePage]="true" {{ "item.version.history.selected.alert" | translate : {version: itemVersion.version} }}
[paginationOptions]="options" </ds-alert>
[collectionSize]="versionsDTO.totalElements" @if (versionsDTO.versionDTOs.length > 0) {
[retainScrollPosition]="true"> <ds-pagination
<table class="table table-striped table-bordered align-middle my-2"> (paginationChange)="onPageChange()"
<thead> [hideGear]="true"
<tr> [hidePagerWhenSinglePage]="true"
<th scope="col">{{"item.version.history.table.version" | translate}}</th> [paginationOptions]="options"
<th scope="col" *ngIf="(showSubmitter$ | async)">{{"item.version.history.table.editor" | translate}}</th> [collectionSize]="versionsDTO.totalElements"
<th scope="col">{{"item.version.history.table.date" | translate}}</th> [retainScrollPosition]="true">
<th scope="col">{{"item.version.history.table.summary" | translate}}</th> <table class="table table-striped table-bordered align-middle my-2">
</tr> <thead>
</thead> <tr>
<tbody> <th scope="col">{{"item.version.history.table.version" | translate}}</th>
<tr *ngFor="let versionDTO of versionsDTO.versionDTOs" [id]="'version-row-' + versionDTO.version.id"> @if ((showSubmitter$ | async)) {
<td class="version-row-element-version"> <th scope="col">{{"item.version.history.table.editor" | translate}}</th>
<ds-item-versions-row-element-version [hasDraftVersion]="hasDraftVersion$ | async" }
[version]="versionDTO.version" <th scope="col">{{"item.version.history.table.date" | translate}}</th>
[item]="item" [displayActions]="displayActions" <th scope="col">{{"item.version.history.table.summary" | translate}}</th>
[itemVersion]="itemVersion" </tr>
[versionBeingEditedNumber]="versionBeingEditedNumber" </thead>
(versionsHistoryChange)="getAllVersions($event)" <tbody>
></ds-item-versions-row-element-version> @for (versionDTO of versionsDTO.versionDTOs; track versionDTO) {
</td> <tr [id]="'version-row-' + versionDTO.version.id">
<td class="version-row-element-editor" *ngIf="(showSubmitter$ | async)"> <td class="version-row-element-version">
{{versionDTO.version.submitterName}} <ds-item-versions-row-element-version [hasDraftVersion]="hasDraftVersion$ | async"
</td> [version]="versionDTO.version"
<td class="version-row-element-date"> [item]="item" [displayActions]="displayActions"
{{versionDTO.version.created | date : 'yyyy-MM-dd HH:mm:ss'}} [itemVersion]="itemVersion"
</td> [versionBeingEditedNumber]="versionBeingEditedNumber"
<td class="version-row-element-summary"> (versionsHistoryChange)="getAllVersions($event)"
<div class="float-start"> ></ds-item-versions-row-element-version>
<ng-container *ngIf="isThisBeingEdited(versionDTO.version); then editSummary else showSummary"></ng-container> </td>
<ng-template #showSummary>{{versionDTO.version.summary}}</ng-template> @if ((showSubmitter$ | async)) {
<ng-template #editSummary> <td class="version-row-element-editor">
<input [attr.aria-label]="'item.version.history.table.action.editSummary' | translate" {{versionDTO.version.submitterName}}
[(ngModel)]="versionBeingEditedSummary" (keyup.enter)="onSummarySubmit()" </td>
class="form-control" type="text"/> }
</ng-template> <td class="version-row-element-date">
</div> {{versionDTO.version.created | date : 'yyyy-MM-dd HH:mm:ss'}}
</td>
<div class="float-end btn-group edit-field space-children-mr" *ngIf="displayActions && versionDTO.canEditVersion | async"> <td class="version-row-element-summary">
<ng-container *ngIf="isThisBeingEdited(versionDTO.version); else notThisBeingEdited"> <div class="float-start">
<!--DISCARD EDIT--> @if (isThisBeingEdited(versionDTO.version)) {
<button class="btn btn-sm btn-outline-warning" <input [attr.aria-label]="'item.version.history.table.action.editSummary' | translate"
(click)="disableVersionEditing()" [(ngModel)]="versionBeingEditedSummary" (keyup.enter)="onSummarySubmit()"
title="{{'item.version.history.table.action.discardSummary' | translate}}"> class="form-control" type="text"/>
<i class="fas fa-undo-alt fa-fw"></i> } @else {
</button> <span>{{versionDTO.version.summary}}</span>
<!--SAVE--> }
<button class="btn btn-outline-success btn-sm" <ng-template #editSummary>
(click)="onSummarySubmit()" <input [attr.aria-label]="'item.version.history.table.action.editSummary' | translate"
title="{{'item.version.history.table.action.saveSummary' | translate}}"> [(ngModel)]="versionBeingEditedSummary" (keyup.enter)="onSummarySubmit()"
<i class="fas fa-check fa-fw"></i> class="form-control" type="text"/>
</button> </ng-template>
</ng-container> </div>
<ng-template #notThisBeingEdited> @if (displayActions && versionDTO.canEditVersion | async) {
<!--EDIT--> <div class="float-end btn-group edit-field space-children-mr">
<button class="btn btn-outline-primary btn-sm version-row-element-edit" @if (isThisBeingEdited(versionDTO.version)) {
[dsBtnDisabled]="isAnyBeingEdited()" <!--DISCARD EDIT-->
(click)="enableVersionEditing(versionDTO.version)" <button class="btn btn-sm btn-outline-warning"
title="{{'item.version.history.table.action.editSummary' | translate}}"> (click)="disableVersionEditing()"
<i class="fas fa-edit fa-fw"></i> title="{{'item.version.history.table.action.discardSummary' | translate}}">
</button> <i class="fas fa-undo-alt fa-fw"></i>
</ng-template> </button>
</div> <!--SAVE-->
</td> <button class="btn btn-outline-success btn-sm"
</tr> (click)="onSummarySubmit()"
</tbody> title="{{'item.version.history.table.action.saveSummary' | translate}}">
</table> <i class="fas fa-check fa-fw"></i>
<div>*&nbsp;{{"item.version.history.selected" | translate}}</div> </button>
</ds-pagination> } @else {
</div> <!--EDIT-->
<button class="btn btn-outline-primary btn-sm version-row-element-edit"
[dsBtnDisabled]="isAnyBeingEdited()"
(click)="enableVersionEditing(versionDTO.version)"
title="{{'item.version.history.table.action.editSummary' | translate}}">
<i class="fas fa-edit fa-fw"></i>
</button>
}
</div>
}
</td>
</tr>
}
</tbody>
</table>
<div>*&nbsp;{{"item.version.history.selected" | translate}}</div>
</ds-pagination>
}
</div>
}
</div>
}
</div> </div>
</div> } @else {
@if (displayWhenEmpty) {
<ds-alert
[content]="'item.version.history.empty'"
[type]="AlertTypeEnum.Info">
</ds-alert>
}
}
<ng-template #noItemVersion>
<ds-alert *ngIf="displayWhenEmpty"
[content]="'item.version.history.empty'"
[type]="AlertTypeEnum.Info">
</ds-alert>
</ng-template>

View File

@@ -1,26 +1,30 @@
<div class="add" *ngIf="(moreThanOne$ | async) !== true"> @if ((moreThanOne$ | async) !== true) {
<div class="add">
<button class="btn btn-lg btn-outline-primary mt-1 ms-2" <button class="btn btn-lg btn-outline-primary mt-1 ms-2"
[attr.aria-label]="'mydspace.new-submission-external' | translate" [dsBtnDisabled]="(initialized$ | async) !== true" [attr.aria-label]="'mydspace.new-submission-external' | translate" [dsBtnDisabled]="(initialized$ | async) !== true"
(click)="openPage(singleEntity)" role="button" (click)="openPage(singleEntity)" role="button"
title="{{'mydspace.new-submission-external' | translate}}"> title="{{'mydspace.new-submission-external' | translate}}">
<i class="fa fa-file-import" aria-hidden="true"></i> <i class="fa fa-file-import" aria-hidden="true"></i>
</button> </button>
</div> </div>
}
@if ((moreThanOne$ | async)) {
<div class="add w-100" display="dynamic" placement="bottom-right" <div class="add w-100" display="dynamic" placement="bottom-right"
ngbDropdown ngbDropdown
*ngIf="(moreThanOne$ | async)"> >
<button class="btn btn-lg btn-outline-primary mt-1 ms-2" id="dropdownImport" ngbDropdownToggle <button class="btn btn-lg btn-outline-primary mt-1 ms-2" id="dropdownImport" ngbDropdownToggle
type="button" [dsBtnDisabled]="(initialized$ | async) !== true" type="button" [dsBtnDisabled]="(initialized$ | async) !== true"
[attr.aria-label]="'mydspace.new-submission-external' | translate" [attr.aria-label]="'mydspace.new-submission-external' | translate"
[attr.data-test]="'import-dropdown' | dsBrowserOnly" [attr.data-test]="'import-dropdown' | dsBrowserOnly"
title="{{'mydspace.new-submission-external' | translate}}"> title="{{'mydspace.new-submission-external' | translate}}">
<i class="fa fa-file-import" aria-hidden="true"></i> <i class="fa fa-file-import" aria-hidden="true"></i>
<span class="caret" aria-hidden="true"></span> <span class="caret" aria-hidden="true"></span>
</button> </button>
<div ngbDropdownMenu <div ngbDropdownMenu
class="dropdown-menu p-0" class="dropdown-menu p-0"
id="importControlsDropdownMenu" id="importControlsDropdownMenu"
aria-labelledby="dropdownImport"> aria-labelledby="dropdownImport">
<ds-entity-dropdown [isSubmission]="false" (selectionChange)="openPage($event)"></ds-entity-dropdown> <ds-entity-dropdown [isSubmission]="false" (selectionChange)="openPage($event)"></ds-entity-dropdown>
</div> </div>
</div> </div>
}

View File

@@ -1,24 +1,28 @@
<div class="add" *ngIf="(moreThanOne$ | async) !== true"> @if ((moreThanOne$ | async) !== true) {
<div class="add">
<button class="btn btn-lg btn-primary mt-1 ms-2" [attr.aria-label]="'mydspace.new-submission' | translate" <button class="btn btn-lg btn-primary mt-1 ms-2" [attr.aria-label]="'mydspace.new-submission' | translate"
[dsBtnDisabled]="(initialized$ | async) !== true" (click)="openDialog(singleEntity)" role="button"> [dsBtnDisabled]="(initialized$ | async) !== true" (click)="openDialog(singleEntity)" role="button">
<i class="fa fa-plus-circle" aria-hidden="true"></i> <i class="fa fa-plus-circle" aria-hidden="true"></i>
</button> </button>
</div> </div>
}
@if ((moreThanOne$ | async)) {
<div class="add w-100" display="dynamic" placement="bottom-right" <div class="add w-100" display="dynamic" placement="bottom-right"
ngbDropdown ngbDropdown
*ngIf="(moreThanOne$ | async)"> >
<button class="btn btn-lg btn-primary mt-1 ms-2" id="dropdownSubmission" ngbDropdownToggle <button class="btn btn-lg btn-primary mt-1 ms-2" id="dropdownSubmission" ngbDropdownToggle
type="button" [dsBtnDisabled]="(initialized$ | async) !== true" type="button" [dsBtnDisabled]="(initialized$ | async) !== true"
[attr.aria-label]="'mydspace.new-submission' | translate" [attr.aria-label]="'mydspace.new-submission' | translate"
[attr.data-test]="'submission-dropdown' | dsBrowserOnly" [attr.data-test]="'submission-dropdown' | dsBrowserOnly"
title="{{'mydspace.new-submission' | translate}}"> title="{{'mydspace.new-submission' | translate}}">
<i class="fa fa-plus-circle" aria-hidden="true"></i> <i class="fa fa-plus-circle" aria-hidden="true"></i>
<span class="caret" aria-hidden="true"></span> <span class="caret" aria-hidden="true"></span>
</button> </button>
<div ngbDropdownMenu <div ngbDropdownMenu
class="dropdown-menu p-0" class="dropdown-menu p-0"
id="entityControlsDropdownMenu" id="entityControlsDropdownMenu"
aria-labelledby="dropdownSubmission"> aria-labelledby="dropdownSubmission">
<ds-entity-dropdown [isSubmission]="true" (selectionChange)="openDialog($event)"></ds-entity-dropdown> <ds-entity-dropdown [isSubmission]="true" (selectionChange)="openDialog($event)"></ds-entity-dropdown>
</div> </div>
</div> </div>
}

View File

@@ -1,29 +1,30 @@
<ng-container *ngIf="(sources$ | async)?.length > 0"> @if ((sources$ | async)?.length > 0) {
<ng-container *ngFor="let source of sources$ | async"> @for (source of sources$ | async; track source) {
<div @if (source.totalEvents > 0) {
class="alert alert-info d-flex flex-row" <div
*ngIf="source.totalEvents > 0" class="alert alert-info d-flex flex-row"
>
<div class="source-logo-container">
<img
class="source-logo"
src="assets/images/qa-{{ source.id }}-logo.png"
onerror="this.src='assets/images/dspace-logo.svg'"
alt="{{ source.id }} logo"
/>
</div>
<div class="w-100 d-flex justify-content-between">
<div class="ps-4 align-self-center">
{{ "mydspace.qa-event-notification.check.notification-info" | translate : { num: source.totalEvents } }}
</div>
<button
[routerLink]="[getQualityAssuranceRoute(), source.id]"
[queryParams]="{ forward: true }"
class="btn btn-primary align-self-center"
> >
{{ "mydspace.qa-event-notification-info.check.button" | translate }} <div class="source-logo-container">
</button> <img
class="source-logo"
src="assets/images/qa-{{ source.id }}-logo.png"
onerror="this.src='assets/images/dspace-logo.svg'"
alt="{{ source.id }} logo"
/>
</div>
<div class="w-100 d-flex justify-content-between">
<div class="ps-4 align-self-center">
{{ "mydspace.qa-event-notification.check.notification-info" | translate : { num: source.totalEvents } }}
</div>
<button
[routerLink]="[getQualityAssuranceRoute(), source.id]"
[queryParams]="{ forward: true }"
class="btn btn-primary align-self-center"
>
{{ "mydspace.qa-event-notification-info.check.button" | translate }}
</button>
</div>
</div> </div>
</div> }
</ng-container> }
</ng-container> }

View File

@@ -1,19 +1,19 @@
<nav [ngClass]="{'open': (menuCollapsed | async) !== true}" <nav [ngClass]="{'open': (menuCollapsed | async) !== true}"
[@slideMobileNav]="(windowService.isXsOrSm() | async) !== true ? 'default' : ((menuCollapsed | async) ? 'collapsed' : 'expanded')" [@slideMobileNav]="(windowService.isXsOrSm() | async) !== true ? 'default' : ((menuCollapsed | async) ? 'collapsed' : 'expanded')"
class="navbar navbar-light navbar-expand-md px-md-0 navbar-container" role="navigation" class="navbar navbar-light navbar-expand-md px-md-0 navbar-container" role="navigation"
[attr.aria-label]="'nav.main.description' | translate" id="main-navbar"> [attr.aria-label]="'nav.main.description' | translate" id="main-navbar">
<!-- TODO remove navbar-container class when https://github.com/twbs/bootstrap/issues/24726 is fixed --> <!-- TODO remove navbar-container class when https://github.com/twbs/bootstrap/issues/24726 is fixed -->
<div class="navbar-inner-container w-100" [class.container]="(isMobile$ | async) !== true"> <div class="navbar-inner-container w-100" [class.container]="(isMobile$ | async) !== true">
<div class="w-100"> <div class="w-100">
<div id="collapsingNav"> <div id="collapsingNav">
<ng-container *ngIf="(isMobile$ | async) && (isAuthenticated$ | async)"> @if ((isMobile$ | async) && (isAuthenticated$ | async)) {
<ds-user-menu [inExpandableNavbar]="true"></ds-user-menu> <ds-user-menu [inExpandableNavbar]="true"></ds-user-menu>
</ng-container> }
<div class="navbar-nav align-items-md-center me-auto shadow-none gapx-3"> <div class="navbar-nav align-items-md-center me-auto shadow-none gapx-3">
<ng-container *ngFor="let section of (sections | async)"> @for (section of (sections | async); track section) {
<ng-container <ng-container
*ngComponentOutlet="(sectionMap$ | async).get(section.id)?.component; injector: (sectionMap$ | async).get(section.id)?.injector;"></ng-container> *ngComponentOutlet="(sectionMap$ | async).get(section.id)?.component; injector: (sectionMap$ | async).get(section.id)?.injector;"></ng-container>
</ng-container> }
</div> </div>
</div> </div>
</div> </div>

View File

@@ -6,13 +6,17 @@
{{'notifications.events.title'| translate}} {{'notifications.events.title'| translate}}
</div> </div>
</h1> </h1>
<ds-alert *ngIf="!targetId" [type]="'alert-info'"> @if (!targetId) {
<span [innerHTML]="'quality-assurance.events.description' | translate : {topic: selectedTopicName, source: sourceId}"></span> <ds-alert [type]="'alert-info'">
</ds-alert> <span [innerHTML]="'quality-assurance.events.description' | translate : {topic: selectedTopicName, source: sourceId}"></span>
<ds-alert *ngIf="targetId" [type]="'alert-info'"> </ds-alert>
<span [innerHTML]="'quality-assurance.events.description-with-topic-and-target' | translate : {topic: selectedTopicName, source: sourceId}"></span> }
<a [routerLink]="itemPageUrl" target="_blank">{{(getTargetItemTitle() | async)}}</a> @if (targetId) {
</ds-alert> <ds-alert [type]="'alert-info'">
<span [innerHTML]="'quality-assurance.events.description-with-topic-and-target' | translate : {topic: selectedTopicName, source: sourceId}"></span>
<a [routerLink]="itemPageUrl" target="_blank">{{(getTargetItemTitle() | async)}}</a>
</ds-alert>
}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@@ -21,208 +25,271 @@
{{'quality-assurance.events.topic' | translate}} {{this.showTopic}} {{'quality-assurance.events.topic' | translate}} {{this.showTopic}}
</h2> </h2>
<ds-loading class="container" *ngIf="(isEventPageLoading | async)" message="{{'quality-assurance.loading' | translate}}"></ds-loading> @if ((isEventPageLoading | async)) {
<ds-loading class="container" message="{{'quality-assurance.loading' | translate}}"></ds-loading>
}
<ds-pagination *ngIf="(isEventPageLoading | async) !== true" @if ((isEventPageLoading | async) !== true) {
[paginationOptions]="paginationConfig" <ds-pagination
[collectionSize]="(totalElements$ | async)" [paginationOptions]="paginationConfig"
[sortOptions]="paginationSortConfig" [collectionSize]="(totalElements$ | async)"
(paginationChange)="getQualityAssuranceEvents()"> [sortOptions]="paginationSortConfig"
(paginationChange)="getQualityAssuranceEvents()">
<ng-container> <ng-container>
<div *ngIf="(eventsUpdated$ | async)?.length === 0" class="alert alert-info w-100 mb-2 mt-2" role="alert"> @if ((eventsUpdated$ | async)?.length === 0) {
{{'quality-assurance.noEvents' | translate}} <div class="alert alert-info w-100 mb-2 mt-2" role="alert">
</div> {{'quality-assurance.noEvents' | translate}}
<div *ngIf="(eventsUpdated$ | async)?.length !== 0" class="table-responsive mt-2"> </div>
<table id="events" class="table table-striped table-hover table-bordered"> }
<thead> @if ((eventsUpdated$ | async)?.length !== 0) {
<tr> <div class="table-responsive mt-2">
<th scope="col" class="trust-col">{{'quality-assurance.event.table.trust' | translate}}</th> <table id="events" class="table table-striped table-hover table-bordered">
<th scope="col" class="title-col">{{'quality-assurance.event.table.publication' | translate}}</th> <thead>
<th *ngIf="hasDetailColumn() && showTopic.indexOf('/PROJECT') !== -1" scope="col" class="content-col"> <tr>
{{'quality-assurance.event.table.project-details' | translate}} <th scope="col" class="trust-col">{{'quality-assurance.event.table.trust' | translate}}</th>
</th> <th scope="col" class="title-col">{{'quality-assurance.event.table.publication' | translate}}</th>
<ng-container *ngIf="hasDetailColumn() && (showTopic.indexOf('/REINSTATE') !== -1 || showTopic.indexOf('/WITHDRAWN') !== -1)"> @if (hasDetailColumn() && showTopic.indexOf('/PROJECT') !== -1) {
<th scope="col" class="content-col"> <th scope="col" class="content-col">
{{'quality-assurance.event.table.reasons' | translate}} {{'quality-assurance.event.table.project-details' | translate}}
</th> </th>
<th scope="col" class="content-col"> }
{{'quality-assurance.event.table.person-who-requested' | translate}} @if (hasDetailColumn() && (showTopic.indexOf('/REINSTATE') !== -1 || showTopic.indexOf('/WITHDRAWN') !== -1)) {
</th> <th scope="col" class="content-col">
</ng-container> {{'quality-assurance.event.table.reasons' | translate}}
<th scope="col" class="button-col">{{'quality-assurance.event.table.actions' | translate}}</th> </th>
</tr> <th scope="col" class="content-col">
</thead> {{'quality-assurance.event.table.person-who-requested' | translate}}
<tbody> </th>
<tr *ngFor="let eventElement of (eventsUpdated$ | async); let i = index"> }
<td>{{eventElement?.event?.trust}} <th scope="col" class="button-col">{{'quality-assurance.event.table.actions' | translate}}</th>
</td> </tr>
<td><a *ngIf="eventElement?.target" </thead>
target="_blank" <tbody>
rel="noopener noreferrer" @for (eventElement of (eventsUpdated$ | async); track eventElement; let i = $index) {
[routerLink]="['/items', eventElement?.target?.id]">{{eventElement.title}}</a> <tr>
<span *ngIf="!eventElement?.target">{{eventElement.title}}</span> <td>{{eventElement?.event?.trust}}
<div *ngIf="eventElement?.event?.message?.serviceId"> </td>
<span class="small pe-1">{{'quality-assurance.event.table.event.message.serviceUrl' | translate}}</span> <td>@if (eventElement?.target) {
<span [title]="eventElement.event.message.serviceId"> <a
<a [href]="eventElement.event.message.serviceId" target="_blank">{{eventElement.event.message.serviceId}}</a> target="_blank"
</span> rel="noopener noreferrer"
</div> [routerLink]="['/items', eventElement?.target?.id]">{{eventElement.title}}</a>
<div *ngIf="eventElement?.event?.message?.href" class="d-flex align-items-center"> }
<span class="small pe-1">{{'quality-assurance.event.table.event.message.link' | translate}}</span> @if (!eventElement?.target) {
<span [title]="eventElement.event.message.href" class="text-truncate d-inline-block w-75"> <span>{{eventElement.title}}</span>
<a [href]="eventElement.event.message.href" target="_blank">{{eventElement.event.message.href}}</a> }
</span> @if (eventElement?.event?.message?.serviceId) {
</div> <div>
</td> <span class="small pe-1">{{'quality-assurance.event.table.event.message.serviceUrl' | translate}}</span>
<td *ngIf="showTopic.indexOf('/PID') !== -1"> <span [title]="eventElement.event.message.serviceId">
<p><span class="small">{{'quality-assurance.event.table.pidtype' | translate}}</span>&nbsp;<span class="badge bg-info">{{eventElement.event.message.type}}</span></p> <a [href]="eventElement.event.message.serviceId" target="_blank">{{eventElement.event.message.serviceId}}</a>
<p><span class="small">{{'quality-assurance.event.table.pidvalue' | translate}}</span><br> </span>
<a *ngIf="hasPIDHref(eventElement.event.message); else noPID" href="{{getPIDHref(eventElement.event.message)}}" rel="noopener noreferrer" target="_blank"> </div>
{{eventElement.event.message.value}} }
</a> @if (eventElement?.event?.message?.href) {
<ng-template #noPID><span class="badge bg-info">{{eventElement.event.message.value}}</span></ng-template> <div class="d-flex align-items-center">
</p> <span class="small pe-1">{{'quality-assurance.event.table.event.message.link' | translate}}</span>
</td> <span [title]="eventElement.event.message.href" class="text-truncate d-inline-block w-75">
<td *ngIf="showTopic.indexOf('/SUBJECT') !== -1"> <a [href]="eventElement.event.message.href" target="_blank">{{eventElement.event.message.href}}</a>
<p><span class="small">{{'quality-assurance.event.table.subjectValue' | translate}} </span>
</span><br><span class="badge bg-info">{{eventElement.event.message.value}}</span></p> </div>
</td> }
<td *ngIf="showTopic.indexOf('/ABSTRACT') !== -1"> </td>
<p class="abstract-container" [class.show]="showMore"> @if (showTopic.indexOf('/PID') !== -1) {
<span class="small">{{'quality-assurance.event.table.abstract' | translate}}</span><br> <td>
<span class="text-ellipsis">{{eventElement.event.message.abstract}}</span> <p><span class="small">{{'quality-assurance.event.table.pidtype' | translate}}</span>&nbsp;<span class="badge bg-info">{{eventElement.event.message.type}}</span></p>
</p> <p><span class="small">{{'quality-assurance.event.table.pidvalue' | translate}}</span><br>
<button class="btn btn-outline-primary btn-sm" (click)="showMore = !showMore"> @if (hasPIDHref(eventElement.event.message)) {
<i *ngIf="!showMore" class="fas fa-angle-down"></i> <a href="{{getPIDHref(eventElement.event.message)}}" rel="noopener noreferrer" target="_blank">
<i *ngIf="showMore" class="fas fa-angle-up"></i> {{eventElement.event.message.value}}
{{ (showMore ? 'quality-assurance.event.table.less': 'quality-assurance.event.table.more') | translate }} </a>
</button> } @else {
</td> <span class="badge bg-info">{{eventElement.event.message.value}}</span>
<ng-container *ngIf="showTopic.indexOf('/REINSTATE') !== -1 || showTopic.indexOf('/WITHDRAWN') !== -1"> }
<td> </p>
<p> </td>
<span *ngIf="eventElement.event.message"> }
<span>{{eventElement.event.message.reason}}</span><br> @if (showTopic.indexOf('/SUBJECT') !== -1) {
</span> <td>
</p> <p><span class="small">{{'quality-assurance.event.table.subjectValue' | translate}}
</td> </span><br><span class="badge bg-info">{{eventElement.event.message.value}}</span></p>
<td> </td>
<p> }
<span *ngIf="eventElement.event.originalId"> @if (showTopic.indexOf('/ABSTRACT') !== -1) {
<ds-eperson-data [ePersonId]="eventElement.event.originalId" [properties]="['email']"></ds-eperson-data> <td>
</span> <p class="abstract-container" [class.show]="showMore">
</p> <span class="small">{{'quality-assurance.event.table.abstract' | translate}}</span><br>
</td> <span class="text-ellipsis">{{eventElement.event.message.abstract}}</span>
</ng-container> </p>
<button class="btn btn-outline-primary btn-sm" (click)="showMore = !showMore">
<td *ngIf="showTopic.indexOf('/PROJECT') !== -1"> @if (!showMore) {
<p> <i class="fas fa-angle-down"></i>
{{'quality-assurance.event.table.suggestedProject' | translate}} }
</p> @if (showMore) {
<p> <i class="fas fa-angle-up"></i>
<span class="small">{{'quality-assurance.event.table.project' | translate}}</span><br> }
<a href="{{sourceUrlForProjectSearch}}{{ eventElement.event.message.sourceId}}" rel="noopener noreferrer" target="_blank">{{eventElement.event.message.title}}</a> {{ (showMore ? 'quality-assurance.event.table.less': 'quality-assurance.event.table.more') | translate }}
</p> </button>
<p> </td>
<span *ngIf="eventElement.event.message.acronym"><span class="small">{{'quality-assurance.event.table.acronym' | translate}}</span>&nbsp;<span class="badge bg-info">{{eventElement.event.message.acronym}}</span><br></span> }
<span *ngIf="eventElement.event.message.code"><span class="small">{{'quality-assurance.event.table.code' | translate}}</span>&nbsp;<span class="badge bg-info">{{eventElement.event.message.code}}</span><br></span> @if (showTopic.indexOf('/REINSTATE') !== -1 || showTopic.indexOf('/WITHDRAWN') !== -1) {
<span *ngIf="eventElement.event.message.funder"><span class="small">{{'quality-assurance.event.table.funder' | translate}}</span>&nbsp;<span class="badge bg-info">{{eventElement.event.message.funder}}</span><br></span> <td>
<span *ngIf="eventElement.event.message.fundingProgram"><span class="small">{{'quality-assurance.event.table.fundingProgram' | translate}}</span>&nbsp;<span class="badge bg-info">{{eventElement.event.message.fundingProgram}}</span><br></span> <p>
<span *ngIf="eventElement.event.message.jurisdiction"><span class="small">{{'quality-assurance.event.table.jurisdiction' | translate}}</span>&nbsp;<span class="badge bg-info">{{eventElement.event.message.jurisdiction}}</span></span> @if (eventElement.event.message) {
</p> <span>
<hr> <span>{{eventElement.event.message.reason}}</span><br>
<div> </span>
{{(eventElement.hasProject ? 'quality-assurance.event.project.found' : 'quality-assurance.event.project.notFound') | translate}} }
<a target="_blank" rel="noopener noreferrer" *ngIf="eventElement.hasProject" title="{{eventElement.projectTitle}}" [routerLink]="['/items', eventElement.projectId]">{{eventElement.handle}} </a> </p>
<div class="btn-group"> </td>
<button *ngIf="!eventElement.hasProject" <td>
class="btn btn-outline-primary btn-sm" <p>
@if (eventElement.event.originalId) {
<span>
<ds-eperson-data [ePersonId]="eventElement.event.originalId" [properties]="['email']"></ds-eperson-data>
</span>
}
</p>
</td>
}
@if (showTopic.indexOf('/PROJECT') !== -1) {
<td>
<p>
{{'quality-assurance.event.table.suggestedProject' | translate}}
</p>
<p>
<span class="small">{{'quality-assurance.event.table.project' | translate}}</span><br>
<a href="{{sourceUrlForProjectSearch}}{{ eventElement.event.message.sourceId}}" rel="noopener noreferrer" target="_blank">{{eventElement.event.message.title}}</a>
</p>
<p>
@if (eventElement.event.message.acronym) {
<span><span class="small">{{'quality-assurance.event.table.acronym' | translate}}</span>&nbsp;<span class="badge bg-info">{{eventElement.event.message.acronym}}</span><br></span>
}
@if (eventElement.event.message.code) {
<span><span class="small">{{'quality-assurance.event.table.code' | translate}}</span>&nbsp;<span class="badge bg-info">{{eventElement.event.message.code}}</span><br></span>
}
@if (eventElement.event.message.funder) {
<span><span class="small">{{'quality-assurance.event.table.funder' | translate}}</span>&nbsp;<span class="badge bg-info">{{eventElement.event.message.funder}}</span><br></span>
}
@if (eventElement.event.message.fundingProgram) {
<span><span class="small">{{'quality-assurance.event.table.fundingProgram' | translate}}</span>&nbsp;<span class="badge bg-info">{{eventElement.event.message.fundingProgram}}</span><br></span>
}
@if (eventElement.event.message.jurisdiction) {
<span><span class="small">{{'quality-assurance.event.table.jurisdiction' | translate}}</span>&nbsp;<span class="badge bg-info">{{eventElement.event.message.jurisdiction}}</span></span>
}
</p>
<hr>
<div>
{{(eventElement.hasProject ? 'quality-assurance.event.project.found' : 'quality-assurance.event.project.notFound') | translate}}
@if (eventElement.hasProject) {
<a target="_blank" rel="noopener noreferrer" title="{{eventElement.projectTitle}}" [routerLink]="['/items', eventElement.projectId]">{{eventElement.handle}} </a>
}
<div class="btn-group">
@if (!eventElement.hasProject) {
<button
class="btn btn-outline-primary btn-sm"
[dsBtnDisabled]="eventElement.isRunning"
(click)="openModalLookup(eventElement); $event.stopPropagation();"
[attr.aria-label]="'quality-assurance.event.modal.project.select' | translate"
>
<i class="fas fa-search"></i>
</button>
}
@if (eventElement.hasProject) {
<button
class="btn btn-outline-danger btn-sm"
[dsBtnDisabled]="eventElement.isRunning"
(click)="removeProject(eventElement)"
[attr.aria-label]="'quality-assurance.event.modal.project.remove' | translate"
>
<i class="fas fa-trash-alt"></i>
</button>
}
</div>
</div>
</td>
}
<td>
@if ((isAdmin$ | async) || !isReinstateWithdrawnRequest) {
<div class="btn-group button-width">
@if (showTopic.indexOf('/PROJECT') !== -1) {
<button
class="btn btn-outline-success btn-sm button-width"
ngbTooltip="{{'quality-assurance.event.action.import' | translate}}"
container="body"
[dsBtnDisabled]="eventElement.isRunning"
(click)="modalChoice('ACCEPTED', eventElement, acceptModal)"
[attr.aria-label]="'quality-assurance.event.action.import' | translate"
>
<i class="fas fa-check"></i>
</button>
}
@if (showTopic.indexOf('/PROJECT') === -1) {
<button
class="btn btn-outline-success btn-sm button-width"
ngbTooltip="{{'quality-assurance.event.action.accept' | translate}}"
container="body"
[dsBtnDisabled]="eventElement.isRunning"
(click)="executeAction('ACCEPTED', eventElement)"
[attr.aria-label]="'quality-assurance.event.action.accept' | translate"
>
<i class="fas fa-check"></i>
</button>
}
<button class="btn btn-outline-dark btn-sm button-width"
ngbTooltip="{{'quality-assurance.event.action.ignore' | translate}}"
container="body"
[dsBtnDisabled]="eventElement.isRunning" [dsBtnDisabled]="eventElement.isRunning"
(click)="openModalLookup(eventElement); $event.stopPropagation();" (click)="openModal('DISCARDED', eventElement, ignoreModal)"
[attr.aria-label]="'quality-assurance.event.modal.project.select' | translate" [attr.aria-label]="'quality-assurance.event.action.ignore' | translate"
> >
<i class="fas fa-search"></i> <i class="fas fa-ban"></i>
</button> </button>
<button *ngIf="eventElement.hasProject" @if ((isAdmin$ | async)) {
class="btn btn-outline-danger btn-sm" <button class="btn btn-outline-danger btn-sm button-width"
ngbTooltip="{{'quality-assurance.event.action.reject' | translate}}"
container="body"
[dsBtnDisabled]="eventElement.isRunning"
(click)="openModal('REJECTED', eventElement, rejectModal)"
[attr.aria-label]="'quality-assurance.event.action.reject' | translate"
>
<i class="fas fa-trash-alt"></i>
</button>
}
@if ((isAdmin$ | async) === false) {
<button class="btn btn-outline-danger btn-sm button-width"
ngbTooltip="{{'quality-assurance.event.action.undo' | translate }}"
container="body"
[dsBtnDisabled]="eventElement.isRunning"
[attr.aria-label]="'quality-assurance.event.action.undo' | translate"
(click)="openModal('UNDO', eventElement, undoModal)">
<i class="fas fa-trash-alt"></i>
</button>
}
</div>
}
@if ((isAdmin$ | async) !== true && isReinstateWithdrawnRequest) {
<div class="btn-group button-width">
<button class="btn btn-outline-danger btn-sm button-width"
ngbTooltip="{{'quality-assurance.event.action.undo' | translate}}"
container="body"
[dsBtnDisabled]="eventElement.isRunning" [dsBtnDisabled]="eventElement.isRunning"
(click)="removeProject(eventElement)" [attr.aria-label]="'quality-assurance.event.action.undo' | translate"
[attr.aria-label]="'quality-assurance.event.modal.project.remove' | translate" (click)="openModal('UNDO', eventElement, undoModal)">
> <i class="fas fa-trash-alt"></i>
<i class="fas fa-trash-alt"></i> </button>
</button> </div>
</div> }
</div> </td>
</td> </tr>
<td> }
<div *ngIf="(isAdmin$ | async) || !isReinstateWithdrawnRequest" class="btn-group button-width"> </tbody>
<button *ngIf="showTopic.indexOf('/PROJECT') !== -1" </table>
class="btn btn-outline-success btn-sm button-width" </div>
ngbTooltip="{{'quality-assurance.event.action.import' | translate}}" }
container="body" </ng-container>
[dsBtnDisabled]="eventElement.isRunning" </ds-pagination>
(click)="modalChoice('ACCEPTED', eventElement, acceptModal)" }
[attr.aria-label]="'quality-assurance.event.action.import' | translate"
>
<i class="fas fa-check"></i>
</button>
<button *ngIf="showTopic.indexOf('/PROJECT') === -1"
class="btn btn-outline-success btn-sm button-width"
ngbTooltip="{{'quality-assurance.event.action.accept' | translate}}"
container="body"
[dsBtnDisabled]="eventElement.isRunning"
(click)="executeAction('ACCEPTED', eventElement)"
[attr.aria-label]="'quality-assurance.event.action.accept' | translate"
>
<i class="fas fa-check"></i>
</button>
<button class="btn btn-outline-dark btn-sm button-width"
ngbTooltip="{{'quality-assurance.event.action.ignore' | translate}}"
container="body"
[dsBtnDisabled]="eventElement.isRunning"
(click)="openModal('DISCARDED', eventElement, ignoreModal)"
[attr.aria-label]="'quality-assurance.event.action.ignore' | translate"
>
<i class="fas fa-ban"></i>
</button>
<button class="btn btn-outline-danger btn-sm button-width"
*ngIf="(isAdmin$ | async)"
ngbTooltip="{{'quality-assurance.event.action.reject' | translate}}"
container="body"
[dsBtnDisabled]="eventElement.isRunning"
(click)="openModal('REJECTED', eventElement, rejectModal)"
[attr.aria-label]="'quality-assurance.event.action.reject' | translate"
>
<i class="fas fa-trash-alt"></i>
</button>
<button class="btn btn-outline-danger btn-sm button-width"
*ngIf="(isAdmin$ | async) === false"
ngbTooltip="{{'quality-assurance.event.action.undo' | translate }}"
container="body"
[dsBtnDisabled]="eventElement.isRunning"
[attr.aria-label]="'quality-assurance.event.action.undo' | translate"
(click)="openModal('UNDO', eventElement, undoModal)">
<i class="fas fa-trash-alt"></i>
</button>
</div>
<div *ngIf="(isAdmin$ | async) !== true && isReinstateWithdrawnRequest" class="btn-group button-width">
<button class="btn btn-outline-danger btn-sm button-width"
ngbTooltip="{{'quality-assurance.event.action.undo' | translate}}"
container="body"
[dsBtnDisabled]="eventElement.isRunning"
[attr.aria-label]="'quality-assurance.event.action.undo' | translate"
(click)="openModal('UNDO', eventElement, undoModal)">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</ng-container>
</ds-pagination>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,58 +1,69 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<h1 class="border-bottom pb-2">{{'quality-assurance.title'| translate}}</h1> <h1 class="border-bottom pb-2">{{'quality-assurance.title'| translate}}</h1>
<ds-alert [type]="'alert-info'" [content]="'quality-assurance.source.description'"></ds-alert> <ds-alert [type]="'alert-info'" [content]="'quality-assurance.source.description'"></ds-alert>
</div>
</div>
<div class="row">
<div class="col-12">
<h2 class="h4 border-bottom pb-2">{{'quality-assurance.source'| translate}}</h2>
<ds-loading class="container" *ngIf="(isSourceLoading() | async)" message="{{'quality-assurance.loading' | translate}}"></ds-loading>
<ds-pagination *ngIf="(isSourceLoading() | async) !== true"
[paginationOptions]="paginationConfig"
[collectionSize]="(totalElements$ | async)"
[hideGear]="false"
[hideSortOptions]="true"
(paginationChange)="getQualityAssuranceSource()">
<ds-loading class="container" *ngIf="(isSourceProcessing() | async)" message="'quality-assurance.loading' | translate"></ds-loading>
<ng-container *ngIf="(isSourceProcessing() | async) !== true">
<div *ngIf="(sources$ | async)?.length === 0" class="alert alert-info w-100 mb-2 mt-2" role="alert">
{{'quality-assurance.noSource' | translate}}
</div>
<div *ngIf="(sources$ | async)?.length !== 0" class="table-responsive mt-2">
<table id="epeople" class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th scope="col">{{'quality-assurance.table.source' | translate}}</th>
<th scope="col">{{'quality-assurance.table.last-event' | translate}}</th>
<th scope="col">{{'quality-assurance.table.actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let sourceElement of (sources$ | async); let i = index">
<td>{{sourceElement.id}}</td>
<td>{{sourceElement.lastEvent | date: 'dd/MM/yyyy hh:mm' }}</td>
<td>
<div class="btn-group edit-field">
<button
class="btn btn-outline-primary btn-sm"
title="{{'quality-assurance.source-list.button.detail' | translate : { param: sourceElement.id } }}"
[routerLink]="[sourceElement.id]">
<span class="badge bg-info">{{sourceElement.totalEvents}}</span>
<i class="fas fa-info fa-fw"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</ng-container>
</ds-pagination>
</div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-12">
<h2 class="h4 border-bottom pb-2">{{'quality-assurance.source'| translate}}</h2>
@if ((isSourceLoading() | async)) {
<ds-loading class="container" message="{{'quality-assurance.loading' | translate}}"></ds-loading>
}
@if ((isSourceLoading() | async) !== true) {
<ds-pagination
[paginationOptions]="paginationConfig"
[collectionSize]="(totalElements$ | async)"
[hideGear]="false"
[hideSortOptions]="true"
(paginationChange)="getQualityAssuranceSource()">
@if ((isSourceProcessing() | async)) {
<ds-loading class="container" message="'quality-assurance.loading' | translate"></ds-loading>
}
@if ((isSourceProcessing() | async) !== true) {
@if ((sources$ | async)?.length === 0) {
<div class="alert alert-info w-100 mb-2 mt-2" role="alert">
{{'quality-assurance.noSource' | translate}}
</div>
}
@if ((sources$ | async)?.length !== 0) {
<div class="table-responsive mt-2">
<table id="epeople" class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th scope="col">{{'quality-assurance.table.source' | translate}}</th>
<th scope="col">{{'quality-assurance.table.last-event' | translate}}</th>
<th scope="col">{{'quality-assurance.table.actions' | translate}}</th>
</tr>
</thead>
<tbody>
@for (sourceElement of (sources$ | async); track sourceElement; let i = $index) {
<tr>
<td>{{sourceElement.id}}</td>
<td>{{sourceElement.lastEvent | date: 'dd/MM/yyyy hh:mm' }}</td>
<td>
<div class="btn-group edit-field">
<button
class="btn btn-outline-primary btn-sm"
title="{{'quality-assurance.source-list.button.detail' | translate : { param: sourceElement.id } }}"
[routerLink]="[sourceElement.id]">
<span class="badge bg-info">{{sourceElement.totalEvents}}</span>
<i class="fas fa-info fa-fw"></i>
</button>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
}
}
</ds-pagination>
}
</div>
</div>
</div>

View File

@@ -2,60 +2,75 @@
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<h1 class="border-bottom pb-2">{{'quality-assurance.title'| translate}}</h1> <h1 class="border-bottom pb-2">{{'quality-assurance.title'| translate}}</h1>
<ds-alert *ngIf="!targetId" [type]="'alert-info'">{{'quality-assurance.topics.description'| translate:{source: sourceId} }}</ds-alert> @if (!targetId) {
<ds-alert *ngIf="targetId" [type]="'alert-info'"> <ds-alert [type]="'alert-info'">{{'quality-assurance.topics.description'| translate:{source: sourceId} }}</ds-alert>
{{'quality-assurance.topics.description-with-target'| translate:{source: sourceId} }} }
<a [routerLink]="itemPageUrl">{{(getTargetItemTitle() | async)}}</a> @if (targetId) {
</ds-alert> <ds-alert [type]="'alert-info'">
{{'quality-assurance.topics.description-with-target'| translate:{source: sourceId} }}
<a [routerLink]="itemPageUrl">{{(getTargetItemTitle() | async)}}</a>
</ds-alert>
}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<h2 class="border-bottom pb-2">{{'quality-assurance.topics'| translate}}</h2> <h2 class="border-bottom pb-2">{{'quality-assurance.topics'| translate}}</h2>
<ds-loading class="container" *ngIf="(isTopicsLoading() | async)" message="{{'quality-assurance.loading' | translate}}"></ds-loading> @if ((isTopicsLoading() | async)) {
<ds-pagination *ngIf="(isTopicsLoading() | async) !== true" <ds-loading class="container" message="{{'quality-assurance.loading' | translate}}"></ds-loading>
[paginationOptions]="paginationConfig" }
[collectionSize]="(totalElements$ | async)" @if ((isTopicsLoading() | async) !== true) {
[hideGear]="false" <ds-pagination
[hideSortOptions]="true" [paginationOptions]="paginationConfig"
(paginationChange)="getQualityAssuranceTopics(sourceId, targetId)"> [collectionSize]="(totalElements$ | async)"
[hideGear]="false"
<ds-loading class="container" *ngIf="(isTopicsProcessing() | async)" message="'quality-assurance.loading' | translate"></ds-loading> [hideSortOptions]="true"
<ng-container *ngIf="(isTopicsProcessing() | async) !== true"> (paginationChange)="getQualityAssuranceTopics(sourceId, targetId)">
<div *ngIf="(topics$ | async)?.length === 0" class="alert alert-info w-100 mb-2 mt-2" role="alert"> @if ((isTopicsProcessing() | async)) {
{{'quality-assurance.noTopics' | translate}} <ds-loading class="container" message="'quality-assurance.loading' | translate"></ds-loading>
</div> }
<div *ngIf="(topics$ | async)?.length !== 0" class="table-responsive mt-2"> @if ((isTopicsProcessing() | async) !== true) {
<table id="epeople" class="table table-striped table-hover table-bordered"> @if ((topics$ | async)?.length === 0) {
<thead> <div class="alert alert-info w-100 mb-2 mt-2" role="alert">
<tr> {{'quality-assurance.noTopics' | translate}}
<th scope="col">{{'quality-assurance.table.topic' | translate}}</th> </div>
<th scope="col">{{'quality-assurance.table.last-event' | translate}}</th> }
<th scope="col">{{'quality-assurance.table.actions' | translate}}</th> @if ((topics$ | async)?.length !== 0) {
</tr> <div class="table-responsive mt-2">
</thead> <table id="epeople" class="table table-striped table-hover table-bordered">
<tbody> <thead>
<tr *ngFor="let topicElement of (topics$ | async); let i = index"> <tr>
<td>{{topicElement.name}}</td> <th scope="col">{{'quality-assurance.table.topic' | translate}}</th>
<td>{{topicElement.lastEvent | date: 'dd/MM/yyyy hh:mm' }}</td> <th scope="col">{{'quality-assurance.table.last-event' | translate}}</th>
<td> <th scope="col">{{'quality-assurance.table.actions' | translate}}</th>
<div class="btn-group edit-field"> </tr>
<button </thead>
<tbody>
@for (topicElement of (topics$ | async); track topicElement; let i = $index) {
<tr>
<td>{{topicElement.name}}</td>
<td>{{topicElement.lastEvent | date: 'dd/MM/yyyy hh:mm' }}</td>
<td>
<div class="btn-group edit-field">
<button
class="btn btn-outline-primary btn-sm" class="btn btn-outline-primary btn-sm"
title="{{'quality-assurance.topics-list.button.detail' | translate : { param: topicElement.name } }}" title="{{'quality-assurance.topics-list.button.detail' | translate : { param: topicElement.name } }}"
[routerLink]="[getQualityAssuranceRoute(), sourceId, topicElement.id]"> [routerLink]="[getQualityAssuranceRoute(), sourceId, topicElement.id]">
<span class="badge bg-info">{{topicElement.totalEvents}}</span> <span class="badge bg-info">{{topicElement.totalEvents}}</span>
<i class="fas fa-info fa-fw"></i> <i class="fas fa-info fa-fw"></i>
</button> </button>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> }
</table> </tbody>
</div> </table>
</ng-container> </div>
</ds-pagination> }
}
</ds-pagination>
}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,29 +1,35 @@
<div class="d-inline"> <div class="d-inline">
<div ngbDropdown class="d-inline"> <div ngbDropdown class="d-inline">
<button *ngIf="isCollectionFixed; else chooseCollection" class="btn btn-success" type="button" (click)="approveAndImportCollectionFixed()"> @if (isCollectionFixed) {
<i class="fa fa-check" aria-hidden="true"></i> {{ approveAndImportLabel() | translate}} <button class="btn btn-success" type="button" (click)="approveAndImportCollectionFixed()">
</button> <i class="fa fa-check" aria-hidden="true"></i> {{ approveAndImportLabel() | translate}}
<ng-template #chooseCollection> </button>
} @else {
<button class="btn btn-success" id="dropdownSubmission" ngbDropdownToggle <button class="btn btn-success" id="dropdownSubmission" ngbDropdownToggle
type="button"> type="button">
<i class="fa fa-check" aria-hidden="true"></i> {{ approveAndImportLabel() | translate}} <i class="fa fa-check" aria-hidden="true"></i> {{ approveAndImportLabel() | translate}}
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<div ngbDropdownMenu <div ngbDropdownMenu
class="dropdown-menu" class="dropdown-menu"
id="entityControlsDropdownMenu" id="entityControlsDropdownMenu"
aria-labelledby="dropdownSubmission"> aria-labelledby="dropdownSubmission">
<ds-entity-dropdown (selectionChange)="openDialog($event)"></ds-entity-dropdown> <ds-entity-dropdown (selectionChange)="openDialog($event)"></ds-entity-dropdown>
</div> </div>
</ng-template> }
</div> </div>
<button (click)="ignoreSuggestion()" class="btn btn-danger ms-2"><i class="fa fa-ban"></i> <button (click)="ignoreSuggestion()" class="btn btn-danger ms-2"><i class="fa fa-ban"></i>
{{ ignoreSuggestionLabel() | translate}}</button> {{ ignoreSuggestionLabel() | translate}}</button>
<button *ngIf="!isBulk" (click)="toggleSeeEvidences()" [dsBtnDisabled]="!hasEvidence" class="btn btn-info ms-2"> @if (!isBulk) {
<button (click)="toggleSeeEvidences()" [dsBtnDisabled]="!hasEvidence" class="btn btn-info ms-2">
<i class="fa fa-eye"></i> <i class="fa fa-eye"></i>
<ng-container *ngIf="!seeEvidence"> {{ 'suggestion.seeEvidence' | translate}}</ng-container> @if (!seeEvidence) {
<ng-container *ngIf="seeEvidence"> {{ 'suggestion.hideEvidence' | translate}}</ng-container> {{ 'suggestion.seeEvidence' | translate}}
}
@if (seeEvidence) {
{{ 'suggestion.hideEvidence' | translate}}
}
</button> </button>
}
</div> </div>

View File

@@ -1,27 +1,32 @@
<div class="notifications-wrapper position-fixed top right" *ngIf="suggestionsRD$ | async"> @if (suggestionsRD$ | async) {
<div class="notification alert alert-success alert-dismissible m-3 shadow" role="alert"> <div class="notifications-wrapper position-fixed top right">
<button (click)="removePopup()" <div class="notification alert alert-success alert-dismissible m-3 shadow" role="alert">
type="button" class="close pt-0 pe-1 pe-0 pb-0" data-dismiss="alert" aria-label="Close"> <button (click)="removePopup()"
<span aria-hidden="true">&times;</span> type="button" class="close pt-0 pe-1 pe-0 pb-0" data-dismiss="alert" aria-label="Close">
</button> <span aria-hidden="true">&times;</span>
</button>
<div class="d-flex flex-row"> <div class="d-flex flex-row">
<div class="d-flex flex-column justify-content-center align-items-center"> <div class="d-flex flex-column justify-content-center align-items-center">
<div class="notification-icon d-flex justify-content-center"><i class="fas fa-2x fa-check-circle"></i></div> <div class="notification-icon d-flex justify-content-center"><i class="fas fa-2x fa-check-circle"></i></div>
</div> </div>
<div class="d-flex flex-column justify-content-center align-content-stretch text-left p-2"> <div class="d-flex flex-column justify-content-center align-content-stretch text-left p-2">
<ng-container *ngIf="(suggestionsRD$ | async) as suggestions"> @if ((suggestionsRD$ | async); as suggestions) {
<ng-container *ngFor="let suggestion of suggestions" class="alert alert-info"> @for (suggestion of suggestions; track suggestion) {
<div *ngIf="suggestion.total > 0"> <ng-container class="alert alert-info">
<div [innerHTML]="'notification.suggestion' | translate: getNotificationSuggestionInterpolation(suggestion)"></div> @if (suggestion.total > 0) {
{{ 'notification.suggestion.please' | translate }} <div>
<a [routerLink]="getNotificationSuggestionInterpolation(suggestion).url"> {{ 'notification.suggestion.review' | translate}} </a> <div [innerHTML]="'notification.suggestion' | translate: getNotificationSuggestionInterpolation(suggestion)"></div>
</div> {{ 'notification.suggestion.please' | translate }}
</ng-container> <a [routerLink]="getNotificationSuggestionInterpolation(suggestion).url"> {{ 'notification.suggestion.review' | translate}} </a>
</ng-container> </div>
}
</ng-container>
}
}
</div>
</div> </div>
</div> </div>
</div> </div>
</div> }

View File

@@ -1,106 +1,125 @@
<div class="container" *ngIf="(processRD$ | async)?.payload as process"> @if ((processRD$ | async)?.payload; as process) {
<div class="container">
<div class="row"> <div class="row">
<div class="col-10"> <div class="col-10">
<h1 class="flex-grow-1"> <h1 class="flex-grow-1">
{{ 'process.detail.title' | translate:{ id: process?.processId, name: process?.scriptName } }} {{ 'process.detail.title' | translate:{ id: process?.processId, name: process?.scriptName } }}
</h1> </h1>
</div> </div>
<div *ngIf="isRefreshing$ | async" class="col-2 refresh-counter"> @if (isRefreshing$ | async) {
{{ 'process.detail.refreshing' | translate }} <i class="fas fa-sync-alt fa-spin"></i> <div class="col-2 refresh-counter">
</div> {{ 'process.detail.refreshing' | translate }} <i class="fas fa-sync-alt fa-spin"></i>
</div>
}
</div> </div>
<ds-process-detail-field id="process-name" [title]="'process.detail.script'"> <ds-process-detail-field id="process-name" [title]="'process.detail.script'">
<div>{{ process?.scriptName }}</div> <div>{{ process?.scriptName }}</div>
</ds-process-detail-field> </ds-process-detail-field>
@if (process?.parameters && process?.parameters?.length > 0) {
<ds-process-detail-field *ngIf="process?.parameters && process?.parameters?.length > 0" id="process-arguments" <ds-process-detail-field id="process-arguments"
[title]="'process.detail.arguments'"> [title]="'process.detail.arguments'">
<div *ngFor="let argument of process?.parameters">{{ argument?.name }} {{ argument?.value }}</div> @for (argument of process?.parameters; track argument) {
</ds-process-detail-field> <div>{{ argument?.name }} {{ argument?.value }}</div>
}
</ds-process-detail-field>
}
<div *ngVar="(filesRD$ | async)?.payload?.page as files"> <div *ngVar="(filesRD$ | async)?.payload?.page as files">
<ds-process-detail-field *ngIf="files && files?.length > 0" id="process-files" @if (files && files?.length > 0) {
[title]="'process.detail.output-files'"> <ds-process-detail-field id="process-files"
[title]="'process.detail.output-files'">
<div class="d-flex flex-column"> <div class="d-flex flex-column">
<ds-file-download-link *ngFor="let file of files; let last=last;" [bitstream]="file"> @for (file of files; track file; let last = $last) {
<span>{{getFileName(file)}}</span> <ds-file-download-link [bitstream]="file">
<span>({{(file?.sizeBytes) | dsFileSize }})</span> <span>{{getFileName(file)}}</span>
</ds-file-download-link> <span>({{(file?.sizeBytes) | dsFileSize }})</span>
</ds-file-download-link>
}
</div> </div>
</ds-process-detail-field> </ds-process-detail-field>
}
</div> </div>
@if (process && process.startTime) {
<ds-process-detail-field *ngIf="process && process.startTime" id="process-start-time" <ds-process-detail-field id="process-start-time"
[title]="'process.detail.start-time' | translate"> [title]="'process.detail.start-time' | translate">
<div>{{ process.startTime | date:dateFormat:'UTC' }}</div> <div>{{ process.startTime | date:dateFormat:'UTC' }}</div>
</ds-process-detail-field> </ds-process-detail-field>
}
<ds-process-detail-field *ngIf="process && process.endTime" id="process-end-time" @if (process && process.endTime) {
[title]="'process.detail.end-time' | translate"> <ds-process-detail-field id="process-end-time"
[title]="'process.detail.end-time' | translate">
<div>{{ process.endTime | date:dateFormat:'UTC' }}</div> <div>{{ process.endTime | date:dateFormat:'UTC' }}</div>
</ds-process-detail-field> </ds-process-detail-field>
}
<ds-process-detail-field *ngIf="process && process.processStatus" id="process-status" @if (process && process.processStatus) {
[title]="'process.detail.status' | translate"> <ds-process-detail-field id="process-status"
[title]="'process.detail.status' | translate">
<div>{{ process.processStatus }}</div> <div>{{ process.processStatus }}</div>
</ds-process-detail-field> </ds-process-detail-field>
}
<ds-process-detail-field *ngIf="isProcessFinished(process)" id="process-output" [title]="'process.detail.output'"> @if (isProcessFinished(process)) {
<button *ngIf="!showOutputLogs && process?._links?.output?.href !== undefined" id="showOutputButton" <ds-process-detail-field id="process-output" [title]="'process.detail.output'">
class="btn btn-primary" (click)="showProcessOutputLogs()"> @if (!showOutputLogs && process?._links?.output?.href !== undefined) {
<button id="showOutputButton"
class="btn btn-primary" (click)="showProcessOutputLogs()">
{{ 'process.detail.logs.button' | translate }} {{ 'process.detail.logs.button' | translate }}
</button> </button>
<ds-loading *ngIf="retrievingOutputLogs$ | async" class="ds-themed-loading" }
message="{{ 'process.detail.logs.loading' | translate }}"></ds-loading> @if (retrievingOutputLogs$ | async) {
<pre class="fw-bold text-secondary bg-light p-3" tabindex="0" <ds-loading class="ds-themed-loading"
*ngIf="showOutputLogs && (outputLogs$ | async)?.length > 0">{{ (outputLogs$ | async) }}</pre> message="{{ 'process.detail.logs.loading' | translate }}"></ds-loading>
<p id="no-output-logs-message" *ngIf="((retrievingOutputLogs$ | async) !== true && showOutputLogs) }
&& (outputLogs$ | async | dsHasNoValue) || (outputLogs$ | async)?.length === 0 || !process._links.output"> @if (showOutputLogs && (outputLogs$ | async)?.length > 0) {
<pre class="fw-bold text-secondary bg-light p-3" tabindex="0"
>{{ (outputLogs$ | async) }}</pre>
}
@if (((retrievingOutputLogs$ | async) !== true && showOutputLogs)
&& (outputLogs$ | async | dsHasNoValue) || (outputLogs$ | async)?.length === 0 || !process._links.output) {
<p id="no-output-logs-message">
{{ 'process.detail.logs.none' | translate }} {{ 'process.detail.logs.none' | translate }}
</p> </p>
</ds-process-detail-field> }
</ds-process-detail-field>
}
<ds-process-detail-field id="process-actions" [title]="'process.detail.actions'"> <ds-process-detail-field id="process-actions" [title]="'process.detail.actions'">
<button class="btn btn-success me-2" routerLink="/processes/new" [queryParams]="{id: process?.processId}"><i <button class="btn btn-success me-2" routerLink="/processes/new" [queryParams]="{id: process?.processId}"><i
class="fas fa-plus pe-2"></i>{{'process.detail.create' | translate}}</button> class="fas fa-plus pe-2"></i>{{'process.detail.create' | translate}}</button>
<button *ngIf="isProcessFinished(process)" id="delete" class="btn btn-danger" @if (isProcessFinished(process)) {
(click)="openDeleteModal(deleteModal)"> <button id="delete" class="btn btn-danger"
<i class="fas fa-trash pe-2"></i>{{ 'process.detail.delete.button' | translate }} (click)="openDeleteModal(deleteModal)">
<i class="fas fa-trash pe-2"></i>{{ 'process.detail.delete.button' | translate }}
</button> </button>
}
</ds-process-detail-field> </ds-process-detail-field>
<div style="text-align: right;"> <div style="text-align: right;">
<a class="btn btn-outline-secondary mt-3" [routerLink]="'/processes'">{{'process.detail.back' | translate}}</a> <a class="btn btn-outline-secondary mt-3" [routerLink]="'/processes'">{{'process.detail.back' | translate}}</a>
</div> </div>
</div> </div>
}
<ng-template #deleteModal > <ng-template #deleteModal >
<div *ngIf="(processRD$ | async)?.payload as process"> @if ((processRD$ | async)?.payload; as process) {
<div>
<div class="modal-header"> <div class="modal-header">
<div> <div>
<h4>{{'process.detail.delete.header' | translate }}</h4> <h4>{{'process.detail.delete.header' | translate }}</h4>
</div>
<button type="button" class="btn-close"
(click)="closeModal()" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div> </div>
<button type="button" class="btn-close"
<div class="modal-body"> (click)="closeModal()" aria-label="Close">
<div>{{'process.detail.delete.body' | translate }}</div> <span aria-hidden="true">×</span>
<div class="mt-4"> </button>
<button class="btn btn-primary me-2" (click)="closeModal()">{{'process.detail.delete.cancel' | translate}}</button> </div>
<button id="delete-confirm" class="btn btn-danger" <div class="modal-body">
(click)="deleteProcess(process)">{{ 'process.detail.delete.confirm' | translate }} <div>{{'process.detail.delete.body' | translate }}</div>
</button> <div class="mt-4">
</div> <button class="btn btn-primary me-2" (click)="closeModal()">{{'process.detail.delete.cancel' | translate}}</button>
<button id="delete-confirm" class="btn btn-danger"
(click)="deleteProcess(process)">{{ 'process.detail.delete.confirm' | translate }}
</button>
</div> </div>
</div>
</div> </div>
}
</ng-template> </ng-template>

View File

@@ -1,25 +1,31 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<h1 class="col-12"> <h1 class="col-12">
{{headerKey | translate}} {{headerKey | translate}}
</h1> </h1>
<div class="col-12 col-md-6 mb-2"> <div class="col-12 col-md-6 mb-2">
<form #form="ngForm" (ngSubmit)="submitForm(form)"> <form #form="ngForm" (ngSubmit)="submitForm(form)">
<ds-scripts-select [script]="selectedScript" (select)="selectedScript = $event; parameters = undefined"></ds-scripts-select> <ds-scripts-select [script]="selectedScript" (select)="selectedScript = $event; parameters = undefined"></ds-scripts-select>
<ds-process-parameters [initialParams]="parameters" [script]="selectedScript" (updateParameters)="parameters = $event"></ds-process-parameters> <ds-process-parameters [initialParams]="parameters" [script]="selectedScript" (updateParameters)="parameters = $event"></ds-process-parameters>
<a [routerLink]="['/processes']" class="btn btn-danger float-start">{{ 'process.new.cancel' | translate }}</a> <a [routerLink]="['/processes']" class="btn btn-danger float-start">{{ 'process.new.cancel' | translate }}</a>
<button type="submit" class="btn btn-primary float-end">{{ 'process.new.submit' | translate }}</button> <button type="submit" class="btn btn-primary float-end">{{ 'process.new.submit' | translate }}</button>
</form> </form>
</div>
<div class="col-12 col-md-6">
<ds-script-help *ngIf="selectedScript" [script]="selectedScript"></ds-script-help>
</div>
</div> </div>
<div class="col-12 col-md-6">
@if (selectedScript) {
<ds-script-help [script]="selectedScript"></ds-script-help>
}
</div>
</div>
<div *ngIf="missingParameters.length > 0" class="mt-3 alert alert-danger validation-error"> @if (missingParameters.length > 0) {
{{'process.new.parameter.required.missing' | translate}} <div class="mt-3 alert alert-danger validation-error">
<ul> {{'process.new.parameter.required.missing' | translate}}
<li *ngFor="let missing of missingParameters">{{missing}}</li> <ul>
</ul> @for (missing of missingParameters; track missing) {
<li>{{missing}}</li>
}
</ul>
</div> </div>
}
</div> </div>

View File

@@ -1,20 +1,26 @@
<div class="row gap-2 mb-2 mx-0"> <div class="row gap-2 mb-2 mx-0">
<select id="process-parameters" <select id="process-parameters"
class="form-select col" class="form-select col"
name="parameter-{{index}}" name="parameter-{{index}}"
[attr.aria-label]="'process.new.select-parameter' | translate" [attr.aria-label]="'process.new.select-parameter' | translate"
[(ngModel)]="selectedParameter" [(ngModel)]="selectedParameter"
#param="ngModel"> #param="ngModel">
<option [ngValue]="undefined">{{ 'process.new.add-parameter' | translate }}</option> <option [ngValue]="undefined">{{ 'process.new.add-parameter' | translate }}</option>
<option *ngFor="let param of parameters" [ngValue]="param.name"> @for (param of parameters; track param) {
{{param.nameLong || param.name}} <option [ngValue]="param.name">
</option> {{param.nameLong || param.name}}
</select> </option>
<ds-parameter-value-input [initialValue]="parameterValue.value" [parameter]="selectedScriptParameter" (updateValue)="selectedParameterValue = $event" class="d-block col px-0" [index]="index"></ds-parameter-value-input> }
<button *ngIf="removable" [attr.aria-label]="'process.new.delete-parameter' | translate" </select>
(click)="removeParameter.emit(parameterValue);" class="btn btn-danger col-1 remove-button"> <ds-parameter-value-input [initialValue]="parameterValue.value" [parameter]="selectedScriptParameter" (updateValue)="selectedParameterValue = $event" class="d-block col px-0" [index]="index"></ds-parameter-value-input>
@if (removable) {
<button [attr.aria-label]="'process.new.delete-parameter' | translate"
(click)="removeParameter.emit(parameterValue);" class="btn btn-danger col-1 remove-button">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
<span *ngIf="!removable" class="col-1"></span> }
@if (!removable) {
<span class="col-1"></span>
}
</div> </div>

View File

@@ -1,13 +1,17 @@
<label for="file-upload-{{index}}" class="form-label d-flex align-items-center m-0"> <label for="file-upload-{{index}}" class="form-label d-flex align-items-center m-0">
<span class="btn btn-primary"> <span class="btn btn-primary">
{{'process.new.parameter.file.upload-button' | translate}} {{'process.new.parameter.file.upload-button' | translate}}
</span> </span>
<span class="file-name ms-1">{{fileObject?.name}}</span> <span class="file-name ms-1">{{fileObject?.name}}</span>
</label> </label>
<input requireFile #file="ngModel" type="file" name="file-upload-{{index}}" id="file-upload-{{index}}" class="form-control-file d-none" [ngModel]="fileObject" (ngModelChange)="setFile($event)"/> <input requireFile #file="ngModel" type="file" name="file-upload-{{index}}" id="file-upload-{{index}}" class="form-control-file d-none" [ngModel]="fileObject" (ngModelChange)="setFile($event)"/>
<div *ngIf="file.invalid && (file.dirty || file.touched)" @if (file.invalid && (file.dirty || file.touched)) {
class="alert alert-danger validation-error"> <div
<div *ngIf="file.errors.required"> class="alert alert-danger validation-error">
@if (file.errors.required) {
<div>
{{'process.new.parameter.file.required' | translate}} {{'process.new.parameter.file.required' | translate}}
</div> </div>
</div> }
</div>
}

View File

@@ -1,11 +1,14 @@
<div class="mb-3" *ngIf="script?.parameters?.length" data-testID="parameters-select-container"> @if (script?.parameters?.length) {
<label class="form-label">{{'process.new.select-parameters' | translate}}</label> <div class="mb-3" data-testID="parameters-select-container">
<ds-parameter-select <label class="form-label">{{'process.new.select-parameters' | translate}}</label>
*ngFor="let value of parameterValues; let i = index; let last = last" @for (value of parameterValues; track value; let i = $index; let last = $last) {
[parameters]="script.parameters" <ds-parameter-select
[parameterValue]="value" [parameters]="script.parameters"
[removable]="!last" [parameterValue]="value"
[index]="i" [removable]="!last"
(removeParameter)="removeParameter(i)" [index]="i"
(changeParameter)="updateParameter($event, i)"></ds-parameter-select> (removeParameter)="removeParameter(i)"
</div> (changeParameter)="updateParameter($event, i)"></ds-parameter-select>
}
</div>
}

View File

@@ -2,46 +2,51 @@
<div> <div>
<div ngbDropdown class="d-flex"> <div ngbDropdown class="d-flex">
<input id="process-script" <input id="process-script"
class="form-select" class="form-select"
required required
[ngModel]="selectedScript" [ngModel]="selectedScript"
placeholder="{{'process.new.select-script.placeholder' | translate}}" placeholder="{{'process.new.select-script.placeholder' | translate}}"
[ngModelOptions]="{standalone: true}" [ngModelOptions]="{standalone: true}"
ngbDropdownToggle ngbDropdownToggle
role="combobox" role="combobox"
#script="ngModel"> #script="ngModel">
<div ngbDropdownMenu aria-labelledby="process-script" class="w-100 scrollable-menu" <div ngbDropdownMenu aria-labelledby="process-script" class="w-100 scrollable-menu"
role="menu" role="menu"
(scroll)="onScroll($event)" (scroll)="onScroll($event)"
infiniteScroll infiniteScroll
[infiniteScrollDistance]="5" [infiniteScrollDistance]="5"
[infiniteScrollThrottle]="300" [infiniteScrollThrottle]="300"
[infiniteScrollUpDistance]="1.5" [infiniteScrollUpDistance]="1.5"
[fromRoot]="true" [fromRoot]="true"
[scrollWindow]="false"> [scrollWindow]="false">
<button class="dropdown-item" @for (script of scripts; track script) {
*ngFor="let script of scripts" <button class="dropdown-item"
role="menuitem" role="menuitem"
type="button" type="button"
title="{{ script.name }}" title="{{ script.name }}"
(click)="onSelect(script);"> (click)="onSelect(script);">
<span class="text-truncate">{{ script.name }}</span> <span class="text-truncate">{{ script.name }}</span>
</button> </button>
<ng-container *ngIf="(isLoading$ | async)"> }
<button class="dropdown-item disabled" role="menuitem"> @if ((isLoading$ | async)) {
<ds-loading message="{{'loading.default' | translate}}"> <button class="dropdown-item disabled" role="menuitem">
</ds-loading> <ds-loading message="{{'loading.default' | translate}}">
</button> </ds-loading>
</ng-container> </button>
}
</div> </div>
</div> </div>
</div> </div>
<div> <div>
<div *ngIf="script.invalid && (script.dirty || script.touched)" @if (script.invalid && (script.dirty || script.touched)) {
class="alert alert-danger validation-error"> <div
<div *ngIf="script.errors.required"> class="alert alert-danger validation-error">
{{ 'process.new.select-script.required' | translate }} @if (script.errors.required) {
<div>
{{ 'process.new.select-script.required' | translate }}
</div>
}
</div> </div>
</div> }
</div> </div>
</div> </div>

View File

@@ -1,78 +1,86 @@
<div class="container"> <div class="container">
<div class="d-flex"> <div class="d-flex">
<h1 class="flex-grow-1">{{'process.overview.title' | translate}}</h1> <h1 class="flex-grow-1">{{'process.overview.title' | translate}}</h1>
</div> </div>
<ng-container *ngTemplateOutlet="buttons"></ng-container> <ng-container *ngTemplateOutlet="buttons"></ng-container>
<div class="sections"> <div class="sections">
<ds-process-overview-table <ds-process-overview-table
[processStatus]="ProcessStatus.RUNNING" [processStatus]="ProcessStatus.RUNNING"
[useAutoRefreshingSearchBy]="true" [useAutoRefreshingSearchBy]="true"
[getInfoValueMethod]="processOverviewService.timeStarted"/> [getInfoValueMethod]="processOverviewService.timeStarted"/>
<ds-process-overview-table <ds-process-overview-table
[processStatus]="ProcessStatus.SCHEDULED" [processStatus]="ProcessStatus.SCHEDULED"
[useAutoRefreshingSearchBy]="true" [useAutoRefreshingSearchBy]="true"
[getInfoValueMethod]="processOverviewService.timeCreated"/> [getInfoValueMethod]="processOverviewService.timeCreated"/>
<ds-process-overview-table <ds-process-overview-table
[processStatus]="ProcessStatus.COMPLETED" [processStatus]="ProcessStatus.COMPLETED"
[sortField]="ProcessSortField.endTime" [sortField]="ProcessSortField.endTime"
[useAutoRefreshingSearchBy]="true" [useAutoRefreshingSearchBy]="true"
[getInfoValueMethod]="processOverviewService.timeCompleted"/> [getInfoValueMethod]="processOverviewService.timeCompleted"/>
<ds-process-overview-table <ds-process-overview-table
[processStatus]="ProcessStatus.FAILED" [processStatus]="ProcessStatus.FAILED"
[sortField]="ProcessSortField.endTime" [sortField]="ProcessSortField.endTime"
[useAutoRefreshingSearchBy]="true" [useAutoRefreshingSearchBy]="true"
[getInfoValueMethod]="processOverviewService.timeCompleted"/> [getInfoValueMethod]="processOverviewService.timeCompleted"/>
</div> </div>
<ng-container *ngTemplateOutlet="buttons"></ng-container> <ng-container *ngTemplateOutlet="buttons"></ng-container>
</div> </div>
<ng-template #buttons> <ng-template #buttons>
<div class="d-flex justify-content-end mb-2"> <div class="d-flex justify-content-end mb-2">
<button *ngIf="processBulkDeleteService.hasSelected()" class="btn btn-primary me-2" @if (processBulkDeleteService.hasSelected()) {
(click)="processBulkDeleteService.clearAllProcesses()"><i <button class="btn btn-primary me-2"
class="fas fa-undo pe-2"></i>{{'process.overview.delete.clear' | translate }} (click)="processBulkDeleteService.clearAllProcesses()"><i
</button> class="fas fa-undo pe-2"></i>{{'process.overview.delete.clear' | translate }}
<button *ngIf="processBulkDeleteService.hasSelected()" class="btn btn-danger me-2" </button>
(click)="openDeleteModal(deleteModal)"><i }
class="fas fa-trash pe-2"></i>{{'process.overview.delete' | translate: {count: processBulkDeleteService.getAmountOfSelectedProcesses()} }} @if (processBulkDeleteService.hasSelected()) {
</button> <button class="btn btn-danger me-2"
<button class="btn btn-success" routerLink="/processes/new"><i (click)="openDeleteModal(deleteModal)"><i
class="fas fa-plus pe-2"></i>{{'process.overview.new' | translate}}</button> class="fas fa-trash pe-2"></i>{{'process.overview.delete' | translate: {count: processBulkDeleteService.getAmountOfSelectedProcesses()} }}
</div> </button>
}
<button class="btn btn-success" routerLink="/processes/new"><i
class="fas fa-plus pe-2"></i>{{'process.overview.new' | translate}}</button>
</div>
</ng-template> </ng-template>
<ng-template #deleteModal> <ng-template #deleteModal>
<div> <div>
<div class="modal-header"> <div class="modal-header">
<div> <div>
<h4>{{'process.overview.delete.header' | translate }}</h4> <h4>{{'process.overview.delete.header' | translate }}</h4>
</div> </div>
<button type="button" class="btn-close" <button type="button" class="btn-close"
(click)="closeModal()" aria-label="Close"> (click)="closeModal()" aria-label="Close">
<span aria-hidden="true">×</span> <span aria-hidden="true">×</span>
</button> </button>
</div>
<div class="modal-body">
<div *ngIf="(processBulkDeleteService.isProcessing$() | async) !== true">{{'process.overview.delete.body' | translate: {count: processBulkDeleteService.getAmountOfSelectedProcesses()} }}</div>
<div *ngIf="processBulkDeleteService.isProcessing$() |async" class="alert alert-info">
<span class="spinner-border spinner-border-sm spinner-button" role="status" aria-hidden="true"></span>
<span> {{ 'process.overview.delete.processing' | translate: {count: processBulkDeleteService.getAmountOfSelectedProcesses()} }}</span>
</div>
<div class="mt-4">
<button class="btn btn-primary me-2" [dsBtnDisabled]="processBulkDeleteService.isProcessing$() |async"
(click)="closeModal()">{{'process.detail.delete.cancel' | translate}}</button>
<button id="delete-confirm" class="btn btn-danger"
[dsBtnDisabled]="processBulkDeleteService.isProcessing$() |async"
(click)="deleteSelected()">{{ 'process.overview.delete' | translate: {count: processBulkDeleteService.getAmountOfSelectedProcesses()} }}
</button>
</div>
</div>
</div> </div>
<div class="modal-body">
@if ((processBulkDeleteService.isProcessing$() | async) !== true) {
<div>{{'process.overview.delete.body' | translate: {count: processBulkDeleteService.getAmountOfSelectedProcesses()} }}</div>
}
@if (processBulkDeleteService.isProcessing$() |async) {
<div class="alert alert-info">
<span class="spinner-border spinner-border-sm spinner-button" role="status" aria-hidden="true"></span>
<span> {{ 'process.overview.delete.processing' | translate: {count: processBulkDeleteService.getAmountOfSelectedProcesses()} }}</span>
</div>
}
<div class="mt-4">
<button class="btn btn-primary me-2" [dsBtnDisabled]="processBulkDeleteService.isProcessing$() |async"
(click)="closeModal()">{{'process.detail.delete.cancel' | translate}}</button>
<button id="delete-confirm" class="btn btn-danger"
[dsBtnDisabled]="processBulkDeleteService.isProcessing$() |async"
(click)="deleteSelected()">{{ 'process.overview.delete' | translate: {count: processBulkDeleteService.getAmountOfSelectedProcesses()} }}
</button>
</div>
</div>
</div>
</ng-template> </ng-template>

View File

@@ -2,10 +2,12 @@
<div class="d-flex" (click)="collapse.toggle()" [attr.aria-expanded]="!collapse.collapsed" role="button"> <div class="d-flex" (click)="collapse.toggle()" [attr.aria-expanded]="!collapse.collapsed" role="button">
<h2 class="flex-grow-1"> <h2 class="flex-grow-1">
{{'process.overview.table.' + processStatus.toLowerCase() + '.title' | translate}} {{'process.overview.table.' + processStatus.toLowerCase() + '.title' | translate}}
<span class="badge rounded-pill bg-primary badge-nb-processes ps-2 pe-2" @if ((processesRD$ | async); as processesRD) {
*ngIf="(processesRD$ | async) as processesRD"> <span class="badge rounded-pill bg-primary badge-nb-processes ps-2 pe-2"
{{processesRD?.payload?.totalElements}} >
</span> {{processesRD?.payload?.totalElements}}
</span>
}
<span class="ms-2 toggle-icon"> <span class="ms-2 toggle-icon">
<i class="fas" [ngClass]="collapse.collapsed ? 'fa-angle-right' : 'fa-angle-down'"></i> <i class="fas" [ngClass]="collapse.collapsed ? 'fa-angle-right' : 'fa-angle-down'"></i>
</span> </span>
@@ -15,50 +17,55 @@
<div ngbCollapse #collapse="ngbCollapse" [ngbCollapse]="isCollapsed"> <div ngbCollapse #collapse="ngbCollapse" [ngbCollapse]="isCollapsed">
<ng-container *ngVar="(processesRD$ | async) as processesRD"> <ng-container *ngVar="(processesRD$ | async) as processesRD">
<ds-loading *ngIf="!processesRD || processesRD.isLoading"/> @if (!processesRD || processesRD.isLoading) {
<ds-loading/>
}
<ds-pagination *ngIf="processesRD?.payload?.totalElements > 0" @if (processesRD?.payload?.totalElements > 0) {
[paginationOptions]="(paginationOptions$ | async)" <ds-pagination
[collectionSize]="processesRD?.payload?.totalElements" [paginationOptions]="(paginationOptions$ | async)"
[retainScrollPosition]="true" [collectionSize]="processesRD?.payload?.totalElements"
[hideGear]="true"> [retainScrollPosition]="true"
[hideGear]="true">
<div class="table-responsive mt-1"> <div class="table-responsive mt-1">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col" class="id-header">{{'process.overview.table.id' | translate}}</th> <th scope="col" class="id-header">{{'process.overview.table.id' | translate}}</th>
<th scope="col" class="name-header">{{'process.overview.table.name' | translate}}</th> <th scope="col" class="name-header">{{'process.overview.table.name' | translate}}</th>
<th scope="col" class="user-header">{{'process.overview.table.user' | translate}}</th> <th scope="col" class="user-header">{{'process.overview.table.user' | translate}}</th>
<th scope="col" class="info-header">{{'process.overview.table.' + processStatus.toLowerCase() + '.info' | translate}}</th> <th scope="col" class="info-header">{{'process.overview.table.' + processStatus.toLowerCase() + '.info' | translate}}</th>
<th scope="col" class="actions-header">{{'process.overview.table.actions' | translate}}</th> <th scope="col" class="actions-header">{{'process.overview.table.actions' | translate}}</th>
</tr> </tr>
</thead> </thead>
<tbody>
<tbody> @for (tableEntry of processesRD?.payload?.page; track tableEntry) {
<tr *ngFor="let tableEntry of processesRD?.payload?.page" <tr
[class]="getRowClass(tableEntry.process)"> [class]="getRowClass(tableEntry.process)">
<td><a [routerLink]="['/processes/', tableEntry.process.processId]">{{tableEntry.process.processId}}</a></td> <td><a [routerLink]="['/processes/', tableEntry.process.processId]">{{tableEntry.process.processId}}</a></td>
<td><a [routerLink]="['/processes/', tableEntry.process.processId]">{{tableEntry.process.scriptName}}</a></td> <td><a [routerLink]="['/processes/', tableEntry.process.processId]">{{tableEntry.process.scriptName}}</a></td>
<td>{{tableEntry.user}}</td> <td>{{tableEntry.user}}</td>
<td>{{tableEntry.info}}</td> <td>{{tableEntry.info}}</td>
<td> <td>
<button [attr.aria-label]="'process.overview.delete-process' | translate" <button [attr.aria-label]="'process.overview.delete-process' | translate"
(click)="processBulkDeleteService.toggleDelete(tableEntry.process.processId)" (click)="processBulkDeleteService.toggleDelete(tableEntry.process.processId)"
class="btn btn-outline-danger"> class="btn btn-outline-danger">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
</td> </td>
</tr> </tr>
</tbody> }
</table> </tbody>
</table>
</div>
</ds-pagination>
}
@if (processesRD?.payload?.totalElements === 0) {
<div>
<p>{{'process.overview.table.empty' | translate}}</p>
</div> </div>
}
</ds-pagination>
<div *ngIf="processesRD?.payload?.totalElements === 0">
<p>{{'process.overview.table.empty' | translate}}</p>
</div>
</ng-container> </ng-container>
</div> </div>

View File

@@ -1,36 +1,38 @@
<div> <div>
<div class="modal-header"><h4>{{'dso-selector.claim.item.head' | translate}}</h4> <div class="modal-header"><h4>{{'dso-selector.claim.item.head' | translate}}</h4>
<button type="button" class="btn-close" (click)="close()" aria-label="Close"> <button type="button" class="btn-close" (click)="close()" aria-label="Close">
</button> </button>
</div>
<div class="modal-body">
<div class="alert alert-info" role="alert">
{{'dso-selector.claim.item.body' | translate}}
</div> </div>
<div class="modal-body"> <div class="dropdown-divider"></div>
<div class="alert alert-info" role="alert"> <div class="scrollable-menu list-group container">
{{'dso-selector.claim.item.body' | translate}} @for (listEntry of (listEntries$ | async)?.payload.page; track listEntry) {
</div> <div class="row">
<div class="dropdown-divider"></div>
<div class="scrollable-menu list-group container">
<div *ngFor="let listEntry of (listEntries$ | async)?.payload.page" class="row">
<button class="list-group-item list-group-item-action border-0 list-entry" <button class="list-group-item list-group-item-action border-0 list-entry"
title="{{ dsoNameService.getName(listEntry.indexableObject) }}" title="{{ dsoNameService.getName(listEntry.indexableObject) }}"
(click)="selectItem(listEntry.indexableObject)" #listEntryElement> (click)="selectItem(listEntry.indexableObject)" #listEntryElement>
<ds-listable-object-component-loader [object]="listEntry" [viewMode]="viewMode" <ds-listable-object-component-loader [object]="listEntry" [viewMode]="viewMode"
[linkType]=linkTypes.None></ds-listable-object-component-loader> [linkType]=linkTypes.None></ds-listable-object-component-loader>
</button> </button>
</div> </div>
<div class="row"> }
<div class="col-md mt-2"> <div class="row">
</div> <div class="col-md mt-2">
</div> </div>
</div> </div>
</div> </div>
<div class="modal-footer"> </div>
<div class="me-5"> <div class="modal-footer">
<input type="checkbox" [checked]="checked" (change)="toggleCheckbox()"/> <div class="me-5">
{{ 'dso-selector.claim.item.not-mine-label' | translate }} <input type="checkbox" [checked]="checked" (change)="toggleCheckbox()"/>
</div> {{ 'dso-selector.claim.item.not-mine-label' | translate }}
<button type="submit" class="btn btn-primary ms-5 me-2" (click)="createFromScratch()" [dsBtnDisabled]="!checked"> </div>
<i class="fas fa-plus"></i> <button type="submit" class="btn btn-primary ms-5 me-2" (click)="createFromScratch()" [dsBtnDisabled]="!checked">
{{ 'dso-selector.claim.item.create-from-scratch' | translate }} <i class="fas fa-plus"></i>
</button> {{ 'dso-selector.claim.item.create-from-scratch' | translate }}
</div> </button>
</div>
</div> </div>

View File

@@ -1,38 +1,52 @@
<div *ngVar="(researcherProfile$ | async) as researcherProfile"> <div *ngVar="(researcherProfile$ | async) as researcherProfile">
<div *ngIf="researcherProfile"> @if (researcherProfile) {
<p>{{'researcher.profile.associated' | translate}}</p> <div>
<p class="align-items-center researcher-profile-switch" > <p>{{'researcher.profile.associated' | translate}}</p>
<span class="me-3">{{'researcher.profile.status' | translate}}</span> <p class="align-items-center researcher-profile-switch" >
<ui-switch [checkedLabel]="'researcher.profile.public.visibility' | translate" <span class="me-3">{{'researcher.profile.status' | translate}}</span>
[uncheckedLabel]="'researcher.profile.private.visibility' | translate" <ui-switch [checkedLabel]="'researcher.profile.public.visibility' | translate"
[checked]="researcherProfile.visible" [uncheckedLabel]="'researcher.profile.private.visibility' | translate"
(change)="toggleProfileVisibility(researcherProfile)" ></ui-switch> [checked]="researcherProfile.visible"
</p> (change)="toggleProfileVisibility(researcherProfile)" ></ui-switch>
</div> </p>
<div *ngIf="!researcherProfile"> </div>
<p>{{'researcher.profile.not.associated' | translate}}</p> }
</div> @if (!researcherProfile) {
<button *ngIf="!researcherProfile" class="btn btn-primary me-2" <div>
[dsBtnDisabled]="(isProcessingCreate() | async)" <p>{{'researcher.profile.not.associated' | translate}}</p>
(click)="createProfile()"> </div>
<span *ngIf="(isProcessingCreate() | async)"> }
<i class='fas fa-circle-notch fa-spin'></i> {{'researcher.profile.action.processing' | translate}} @if (!researcherProfile) {
</span> <button class="btn btn-primary me-2"
<span *ngIf="(isProcessingCreate() | async) !== true"> [dsBtnDisabled]="(isProcessingCreate() | async)"
<i class="fas fa-plus"></i> &nbsp;{{'researcher.profile.create.new' | translate}} (click)="createProfile()">
</span> @if ((isProcessingCreate() | async)) {
</button> <span>
<ng-container *ngIf="researcherProfile"> <i class='fas fa-circle-notch fa-spin'></i> {{'researcher.profile.action.processing' | translate}}
</span>
}
@if ((isProcessingCreate() | async) !== true) {
<span>
<i class="fas fa-plus"></i> &nbsp;{{'researcher.profile.create.new' | translate}}
</span>
}
</button>
}
@if (researcherProfile) {
<button class="btn btn-primary me-2" [dsBtnDisabled]="!researcherProfile" (click)="viewProfile(researcherProfile)"> <button class="btn btn-primary me-2" [dsBtnDisabled]="!researcherProfile" (click)="viewProfile(researcherProfile)">
<i class="fas fa-info-circle"></i> {{'researcher.profile.view' | translate}} <i class="fas fa-info-circle"></i> {{'researcher.profile.view' | translate}}
</button> </button>
<button class="btn btn-danger" [dsBtnDisabled]="!researcherProfile" (click)="deleteProfile(researcherProfile)"> <button class="btn btn-danger" [dsBtnDisabled]="!researcherProfile" (click)="deleteProfile(researcherProfile)">
<span *ngIf="(isProcessingDelete() | async)"> @if ((isProcessingDelete() | async)) {
<i class='fas fa-circle-notch fa-spin'></i> {{'researcher.profile.action.processing' | translate}} <span>
<i class='fas fa-circle-notch fa-spin'></i> {{'researcher.profile.action.processing' | translate}}
</span> </span>
<span *ngIf="(isProcessingDelete() | async) !== true"> }
<i class="fas fa-trash-alt"></i> &nbsp;{{'researcher.profile.delete' | translate}} @if ((isProcessingDelete() | async) !== true) {
<span>
<i class="fas fa-trash-alt"></i> &nbsp;{{'researcher.profile.delete' | translate}}
</span> </span>
}
</button> </button>
</ng-container> }
</div> </div>

View File

@@ -1,72 +1,86 @@
<ng-container *ngVar="(user$ | async) as user"> <ng-container *ngVar="(user$ | async) as user">
<div class="container" *ngIf="user"> @if (user) {
<h1>{{'profile.title' | translate}}</h1> <div class="container">
<ng-container *ngIf="isResearcherProfileEnabled$ | async"> <h1>{{'profile.title' | translate}}</h1>
<div class="card mb-4"> @if (isResearcherProfileEnabled$ | async) {
<div class="card-header">{{'profile.card.researcher' | translate}}</div> <div class="card mb-4">
<div class="card-body"> <div class="card-header">{{'profile.card.researcher' | translate}}</div>
<div class="mb-4"> <div class="card-body">
<ds-profile-page-researcher-form [user]="user" ></ds-profile-page-researcher-form> <div class="mb-4">
<ds-profile-page-researcher-form [user]="user" ></ds-profile-page-researcher-form>
</div>
<ds-suggestions-notification></ds-suggestions-notification>
</div> </div>
<ds-suggestions-notification></ds-suggestions-notification> </div>
}
<div class="card mb-4">
<div class="card-header">{{'profile.card.identify' | translate}}</div>
<div class="card-body">
<ds-profile-page-metadata-form [user]="user"></ds-profile-page-metadata-form>
</div> </div>
</div> </div>
</ng-container> @if (canChangePassword$ | async) {
<div class="card mb-4"> <div class="card mb-4 security-section">
<div class="card-header">{{'profile.card.identify' | translate}}</div> <div class="card-header">{{'profile.card.security' | translate}}</div>
<div class="card-body"> <div class="card-body">
<ds-profile-page-metadata-form [user]="user"></ds-profile-page-metadata-form> <ds-profile-page-security-form
[FORM_PREFIX]="'profile.security.form.'"
(isInvalid)="setInvalid($event)"
(passwordValue)="setPasswordValue($event)"
(currentPasswordValue)="setCurrentPasswordValue($event)"
></ds-profile-page-security-form>
</div>
</div>
}
<div class="col-12 text-end pe-0">
<button class="btn btn-primary" (click)="updateProfile()"><i class="fas fa-edit"></i> {{'profile.form.submit' | translate}}</button>
</div> </div>
@if ((groupsRD$ | async); as groupsRD;) {
<ng-container *ngTemplateOutlet="groupsRD?.isLoading ? loader : content"></ng-container>
<ng-template #content>
@if (groupsRD?.payload) {
<ds-pagination
[hideGear]="true"
[hidePagerWhenSinglePage]="true"
[hidePaginationDetail]="true"
[paginationOptions]="optionsGroupsPagination"
[collectionSize]="groupsRD?.payload?.totalElements">
@if (groupsRD?.payload?.page; as groups) {
@if (groups?.length > 0) {
<div>
<h2 class="mt-4">{{ 'profile.groups.head' | translate }}</h2>
<ul class="list-group list-group-flush">
@for (group of groups; track group) {
<li class="list-group-item">{{ dsoNameService.getName(group) }}</li>
}
</ul>
</div>
}
}
</ds-pagination>
}
</ng-template>
<ng-template #loader>
<ds-loading [showMessage]="false"></ds-loading>
</ng-template>
@if (groupsRD?.hasFailed) {
<ds-error message="{{ 'error.profile-groups' | translate }}"></ds-error>
}
}
<ng-container *ngVar="(specialGroupsRD$ | async)?.payload?.page as specialGroups">
@if (specialGroups?.length > 0) {
<div data-test="specialGroups">
<h2 class="mt-4">{{'profile.special.groups.head' | translate}}</h2>
<ul class="list-group list-group-flush">
@for (specialGroup of specialGroups; track specialGroup) {
<li class="list-group-item">
{{ dsoNameService.getName(specialGroup) }}
</li>
}
</ul>
</div>
}
</ng-container>
</div> </div>
<div *ngIf="canChangePassword$ | async" class="card mb-4 security-section"> }
<div class="card-header">{{'profile.card.security' | translate}}</div>
<div class="card-body">
<ds-profile-page-security-form
[FORM_PREFIX]="'profile.security.form.'"
(isInvalid)="setInvalid($event)"
(passwordValue)="setPasswordValue($event)"
(currentPasswordValue)="setCurrentPasswordValue($event)"
></ds-profile-page-security-form>
</div>
</div>
<div class="col-12 text-end pe-0">
<button class="btn btn-primary" (click)="updateProfile()"><i class="fas fa-edit"></i> {{'profile.form.submit' | translate}}</button>
</div>
<ng-container *ngIf="(groupsRD$ | async) as groupsRD;">
<ng-container *ngTemplateOutlet="groupsRD?.isLoading ? loader : content"></ng-container>
<ng-template #content>
<ds-pagination *ngIf="groupsRD?.payload"
[hideGear]="true"
[hidePagerWhenSinglePage]="true"
[hidePaginationDetail]="true"
[paginationOptions]="optionsGroupsPagination"
[collectionSize]="groupsRD?.payload?.totalElements">
<ng-container *ngIf="groupsRD?.payload?.page as groups">
<div *ngIf="groups?.length > 0">
<h2 class="mt-4">{{ 'profile.groups.head' | translate }}</h2>
<ul class="list-group list-group-flush">
<li *ngFor="let group of groups" class="list-group-item">{{ dsoNameService.getName(group) }}</li>
</ul>
</div>
</ng-container>
</ds-pagination>
</ng-template>
<ng-template #loader>
<ds-loading [showMessage]="false"></ds-loading>
</ng-template>
<ds-error *ngIf="groupsRD?.hasFailed" message="{{ 'error.profile-groups' | translate }}"></ds-error>
</ng-container>
<ng-container *ngVar="(specialGroupsRD$ | async)?.payload?.page as specialGroups">
<div *ngIf="specialGroups?.length > 0" data-test="specialGroups">
<h2 class="mt-4">{{'profile.special.groups.head' | translate}}</h2>
<ul class="list-group list-group-flush">
<li *ngFor="let specialGroup of specialGroups" class="list-group-item">
{{ dsoNameService.getName(specialGroup) }}
</li>
</ul>
</div>
</ng-container>
</div>
</ng-container> </ng-container>

View File

@@ -1,66 +1,77 @@
<div class="container"> <div class="container">
<h1>{{MESSAGE_PREFIX + '.header'|translate}}</h1> <h1>{{MESSAGE_PREFIX + '.header'|translate}}</h1>
<p>{{MESSAGE_PREFIX + '.info' | translate}}</p> <p>{{MESSAGE_PREFIX + '.info' | translate}}</p>
<p *ngIf="validMailDomains.length !== 0 && typeRequest === TYPE_REQUEST_REGISTER"> @if (validMailDomains.length !== 0 && typeRequest === TYPE_REQUEST_REGISTER) {
<p>
{{ MESSAGE_PREFIX + '.info.maildomain' | translate}} {{ validMailDomains.join(', ')}} {{ MESSAGE_PREFIX + '.info.maildomain' | translate}} {{ validMailDomains.join(', ')}}
</p> </p>
}
<form [class]="'ng-invalid'" [formGroup]="form"> <form [class]="'ng-invalid'" [formGroup]="form">
<div class="mb-3">
<div class="row">
<div class="col-12">
<label class="fw-bold form-label"
for="email">{{MESSAGE_PREFIX + '.email' | translate}}</label>
<input [className]="(email.invalid) && (email.dirty || email.touched) ? 'form-control is-invalid' :'form-control'"
type="text" id="email" formControlName="email"
[attr.aria-label]="MESSAGE_PREFIX + '.aria.label' | translate"
[attr.aria-describedby]="(!email.errors) ? '' : (email.errors.required ? 'email-errors-required' : 'email-error-not-valid')"
[attr.aria-invalid]="email.invalid"/>
<div *ngIf="email.invalid && (email.dirty || email.touched)"
class="invalid-feedback show-feedback">
<span *ngIf="email.errors && email.errors.required" id="email-errors-required">
{{ MESSAGE_PREFIX + '.email.error.required' | translate }}
</span>
<span *ngIf="email.errors && ((email.errors.pattern && this.typeRequest === TYPE_REQUEST_REGISTER) || email.errors.email)" id="email-error-not-valid">
{{ MESSAGE_PREFIX + '.email.error.not-email-form' | translate }}
<ng-container *ngIf="validMailDomains.length > 0">
{{ MESSAGE_PREFIX + '.email.error.not-valid-domain' | translate: { domains: validMailDomains.join(', ') } }}
</ng-container>
</span>
</div>
</div>
<div class="col-12">
{{MESSAGE_PREFIX + '.email.hint' |translate}}
</div>
<div class="mb-3">
<div class="row">
<div class="col-12">
<label class="fw-bold form-label"
for="email">{{MESSAGE_PREFIX + '.email' | translate}}</label>
<input [className]="(email.invalid) && (email.dirty || email.touched) ? 'form-control is-invalid' :'form-control'"
type="text" id="email" formControlName="email"
[attr.aria-label]="MESSAGE_PREFIX + '.aria.label' | translate"
[attr.aria-describedby]="(!email.errors) ? '' : (email.errors.required ? 'email-errors-required' : 'email-error-not-valid')"
[attr.aria-invalid]="email.invalid"/>
@if (email.invalid && (email.dirty || email.touched)) {
<div
class="invalid-feedback show-feedback">
@if (email.errors && email.errors.required) {
<span id="email-errors-required">
{{ MESSAGE_PREFIX + '.email.error.required' | translate }}
</span>
}
@if (email.errors && ((email.errors.pattern && this.typeRequest === TYPE_REQUEST_REGISTER) || email.errors.email)) {
<span id="email-error-not-valid">
{{ MESSAGE_PREFIX + '.email.error.not-email-form' | translate }}
@if (validMailDomains.length > 0) {
{{ MESSAGE_PREFIX + '.email.error.not-valid-domain' | translate: { domains: validMailDomains.join(', ') } }}
}
</span>
}
</div> </div>
}
</div>
<div class="col-12">
{{MESSAGE_PREFIX + '.email.hint' |translate}}
</div> </div>
<ds-alert [type]="AlertTypeEnum.Warning" *ngIf="registrationVerification && !isRecaptchaCookieAccepted()"> </div>
<p class="m-0" [innerHTML]="MESSAGE_PREFIX + '.google-recaptcha.must-accept-cookies' | translate"></p>
<p class="m-0"><a href="javascript:void(0);" (click)="this.orejimeService.showSettings()">{{ MESSAGE_PREFIX + '.google-recaptcha.open-cookie-settings' | translate }}</a></p>
</ds-alert>
<div class="my-3" *ngIf="isRecaptchaCookieAccepted() && (googleRecaptchaService.captchaVersion() | async) === 'v2'">
<ds-google-recaptcha [captchaMode]="(googleRecaptchaService.captchaMode() | async)"
(executeRecaptcha)="register($event)" (checkboxChecked)="onCheckboxChecked($event)"
(showNotification)="showNotification($event)"></ds-google-recaptcha>
</div> </div>
<ng-container *ngIf="(!registrationVerification || ((googleRecaptchaService.captchaVersion() | async) !== 'v2' && (googleRecaptchaService.captchaMode() | async) === 'invisible')); else v2Invisible"> @if (registrationVerification && !isRecaptchaCookieAccepted()) {
<ds-alert [type]="AlertTypeEnum.Warning">
<p class="m-0" [innerHTML]="MESSAGE_PREFIX + '.google-recaptcha.must-accept-cookies' | translate"></p>
<p class="m-0"><a href="javascript:void(0);" (click)="this.orejimeService.showSettings()">{{ MESSAGE_PREFIX + '.google-recaptcha.open-cookie-settings' | translate }}</a></p>
</ds-alert>
}
@if (isRecaptchaCookieAccepted() && (googleRecaptchaService.captchaVersion() | async) === 'v2') {
<div class="my-3">
<ds-google-recaptcha [captchaMode]="(googleRecaptchaService.captchaMode() | async)"
(executeRecaptcha)="register($event)" (checkboxChecked)="onCheckboxChecked($event)"
(showNotification)="showNotification($event)"></ds-google-recaptcha>
</div>
}
@if ((!registrationVerification || ((googleRecaptchaService.captchaVersion() | async) !== 'v2' && (googleRecaptchaService.captchaMode() | async) === 'invisible'))) {
<button class="btn btn-primary" [dsBtnDisabled]="form.invalid || registrationVerification && !isRecaptchaCookieAccepted() || disableUntilChecked" (click)="register()"> <button class="btn btn-primary" [dsBtnDisabled]="form.invalid || registrationVerification && !isRecaptchaCookieAccepted() || disableUntilChecked" (click)="register()">
{{ MESSAGE_PREFIX + '.submit' | translate }} {{ MESSAGE_PREFIX + '.submit' | translate }}
</button> </button>
</ng-container> } @else {
<ng-template #v2Invisible>
<button class="btn btn-primary" [dsBtnDisabled]="form.invalid" (click)="executeRecaptcha()"> <button class="btn btn-primary" [dsBtnDisabled]="form.invalid" (click)="executeRecaptcha()">
{{ MESSAGE_PREFIX + '.submit' | translate }} {{ MESSAGE_PREFIX + '.submit' | translate }}
</button> </button>
</ng-template> }
</form>
</form>
</div> </div>

View File

@@ -1,91 +1,94 @@
<div class="container" *ngIf="(registration$ |async)"> @if ((registration$ |async)) {
<div class="container">
<h3 class="mb-4">{{'register-page.create-profile.header' | translate}}</h3> <h3 class="mb-4">{{'register-page.create-profile.header' | translate}}</h3>
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header">{{'register-page.create-profile.identification.header' | translate}}</div> <div class="card-header">{{'register-page.create-profile.identification.header' | translate}}</div>
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
<div class="col-12">
<label class="fw-bold form-label me-4"
for="email">{{'register-page.create-profile.identification.email' | translate}}</label>
<span id="email">{{(registration$ |async).email}}</span></div>
</div>
<form [class]="'ng-invalid'" [formGroup]="userInfoForm" (ngSubmit)="submitEperson()">
<div class="mb-3">
<div class="row">
<div class="col-12"> <div class="col-12">
<label class="fw-bold form-label me-4" <label for="firstName" class="form-label">{{'register-page.create-profile.identification.first-name' | translate}}</label>
for="email">{{'register-page.create-profile.identification.email' | translate}}</label> <input [className]="(firstName.invalid) && (firstName.dirty || firstName.touched) ? 'form-control is-invalid' :'form-control'"
<span id="email">{{(registration$ |async).email}}</span></div> type="text" id="firstName" formControlName="firstName"/>
</div> @if (firstName.invalid && (firstName.dirty || firstName.touched)) {
<form [class]="'ng-invalid'" [formGroup]="userInfoForm" (ngSubmit)="submitEperson()"> <div
class="invalid-feedback show-feedback">
<div class="mb-3"> @if (firstName.errors && firstName.errors.required) {
<div class="row"> <span>
<div class="col-12"> {{ 'register-page.create-profile.identification.first-name.error' | translate }}
<label for="firstName" class="form-label">{{'register-page.create-profile.identification.first-name' | translate}}</label> </span>
<input [className]="(firstName.invalid) && (firstName.dirty || firstName.touched) ? 'form-control is-invalid' :'form-control'" }
type="text" id="firstName" formControlName="firstName"/>
<div *ngIf="firstName.invalid && (firstName.dirty || firstName.touched)"
class="invalid-feedback show-feedback">
<span *ngIf="firstName.errors && firstName.errors.required">
{{ 'register-page.create-profile.identification.first-name.error' | translate }}
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<label
for="lastName" class="form-label">{{'register-page.create-profile.identification.last-name' | translate}}</label>
<input
[className]="(lastName.invalid) && (lastName.dirty || lastName.touched) ? 'form-control is-invalid' :'form-control'"
id="lastName" formControlName="lastName">
<div *ngIf="lastName.invalid && (lastName.dirty || lastName.touched)"
class="invalid-feedback show-feedback">
<span *ngIf="lastName.errors && lastName.errors.required">
{{ 'register-page.create-profile.identification.last-name.error' | translate }}
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<label class="form-label"
for="contactPhone">{{'register-page.create-profile.identification.contact' | translate}}</label>
<input class="form-control" id="contactPhone" formControlName="contactPhone">
</div>
</div>
<div class="row">
<div class="col-12">
<label class="form-label"
for="language">{{'register-page.create-profile.identification.language' |translate}}</label>
<select id="language" formControlName="language" class="form-select">
<option [value]="''"></option>
<option *ngFor="let lang of activeLangs" [value]="lang.code">{{lang.label}}</option>
</select>
</div>
</div> </div>
}
</div> </div>
</form> </div>
<div class="row">
<div class="col-12">
<label
for="lastName" class="form-label">{{'register-page.create-profile.identification.last-name' | translate}}</label>
<input
[className]="(lastName.invalid) && (lastName.dirty || lastName.touched) ? 'form-control is-invalid' :'form-control'"
id="lastName" formControlName="lastName">
@if (lastName.invalid && (lastName.dirty || lastName.touched)) {
<div
class="invalid-feedback show-feedback">
@if (lastName.errors && lastName.errors.required) {
<span>
{{ 'register-page.create-profile.identification.last-name.error' | translate }}
</span>
}
</div>
}
</div>
</div>
<div class="row">
<div class="col-12">
<label class="form-label"
for="contactPhone">{{'register-page.create-profile.identification.contact' | translate}}</label>
<input class="form-control" id="contactPhone" formControlName="contactPhone">
</div>
</div>
<div class="row">
<div class="col-12">
<label class="form-label"
for="language">{{'register-page.create-profile.identification.language' |translate}}</label>
<select id="language" formControlName="language" class="form-select">
<option [value]="''"></option>
@for (lang of activeLangs; track lang) {
<option [value]="lang.code">{{lang.label}}</option>
}
</select>
</div>
</div>
</div>
</form>
</div> </div>
</div> </div>
<div class="card mb-4">
<div class="card mb-4">
<div class="card-header">{{'register-page.create-profile.security.header' | translate}}</div> <div class="card-header">{{'register-page.create-profile.security.header' | translate}}</div>
<div class="card-body"> <div class="card-body">
<ds-profile-page-security-form
<ds-profile-page-security-form [passwordCanBeEmpty]="false"
[passwordCanBeEmpty]="false" [FORM_PREFIX]="'register-page.create-profile.security.'"
[FORM_PREFIX]="'register-page.create-profile.security.'" (isInvalid)="setInValid($event)"
(isInvalid)="setInValid($event)" (passwordValue)="setPasswordValue($event)"
(passwordValue)="setPasswordValue($event)" ></ds-profile-page-security-form>
></ds-profile-page-security-form>
</div> </div>
</div> </div>
<div class="row">
<div class="row">
<div class="col-12"> <div class="col-12">
<button <button
[dsBtnDisabled]="isInValidPassword || userInfoForm.invalid" [dsBtnDisabled]="isInValidPassword || userInfoForm.invalid"
class="btn btn-default btn-primary" class="btn btn-default btn-primary"
(click)="submitEperson()">{{'register-page.create-profile.submit' | translate}}</button> (click)="submitEperson()">{{'register-page.create-profile.submit' | translate}}</button>
</div> </div>
</div>
</div> </div>
}
</div>

View File

@@ -2,9 +2,11 @@
<div class="mb-3"> <div class="mb-3">
<label for="subject" class="form-label">{{ 'grant-deny-request-copy.email.subject' | translate }}</label> <label for="subject" class="form-label">{{ 'grant-deny-request-copy.email.subject' | translate }}</label>
<input type="text" class="form-control" id="subject" [ngClass]="{'is-invalid': !subject || subject.length === 0}" [(ngModel)]="subject" name="subject"> <input type="text" class="form-control" id="subject" [ngClass]="{'is-invalid': !subject || subject.length === 0}" [(ngModel)]="subject" name="subject">
<div *ngIf="!subject || subject.length === 0" class="invalid-feedback"> @if (!subject || subject.length === 0) {
{{ 'grant-deny-request-copy.email.subject.empty' | translate }} <div class="invalid-feedback">
</div> {{ 'grant-deny-request-copy.email.subject.empty' | translate }}
</div>
}
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="message" class="form-label">{{ 'grant-deny-request-copy.email.message' | translate }}</label> <label for="message" class="form-label">{{ 'grant-deny-request-copy.email.message' | translate }}</label>
@@ -13,14 +15,14 @@
<ng-content></ng-content> <ng-content></ng-content>
<div class="d-flex flex-row-reverse"> <div class="d-flex flex-row-reverse">
<button (click)="submit()" <button (click)="submit()"
[dsBtnDisabled]="!subject || subject.length === 0" [dsBtnDisabled]="!subject || subject.length === 0"
class="btn btn-primary" class="btn btn-primary"
title="{{'grant-deny-request-copy.email.send' | translate }}"> title="{{'grant-deny-request-copy.email.send' | translate }}">
<i class="fas fa-envelope"></i> {{'grant-deny-request-copy.email.send' | translate }} <i class="fas fa-envelope"></i> {{'grant-deny-request-copy.email.send' | translate }}
</button> </button>
<button (click)="return()" <button (click)="return()"
class="btn btn-outline-secondary me-1" class="btn btn-outline-secondary me-1"
title="{{'grant-deny-request-copy.email.back' | translate }}"> title="{{'grant-deny-request-copy.email.back' | translate }}">
<i class="fas fa-arrow-left"></i> {{'grant-deny-request-copy.email.back' | translate }} <i class="fas fa-arrow-left"></i> {{'grant-deny-request-copy.email.back' | translate }}
</button> </button>
</div> </div>

View File

@@ -1,11 +1,12 @@
<form #ngForm="ngForm"> <form #ngForm="ngForm">
<div *ngIf="showWarning" class="alert alert-warning"> @if (showWarning) {
{{'access-control-no-access-conditions-warning-message' | translate}} <div class="alert alert-warning">
</div> {{'access-control-no-access-conditions-warning-message' | translate}}
</div>
}
<ng-container *ngFor="let control of form.accessControls; trackBy: trackById"> @for (control of form.accessControls; track trackById($index, control)) {
<div ngModelGroup="access-control-item-{{control.id}}" class="access-control-item mt-3"> <div ngModelGroup="access-control-item-{{control.id}}" class="access-control-item mt-3">
<div class="d-flex flex-column"> <div class="d-flex flex-column">
<div> <div>
<label class="form-label" for="accesscontroloption-{{control.id}}"> <label class="form-label" for="accesscontroloption-{{control.id}}">
@@ -19,92 +20,94 @@
name="itemName-{{control.id}}" name="itemName-{{control.id}}"
class="form-select"> class="form-select">
<option value=""></option> <option value=""></option>
<option *ngFor="let option of dropdownOptions" [value]="option.name"> @for (option of dropdownOptions; track option) {
{{ option.name }} <option [value]="option.name">
</option> {{ option.name }}
</option>
}
</select> </select>
<small class="form-text text-muted"> <small class="form-text text-muted">
{{'access-control-option-note' | translate}} {{'access-control-option-note' | translate}}
</small> </small>
</div> </div>
@if (control.hasStartDate) {
<div *ngIf="control.hasStartDate" class="mt-3"> <div class="mt-3">
<label class="form-label" for="accesscontrolstartdate-{{control.id}}"> <label class="form-label" for="accesscontrolstartdate-{{control.id}}">
{{'access-control-option-start-date' | translate}} {{'access-control-option-start-date' | translate}}
</label> </label>
<div class="input-group"> <div class="input-group">
<input <input
id="accesscontrolstartdate-{{control.id}}" id="accesscontrolstartdate-{{control.id}}"
class="form-control" class="form-control"
placeholder="yyyy-mm-dd" placeholder="yyyy-mm-dd"
[(ngModel)]="control.startDate" [(ngModel)]="control.startDate"
name="startDate-{{control.id}}" name="startDate-{{control.id}}"
[maxDate]="control.maxStartDate | toDate" [maxDate]="control.maxStartDate | toDate"
ngbDatepicker ngbDatepicker
placement="top-start top-end bottom-start bottom-end" placement="top-start top-end bottom-start bottom-end"
#d="ngbDatepicker" #d="ngbDatepicker"
/> />
<div class="input-group-append"> <div class="input-group-append">
<button <button
class="btn btn-outline-secondary fas fa-calendar" class="btn btn-outline-secondary fas fa-calendar"
[dsBtnDisabled]="ngForm.disabled" [dsBtnDisabled]="ngForm.disabled"
(click)="d.toggle()" type="button"> (click)="d.toggle()" type="button">
</button> </button>
</div>
</div> </div>
<small class="form-text text-muted">
{{'access-control-option-start-date-note' | translate}}
</small>
</div> </div>
<small class="form-text text-muted"> }
{{'access-control-option-start-date-note' | translate}} @if (control.hasEndDate) {
</small> <div class="mt-3">
</div> <label class="form-label" for="accesscontrolenddate-{{control.id}}">
{{'access-control-option-end-date' | translate}}
<div *ngIf="control.hasEndDate" class="mt-3"> </label>
<label class="form-label" for="accesscontrolenddate-{{control.id}}"> <div class="input-group">
{{'access-control-option-end-date' | translate}} <input
</label> id="accesscontrolenddate-{{control.id}}"
<div class="input-group"> class="form-control"
<input placeholder="yyyy-mm-dd"
id="accesscontrolenddate-{{control.id}}" [(ngModel)]="control.endDate"
class="form-control" name="endDate-{{control.id}}"
placeholder="yyyy-mm-dd" [maxDate]="control.maxEndDate | toDate"
[(ngModel)]="control.endDate" ngbDatepicker
name="endDate-{{control.id}}" placement="top-start top-end bottom-start bottom-end"
[maxDate]="control.maxEndDate | toDate" #d1="ngbDatepicker"
ngbDatepicker />
placement="top-start top-end bottom-start bottom-end" <div class="input-group-append">
#d1="ngbDatepicker" <button
/> type="button" class="btn btn-outline-secondary fas fa-calendar"
<div class="input-group-append"> [dsBtnDisabled]="ngForm.disabled"
<button (click)="d1.toggle()">
type="button" class="btn btn-outline-secondary fas fa-calendar" </button>
[dsBtnDisabled]="ngForm.disabled" </div>
(click)="d1.toggle()">
</button>
</div> </div>
<small class="form-text text-muted">
{{'access-control-option-end-date-note' | translate}}
</small>
</div> </div>
<small class="form-text text-muted"> }
{{'access-control-option-end-date-note' | translate}}
</small>
</div>
</div> </div>
<div> <div>
<span aria-hidden="true" class="mb-2 d-inline-block">&nbsp;</span> <span aria-hidden="true" class="mb-2 d-inline-block">&nbsp;</span>
<div class="input-group"> <div class="input-group">
<button type="button" class="btn btn-outline-danger" <button type="button" class="btn btn-outline-danger"
[attr.aria-label]="'access-control-remove' | translate" [attr.aria-label]="'access-control-remove' | translate"
[dsBtnDisabled]="ngForm.disabled || form.accessControls.length === 1" [dsBtnDisabled]="ngForm.disabled || form.accessControls.length === 1"
(click)="removeAccessControlItem(control.id)"> (click)="removeAccessControlItem(control.id)">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</ng-container> }
<button type="button" id="add-btn-{{type}}" class="btn btn-outline-primary mt-3" <button type="button" id="add-btn-{{type}}" class="btn btn-outline-primary mt-3"
[dsBtnDisabled]="ngForm.disabled" [dsBtnDisabled]="ngForm.disabled"
(click)="addAccessControlItem()"> (click)="addAccessControlItem()">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
{{'access-control-add-more' | translate}} {{'access-control-add-more' | translate}}
</button> </button>

View File

@@ -1,12 +1,13 @@
<div class="container"> <div class="container">
<div class="card border-0"> <div class="card border-0">
<div class="card-body"> <div class="card-body">
<ds-alert @if (titleMessage) {
*ngIf="titleMessage" <ds-alert
[type]="AlertType.Info" [type]="AlertType.Info"
[content]="titleMessage" [content]="titleMessage"
class="d-block pb-3"> class="d-block pb-3">
</ds-alert> </ds-alert>
}
<div class="row mt-5"> <div class="row mt-5">
<div class="col-12 col-md-6 border-end d-flex flex-column justify-content-between"> <div class="col-12 col-md-6 border-end d-flex flex-column justify-content-between">
@@ -30,18 +31,18 @@
<div class="col-12 col-md-8"> <div class="col-12 col-md-8">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" <input class="form-check-input" type="radio"
name="itemMode" id="itemReplace" value="replace" name="itemMode" id="itemReplace" value="replace"
[disabled]="!state.item.toggleStatus" [disabled]="!state.item.toggleStatus"
[(ngModel)]="state.item.accessMode"> [(ngModel)]="state.item.accessMode">
<label class="form-check-label" for="itemReplace"> <label class="form-check-label" for="itemReplace">
{{'access-control-replace-all' | translate}} {{'access-control-replace-all' | translate}}
</label> </label>
</div> </div>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" <input class="form-check-input" type="radio"
name="itemMode" id="itemAdd" value="add" name="itemMode" id="itemAdd" value="add"
[disabled]="!state.item.toggleStatus" [disabled]="!state.item.toggleStatus"
[(ngModel)]="state.item.accessMode"> [(ngModel)]="state.item.accessMode">
<label class="form-check-label" for="itemAdd"> <label class="form-check-label" for="itemAdd">
{{'access-control-add-to-existing' | translate}} {{'access-control-add-to-existing' | translate}}
</label> </label>
@@ -75,42 +76,43 @@
</ui-switch> </ui-switch>
</div> </div>
<div *ngIf="showLimitToSpecificBitstreams" class="row mt-3"> @if (showLimitToSpecificBitstreams) {
<fieldset class="col-12"> <div class="row mt-3">
<legend class="h4"> <fieldset class="col-12">
{{'access-control-limit-to-specific' | translate}} <legend class="h4">
</legend> {{'access-control-limit-to-specific' | translate}}
<div class="form-check"> </legend>
<input class="form-check-input" type="radio" <div class="form-check">
name="changesLimit" id="processAll" value="all" <input class="form-check-input" type="radio"
[disabled]="!state.bitstream.toggleStatus" name="changesLimit" id="processAll" value="all"
[(ngModel)]="state.bitstream.changesLimit"> [disabled]="!state.bitstream.toggleStatus"
<label class="form-check-label" for="processAll"> [(ngModel)]="state.bitstream.changesLimit">
{{'access-control-process-all-bitstreams' | translate}} <label class="form-check-label" for="processAll">
</label> {{'access-control-process-all-bitstreams' | translate}}
</label>
</div>
<div class="form-check">
<input class="form-check-input mt-2" type="radio"
name="changesLimit" id="processSelected" value="selected"
[disabled]="!state.bitstream.toggleStatus"
[(ngModel)]="state.bitstream.changesLimit">
<label class="form-check-label" for="processSelected">
{{ state.bitstream.selectedBitstreams.length }}
{{'access-control-bitstreams-selected' | translate}}
@if (itemRD) {
<button
[attr.aria-label]="'access-control-bitstreams-select' | translate"
[dsBtnDisabled]="!state.bitstream.toggleStatus || state.bitstream.changesLimit !== 'selected'"
(click)="openSelectBitstreamsModal(itemRD.payload)"
class="btn btn-outline-dark border-0" type="button">
<i class="fa fa-search"></i>
</button>
}
</label>
</div>
</fieldset>
</div> </div>
<div class="form-check"> }
<input class="form-check-input mt-2" type="radio"
name="changesLimit" id="processSelected" value="selected"
[disabled]="!state.bitstream.toggleStatus"
[(ngModel)]="state.bitstream.changesLimit">
<label class="form-check-label" for="processSelected">
{{ state.bitstream.selectedBitstreams.length }}
{{'access-control-bitstreams-selected' | translate}}
<button
*ngIf="itemRD"
[attr.aria-label]="'access-control-bitstreams-select' | translate"
[dsBtnDisabled]="!state.bitstream.toggleStatus || state.bitstream.changesLimit !== 'selected'"
(click)="openSelectBitstreamsModal(itemRD.payload)"
class="btn btn-outline-dark border-0" type="button">
<i class="fa fa-search"></i>
</button>
</label>
</div>
</fieldset>
</div>
</div> </div>
<fieldset class="row mt-3"> <fieldset class="row mt-3">
@@ -120,18 +122,18 @@
<div class="col-12 col-md-8"> <div class="col-12 col-md-8">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" <input class="form-check-input" type="radio"
name="bitstreamMode" id="bitstreamReplace" value="replace" name="bitstreamMode" id="bitstreamReplace" value="replace"
[disabled]="!state.bitstream.toggleStatus" [disabled]="!state.bitstream.toggleStatus"
[(ngModel)]="state.bitstream.accessMode"> [(ngModel)]="state.bitstream.accessMode">
<label class="form-check-label" for="bitstreamReplace"> <label class="form-check-label" for="bitstreamReplace">
{{'access-control-replace-all' | translate}} {{'access-control-replace-all' | translate}}
</label> </label>
</div> </div>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" <input class="form-check-input" type="radio"
name="bitstreamMode" id="bitstreamAdd" value="add" name="bitstreamMode" id="bitstreamAdd" value="add"
[disabled]="!state.bitstream.toggleStatus" [disabled]="!state.bitstream.toggleStatus"
[(ngModel)]="state.bitstream.accessMode"> [(ngModel)]="state.bitstream.accessMode">
<label class="form-check-label" for="bitstreamAdd"> <label class="form-check-label" for="bitstreamAdd">
{{'access-control-add-to-existing' | translate}} {{'access-control-add-to-existing' | translate}}
</label> </label>
@@ -152,18 +154,22 @@
</div> </div>
</div> </div>
<hr *ngIf="showSubmit"> @if (showSubmit) {
<hr>
}
<div *ngIf="showSubmit" class="d-flex justify-content-end"> @if (showSubmit) {
<button class="btn btn-outline-primary me-3" (click)="reset()" type="button"> <div class="d-flex justify-content-end">
{{ 'access-control-cancel' | translate }} <button class="btn btn-outline-primary me-3" (click)="reset()" type="button">
</button> {{ 'access-control-cancel' | translate }}
<button class="btn btn-primary" </button>
[dsBtnDisabled]="!state.item.toggleStatus && !state.bitstream.toggleStatus" <button class="btn btn-primary"
(click)="submit()" type="submit"> [dsBtnDisabled]="!state.item.toggleStatus && !state.bitstream.toggleStatus"
{{ 'access-control-execute' | translate }} (click)="submit()" type="submit">
</button> {{ 'access-control-execute' | translate }}
</button>
</div>
}
</div> </div>
</div> </div>
</div> </div>
</div>

View File

@@ -3,32 +3,33 @@
{{'access-control-select-bitstreams-modal.title' | translate}} {{'access-control-select-bitstreams-modal.title' | translate}}
</h4> </h4>
<button type="button" class="btn-close" aria-label="Close" <button type="button" class="btn-close" aria-label="Close"
(click)="activeModal.dismiss('Cross click')"> (click)="activeModal.dismiss('Cross click')">
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<ng-container *ngIf="data$ | async as data"> @if (data$ | async; as data) {
<ds-viewable-collection @if (data.payload.page.length > 0) {
*ngIf="data.payload.page.length > 0" <ds-viewable-collection
[config]="paginationConfig" [config]="paginationConfig"
[context]="context" [context]="context"
[objects]="data" [objects]="data"
[selectable]="true" [selectable]="true"
[selectionConfig]="{ repeatable: true, listId: LIST_ID }" [selectionConfig]="{ repeatable: true, listId: LIST_ID }"
[showPaginator]="true" [showPaginator]="true"
(pageChange)="loadForPage($event)"> (pageChange)="loadForPage($event)">
</ds-viewable-collection> </ds-viewable-collection>
}
<div *ngIf="data && data.payload.page.length === 0" @if (data && data.payload.page.length === 0) {
class="alert alert-info w-100" role="alert"> <div
{{'access-control-select-bitstreams-modal.no-items' | translate}} class="alert alert-info w-100" role="alert">
</div> {{'access-control-select-bitstreams-modal.no-items' | translate}}
</div>
</ng-container> }
}
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-outline-dark" <button type="button" class="btn btn-outline-dark"
(click)="activeModal.close('Close click')"> (click)="activeModal.close('Close click')">
{{'access-control-select-bitstreams-modal.close' | translate}} {{'access-control-select-bitstreams-modal.close' | translate}}
</button> </button>
</div> </div>

View File

@@ -1,8 +1,12 @@
<div *ngIf="!dismissed" class="alert {{type}} alert-dismissible fade show w-100 p-3" role="alert" [@enterLeave]="animate"> @if (!dismissed) {
<span *ngIf="content" [innerHTML]="content | translate"></span> <div class="alert {{type}} alert-dismissible fade show w-100 p-3" role="alert" [@enterLeave]="animate">
@if (content) {
<ng-content></ng-content> <span [innerHTML]="content | translate"></span>
}
<button *ngIf="dismissible" type="button" class="btn-close" data-dismiss="alert" aria-label="Close" (click)="dismiss()"> <ng-content></ng-content>
</button> @if (dismissible) {
</div> <button type="button" class="btn-close" data-dismiss="alert" aria-label="Close" (click)="dismiss()">
</button>
}
</div>
}

View File

@@ -1,52 +1,61 @@
<div class="navbar-nav me-auto" *ngIf="(isMobile$ | async) !== true; else mobileButtons" data-test="auth-nav"> @if ((isMobile$ | async) !== true) {
<div *ngIf="(isAuthenticated | async) !== true && (showAuth | async)" <div class="navbar-nav me-auto" data-test="auth-nav">
class="nav-item" @if ((isAuthenticated | async) !== true && (showAuth | async)) {
(click)="$event.stopPropagation();"> <div
<div ngbDropdown #loginDrop="ngbDropdown" display="dynamic" placement="bottom-right" class="d-inline-block" @fadeInOut> class="nav-item"
<a href="javascript:void(0);" class="dropdownLogin px-0.5" [attr.aria-label]="'nav.login' |translate" (click)="$event.stopPropagation();">
(click)="$event.preventDefault()" [attr.data-test]="'login-menu' | dsBrowserOnly" <div ngbDropdown #loginDrop="ngbDropdown" display="dynamic" placement="bottom-right" class="d-inline-block" @fadeInOut>
role="menuitem" <a href="javascript:void(0);" class="dropdownLogin px-0.5" [attr.aria-label]="'nav.login' |translate"
aria-haspopup="menu" (click)="$event.preventDefault()" [attr.data-test]="'login-menu' | dsBrowserOnly"
aria-controls="loginDropdownMenu" role="menuitem"
[attr.aria-expanded]="loginDrop.isOpen()" aria-haspopup="menu"
ngbDropdownToggle>{{ 'nav.login' | translate }}</a> aria-controls="loginDropdownMenu"
<div id="loginDropdownMenu" [ngClass]="{'ps-3 pe-3': (loading | async)}" ngbDropdownMenu [attr.aria-expanded]="loginDrop.isOpen()"
role="menu" ngbDropdownToggle>{{ 'nav.login' | translate }}</a>
[attr.aria-label]="'nav.login' | translate"> <div id="loginDropdownMenu" [ngClass]="{'ps-3 pe-3': (loading | async)}" ngbDropdownMenu
<ds-log-in role="menu"
[isStandalonePage]="false"></ds-log-in> [attr.aria-label]="'nav.login' | translate">
<ds-log-in
[isStandalonePage]="false"></ds-log-in>
</div>
</div>
</div> </div>
}
@if ((isAuthenticated | async) && (showAuth | async)) {
<div class="nav-item">
<div ngbDropdown #loggedInDrop="ngbDropdown" display="dynamic" placement="bottom-right" class="d-inline-block" @fadeInOut>
<a href="javascript:void(0);"
role="menuitem"
[attr.aria-label]="'nav.user-profile-menu-and-logout' | translate"
aria-controls="user-menu-dropdown"
(click)="$event.preventDefault()" [title]="'nav.user-profile-menu-and-logout' | translate"
class="dropdownLogout px-1"
[attr.data-test]="'user-menu' | dsBrowserOnly"
ngbDropdownToggle>
<i class="fas fa-user-circle fa-lg fa-fw"></i></a>
<div id="logoutDropdownMenu" ngbDropdownMenu>
<ds-user-menu [inExpandableNavbar]="false" (changedRoute)="loggedInDrop.close()"></ds-user-menu>
</div>
</div>
</div>
}
</div> </div>
</div> } @else {
<div *ngIf="(isAuthenticated | async) && (showAuth | async)" class="nav-item"> <div data-test="auth-nav">
<div ngbDropdown #loggedInDrop="ngbDropdown" display="dynamic" placement="bottom-right" class="d-inline-block" @fadeInOut> @if ((isAuthenticated | async) !== true) {
<a href="javascript:void(0);" <a routerLink="/login" routerLinkActive="active" class="loginLink px-0.5" role="button">
role="menuitem" {{ 'nav.login' | translate }}<span class="sr-only">(current)</span>
[attr.aria-label]="'nav.user-profile-menu-and-logout' | translate" </a>
aria-controls="user-menu-dropdown" }
(click)="$event.preventDefault()" [title]="'nav.user-profile-menu-and-logout' | translate" @if ((isAuthenticated | async)) {
class="dropdownLogout px-1" <a role="button" [attr.aria-label]="'nav.logout' |translate" [title]="'nav.logout' | translate" routerLink="/logout" routerLinkActive="active" class="logoutLink px-1">
[attr.data-test]="'user-menu' | dsBrowserOnly" <i class="fas fa-sign-out-alt fa-lg fa-fw"></i>
ngbDropdownToggle> <span class="sr-only">(current)</span>
<i class="fas fa-user-circle fa-lg fa-fw"></i></a> </a>
<div id="logoutDropdownMenu" ngbDropdownMenu> }
<ds-user-menu [inExpandableNavbar]="false" (changedRoute)="loggedInDrop.close()"></ds-user-menu>
</div>
</div> </div>
</div> }
</div>
<ng-template #mobileButtons>
<div data-test="auth-nav">
<a *ngIf="(isAuthenticated | async) !== true" routerLink="/login" routerLinkActive="active" class="loginLink px-0.5" role="button">
{{ 'nav.login' | translate }}<span class="sr-only">(current)</span>
</a>
<a *ngIf="(isAuthenticated | async)" role="button" [attr.aria-label]="'nav.logout' |translate" [title]="'nav.logout' | translate" routerLink="/logout" routerLinkActive="active" class="logoutLink px-1">
<i class="fas fa-sign-out-alt fa-lg fa-fw"></i>
<span class="sr-only">(current)</span>
</a>
</div>
</ng-template>
<!-- Do not use ul/li in this menu as it breaks e2e accessibility tests --> <!-- Do not use ul/li in this menu as it breaks e2e accessibility tests -->

View File

@@ -1,44 +1,54 @@
<div *ngIf="searchField" class="mb-3 w-100 pe-2 ps-2 my-2"> @if (searchField) {
<input type="search" <div class="mb-3 w-100 pe-2 ps-2 my-2">
class="form-control w-100" <input type="search"
(click)="$event.stopPropagation();" class="form-control w-100"
placeholder="{{ 'submission.sections.general.search-collection' | translate }}" (click)="$event.stopPropagation();"
[formControl]="searchField" placeholder="{{ 'submission.sections.general.search-collection' | translate }}"
#searchFieldEl> [formControl]="searchField"
</div> #searchFieldEl>
</div>
}
<div class="dropdown-divider m-0"></div> <div class="dropdown-divider m-0"></div>
<ul class="scrollable-menu p-0 m-0" <ul class="scrollable-menu p-0 m-0"
role="menu" role="menu"
(scroll)="onScroll($event)" (scroll)="onScroll($event)"
infiniteScroll infiniteScroll
[infiniteScrollDistance]="1.5" [infiniteScrollDistance]="1.5"
[infiniteScrollThrottle]="0" [infiniteScrollThrottle]="0"
[infiniteScrollUpDistance]="1.5" [infiniteScrollUpDistance]="1.5"
[fromRoot]="true" [fromRoot]="true"
[scrollWindow]="false" [scrollWindow]="false"
(scrolled)="onScrollDown()"> (scrolled)="onScrollDown()">
<li class="dropdown-item disabled" role="menuitem" *ngIf="searchListCollection?.length === 0 && (isLoading | async) !== true"> @if (searchListCollection?.length === 0 && (isLoading | async) !== true) {
{{'submission.sections.general.no-collection' | translate}} <li class="dropdown-item disabled" role="menuitem">
</li> {{'submission.sections.general.no-collection' | translate}}
<ng-container *ngIf="searchListCollection?.length > 0">
<li *ngFor="let listItem of searchListCollection"
class="dropdown-item collection-item"
role="menuitem"
title="{{ listItem.collection.name }}"
(click)="onSelect(listItem)">
<div class="list-unstyled mb-0">
<div class="list-item text-truncate text-secondary" *ngFor="let item of listItem.communities">
{{ item.name}} <i class="fa fa-level-down" aria-hidden="true"></i>
</div>
<div class="list-item text-truncate text-primary fw-bold">{{ listItem.collection.name}}</div>
</div>
</li> </li>
</ng-container> }
<li *ngIf="(isLoading | async)"> @if (searchListCollection?.length > 0) {
<button class="dropdown-item disabled"> @for (listItem of searchListCollection; track listItem) {
<ds-loading message="{{'loading.default' | translate}}"> <li
</ds-loading> class="dropdown-item collection-item"
</button> role="menuitem"
</li> title="{{ listItem.collection.name }}"
(click)="onSelect(listItem)">
<div class="list-unstyled mb-0">
@for (item of listItem.communities; track item) {
<div class="list-item text-truncate text-secondary">
{{ item.name}} <i class="fa fa-level-down" aria-hidden="true"></i>
</div>
}
<div class="list-item text-truncate text-primary fw-bold">{{ listItem.collection.name}}</div>
</div>
</li>
}
}
@if ((isLoading | async)) {
<li>
<button class="dropdown-item disabled">
<ds-loading message="{{'loading.default' | translate}}">
</ds-loading>
</button>
</li>
}
</ul> </ul>

View File

@@ -4,42 +4,52 @@
<label class="form-label">{{type.value + '.edit.logo.label' | translate}}</label> <label class="form-label">{{type.value + '.edit.logo.label' | translate}}</label>
</div> </div>
<ng-container *ngVar="(dso?.logo | async)?.payload as logo"> <ng-container *ngVar="(dso?.logo | async)?.payload as logo">
<div class="col-12 d-inline-block alert" id="logo-section" *ngIf="logo"> @if (logo) {
<div class="row"> <div class="col-12 d-inline-block alert" id="logo-section">
<div class="col-8 d-inline-block"> <div class="row">
<ds-comcol-page-logo [alternateText]="type.value + '.logo.alt'" [logo]="logo"></ds-comcol-page-logo> <div class="col-8 d-inline-block">
</div> <ds-comcol-page-logo [alternateText]="type.value + '.logo.alt'" [logo]="logo"></ds-comcol-page-logo>
<div class="col-4 d-inline-block"> </div>
<div *ngIf="logo" class="float-end"> <div class="col-4 d-inline-block">
<button @if (logo) {
(click)="confirmLogoDeleteWithModal()" <div class="float-end">
class="btn btn-danger" <button
type="button">{{ 'community.edit.logo.delete.title' | translate }} (click)="confirmLogoDeleteWithModal()"
</button> class="btn btn-danger"
type="button">{{ 'community.edit.logo.delete.title' | translate }}
</button>
</div>
}
</div> </div>
</div> </div>
</div> </div>
</div> }
<div *ngIf="!logo" class="col-12 d-inline-block"> @if (!logo) {
<ds-uploader *ngIf="initializedUploaderOptions | async" <div class="col-12 d-inline-block">
[ariaLabel]="type.value + '.browse.logo'" @if (initializedUploaderOptions | async) {
[dropMsg]="type.value + '.edit.logo.upload'" <ds-uploader
[dropOverDocumentMsg]="type.value + '.edit.logo.upload'" [ariaLabel]="type.value + '.browse.logo'"
[enableDragOverDocument]="true" [dropMsg]="type.value + '.edit.logo.upload'"
[uploadFilesOptions]="uploadFilesOptions" [dropOverDocumentMsg]="type.value + '.edit.logo.upload'"
(onCompleteItem)="onCompleteItem()" [enableDragOverDocument]="true"
(onUploadError)="onUploadError()"></ds-uploader> [uploadFilesOptions]="uploadFilesOptions"
</div> (onCompleteItem)="onCompleteItem()"
(onUploadError)="onUploadError()"></ds-uploader>
}
</div>
}
</ng-container> </ng-container>
</div> </div>
</div> </div>
<ds-form *ngIf="formModel" @if (formModel) {
[formId]="'comcol-form-id'" <ds-form
[formModel]="formModel" [formId]="'comcol-form-id'"
[displayCancel]="false" [formModel]="formModel"
(submitForm)="onSubmit()"> [displayCancel]="false"
<button (click)="back.emit()" before class="btn btn-outline-secondary" type="button"> (submitForm)="onSubmit()">
<i class="fas fa-arrow-left" aria-hidden="true"></i> {{ type.value + '.edit.return' | translate }} <button (click)="back.emit()" before class="btn btn-outline-secondary" type="button">
</button> <i class="fas fa-arrow-left" aria-hidden="true"></i> {{ type.value + '.edit.return' | translate }}
</ds-form> </button>
</ds-form>
}

View File

@@ -5,34 +5,38 @@
<h1>{{ type + '.edit.head' | translate }}</h1> <h1>{{ type + '.edit.head' | translate }}</h1>
<div class="my-auto"> <div class="my-auto">
<a class="btn btn-danger" <a class="btn btn-danger"
[routerLink]="((type === 'community') ? '/communities/' : '/collections/') + (dsoRD$ | async)?.payload.uuid + '/delete'" [routerLink]="((type === 'community') ? '/communities/' : '/collections/') + (dsoRD$ | async)?.payload.uuid + '/delete'"
data-test="delete-button"> data-test="delete-button">
<i class="fas fa-trash" aria-hidden="true"></i> {{type + '.edit.delete' | translate}}</a> <i class="fas fa-trash" aria-hidden="true"></i> {{type + '.edit.delete' | translate}}</a>
</div>
</div>
<div class="pt-2">
<ul class="nav nav-tabs justify-content-start mb-2" role="tablist">
<li *ngFor="let page of pages" class="nav-item" role="presentation">
<a class="nav-link" role="tab"
[attr.aria-selected]="page === currentPage"
[ngClass]="{'active' : page === currentPage}"
[routerLink]="['./' + page]"
[attr.data-test]="page">
{{ type + '.edit.tabs.' + page + '.head' | translate}}
</a>
</li>
</ul>
<div class="tab-pane active">
<div class="mb-4">
<router-outlet></router-outlet>
</div> </div>
<div class="col-12 text-end custom-alignment"> </div>
<a *ngIf="!hideReturnButton" [routerLink]="getPageUrl((dsoRD$ | async)?.payload)" class="btn btn-outline-secondary"> <div class="pt-2">
<i class="fas fa-arrow-left" aria-hidden="true"></i> {{ type + '.edit.return' | translate }} <ul class="nav nav-tabs justify-content-start mb-2" role="tablist">
</a> @for (page of pages; track page) {
<li class="nav-item" role="presentation">
<a class="nav-link" role="tab"
[attr.aria-selected]="page === currentPage"
[ngClass]="{'active' : page === currentPage}"
[routerLink]="['./' + page]"
[attr.data-test]="page">
{{ type + '.edit.tabs.' + page + '.head' | translate}}
</a>
</li>
}
</ul>
<div class="tab-pane active">
<div class="mb-4">
<router-outlet></router-outlet>
</div>
<div class="col-12 text-end custom-alignment">
@if (!hideReturnButton) {
<a [routerLink]="getPageUrl((dsoRD$ | async)?.payload)" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left" aria-hidden="true"></i> {{ type + '.edit.return' | translate }}
</a>
}
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>

View File

@@ -1,30 +1,34 @@
<h2 class="comcol-browse-label">{{'browse.comcol.head' | translate}}</h2> <h2 class="comcol-browse-label">{{'browse.comcol.head' | translate}}</h2>
<nav *ngIf="(allOptions$ | async) as allOptions" class="comcol-browse mb-4" aria-label="Browse Community or Collection"> @if ((allOptions$ | async); as allOptions) {
<div class="d-none d-sm-block"> <nav class="comcol-browse mb-4" aria-label="Browse Community or Collection">
<div class="d-none d-sm-block">
<div class="list-group list-group-horizontal" role="tablist"> <div class="list-group list-group-horizontal" role="tablist">
<a *ngFor="let option of allOptions" @for (option of allOptions; track option) {
[attr.aria-current]="(currentOption$ | async)?.id === option.id" <a
class="list-group-item" [attr.aria-current]="(currentOption$ | async)?.id === option.id"
role="tab" class="list-group-item"
[routerLink]="option.routerLink" role="tab"
[queryParams]="option.params" [routerLink]="option.routerLink"
[class.active]="(currentOption$ | async)?.id === option.id"> [queryParams]="option.params"
{{ option.label | translate }} [class.active]="(currentOption$ | async)?.id === option.id">
</a> {{ option.label | translate }}
</a>
}
</div>
</div> </div>
</div> <div class="d-block d-sm-none">
<select name="browse-type"
<div class="d-block d-sm-none"> class="form-select"
<select name="browse-type" aria-label="Browse Community or Collection"
class="form-select" (change)="onSelectChange($event)">
aria-label="Browse Community or Collection" @for (option of allOptions; track option) {
(change)="onSelectChange($event)"> <option
<option *ngFor="let option of allOptions" [value]="option.id"
[value]="option.id" [attr.selected]="(currentOption$ | async)?.id === option.id ? 'selected' : null">
[attr.selected]="(currentOption$ | async)?.id === option.id ? 'selected' : null"> {{ option.label | translate }}
{{ option.label | translate }} </option>
</option> }
</select> </select>
</div> </div>
</nav> </nav>
}

View File

@@ -1,55 +1,59 @@
<div *ngIf="(this.submitted$ | async) !== true; else waiting"> @if ((this.submitted$ | async) !== true) {
<div *ngIf="this.canWithdraw; else reinstateHeader" class="modal-header"> <div>
{{ 'item.qa.withdrawn.modal.header' | translate }} @if (this.canWithdraw) {
<button type="button" class="close" (click)="onModalClose()" aria-label="Close"> <div class="modal-header">
<span aria-hidden="true">×</span> {{ 'item.qa.withdrawn.modal.header' | translate }}
</button> <button type="button" class="close" (click)="onModalClose()" aria-label="Close">
</div> <span aria-hidden="true">×</span>
<div class="modal-body"> </button>
<div class="form-group"> </div>
<label for="reason">{{ this.canWithdraw ? ('qa-withdrawn.create.modal.form.summary.label' | translate) } @else {
: ('qa-reinstate.create.modal.form.summary.label' | translate) }}</label> @if (!this.canWithdraw) {
<label for="reason">{{ this.canWithdraw ? ('qa-withdrawn.create.modal.form.summary2.label' | translate) <div class="modal-header">
: ('qa-reinstate.create.modal.form.summary2.label' | translate) }}</label> {{'item.qa.reinstate.modal.header' | translate}}
<textarea class="form-control" id="reason" <button type="button" class="close" (click)="onModalClose()" aria-label="Close">
rows="6" <span aria-hidden="true">×</span>
[(ngModel)]="reason" </button>
</div>
}
}
<div class="modal-body">
<div class="form-group">
<label for="reason">{{ this.canWithdraw ? ('qa-withdrawn.create.modal.form.summary.label' | translate)
: ('qa-reinstate.create.modal.form.summary.label' | translate) }}</label>
<label for="reason">{{ this.canWithdraw ? ('qa-withdrawn.create.modal.form.summary2.label' | translate)
: ('qa-reinstate.create.modal.form.summary2.label' | translate) }}</label>
<textarea class="form-control" id="reason"
rows="6"
[(ngModel)]="reason"
placeholder="{{ this.canWithdraw ? ('qa-withdrown.modal.form.summary.placeholder' | translate) placeholder="{{ this.canWithdraw ? ('qa-withdrown.modal.form.summary.placeholder' | translate)
: ('qa-reinstate.modal.form.summary.placeholder' | translate) }}" : ('qa-reinstate.modal.form.summary.placeholder' | translate) }}"
name="message"></textarea> name="message"></textarea>
</div>
</div>
<div class="modal-footer space-children-mr">
<button class="btn btn-outline-secondary btn-sm ms-0"
type="button"
(click)="onModalClose()"
title="{{'item.qa.withdrawn-reinstate.create.modal.button.cancel.tooltip' | translate}}">
<i class="fas fa-times fa-fw"></i> {{'item.qa.withdrawn-reinstate.create.modal.button.cancel' | translate}}
</button>
<button class="btn btn-success btn-sm ms-0"
type="submit"
(click)="onModalSubmit()"
title="{{'item.qa.withdrawn-reinstate.modal.button.confirm.tooltip' | translate}}">
<i class="fas fa-check fa-fw"></i> {{ this.canWithdraw ? ('qa-withdrown.create.modal.button.confirm' | translate)
: ('qa-reinstate.create.modal.button.confirm' | translate) }}
</button>
</div> </div>
</div> </div>
<div class="modal-footer space-children-mr"> } @else {
<button class="btn btn-outline-secondary btn-sm ms-0"
type="button"
(click)="onModalClose()"
title="{{'item.qa.withdrawn-reinstate.create.modal.button.cancel.tooltip' | translate}}">
<i class="fas fa-times fa-fw"></i> {{'item.qa.withdrawn-reinstate.create.modal.button.cancel' | translate}}
</button>
<button class="btn btn-success btn-sm ms-0"
type="submit"
(click)="onModalSubmit()"
title="{{'item.qa.withdrawn-reinstate.modal.button.confirm.tooltip' | translate}}">
<i class="fas fa-check fa-fw"></i> {{ this.canWithdraw ? ('qa-withdrown.create.modal.button.confirm' | translate)
: ('qa-reinstate.create.modal.button.confirm' | translate) }}
</button>
</div>
</div>
<ng-template #waiting>
<div class="modal-header">{{'item.qa.withdrawn.modal.submitted.header' | translate}}</div> <div class="modal-header">{{'item.qa.withdrawn.modal.submitted.header' | translate}}</div>
<div class="modal-body"> <div class="modal-body">
<div class="d-flex justify-content-center"> <div class="d-flex justify-content-center">
<ds-loading [showMessage]="false"></ds-loading> <ds-loading [showMessage]="false"></ds-loading>
</div> </div>
</div> </div>
</ng-template> }
<ng-template #reinstateHeader>
<div *ngIf="!this.canWithdraw" class="modal-header">
{{'item.qa.reinstate.modal.header' | translate}}
<button type="button" class="close" (click)="onModalClose()" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</ng-template>

View File

@@ -2,24 +2,26 @@
<div ngbDropdown class="btn-group" (openChange)="toggled.emit($event)"> <div ngbDropdown class="btn-group" (openChange)="toggled.emit($event)">
<span *ngIf="label" id="dsSelectMenuLabel" class="input-group-text"> @if (label) {
{{ label | translate }} <span id="dsSelectMenuLabel" class="input-group-text">
</span> {{ label | translate }}
</span>
}
<button aria-describedby="dsSelectMenuLabel" <button aria-describedby="dsSelectMenuLabel"
id="dsSelectMenuButton" id="dsSelectMenuButton"
class="btn btn-outline-primary selection" class="btn btn-outline-primary selection"
(blur)="close.emit($event)" (blur)="close.emit($event)"
(click)="close.emit($event)" (click)="close.emit($event)"
[dsBtnDisabled]="disabled" [dsBtnDisabled]="disabled"
ngbDropdownToggle> ngbDropdownToggle>
<ng-content select=".selection"></ng-content> <ng-content select=".selection"></ng-content>
</button> </button>
<div ngbDropdownMenu <div ngbDropdownMenu
class="dropdown-menu" class="dropdown-menu"
id="dsSelectDropdownMenu" id="dsSelectDropdownMenu"
aria-labelledby="dsSelectMenuButton"> aria-labelledby="dsSelectMenuButton">
<div> <div>
<ng-content select=".menu"></ng-content> <ng-content select=".menu"></ng-content>
</div> </div>

View File

@@ -1,21 +1,28 @@
<div class="dso-button-menu mb-1" ngbDropdown container="body" placement="bottom-right"> <div class="dso-button-menu mb-1" ngbDropdown container="body" placement="bottom-right">
<div class="d-flex flex-row flex-nowrap" <div class="d-flex flex-row flex-nowrap"
[ngbTooltip]="itemModel.text | translate" container="body"> [ngbTooltip]="itemModel.text | translate" container="body">
<button [attr.aria-label]="itemModel.text | translate" [title]="itemModel.text | translate" class="btn btn-dark btn-sm" ngbDropdownToggle [dsBtnDisabled]="section.model?.disabled"> <button [attr.aria-label]="itemModel.text | translate" [title]="itemModel.text | translate" class="btn btn-dark btn-sm" ngbDropdownToggle [dsBtnDisabled]="section.model?.disabled">
<i class="fas fa-{{section.icon}} fa-fw"></i> <i class="fas fa-{{section.icon}} fa-fw"></i>
</button> </button>
<ul ngbDropdownMenu class="dso-edit-menu-dropdown"> <ul ngbDropdownMenu class="dso-edit-menu-dropdown">
<li class="nav-item nav-link d-flex flex-row" *ngFor="let subSection of (subSections$ | async)"> @for (subSection of (subSections$ | async); track subSection) {
<div *ngIf="renderIcons$ | async" class="me-2"> <li class="nav-item nav-link d-flex flex-row">
<i *ngIf="subSection.icon; else spacer" class="fas fa-{{subSection.icon}} fa-fw"></i> @if (renderIcons$ | async) {
<ng-template #spacer><i class="fas fa-fw"></i></ng-template> <div class="me-2">
</div> @if (subSection.icon) {
<ng-container <i class="fas fa-{{subSection.icon}} fa-fw"></i>
*ngComponentOutlet="(sectionMap$ | async).get(subSection.id).component; injector: (sectionMap$ | async).get(subSection.id).injector;"> } @else {
</ng-container> <i class="fas fa-fw"></i>
</li> }
</ul> </div>
</div> }
<ng-container
*ngComponentOutlet="(sectionMap$ | async).get(subSection.id).component; injector: (sectionMap$ | async).get(subSection.id).injector;">
</ng-container>
</li>
}
</ul>
</div>
</div> </div>

View File

@@ -1,6 +1,8 @@
<div class="dso-edit-menu d-flex"> <div class="dso-edit-menu d-flex">
<div *ngFor="let section of (sections | async)" class="ms-1"> @for (section of (sections | async); track section) {
<ng-container <div class="ms-1">
*ngComponentOutlet="(sectionMap$ | async).get(section.id)?.component; injector: (sectionMap$ | async).get(section.id)?.injector;"></ng-container> <ng-container
*ngComponentOutlet="(sectionMap$ | async).get(section.id)?.component; injector: (sectionMap$ | async).get(section.id)?.injector;"></ng-container>
</div> </div>
}
</div> </div>

View File

@@ -1,10 +1,10 @@
<div class="mb-3 w-100 pe-2 ps-2"> <div class="mb-3 w-100 pe-2 ps-2">
<input type="search" <input type="search"
class="form-control" class="form-control"
(click)="$event.stopPropagation();" (click)="$event.stopPropagation();"
[attr.aria-label]="'dso-selector.placeholder' | translate: { type: typesString }" [attr.aria-label]="'dso-selector.placeholder' | translate: { type: typesString }"
placeholder="{{'dso-selector.placeholder' | translate: { type: typesString } }}" placeholder="{{'dso-selector.placeholder' | translate: { type: typesString } }}"
[formControl]="input" ngbAutofocus (keyup.enter)="selectSingleResult()"> [formControl]="input" ngbAutofocus (keyup.enter)="selectSingleResult()">
</div> </div>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<div id="scrollable-menu-dso-selector-{{randomSeed}}" class="scrollable-menu list-group"> <div id="scrollable-menu-dso-selector-{{randomSeed}}" class="scrollable-menu list-group">
@@ -15,25 +15,31 @@
[infiniteScrollContainer]="'#scrollable-menu-dso-selector-' + randomSeed" [infiniteScrollContainer]="'#scrollable-menu-dso-selector-' + randomSeed"
[fromRoot]="true" [fromRoot]="true"
(scrolled)="onScrollDown()"> (scrolled)="onScrollDown()">
<ng-container *ngIf="listEntries$ | async"> @if (listEntries$ | async) {
<button class="list-group-item list-group-item-action border-0 disabled" @if ((listEntries$ | async).length === 0) {
*ngIf="(listEntries$ | async).length === 0"> <button class="list-group-item list-group-item-action border-0 disabled"
{{'dso-selector.no-results' | translate: { type: typesString } }} >
{{'dso-selector.no-results' | translate: { type: typesString } }}
</button>
}
@for (listEntry of (listEntries$ | async); track listEntry) {
<button
class="list-group-item list-group-item-action border-0 list-entry"
[ngClass]="{'bg-primary': listEntry['id'] === currentDSOId}"
title="{{ getName(listEntry) }}"
dsHoverClass="ds-hover"
(click)="onClick(listEntry)" #listEntryElement>
<ds-listable-object-component-loader [object]="listEntry" [viewMode]="viewMode"
[linkType]=linkTypes.None [context]="getContext(listEntry['id'])"></ds-listable-object-component-loader>
</button>
}
}
@if (loading) {
<button
title="{{'loading.default' | translate}}"
class="list-group-item list-group-item-action border-0 list-entry">
<ds-loading [showMessage]="false"></ds-loading>
</button> </button>
<button *ngFor="let listEntry of (listEntries$ | async)" }
class="list-group-item list-group-item-action border-0 list-entry"
[ngClass]="{'bg-primary': listEntry['id'] === currentDSOId}"
title="{{ getName(listEntry) }}"
dsHoverClass="ds-hover"
(click)="onClick(listEntry)" #listEntryElement>
<ds-listable-object-component-loader [object]="listEntry" [viewMode]="viewMode"
[linkType]=linkTypes.None [context]="getContext(listEntry['id'])"></ds-listable-object-component-loader>
</button>
</ng-container>
<button *ngIf="loading"
title="{{'loading.default' | translate}}"
class="list-group-item list-group-item-action border-0 list-entry">
<ds-loading [showMessage]="false"></ds-loading>
</button>
</div> </div>
</div> </div>

Some files were not shown because too many files have changed in this diff Show More