Merge remote-tracking branch 'dspace/main' into accessibility-settings-main

# Conflicts:
#	src/app/footer/footer.component.html
#	src/app/profile-page/profile-page.component.html
This commit is contained in:
Andreas Awouters
2025-03-21 13:03:30 +01:00
945 changed files with 24856 additions and 18623 deletions

View File

@@ -294,7 +294,8 @@
"rules": { "rules": {
// Custom DSpace Angular rules // Custom DSpace Angular rules
"dspace-angular-html/themed-component-usages": "error", "dspace-angular-html/themed-component-usages": "error",
"dspace-angular-html/no-disabled-attribute-on-button": "error" "dspace-angular-html/no-disabled-attribute-on-button": "error",
"@angular-eslint/template/prefer-control-flow": "error"
} }
}, },
{ {

View File

@@ -1,5 +1,4 @@
import { testA11y } from 'cypress/support/utils'; import { testA11y } from 'cypress/support/utils';
import { Options } from 'cypress-axe';
describe('Admin Sidebar', () => { describe('Admin Sidebar', () => {
beforeEach(() => { beforeEach(() => {
@@ -16,13 +15,6 @@ describe('Admin Sidebar', () => {
cy.get('ds-expandable-admin-sidebar-section').click({ multiple: true }); cy.get('ds-expandable-admin-sidebar-section').click({ multiple: true });
// Analyze <ds-admin-sidebar> for accessibility // Analyze <ds-admin-sidebar> for accessibility
testA11y('ds-admin-sidebar', testA11y('ds-admin-sidebar');
{
rules: {
// Currently all expandable sections have nested interactive elements
// See https://github.com/DSpace/dspace-angular/issues/2178
'nested-interactive': { enabled: false },
},
} as Options);
}); });
}); });

View File

@@ -12,6 +12,13 @@ describe('Community List Page', () => {
cy.get('[data-test="expand-button"]').click({ multiple: true }); cy.get('[data-test="expand-button"]').click({ multiple: true });
// Analyze <ds-community-list-page> for accessibility issues // Analyze <ds-community-list-page> for accessibility issues
testA11y('ds-community-list-page'); testA11y('ds-community-list-page', {
rules: {
// When expanding a cdk node on the community-list page, the 'aria-posinset' property becomes 0.
// 0 is not a valid value for 'aria-posinset' so the test fails.
// see https://github.com/DSpace/dspace-angular/issues/4068
'aria-valid-attr-value': { enabled: false },
},
});
}); });
}); });

View File

@@ -1,10 +1,16 @@
{ {
"extends": "../tsconfig.json", "extends": "../tsconfig.json",
"include": [ "include": [
"**/*.ts" "**/*.ts",
"../cypress.config.ts"
], ],
"compilerOptions": { "compilerOptions": {
"sourceMap": false, "sourceMap": false,
"typeRoots": [
"../node_modules",
"../node_modules/@types",
"../src/typings.d.ts"
],
"types": [ "types": [
"cypress", "cypress",
"cypress-axe", "cypress-axe",

11546
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -57,68 +57,62 @@
"private": true, "private": true,
"overrides": { "overrides": {
"@kolkov/ngx-gallery": { "@kolkov/ngx-gallery": {
"@angular/animations": "^17.3.11", "@angular/animations": "^18.2.12",
"@angular/common": "^17.3.11", "@angular/common": "^18.2.12",
"@angular/core": "^17.3.11" "@angular/core": "^18.2.12"
}, },
"@ng-bootstrap/ng-bootstrap": { "@ng-bootstrap/ng-bootstrap": {
"@angular/common": "^17.3.11", "@angular/common": "^18.2.12",
"@angular/core": "^17.3.11", "@angular/core": "^18.2.12",
"@angular/forms": "^17.3.11", "@angular/forms": "^18.2.12",
"@angular/localize": "^17.3.11" "@angular/localize": "^18.2.12"
}, },
"@ng-dynamic-forms/core": { "@ng-dynamic-forms/core": {
"@angular/common": "^17.3.11", "@angular/common": "^18.2.12",
"@angular/core": "^17.3.11", "@angular/core": "^18.2.12",
"@angular/forms": "^17.3.11" "@angular/forms": "^18.2.12"
}, },
"@ng-dynamic-forms/ui-ng-bootstrap": { "@ng-dynamic-forms/ui-ng-bootstrap": {
"ngx-mask": "14.2.4", "ngx-mask": "14.2.4",
"@ng-bootstrap/ng-bootstrap": "^12.0.0", "@ng-bootstrap/ng-bootstrap": "^12.0.0",
"bootstrap": "^5.3" "bootstrap": "^5.3"
}, },
"@ngtools/webpack": {
"@angular/compiler-cli": "^17.3.11",
"typescript": "~5.4.5"
},
"@nicky-lenaers/ngx-scroll-to": { "@nicky-lenaers/ngx-scroll-to": {
"@angular/common": "^17.3.11", "@angular/common": "^18.2.12",
"@angular/core": "^17.3.11" "@angular/core": "^18.2.12"
}, },
"eslint-plugin-unused-imports": { "eslint-plugin-unused-imports": {
"@typescript-eslint/eslint-plugin": "^7.2.0" "@typescript-eslint/eslint-plugin": "^7.2.0"
}, },
"ng2-file-upload": {
"@angular/common": "^17.3.11",
"@angular/core": "^17.3.11"
},
"ngx-infinite-scroll": { "ngx-infinite-scroll": {
"@angular/common": "^17.3.11", "@angular/common": "^18.2.12",
"@angular/core": "^17.3.11" "@angular/core": "^18.2.12"
} },
"notistack": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "^17.3.12", "@angular/animations": "^18.2.12",
"@angular/cdk": "^17.3.10", "@angular/cdk": "^18.2.12",
"@angular/common": "^17.3.12", "@angular/common": "^18.2.12",
"@angular/compiler": "^17.3.12", "@angular/compiler": "^18.2.12",
"@angular/core": "^17.3.12", "@angular/core": "^18.2.12",
"@angular/forms": "^17.3.12", "@angular/forms": "^18.2.12",
"@angular/localize": "^17.3.12", "@angular/localize": "^18.2.12",
"@angular/platform-browser": "^17.3.12", "@angular/platform-browser": "^18.2.12",
"@angular/platform-browser-dynamic": "^17.3.12", "@angular/platform-browser-dynamic": "^18.2.12",
"@angular/platform-server": "^17.3.12", "@angular/platform-server": "^18.2.12",
"@angular/router": "^17.3.12", "@angular/router": "^18.2.12",
"@angular/ssr": "^17.3.11", "@angular/ssr": "^18.2.12",
"@babel/runtime": "7.26.7", "@babel/runtime": "7.26.0",
"@kolkov/ngx-gallery": "^2.0.1", "@kolkov/ngx-gallery": "^2.0.1",
"@ng-bootstrap/ng-bootstrap": "^12.0.0", "@ng-bootstrap/ng-bootstrap": "^12.0.0",
"@ng-dynamic-forms/core": "^16.0.0", "@ng-dynamic-forms/core": "^16.0.0",
"@ng-dynamic-forms/ui-ng-bootstrap": "^16.0.0", "@ng-dynamic-forms/ui-ng-bootstrap": "^16.0.0",
"@ngrx/effects": "^17.1.1", "@ngrx/effects": "^18.1.1",
"@ngrx/router-store": "^17.1.1", "@ngrx/operators": "^18.0.0",
"@ngrx/store": "^17.1.1", "@ngrx/router-store": "^18.1.1",
"@ngx-translate/core": "^14.0.0", "@ngrx/store": "^18.1.1",
"@ngx-translate/core": "^16.0.3",
"@nicky-lenaers/ngx-scroll-to": "^14.0.0", "@nicky-lenaers/ngx-scroll-to": "^14.0.0",
"angulartics2": "^12.2.0", "angulartics2": "^12.2.0",
"axios": "^1.7.9", "axios": "^1.7.9",
@@ -152,12 +146,12 @@
"mirador-dl-plugin": "^0.13.0", "mirador-dl-plugin": "^0.13.0",
"mirador-share-plugin": "^0.16.0", "mirador-share-plugin": "^0.16.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"ng2-file-upload": "5.0.0", "ng2-file-upload": "7.0.1",
"ng2-nouislider": "^2.0.0", "ng2-nouislider": "^2.0.0",
"ngx-infinite-scroll": "^16.0.0", "ngx-infinite-scroll": "^18.0.0",
"ngx-pagination": "6.0.3", "ngx-pagination": "6.0.3",
"ngx-skeleton-loader": "^9.0.0", "ngx-skeleton-loader": "^9.0.0",
"ngx-ui-switch": "^14.1.0", "ngx-ui-switch": "^15.0.0",
"nouislider": "^15.7.1", "nouislider": "^15.7.1",
"orejime": "^2.3.1", "orejime": "^2.3.1",
"pem": "1.14.8", "pem": "1.14.8",
@@ -167,22 +161,22 @@
"zone.js": "~0.14.10" "zone.js": "~0.14.10"
}, },
"devDependencies": { "devDependencies": {
"@angular-builders/custom-webpack": "~17.0.2", "@angular-builders/custom-webpack": "~18.0.0",
"@angular-devkit/build-angular": "^17.3.11", "@angular-devkit/build-angular": "^18.2.12",
"@angular-eslint/builder": "^17.5.3", "@angular-eslint/builder": "^18.4.1",
"@angular-eslint/bundled-angular-compiler": "^17.5.3", "@angular-eslint/bundled-angular-compiler": "^18.4.1",
"@angular-eslint/eslint-plugin": "^17.5.3", "@angular-eslint/eslint-plugin": "^18.4.1",
"@angular-eslint/eslint-plugin-template": "^17.5.3", "@angular-eslint/eslint-plugin-template": "^18.4.1",
"@angular-eslint/schematics": "^17.5.3", "@angular-eslint/schematics": "^18.4.1",
"@angular-eslint/template-parser": "^17.5.3", "@angular-eslint/template-parser": "^18.4.1",
"@angular-eslint/utils": "^17.5.3", "@angular-eslint/utils": "^18.4.1",
"@angular/cli": "^17.3.11", "@angular/cli": "^18.2.12",
"@angular/compiler-cli": "^17.3.11", "@angular/compiler-cli": "^18.2.12",
"@angular/language-service": "^17.3.12", "@angular/language-service": "^18.2.12",
"@cypress/schematic": "^1.5.0", "@cypress/schematic": "^1.5.0",
"@fortawesome/fontawesome-free": "^6.7.2", "@fortawesome/fontawesome-free": "^6.7.2",
"@ngrx/store-devtools": "^17.1.1", "@ngrx/store-devtools": "^18.1.1",
"@ngtools/webpack": "^16.2.16", "@ngtools/webpack": "^18.2.12",
"@types/deep-freeze": "0.1.5", "@types/deep-freeze": "0.1.5",
"@types/ejs": "^3.1.2", "@types/ejs": "^3.1.2",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",

View File

@@ -20,10 +20,10 @@ import 'reflect-metadata';
/* eslint-disable import/no-namespace */ /* eslint-disable import/no-namespace */
import * as morgan from 'morgan'; import * as morgan from 'morgan';
import * as express from 'express'; import express from 'express';
import * as ejs from 'ejs'; import * as ejs from 'ejs';
import * as compression from 'compression'; import * as compression from 'compression';
import * as expressStaticGzip from 'express-static-gzip'; import expressStaticGzip from 'express-static-gzip';
/* eslint-enable import/no-namespace */ /* eslint-enable import/no-namespace */
import axios from 'axios'; import axios from 'axios';
import LRU from 'lru-cache'; import LRU from 'lru-cache';

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

@@ -1,8 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgForOf,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
Input, Input,
@@ -59,11 +55,9 @@ import { BrowserOnlyPipe } from '../../../shared/utils/browser-only.pipe';
AsyncPipe, AsyncPipe,
NgbAccordionModule, NgbAccordionModule,
TranslateModule, TranslateModule,
NgIf,
NgbNavModule, NgbNavModule,
ThemedSearchComponent, ThemedSearchComponent,
BrowserOnlyPipe, BrowserOnlyPipe,
NgForOf,
NgxPaginationModule, NgxPaginationModule,
SelectableListItemControlComponent, SelectableListItemControlComponent,
ListableObjectComponentLoaderComponent, ListableObjectComponentLoaderComponent,

View File

@@ -1,4 +1,4 @@
import { NgIf } from '@angular/common';
import { import {
Component, Component,
ViewChild, ViewChild,
@@ -16,7 +16,6 @@ import { AccessControlFormContainerComponent } from '../../../shared/access-cont
imports: [ imports: [
NgbAccordionModule, NgbAccordionModule,
TranslateModule, TranslateModule,
NgIf,
AccessControlFormContainerComponent, AccessControlFormContainerComponent,
], ],
standalone: true, standalone: true,

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

@@ -1,8 +1,6 @@
import { import {
AsyncPipe, AsyncPipe,
NgClass, NgClass,
NgForOf,
NgIf,
} from '@angular/common'; } from '@angular/common';
import { import {
Component, Component,
@@ -72,13 +70,11 @@ import { EPersonFormComponent } from './eperson-form/eperson-form.component';
TranslateModule, TranslateModule,
RouterModule, RouterModule,
AsyncPipe, AsyncPipe,
NgIf,
EPersonFormComponent, EPersonFormComponent,
ReactiveFormsModule, ReactiveFormsModule,
ThemedLoadingComponent, ThemedLoadingComponent,
PaginationComponent, PaginationComponent,
NgClass, NgClass,
NgForOf,
], ],
standalone: true, standalone: true,
}) })

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

@@ -1,8 +1,6 @@
import { import {
AsyncPipe, AsyncPipe,
NgClass, NgClass,
NgFor,
NgIf,
} from '@angular/common'; } from '@angular/common';
import { import {
ChangeDetectorRef, ChangeDetectorRef,
@@ -84,8 +82,6 @@ import { ValidateEmailNotTaken } from './validators/email-taken.validator';
templateUrl: './eperson-form.component.html', templateUrl: './eperson-form.component.html',
imports: [ imports: [
FormComponent, FormComponent,
NgIf,
NgFor,
AsyncPipe, AsyncPipe,
TranslateModule, TranslateModule,
NgClass, NgClass,

View File

@@ -2,13 +2,7 @@
<div class="group-form row"> <div class="group-form row">
<div class="col-12"> <div class="col-12">
<div *ngIf="activeGroup$ | async; then editHeader; else createHeader"></div> @if (activeGroup$ | async) {
<ng-template #createHeader>
<h1 class="border-bottom pb-2">{{messagePrefix + '.head.create' | translate}}</h1>
</ng-template>
<ng-template #editHeader>
<h1 class="border-bottom pb-2"> <h1 class="border-bottom pb-2">
<span <span
*dsContextHelp="{ *dsContextHelp="{
@@ -17,47 +11,61 @@
iconPlacement: 'right', iconPlacement: 'right',
tooltipPlacement: ['right', 'bottom'] tooltipPlacement: ['right', 'bottom']
}" }"
> >
{{messagePrefix + '.head.edit' | translate}} {{messagePrefix + '.head.edit' | translate}}
</span> </span>
</h1> </h1>
</ng-template> } @else {
<h1 class="border-bottom pb-2">{{messagePrefix + '.head.create' | translate}}</h1>
}
<ng-container *ngIf="(activeGroup$ | async) as groupBeingEdited">
<ds-alert *ngIf="groupBeingEdited?.permanent" [type]="AlertType.Warning"
[content]="messagePrefix + '.alert.permanent'"></ds-alert> @if ((activeGroup$ | async); as groupBeingEdited) {
<ng-container *ngIf="(activeGroupLinkedDSO$ | async) as activeGroupLinkedDSO"> @if (groupBeingEdited?.permanent) {
<ds-alert *ngIf="(canEdit$ | async) !== true" [type]="AlertType.Warning" <ds-alert [type]="AlertType.Warning"
[content]="(messagePrefix + '.alert.workflowGroup' | translate:{ name: dsoNameService.getName(activeGroupLinkedDSO), comcol: activeGroupLinkedDSO.type, comcolEditRolesRoute: (linkedEditRolesRoute$ | async) })"> [content]="messagePrefix + '.alert.permanent'"></ds-alert>
</ds-alert> }
</ng-container> @if ((activeGroupLinkedDSO$ | async); as activeGroupLinkedDSO) {
</ng-container> @if ((canEdit$ | async) !== true) {
<ds-alert [type]="AlertType.Warning"
[content]="(messagePrefix + '.alert.workflowGroup' | translate:{ name: dsoNameService.getName(activeGroupLinkedDSO), comcol: activeGroupLinkedDSO.type, comcolEditRolesRoute: (linkedEditRolesRoute$ | async) })">
</ds-alert>
}
}
}
<ds-form [formId]="formId" <ds-form [formId]="formId"
[formModel]="formModel" [formModel]="formModel"
[formGroup]="formGroup" [formGroup]="formGroup"
[formLayout]="formLayout" [formLayout]="formLayout"
[displayCancel]="false" [displayCancel]="false"
(submitForm)="onSubmit()"> (submitForm)="onSubmit()">
<div before class="btn-group"> <div before class="btn-group">
<button (click)="onCancel()" type="button" <button (click)="onCancel()" type="button"
class="btn btn-outline-secondary"><i class="fas fa-arrow-left"></i> {{messagePrefix + '.return' | translate}}</button> class="btn btn-outline-secondary"><i class="fas fa-arrow-left"></i> {{messagePrefix + '.return' | translate}}</button>
</div> </div>
<div after *ngIf="(canEdit$ | async) && !(activeGroup$ | async)?.permanent" class="btn-group"> @if ((canEdit$ | async) && !(activeGroup$ | async)?.permanent) {
<button (click)="delete()" class="btn btn-danger delete-button" type="button"> <div after class="btn-group">
<i class="fa fa-trash"></i> {{ messagePrefix + '.actions.delete' | translate}} <button (click)="delete()" class="btn btn-danger delete-button" type="button">
</button> <i class="fa fa-trash"></i> {{ messagePrefix + '.actions.delete' | translate}}
</div> </button>
</ds-form> </div>
}
</ds-form>
<ng-container *ngIf="(activeGroup$ | async) as groupBeingEdited"> @if ((activeGroup$ | async); as groupBeingEdited) {
<div class="mb-5"> <div class="mb-5">
<ds-members-list *ngIf="groupBeingEdited !== undefined" @if (groupBeingEdited !== undefined) {
[messagePrefix]="messagePrefix + '.members-list'"></ds-members-list> <ds-members-list
</div> [messagePrefix]="messagePrefix + '.members-list'"></ds-members-list>
<ds-subgroups-list *ngIf="groupBeingEdited !== undefined" }
[messagePrefix]="messagePrefix + '.subgroups-list'"></ds-subgroups-list> </div>
</ng-container> @if (groupBeingEdited !== undefined) {
<ds-subgroups-list
[messagePrefix]="messagePrefix + '.subgroups-list'"></ds-subgroups-list>
}
}
</div>
</div> </div>
</div> </div>
</div>

View File

@@ -1,7 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgIf,
} from '@angular/common';
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
@@ -92,7 +89,6 @@ import { ValidateGroupExists } from './validators/group-exists.validator';
imports: [ imports: [
FormComponent, FormComponent,
AlertComponent, AlertComponent,
NgIf,
AsyncPipe, AsyncPipe,
TranslateModule, TranslateModule,
ContextHelpDirective, ContextHelpDirective,

View File

@@ -3,63 +3,70 @@
<h3>{{messagePrefix + '.headMembers' | translate}}</h3> <h3>{{messagePrefix + '.headMembers' | translate}}</h3>
<ds-pagination *ngIf="(ePeopleMembersOfGroup | async)?.totalElements > 0" @if ((ePeopleMembersOfGroup | async)?.totalElements > 0) {
[paginationOptions]="config" <ds-pagination
[collectionSize]="(ePeopleMembersOfGroup | async)?.totalElements" [paginationOptions]="config"
[hideGear]="true" [collectionSize]="(ePeopleMembersOfGroup | async)?.totalElements"
[hidePagerWhenSinglePage]="true"> [hideGear]="true"
[hidePagerWhenSinglePage]="true">
<div class="table-responsive">
<table id="ePeopleMembersOfGroup" class="table table-striped table-hover table-bordered">
<thead>
<tr>
<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.identity' | translate}}</th>
<th class="align-middle">{{messagePrefix + '.table.edit' | translate}}</th>
</tr>
</thead>
<tbody>
@for (epersonDTO of (ePeopleMembersOfGroup | async)?.page; track epersonDTO) {
<tr>
<td class="align-middle">{{epersonDTO.eperson.id}}</td>
<td class="align-middle">
<a [routerLink]="getEPersonEditRoute(epersonDTO.eperson.id)">
{{ dsoNameService.getName(epersonDTO.eperson) }}
</a>
</td>
<td class="align-middle">
{{messagePrefix + '.table.email' | translate}}: {{ epersonDTO.eperson.email ? epersonDTO.eperson.email : '-' }}<br/>
{{messagePrefix + '.table.netid' | translate}}: {{ epersonDTO.eperson.netid ? epersonDTO.eperson.netid : '-' }}
</td>
<td class="align-middle">
<div class="btn-group edit-field">
@if (epersonDTO.ableToDelete) {
<button (click)="deleteMemberFromGroup(epersonDTO.eperson)"
[dsBtnDisabled]="actionConfig.remove.disabled"
[ngClass]="['btn btn-sm', actionConfig.remove.css]"
title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(epersonDTO.eperson) } }}">
<i [ngClass]="actionConfig.remove.icon"></i>
</button>
}
@if (!epersonDTO.ableToDelete) {
<button
(click)="addMemberToGroup(epersonDTO.eperson)"
[dsBtnDisabled]="actionConfig.add.disabled"
[ngClass]="['btn btn-sm', actionConfig.add.css]"
title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(epersonDTO.eperson) } }}">
<i [ngClass]="actionConfig.add.icon"></i>
</button>
}
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
</ds-pagination>
}
<div class="table-responsive"> @if ((ePeopleMembersOfGroup | async) === undefined || (ePeopleMembersOfGroup | async)?.totalElements === 0) {
<table id="ePeopleMembersOfGroup" class="table table-striped table-hover table-bordered"> <div class="alert alert-info w-100 mb-2"
<thead> role="alert">
<tr> {{messagePrefix + '.no-members-yet' | translate}}
<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.identity' | translate}}</th>
<th class="align-middle">{{messagePrefix + '.table.edit' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let epersonDTO of (ePeopleMembersOfGroup | async)?.page">
<td class="align-middle">{{epersonDTO.eperson.id}}</td>
<td class="align-middle">
<a [routerLink]="getEPersonEditRoute(epersonDTO.eperson.id)">
{{ dsoNameService.getName(epersonDTO.eperson) }}
</a>
</td>
<td class="align-middle">
{{messagePrefix + '.table.email' | translate}}: {{ epersonDTO.eperson.email ? epersonDTO.eperson.email : '-' }}<br/>
{{messagePrefix + '.table.netid' | translate}}: {{ epersonDTO.eperson.netid ? epersonDTO.eperson.netid : '-' }}
</td>
<td class="align-middle">
<div class="btn-group edit-field">
<button (click)="deleteMemberFromGroup(epersonDTO.eperson)"
*ngIf="epersonDTO.ableToDelete"
[dsBtnDisabled]="actionConfig.remove.disabled"
[ngClass]="['btn btn-sm', actionConfig.remove.css]"
title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(epersonDTO.eperson) } }}">
<i [ngClass]="actionConfig.remove.icon"></i>
</button>
<button *ngIf="!epersonDTO.ableToDelete"
(click)="addMemberToGroup(epersonDTO.eperson)"
[dsBtnDisabled]="actionConfig.add.disabled"
[ngClass]="['btn btn-sm', actionConfig.add.css]"
title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(epersonDTO.eperson) } }}">
<i [ngClass]="actionConfig.add.icon"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div> </div>
}
</ds-pagination>
<div *ngIf="(ePeopleMembersOfGroup | async) === undefined || (ePeopleMembersOfGroup | async)?.totalElements === 0" class="alert alert-info w-100 mb-2"
role="alert">
{{messagePrefix + '.no-members-yet' | translate}}
</div>
<h3 id="search" class="border-bottom pb-2"> <h3 id="search" class="border-bottom pb-2">
<span <span
@@ -69,7 +76,7 @@
iconPlacement: 'right', iconPlacement: 'right',
tooltipPlacement: ['top', 'right', 'bottom'] tooltipPlacement: ['top', 'right', 'bottom']
}" }"
> >
{{messagePrefix + '.search.head' | translate}} {{messagePrefix + '.search.head' | translate}}
</span> </span>
</h3> </h3>
@@ -78,68 +85,72 @@
<div class="flex-grow-1 me-3"> <div class="flex-grow-1 me-3">
<div class="form-group input-group me-3"> <div class="form-group input-group me-3">
<input type="text" name="query" id="query" formControlName="query" <input type="text" name="query" id="query" formControlName="query"
class="form-control" aria-label="Search input"> class="form-control" aria-label="Search input">
<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 }}</button> <i class="fas fa-search"></i> {{ messagePrefix + '.search.button' | translate }}</button>
</span> </span>
</div>
</div> </div>
</div> <div>
<div> <button (click)="clearFormAndResetResult();"
<button (click)="clearFormAndResetResult();" class="btn btn-secondary">{{messagePrefix + '.button.see-all' | translate}}</button>
class="btn btn-secondary">{{messagePrefix + '.button.see-all' | translate}}</button> </div>
</div> </form>
</form>
<ds-pagination *ngIf="(ePeopleSearch | async)?.totalElements > 0" @if ((ePeopleSearch | async)?.totalElements > 0) {
[paginationOptions]="configSearch" <ds-pagination
[collectionSize]="(ePeopleSearch | async)?.totalElements" [paginationOptions]="configSearch"
[hideGear]="true" [collectionSize]="(ePeopleSearch | async)?.totalElements"
[hidePagerWhenSinglePage]="true"> [hideGear]="true"
[hidePagerWhenSinglePage]="true">
<div class="table-responsive">
<table id="epersonsSearch" class="table table-striped table-hover table-bordered">
<thead>
<tr>
<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.identity' | translate}}</th>
<th class="align-middle">{{messagePrefix + '.table.edit' | translate}}</th>
</tr>
</thead>
<tbody>
@for (eperson of (ePeopleSearch | async)?.page; track eperson) {
<tr>
<td class="align-middle">{{eperson.id}}</td>
<td class="align-middle">
<a [routerLink]="getEPersonEditRoute(eperson.id)">
{{ dsoNameService.getName(eperson) }}
</a>
</td>
<td class="align-middle">
{{messagePrefix + '.table.email' | translate}}: {{ eperson.email ? eperson.email : '-' }}<br/>
{{messagePrefix + '.table.netid' | translate}}: {{ eperson.netid ? eperson.netid : '-' }}
</td>
<td class="align-middle">
<div class="btn-group edit-field">
<button (click)="addMemberToGroup(eperson)"
[dsBtnDisabled]="actionConfig.add.disabled"
[ngClass]="['btn btn-sm', actionConfig.add.css]"
title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(eperson) } }}">
<i [ngClass]="actionConfig.add.icon"></i>
</button>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
</ds-pagination>
}
<div class="table-responsive"> @if ((ePeopleSearch | async)?.totalElements === 0 && searchDone) {
<table id="epersonsSearch" class="table table-striped table-hover table-bordered"> <div
<thead> class="alert alert-info w-100 mb-2"
<tr> role="alert">
<th scope="col" class="align-middle">{{messagePrefix + '.table.id' | translate}}</th> {{messagePrefix + '.no-items' | translate}}
<th scope="col" class="align-middle">{{messagePrefix + '.table.name' | translate}}</th> </div>
<th scope="col" class="align-middle">{{messagePrefix + '.table.identity' | translate}}</th> }
<th class="align-middle">{{messagePrefix + '.table.edit' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let eperson of (ePeopleSearch | async)?.page">
<td class="align-middle">{{eperson.id}}</td>
<td class="align-middle">
<a [routerLink]="getEPersonEditRoute(eperson.id)">
{{ dsoNameService.getName(eperson) }}
</a>
</td>
<td class="align-middle">
{{messagePrefix + '.table.email' | translate}}: {{ eperson.email ? eperson.email : '-' }}<br/>
{{messagePrefix + '.table.netid' | translate}}: {{ eperson.netid ? eperson.netid : '-' }}
</td>
<td class="align-middle">
<div class="btn-group edit-field">
<button (click)="addMemberToGroup(eperson)"
[dsBtnDisabled]="actionConfig.add.disabled"
[ngClass]="['btn btn-sm', actionConfig.add.css]"
title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(eperson) } }}">
<i [ngClass]="actionConfig.add.icon"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</ds-pagination> </ng-container>
<div *ngIf="(ePeopleSearch | async)?.totalElements === 0 && searchDone"
class="alert alert-info w-100 mb-2"
role="alert">
{{messagePrefix + '.no-items' | translate}}
</div>
</ng-container>

View File

@@ -1,8 +1,6 @@
import { import {
AsyncPipe, AsyncPipe,
NgClass, NgClass,
NgForOf,
NgIf,
} from '@angular/common'; } from '@angular/common';
import { import {
Component, Component,
@@ -109,11 +107,9 @@ export interface EPersonListActionConfig {
ContextHelpDirective, ContextHelpDirective,
ReactiveFormsModule, ReactiveFormsModule,
PaginationComponent, PaginationComponent,
NgIf,
AsyncPipe, AsyncPipe,
RouterLink, RouterLink,
NgClass, NgClass,
NgForOf,
BtnDisabledDirective, BtnDisabledDirective,
], ],
standalone: true, standalone: true,

View File

@@ -3,51 +3,56 @@
<h4>{{messagePrefix + '.headSubgroups' | translate}}</h4> <h4>{{messagePrefix + '.headSubgroups' | translate}}</h4>
<ds-pagination *ngIf="(subGroups$ | async)?.payload?.totalElements > 0" @if ((subGroups$ | async)?.payload?.totalElements > 0) {
[paginationOptions]="config" <ds-pagination
[collectionSize]="(subGroups$ | async)?.payload?.totalElements" [paginationOptions]="config"
[hideGear]="true" [collectionSize]="(subGroups$ | async)?.payload?.totalElements"
[hidePagerWhenSinglePage]="true"> [hideGear]="true"
[hidePagerWhenSinglePage]="true">
<div class="table-responsive"> <div class="table-responsive">
<table id="subgroupsOfGroup" class="table table-striped table-hover table-bordered"> <table id="subgroupsOfGroup" 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>
<th>{{messagePrefix + '.table.edit' | translate}}</th> <th>{{messagePrefix + '.table.edit' | translate}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let group of (subGroups$ | async)?.payload?.page"> @for (group of (subGroups$ | 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)="groupDataService.startEditingNewGroup(group)" <td class="align-middle">
[routerLink]="[groupDataService.getGroupEditPageRouterLink(group)]"> <a (click)="groupDataService.startEditingNewGroup(group)"
{{ dsoNameService.getName(group) }} [routerLink]="[groupDataService.getGroupEditPageRouterLink(group)]">
</a> {{ dsoNameService.getName(group) }}
</td> </a>
<td class="align-middle">{{ dsoNameService.getName((group.object | async)?.payload)}}</td> </td>
<td class="align-middle"> <td class="align-middle">{{ dsoNameService.getName((group.object | async)?.payload)}}</td>
<div class="btn-group edit-field"> <td class="align-middle">
<button (click)="deleteSubgroupFromGroup(group)" <div class="btn-group edit-field">
<button (click)="deleteSubgroupFromGroup(group)"
class="btn btn-outline-danger btn-sm deleteButton" class="btn btn-outline-danger btn-sm deleteButton"
title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(group) } }}"> title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(group) } }}">
<i class="fas fa-trash-alt fa-fw"></i> <i class="fas fa-trash-alt fa-fw"></i>
</button> </button>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> }
</table> </tbody>
</div> </table>
</ds-pagination> </div>
</ds-pagination>
}
<div *ngIf="(subGroups$ | async)?.payload?.totalElements === 0" class="alert alert-info w-100 mb-2" @if ((subGroups$ | async)?.payload?.totalElements === 0) {
role="alert"> <div class="alert alert-info w-100 mb-2"
{{messagePrefix + '.no-subgroups-yet' | translate}} role="alert">
</div> {{messagePrefix + '.no-subgroups-yet' | translate}}
</div>
}
<h4 id="search" class="border-bottom pb-2"> <h4 id="search" class="border-bottom pb-2">
<span *dsContextHelp="{ <span *dsContextHelp="{
@@ -56,7 +61,7 @@
iconPlacement: 'right', iconPlacement: 'right',
tooltipPlacement: ['top', 'right', 'bottom'] tooltipPlacement: ['top', 'right', 'bottom']
}" }"
> >
{{messagePrefix + '.search.head' | translate}} {{messagePrefix + '.search.head' | translate}}
</span> </span>
@@ -65,11 +70,11 @@
<div class="flex-grow-1 me-3"> <div class="flex-grow-1 me-3">
<div class="mb-3 input-group me-3"> <div class="mb-3 input-group me-3">
<input type="text" name="query" id="query" formControlName="query" <input type="text" name="query" id="query" formControlName="query"
class="form-control" aria-label="Search input"> class="form-control" aria-label="Search input">
<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 }}
</button> </button>
</span> </span>
</div> </div>
@@ -81,50 +86,55 @@
</div> </div>
</form> </form>
<ds-pagination *ngIf="(searchResults$ | async)?.payload?.totalElements > 0" @if ((searchResults$ | async)?.payload?.totalElements > 0) {
[paginationOptions]="configSearch" <ds-pagination
[collectionSize]="(searchResults$ | async)?.payload?.totalElements" [paginationOptions]="configSearch"
[hideGear]="true" [collectionSize]="(searchResults$ | async)?.payload?.totalElements"
[hidePagerWhenSinglePage]="true"> [hideGear]="true"
[hidePagerWhenSinglePage]="true">
<div class="table-responsive"> <div class="table-responsive">
<table id="groupsSearch" class="table table-striped table-hover table-bordered"> <table id="groupsSearch" 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>
<th class="align-middle">{{messagePrefix + '.table.edit' | translate}}</th> <th class="align-middle">{{messagePrefix + '.table.edit' | translate}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let group of (searchResults$ | async)?.payload?.page"> @for (group of (searchResults$ | 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)="groupDataService.startEditingNewGroup(group)" <td class="align-middle">
[routerLink]="[groupDataService.getGroupEditPageRouterLink(group)]"> <a (click)="groupDataService.startEditingNewGroup(group)"
{{ dsoNameService.getName(group) }} [routerLink]="[groupDataService.getGroupEditPageRouterLink(group)]">
</a> {{ dsoNameService.getName(group) }}
</td> </a>
<td class="align-middle">{{ dsoNameService.getName((group.object | async)?.payload) }}</td> </td>
<td class="align-middle"> <td class="align-middle">{{ dsoNameService.getName((group.object | async)?.payload) }}</td>
<div class="btn-group edit-field"> <td class="align-middle">
<button (click)="addSubgroupToGroup(group)" <div class="btn-group edit-field">
<button (click)="addSubgroupToGroup(group)"
class="btn btn-outline-primary btn-sm addButton" class="btn btn-outline-primary btn-sm addButton"
title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(group) } }}"> title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(group) } }}">
<i class="fas fa-plus fa-fw"></i> <i class="fas fa-plus fa-fw"></i>
</button> </button>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> }
</table> </tbody>
</div> </table>
</ds-pagination> </div>
</ds-pagination>
}
<div *ngIf="(searchResults$ | async)?.payload?.totalElements === 0 && searchDone" class="alert alert-info w-100 mb-2" @if ((searchResults$ | async)?.payload?.totalElements === 0 && searchDone) {
role="alert"> <div class="alert alert-info w-100 mb-2"
{{messagePrefix + '.no-items' | translate}} role="alert">
</div> {{messagePrefix + '.no-items' | translate}}
</div>
}
</ng-container> </ng-container>

View File

@@ -1,8 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgForOf,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
Input, Input,
@@ -65,12 +61,10 @@ enum SubKey {
imports: [ imports: [
RouterLink, RouterLink,
AsyncPipe, AsyncPipe,
NgForOf,
ContextHelpDirective, ContextHelpDirective,
TranslateModule, TranslateModule,
ReactiveFormsModule, ReactiveFormsModule,
PaginationComponent, PaginationComponent,
NgIf,
], ],
standalone: true, standalone: true,
}) })

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,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgForOf,
NgIf,
NgSwitch,
NgSwitchCase,
} from '@angular/common';
import { import {
Component, Component,
OnDestroy, OnDestroy,
@@ -79,12 +73,8 @@ import { followLink } from '../../shared/utils/follow-link-config.model';
RouterLink, RouterLink,
ReactiveFormsModule, ReactiveFormsModule,
AsyncPipe, AsyncPipe,
NgIf,
PaginationComponent, PaginationComponent,
NgSwitch,
NgSwitchCase,
NgbTooltipModule, NgbTooltipModule,
NgForOf,
BtnDisabledDirective, BtnDisabledDirective,
], ],
standalone: true, standalone: true,

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

@@ -1,7 +1,4 @@
import { import { Location } from '@angular/common';
Location,
NgIf,
} from '@angular/common';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
@@ -36,7 +33,6 @@ import { FileDropzoneNoUploaderComponent } from '../../shared/upload/file-dropzo
selector: 'ds-batch-import-page', selector: 'ds-batch-import-page',
templateUrl: './batch-import-page.component.html', templateUrl: './batch-import-page.component.html',
imports: [ imports: [
NgIf,
TranslateModule, TranslateModule,
FormsModule, FormsModule,
UiSwitchModule, UiSwitchModule,

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,258 @@
<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>
<!-- In the usesActorEmailId section -->
<div class="mb-5 mt-5">
<label class="status-label font-weight-bold" for="usesActorEmailId">{{ 'ldn-service-usesActorEmailId' | translate }}</label>
<div>
<input formControlName="usesActorEmailId" hidden id="usesActorEmailId"
name="usesActorEmailId" type="checkbox">
<div (click)="toggleUsesActorEmailId()"
[class.checked]="formModel.get('usesActorEmailId').value" class="toggle-switch">
<div class="slider"></div>
</div>
<div class="text-muted">
{{ 'ldn-service-usesActorEmailId-description' | translate }}
</div> </div>
</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

@@ -5,11 +5,7 @@ import {
transition, transition,
trigger, trigger,
} from '@angular/animations'; } from '@angular/animations';
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgForOf,
NgIf,
} from '@angular/common';
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
@@ -77,9 +73,7 @@ import { notifyPatterns } from '../ldn-services-patterns/ldn-service-coar-patter
imports: [ imports: [
ReactiveFormsModule, ReactiveFormsModule,
TranslateModule, TranslateModule,
NgIf,
NgbDropdownModule, NgbDropdownModule,
NgForOf,
AsyncPipe, AsyncPipe,
], ],
}) })
@@ -131,6 +125,7 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
score: ['', [Validators.required, Validators.pattern('^0*(\.[0-9]+)?$|^1(\.0+)?$')]], inboundPattern: [''], score: ['', [Validators.required, Validators.pattern('^0*(\.[0-9]+)?$|^1(\.0+)?$')]], inboundPattern: [''],
constraintPattern: [''], constraintPattern: [''],
enabled: [''], enabled: [''],
usesActorEmailId: [''],
type: LDN_SERVICE.value, type: LDN_SERVICE.value,
}); });
} }
@@ -184,7 +179,8 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
return rest; return rest;
}); });
const values = { ...this.formModel.value, enabled: true }; const values = { ...this.formModel.value, enabled: true,
usesActorEmailId: this.formModel.get('usesActorEmailId').value };
const ldnServiceData = this.ldnServicesService.create(values); const ldnServiceData = this.ldnServicesService.create(values);
@@ -243,6 +239,7 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
ldnUrl: this.ldnService.ldnUrl, ldnUrl: this.ldnService.ldnUrl,
type: this.ldnService.type, type: this.ldnService.type,
enabled: this.ldnService.enabled, enabled: this.ldnService.enabled,
usesActorEmailId: this.ldnService.usesActorEmailId,
lowerIp: this.ldnService.lowerIp, lowerIp: this.ldnService.lowerIp,
upperIp: this.ldnService.upperIp, upperIp: this.ldnService.upperIp,
}); });
@@ -390,6 +387,32 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
); );
} }
/**
* Toggles the usesActorEmailId field of the LDN service by sending a patch request
*/
toggleUsesActorEmailId() {
const newStatus = !this.formModel.get('usesActorEmailId').value;
if (!this.isNewService) {
const patchOperation: Operation = {
op: 'replace',
path: '/usesActorEmailId',
value: newStatus,
};
this.ldnServicesService.patch(this.ldnService, [patchOperation]).pipe(
getFirstCompletedRemoteData(),
).subscribe(
() => {
this.formModel.get('usesActorEmailId').setValue(newStatus);
this.cdRef.detectChanges();
},
);
} else {
this.formModel.get('usesActorEmailId').setValue(newStatus);
this.cdRef.detectChanges();
}
}
/** /**
* Closes the modal * Closes the modal
*/ */

View File

@@ -12,6 +12,7 @@ import { LdnService } from '../ldn-services-model/ldn-services.model';
export const mockLdnService: LdnService = { export const mockLdnService: LdnService = {
uuid: '1', uuid: '1',
enabled: false, enabled: false,
usesActorEmailId: false,
score: 0, score: 0,
id: 1, id: 1,
lowerIp: '192.0.2.146', lowerIp: '192.0.2.146',
@@ -49,6 +50,7 @@ export const mockLdnServiceRD$ = createSuccessfulRemoteDataObject$(mockLdnServic
export const mockLdnServices: LdnService[] = [{ export const mockLdnServices: LdnService[] = [{
uuid: '1', uuid: '1',
enabled: false, enabled: false,
usesActorEmailId: false,
score: 0, score: 0,
id: 1, id: 1,
lowerIp: '192.0.2.146', lowerIp: '192.0.2.146',
@@ -81,6 +83,7 @@ export const mockLdnServices: LdnService[] = [{
}, { }, {
uuid: '2', uuid: '2',
enabled: false, enabled: false,
usesActorEmailId: false,
score: 0, score: 0,
id: 2, id: 2,
lowerIp: '192.0.2.146', lowerIp: '192.0.2.146',

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

@@ -1,8 +1,6 @@
import { import {
AsyncPipe, AsyncPipe,
NgClass, NgClass,
NgFor,
NgIf,
} from '@angular/common'; } from '@angular/common';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
@@ -54,8 +52,6 @@ import { LdnService } from '../ldn-services-model/ldn-services.model';
styleUrls: ['./ldn-services-directory.component.scss'], styleUrls: ['./ldn-services-directory.component.scss'],
changeDetection: ChangeDetectionStrategy.Default, changeDetection: ChangeDetectionStrategy.Default,
imports: [ imports: [
NgIf,
NgFor,
TranslateModule, TranslateModule,
AsyncPipe, AsyncPipe,
PaginationComponent, PaginationComponent,

View File

@@ -52,6 +52,9 @@ export class LdnService extends CacheableObject {
@autoserialize @autoserialize
enabled: boolean; enabled: boolean;
@autoserialize
usesActorEmailId: boolean;
@autoserialize @autoserialize
ldnUrl: string; ldnUrl: string;

View File

@@ -13,11 +13,13 @@
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" [routerLink]="'outbound'" [queryParams]="{view: 'table'}">{{'admin.notify.dashboard.outbound-logs' | translate}}</a> <a class="nav-link" [routerLink]="'outbound'" [queryParams]="{view: 'table'}">{{'admin.notify.dashboard.outbound-logs' | translate}}</a>
</ul> </ul>
<div class="mt-2"> <div class="mt-2">
<ds-admin-notify-metrics *ngIf="(notifyMetricsRows$ | async)?.length" [boxesConfig]="notifyMetricsRows$ | async"></ds-admin-notify-metrics> @if ((notifyMetricsRows$ | async)?.length) {
<ds-admin-notify-metrics [boxesConfig]="notifyMetricsRows$ | async"></ds-admin-notify-metrics>
}
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>

View File

@@ -1,7 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
Inject, Inject,
@@ -46,7 +43,6 @@ import {
imports: [ imports: [
AdminNotifyMetricsComponent, AdminNotifyMetricsComponent,
RouterLink, RouterLink,
NgIf,
TranslateModule, TranslateModule,
AsyncPipe, AsyncPipe,
], ],

View File

@@ -5,12 +5,14 @@
</button> </button>
</div> </div>
<div class="modal-body p-4"> <div class="modal-body p-4">
<div *ngFor="let key of notifyMessageKeys"> @for (key of notifyMessageKeys; track key) {
<div>
<div class="row mb-4"> <div class="row mb-4">
<div class="font-weight-bold col">{{ key + '.notify-detail-modal' | translate}}</div> <div class="font-weight-bold col">{{ key + '.notify-detail-modal' | translate}}</div>
<div class="col text-right">{{'notify-detail-modal.' + notifyMessage[key] | translate: {default: notifyMessage[key] ?? "n/a" } }}</div> <div class="col text-right">{{'notify-detail-modal.' + notifyMessage[key] | translate: {default: notifyMessage[key] ?? "n/a" } }}</div>
</div> </div>
</div> </div>
}
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<button class="btn-primary" (click)="toggleCoarMessage()"> <button class="btn-primary" (click)="toggleCoarMessage()">
@@ -18,5 +20,7 @@
</button> </button>
</div> </div>
<pre @fadeIn [innerHTML]="notifyMessage.message" class="bg-secondary text-white mt-2 p-2" *ngIf="isCoarMessageVisible"></pre> @if (isCoarMessageVisible) {
<pre @fadeIn [innerHTML]="notifyMessage.message" class="bg-secondary text-white mt-2 p-2"></pre>
}
</div> </div>

View File

@@ -1,7 +1,4 @@
import {
NgForOf,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -26,9 +23,7 @@ import { AdminNotifyMessage } from '../models/admin-notify-message.model';
], ],
standalone: true, standalone: true,
imports: [ imports: [
NgForOf,
TranslateModule, TranslateModule,
NgIf,
], ],
}) })
/** /**

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

@@ -1,7 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
Inject, Inject,
@@ -39,7 +36,6 @@ import { ThemedSearchComponent } from '../../../../shared/search/themed-search.c
ThemedSearchComponent, ThemedSearchComponent,
AsyncPipe, AsyncPipe,
TranslateModule, TranslateModule,
NgIf,
], ],
}) })

View File

@@ -1,9 +1,13 @@
<div class="mb-5" *ngFor="let row of boxesConfig"> @for (row of boxesConfig; track row) {
<div class="mb-2">{{ row.title | translate }}</div> <div class="mb-5">
<div class="row justify-content-between"> <div class="mb-2">{{ row.title | translate }}</div>
<div class="col-sm" *ngFor="let box of row.boxes"> <div class="row justify-content-between">
<ds-notification-box (selectedBoxConfig)="navigateToSelectedSearchConfig($event)" [boxConfig]="box"></ds-notification-box> @for (box of row.boxes; track box) {
<div class="col-sm">
<ds-notification-box (selectedBoxConfig)="navigateToSelectedSearchConfig($event)" [boxConfig]="box"></ds-notification-box>
</div>
}
</div> </div>
</div> </div>
</div> }

View File

@@ -1,4 +1,4 @@
import { NgForOf } from '@angular/common';
import { import {
Component, Component,
Input, Input,
@@ -17,7 +17,6 @@ import { AdminNotifyMetricsRow } from './admin-notify-metrics.model';
imports: [ imports: [
NotificationBoxComponent, NotificationBoxComponent,
TranslateModule, TranslateModule,
NgForOf,
], ],
}) })
/** /**

View File

@@ -11,41 +11,57 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let message of (messagesSubject$ | async)"> @for (message of (messagesSubject$ | async); track message) {
<td class="text-nowrap"> <tr>
<div *ngIf="message.queueLastStartTime">{{ message.queueLastStartTime | date:"YYYY/MM/d hh:mm:ss" }}</div> <td class="text-nowrap">
<div *ngIf="!message.queueLastStartTime">n/a</div> @if (message.queueLastStartTime) {
</td> <div>{{ message.queueLastStartTime | date:"YYYY/MM/d hh:mm:ss" }}</div>
<td> }
<ds-truncatable [id]="message.id"> @if (!message.queueLastStartTime) {
<ds-truncatable-part [id]="message.id" [minLines]="2"> <div>n/a</div>
<a *ngIf="message.relatedItem" [routerLink]="'/items/' + (message.context || message.object)">{{ message.relatedItem }}</a> }
</ds-truncatable-part> </td>
</ds-truncatable> <td>
<div *ngIf="!message.relatedItem">n/a</div> <ds-truncatable [id]="message.id">
</td> <ds-truncatable-part [id]="message.id" [minLines]="2">
<td> @if (message.relatedItem) {
<div *ngIf="message.ldnService">{{ message.ldnService }}</div> <a [routerLink]="'/items/' + (message.context || message.object)">{{ message.relatedItem }}</a>
<div *ngIf="!message.ldnService">n/a</div> }
</td> </ds-truncatable-part>
<td> </ds-truncatable>
<div>{{ message.activityStreamType }}</div> @if (!message.relatedItem) {
</td> <div>n/a</div>
<td> }
<div class="text-nowrap">{{ 'notify-detail-modal.' + message.queueStatusLabel | translate }}</div> </td>
</td> <td>
<td> @if (message.ldnService) {
<div class="d-flex flex-column"> <div>{{ message.ldnService }}</div>
<button class="btn mb-2 btn-dark" (click)="openDetailModal(message)">{{ 'notify-message-result.detail' | translate }}</button> }
<button *ngIf="message.queueStatusLabel !== reprocessStatus && validStatusesForReprocess.includes(message.queueStatusLabel)" @if (!message.ldnService) {
(click)="reprocessMessage(message)" <div>n/a</div>
class="btn btn-warning" }
> </td>
{{ 'notify-message-result.reprocess' | translate }} <td>
</button> <div>{{ message.activityStreamType }}</div>
</div> </td>
</td> <td>
</tr> <div class="text-nowrap">{{ 'notify-detail-modal.' + message.queueStatusLabel | translate }}</div>
</td>
<td>
<div class="d-flex flex-column">
<button class="btn mb-2 btn-dark" (click)="openDetailModal(message)">{{ 'notify-message-result.detail' | translate }}</button>
@if (message.queueStatusLabel !== reprocessStatus && validStatusesForReprocess.includes(message.queueStatusLabel)) {
<button
(click)="reprocessMessage(message)"
class="btn btn-warning"
>
{{ 'notify-message-result.reprocess' | translate }}
</button>
}
</div>
</td>
</tr>
}
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@@ -1,8 +1,6 @@
import { import {
AsyncPipe, AsyncPipe,
DatePipe, DatePipe,
NgForOf,
NgIf,
} from '@angular/common'; } from '@angular/common';
import { import {
Component, Component,
@@ -42,8 +40,6 @@ import { AdminNotifyMessagesService } from '../services/admin-notify-messages.se
standalone: true, standalone: true,
imports: [ imports: [
TranslateModule, TranslateModule,
NgForOf,
NgIf,
DatePipe, DatePipe,
AsyncPipe, AsyncPipe,
TruncatableComponent, TruncatableComponent,

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,8 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgForOf,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
OnDestroy, OnDestroy,
@@ -41,12 +37,10 @@ import { PaginationComponentOptions } from '../../../shared/pagination/paginatio
selector: 'ds-bitstream-formats', selector: 'ds-bitstream-formats',
templateUrl: './bitstream-formats.component.html', templateUrl: './bitstream-formats.component.html',
imports: [ imports: [
NgIf,
AsyncPipe, AsyncPipe,
RouterLink, RouterLink,
TranslateModule, TranslateModule,
PaginationComponent, PaginationComponent,
NgForOf,
], ],
standalone: true, standalone: true,
}) })

View File

@@ -1,3 +1,5 @@
<ds-form *ngIf="formModel" @if (formModel) {
[formId]="'comcol-form-id'" <ds-form
[formModel]="formModel" (submitForm)="onSubmit()" (cancel)="onCancel()"></ds-form> [formId]="'comcol-form-id'"
[formModel]="formModel" (submitForm)="onSubmit()" (cancel)="onCancel()"></ds-form>
}

View File

@@ -1,4 +1,4 @@
import { NgIf } from '@angular/common';
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -35,7 +35,6 @@ import { getBitstreamFormatsModuleRoute } from '../../admin-registries-routing-p
templateUrl: './format-form.component.html', templateUrl: './format-form.component.html',
imports: [ imports: [
FormComponent, FormComponent,
NgIf,
], ],
standalone: true, standalone: true,
}) })

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

@@ -1,8 +1,6 @@
import { import {
AsyncPipe, AsyncPipe,
NgClass, NgClass,
NgForOf,
NgIf,
} from '@angular/common'; } from '@angular/common';
import { import {
Component, Component,
@@ -49,8 +47,6 @@ import { MetadataSchemaFormComponent } from './metadata-schema-form/metadata-sch
TranslateModule, TranslateModule,
AsyncPipe, AsyncPipe,
PaginationComponent, PaginationComponent,
NgIf,
NgForOf,
NgClass, NgClass,
RouterLink, RouterLink,
], ],

View File

@@ -1,18 +1,16 @@
<div *ngIf="activeMetadataSchema$ | async; then editheader; else createHeader"></div> @if (activeMetadataSchema$ | async) {
<h2>{{messagePrefix + '.edit' | translate}}</h2>
} @else {
<h2>{{messagePrefix + '.create' | translate}}</h2>
}
<ng-template #createHeader>
<h2>{{messagePrefix + '.create' | translate}}</h2>
</ng-template>
<ng-template #editheader>
<h2>{{messagePrefix + '.edit' | translate}}</h2>
</ng-template>
<ds-form [formId]="formId" <ds-form [formId]="formId"
[formModel]="formModel" [formModel]="formModel"
[formGroup]="formGroup" [formGroup]="formGroup"
[formLayout]="formLayout" [formLayout]="formLayout"
(cancel)="onCancel()" (cancel)="onCancel()"
(submitForm)="onSubmit()"> (submitForm)="onSubmit()">
</ds-form> </ds-form>

View File

@@ -1,7 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -39,7 +36,6 @@ import { FormComponent } from '../../../../shared/form/form.component';
selector: 'ds-metadata-schema-form', selector: 'ds-metadata-schema-form',
templateUrl: './metadata-schema-form.component.html', templateUrl: './metadata-schema-form.component.html',
imports: [ imports: [
NgIf,
AsyncPipe, AsyncPipe,
TranslateModule, TranslateModule,
FormComponent, FormComponent,

View File

@@ -1,18 +1,16 @@
<div *ngIf="registryService.getActiveMetadataField() | async; then editheader; else createHeader"></div> @if (registryService.getActiveMetadataField() | async) {
<h2>{{messagePrefix + '.edit' | translate}}</h2>
} @else {
<h2>{{messagePrefix + '.create' | translate}}</h2>
}
<ng-template #createHeader>
<h2>{{messagePrefix + '.create' | translate}}</h2>
</ng-template>
<ng-template #editheader>
<h2>{{messagePrefix + '.edit' | translate}}</h2>
</ng-template>
<ds-form [formId]="formId" <ds-form [formId]="formId"
[formModel]="formModel" [formModel]="formModel"
[formLayout]="formLayout" [formLayout]="formLayout"
[formGroup]="formGroup" [formGroup]="formGroup"
(cancel)="onCancel()" (cancel)="onCancel()"
(submit)="onSubmit()"> (submit)="onSubmit()">
</ds-form> </ds-form>

View File

@@ -1,7 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -35,7 +32,6 @@ import { FormComponent } from '../../../../shared/form/form.component';
selector: 'ds-metadata-field-form', selector: 'ds-metadata-field-form',
templateUrl: './metadata-field-form.component.html', templateUrl: './metadata-field-form.component.html',
imports: [ imports: [
NgIf,
FormComponent, FormComponent,
TranslateModule, TranslateModule,
AsyncPipe, AsyncPipe,

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,8 +1,6 @@
import { import {
AsyncPipe, AsyncPipe,
NgClass, NgClass,
NgForOf,
NgIf,
} from '@angular/common'; } from '@angular/common';
import { import {
Component, Component,
@@ -59,8 +57,6 @@ import { MetadataFieldFormComponent } from './metadata-field-form/metadata-field
MetadataFieldFormComponent, MetadataFieldFormComponent,
TranslateModule, TranslateModule,
PaginationComponent, PaginationComponent,
NgIf,
NgForOf,
NgClass, NgClass,
RouterLink, RouterLink,
], ],

View File

@@ -1,64 +1,72 @@
<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.reports.collections.head" | translate }}</h1> <h1 id="header" class="border-bottom pb-2">{{ "admin.reports.collections.head" | translate }}</h1>
<div id="metadatadiv"> <div id="metadatadiv">
<ngb-accordion [closeOthers]="true" activeIds="filters" #acc="ngbAccordion"> <ngb-accordion [closeOthers]="true" activeIds="filters" #acc="ngbAccordion">
<ngb-panel id="filters"> <ngb-panel id="filters">
<ng-template ngbPanelTitle> <ng-template ngbPanelTitle>
{{ "admin.reports.commons.filters" | translate }} {{ "admin.reports.commons.filters" | translate }}
</ng-template> </ng-template>
<ng-template ngbPanelContent> <ng-template ngbPanelContent>
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<span class="col-3"></span> <span class="col-3"></span>
<button class="btn btn-primary mt-1 col-6" (click)="submit()">{{ "admin.reports.button.show-collections" | translate }}</button> <button class="btn btn-primary mt-1 col-6" (click)="submit()">{{ "admin.reports.button.show-collections" | translate }}</button>
</div> </div>
<ds-filters [filtersForm]="filtersFormGroup()"></ds-filters> <ds-filters [filtersForm]="filtersFormGroup()"></ds-filters>
<div class="row"> <div class="row">
<span class="col-3"></span> <span class="col-3"></span>
<button class="btn btn-primary mt-1 col-6" (click)="submit()">{{ "admin.reports.button.show-collections" | translate }}</button> <button class="btn btn-primary mt-1 col-6" (click)="submit()">{{ "admin.reports.button.show-collections" | translate }}</button>
</div> </div>
</div> </div>
</ng-template> </ng-template>
</ngb-panel> </ngb-panel>
<ngb-panel id="collections"> <ngb-panel id="collections">
<ng-template ngbPanelTitle> <ng-template ngbPanelTitle>
{{ "admin.reports.collections.collections-report" | translate }} {{ "admin.reports.collections.collections-report" | translate }}
</ng-template> </ng-template>
<ng-template ngbPanelContent> <ng-template ngbPanelContent>
<table id="table" class="table table-striped"> <table id="table" class="table table-striped">
<thead> <thead>
<tr class="header"> <tr class="header">
<th rowspan="2">{{ "admin.reports.collections.community" | translate }}</th> <th rowspan="2">{{ "admin.reports.collections.community" | translate }}</th>
<th rowspan="2">{{ "admin.reports.collections.collection" | translate }}</th> <th rowspan="2">{{ "admin.reports.collections.collection" | translate }}</th>
<th>{{ "admin.reports.collections.nb_items" | translate }}</th> <th>{{ "admin.reports.collections.nb_items" | translate }}</th>
<th>{{ "admin.reports.collections.match_all_selected_filters" | translate }}</th> <th>{{ "admin.reports.collections.match_all_selected_filters" | translate }}</th>
<th *ngFor="let filter of results.summary.values | keyvalue">{{ ("admin.reports.commons.filters." + getGroup(filter.key) + "." + filter.key) | translate }}</th> @for (filter of results.summary.values | keyvalue; track filter) {
</tr> <th>{{ ("admin.reports.commons.filters." + getGroup(filter.key) + "." + filter.key) | translate }}</th>
<tr class="header"> }
<th class="num">{{ results.summary.nbTotalItems }}</th> </tr>
<th class="num">{{ results.summary.allFiltersValue }}</th> <tr class="header">
<th class="num" *ngFor="let filter of results.summary.values | keyvalue">{{ filter.value }}</th> <th class="num">{{ results.summary.nbTotalItems }}</th>
</tr> <th class="num">{{ results.summary.allFiltersValue }}</th>
</thead> @for (filter of results.summary.values | keyvalue; track filter) {
<tbody> <th class="num">{{ filter.value }}</th>
<tr *ngFor="let coll of results.collections"> }
<td><a href="/handle/{{ coll.communityHandle }}" rel="noopener noreferrer" target="_blank">{{ coll.communityLabel }}</a></td> </tr>
<td><a href="/handle/{{ coll.handle }}" rel="noopener noreferrer" target="_blank">{{ coll.label }}</a></td> </thead>
<td class="num">{{ coll.nbTotalItems }}</td> <tbody>
<td class="num">{{ coll.allFiltersValue }}</td> @for (coll of results.collections; track coll) {
<td class="num" *ngFor="let filter of results.summary.values | keyvalue">{{ coll.values[filter.key] || 0 }}</td> <tr>
</tr> <td><a href="/handle/{{ coll.communityHandle }}" rel="noopener noreferrer" target="_blank">{{ coll.communityLabel }}</a></td>
</tbody> <td><a href="/handle/{{ coll.handle }}" rel="noopener noreferrer" target="_blank">{{ coll.label }}</a></td>
</table> <td class="num">{{ coll.nbTotalItems }}</td>
</ng-template> <td class="num">{{ coll.allFiltersValue }}</td>
</ngb-panel> @for (filter of results.summary.values | keyvalue; track filter) {
</ngb-accordion> <td class="num">{{ coll.values[filter.key] || 0 }}</td>
</div> }
</tr>
}
</tbody>
</table>
</ng-template>
</ngb-panel>
</ngb-accordion>
</div>
</div>
</div> </div>
</div>
</div> </div>

View File

@@ -1,4 +1,8 @@
import { HttpClientTestingModule } from '@angular/common/http/testing'; import {
provideHttpClient,
withInterceptorsFromDi,
} from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import { import {
ComponentFixture, ComponentFixture,
@@ -39,22 +43,21 @@ describe('FiltersComponent', () => {
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ schemas: [NO_ERRORS_SCHEMA],
NgbAccordionModule, imports: [NgbAccordionModule,
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {
provide: TranslateLoader, provide: TranslateLoader,
useClass: TranslateLoaderMock, useClass: TranslateLoaderMock,
}, },
}), }),
HttpClientTestingModule, FilteredCollectionsComponent],
FilteredCollectionsComponent,
],
providers: [ providers: [
FormBuilder, FormBuilder,
DspaceRestService, DspaceRestService,
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
], ],
schemas: [NO_ERRORS_SCHEMA],
}); });
})); }));

View File

@@ -1,7 +1,4 @@
import { import { KeyValuePipe } from '@angular/common';
KeyValuePipe,
NgForOf,
} from '@angular/common';
import { import {
Component, Component,
OnInit, OnInit,
@@ -37,7 +34,6 @@ import { FilteredCollections } from './filtered-collections.model';
NgbAccordionModule, NgbAccordionModule,
FiltersComponent, FiltersComponent,
KeyValuePipe, KeyValuePipe,
NgForOf,
], ],
standalone: true, standalone: true,
}) })

View File

@@ -1,8 +1,10 @@
import { Item } from 'src/app/core/shared/item.model'; import { Item } from 'src/app/core/shared/item.model';
import { Collection } from '../../../core/shared/collection.model';
export class FilteredItems { export class FilteredItems {
public items: Item[] = []; public items: FilteredItem[] = [];
public itemCount: number; public itemCount: number;
public clear() { public clear() {
@@ -21,3 +23,8 @@ export class FilteredItems {
} }
} }
export interface FilteredItem extends Omit<Item, 'owningCollection'> {
index: number;
owningCollection?: Collection;
}

View File

@@ -1,175 +1,201 @@
<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.reports.items.head' | translate}}</h1> <h1 id="header" class="border-bottom pb-2">{{'admin.reports.items.head' | translate}}</h1>
<div id="querydiv" [formGroup]="queryForm"> <div id="querydiv" [formGroup]="queryForm">
<ngb-accordion [closeOthers]="true" activeIds="collectionSelector" #acc="ngbAccordion"> <ngb-accordion [closeOthers]="true" activeIds="collectionSelector" #acc="ngbAccordion">
<ngb-panel id="collectionSelector"> <ngb-panel id="collectionSelector">
<ng-template ngbPanelTitle> <ng-template ngbPanelTitle>
{{'admin.reports.items.section.collectionSelector' | translate}} {{'admin.reports.items.section.collectionSelector' | translate}}
</ng-template> </ng-template>
<ng-template ngbPanelContent> <ng-template ngbPanelContent>
<select id="collSel" name="collSel" class="form-control" multiple="multiple" size="10" formControlName="collections"> <select id="collSel" name="collSel" class="form-control" multiple="multiple" size="10" formControlName="collections">
<option *ngFor="let item of collections" [value]="item.id" [disabled]="item.disabled">{{item.name$ | async}}</option> @for (item of collections; track item) {
</select> <option [value]="item.id" [disabled]="item.disabled">{{item.name$ | async}}</option>
<div class="row"> }
<span class="col-3"></span> </select>
<button class="btn btn-primary mt-1 col-6" (click)="submit()">{{'admin.reports.items.run' | translate}}</button> <div class="row">
</div> <span class="col-3"></span>
</ng-template> <button class="btn btn-primary mt-1 col-6" (click)="submit()">{{'admin.reports.items.run' | translate}}</button>
</ngb-panel> </div>
<ngb-panel id="metadataFieldQueries"> </ng-template>
<ng-template ngbPanelTitle> </ngb-panel>
{{'admin.reports.items.section.metadataFieldQueries' | translate}} <ngb-panel id="metadataFieldQueries">
</ng-template> <ng-template ngbPanelTitle>
<ng-template ngbPanelContent> {{'admin.reports.items.section.metadataFieldQueries' | translate}}
<fieldset id="predefqueries" class="form-group"> </ng-template>
<label> <ng-template ngbPanelContent>
{{'admin.reports.items.predefinedQueries' | translate}} <fieldset id="predefqueries" class="form-group">
</label> <label>
<select id="predefselect" formControlName="presetQuery" class="form-control" (change)="setPresetQuery()"> {{'admin.reports.items.predefinedQueries' | translate}}
<option *ngFor="let item of presetQueries" [value]="item.id" [selected]="item.isDefault">{{item.label | translate}}</option> </label>
</select> <select id="predefselect" formControlName="presetQuery" class="form-control" (change)="setPresetQuery()">
</fieldset> @for (item of presetQueries; track item.id) {
<div class="row">&nbsp;</div> <option [value]="item.id" [selected]="item.isDefault">{{item.label | translate}}</option>
<div id="queries"> }
<div class="metadata" *ngFor="let pred of queryPredicatesArray().controls; let i = index"> </select>
<div [formGroup]="pred" class="form-group"> </fieldset>
<div class="form-row"> <div class="row">&nbsp;</div>
<div class="col-4"> <div id="queries">
<select class="query-tool" formControlName="field" class="form-control"> @for (pred of queryPredicatesArray().controls; track pred; let i = $index) {
<option *ngFor="let item of metadataFieldsWithAny" [value]="item.id">{{item.name$ | async}}</option> <div class="metadata">
</select> <div [formGroup]="pred" class="form-group">
</div> <div class="form-row">
<div class="col-auto"> <div class="col-4">
<select class="query-tool" formControlName="operator" class="form-control"> <select class="query-tool" formControlName="field" class="form-control">
<option *ngFor="let item of predicates" [value]="item.id">{{item.name$ | async | translate}}</option> @for (item of metadataFieldsWithAny; track item) {
</select> <option [value]="item.id">{{item.name$ | async}}</option>
</div> }
<div class="col"> </select>
<input class="form-control" formControlName="value"/> </div>
</div> <div class="col-auto">
<div class="col-auto"> <select class="query-tool" formControlName="operator" class="form-control">
<button class="btn btn-light" (click)="addQueryPredicate()">+</button> @for (item of predicates; track item) {
&nbsp; <option [value]="item.id">{{item.name$ | async | translate}}</option>
<button class="btn btn-light" [dsBtnDisabled]="deleteQueryPredicateDisabled()" (click)="deleteQueryPredicate(i)"></button> }
</div> </select>
</div> </div>
</div> <div class="col">
</div> <input class="form-control" formControlName="value"/>
</div> </div>
<div class="row"> <div class="col-auto">
<span class="col-3"></span> <button class="btn btn-light" (click)="addQueryPredicate()">+</button>
<button class="btn btn-primary mt-1 col-6" (click)="submit()">{{'admin.reports.items.run' | translate}}</button> &nbsp;
</div> <button class="btn btn-light" [dsBtnDisabled]="deleteQueryPredicateDisabled()" (click)="deleteQueryPredicate(i)"></button>
</ng-template> </div>
</ngb-panel> </div>
<ngb-panel id="limitPaginateQueries"> </div>
<ng-template ngbPanelTitle> </div>
{{'admin.reports.items.section.limitPaginateQueries' | translate}} }
</ng-template> </div>
<ng-template ngbPanelContent> <div class="row">
<div class="row align-items-center"> <span class="col-3"></span>
<label for="limit" class="col-sm-2 col-form-label">{{'admin.reports.items.limit' | translate}}:</label> <button class="btn btn-primary mt-1 col-6" (click)="submit()">{{'admin.reports.items.run' | translate}}</button>
<div class="col-6"> </div>
<select id="limit" name="limit" formControlName="pageLimit" class="form-control col-6"> </ng-template>
<option *ngFor="let item of pageLimits" value="{{item.id}}" [selected]="item.isDefault">{{item.name$ | async}}</option> </ngb-panel>
</select> <ngb-panel id="limitPaginateQueries">
</div> <ng-template ngbPanelTitle>
</div> {{'admin.reports.items.section.limitPaginateQueries' | translate}}
<div class="row align-items-center"> </ng-template>
<label for="offset" class="col-sm-2 col-form-label">{{'admin.reports.items.offset' | translate}}:</label> <ng-template ngbPanelContent>
<div class="col-6"> <div class="row align-items-center">
<input id="offset" name="offset" value="0" class="form-control col-6"> <label for="limit" class="col-sm-2 col-form-label">{{'admin.reports.items.limit' | translate}}:</label>
</div> <div class="col-6">
</div> <select id="limit" name="limit" formControlName="pageLimit" class="form-control col-6">
<div class="row"> @for (item of pageLimits; track item) {
<span class="col-3"></span> <option value="{{item.id}}" [selected]="item.isDefault">{{item.name$ | async}}</option>
<button class="btn btn-primary mt-1 col-6" (click)="submit()">{{'admin.reports.items.run' | translate}}</button> }
</div> </select>
</ng-template> </div>
</ngb-panel> </div>
<ngb-panel id="filters"> <div class="row align-items-center">
<ng-template ngbPanelTitle> <label for="offset" class="col-sm-2 col-form-label">{{'admin.reports.items.offset' | translate}}:</label>
{{'admin.reports.commons.filters' | translate}} <div class="col-6">
</ng-template> <input id="offset" name="offset" value="0" class="form-control col-6">
<ng-template ngbPanelContent> </div>
<div class="row"> </div>
<span class="col-3"></span> <div class="row">
<button class="btn btn-primary mt-1 col-6" (click)="submit()">{{'admin.reports.items.run' | translate}}</button> <span class="col-3"></span>
</div> <button class="btn btn-primary mt-1 col-6" (click)="submit()">{{'admin.reports.items.run' | translate}}</button>
<ds-filters [filtersForm]="filtersFormGroup()"></ds-filters> </div>
<div class="row"> </ng-template>
<span class="col-3"></span> </ngb-panel>
<button class="btn btn-primary mt-1 col-6" (click)="submit()">{{'admin.reports.items.run' | translate}}</button> <ngb-panel id="filters">
</div> <ng-template ngbPanelTitle>
</ng-template> {{'admin.reports.commons.filters' | translate}}
</ngb-panel> </ng-template>
<ngb-panel id="additionalData"> <ng-template ngbPanelContent>
<ng-template ngbPanelTitle> <div class="row">
{{'admin.reports.commons.additional-data' | translate}} <span class="col-3"></span>
</ng-template> <button class="btn btn-primary mt-1 col-6" (click)="submit()">{{'admin.reports.items.run' | translate}}</button>
<ng-template ngbPanelContent> </div>
<div id="show-fields"> <ds-filters [filtersForm]="filtersFormGroup()"></ds-filters>
<select class="query-tool" name="show_fields" multiple="multiple" size="8" class="form-control" formControlName="additionalFields"> <div class="row">
<option *ngFor="let item of metadataFields" [value]="item.id">{{item.name$ | async}}</option> <span class="col-3"></span>
</select> <button class="btn btn-primary mt-1 col-6" (click)="submit()">{{'admin.reports.items.run' | translate}}</button>
</div> </div>
<div class="row"> </ng-template>
<span class="col-3"></span> </ngb-panel>
<button class="btn btn-primary mt-1 col-6" (click)="submit()">{{'admin.reports.items.run' | translate}}</button> <ngb-panel id="additionalData">
</div> <ng-template ngbPanelTitle>
</ng-template> {{'admin.reports.commons.additional-data' | translate}}
</ngb-panel> </ng-template>
<ngb-panel id="itemResults"> <ng-template ngbPanelContent>
<ng-template ngbPanelTitle> <div id="show-fields">
{{'admin.reports.collections.item-results' | translate}} <select class="query-tool" name="show_fields" multiple="multiple" size="8" class="form-control" formControlName="additionalFields">
</ng-template> @for (item of metadataFields; track item) {
<ng-template ngbPanelContent> <option [value]="item.id">{{item.name$ | async}}</option>
<table id="table" class="table table-striped"> }
<thead> </select>
<tr class="header"> </div>
<th>{{ "admin.reports.items.number" | translate }}</th> <div class="row">
<th>{{ "admin.reports.items.id" | translate }}</th> <span class="col-3"></span>
<th>{{ "admin.reports.items.collection" | translate }}</th> <button class="btn btn-primary mt-1 col-6" (click)="submit()">{{'admin.reports.items.run' | translate}}</button>
<th>{{ "admin.reports.items.handle" | translate }}</th> </div>
<th>{{ "admin.reports.items.title" | translate }}</th> </ng-template>
<th *ngFor="let field of queryForm.value['additionalFields']">{{ field }}</th> </ngb-panel>
</tr> <ngb-panel id="itemResults">
</thead> <ng-template ngbPanelTitle>
<tbody> {{'admin.reports.collections.item-results' | translate}}
<tr *ngFor="let item of results$ | async"> </ng-template>
<td class="num">{{ item.index }}</td> <ng-template ngbPanelContent>
<td>{{ item.uuid }}</td> <table id="table" class="table table-striped">
<td><a *ngIf="item.owningCollection" href="/handle/{{ item.owningCollection.handle }}" rel="noopener noreferrer" target="_blank">{{ item.owningCollection.name }}</a></td> <thead>
<td><a *ngIf="item.handle" href="/handle/{{ item.handle }}" rel="noopener noreferrer" target="_blank">{{ item.handle }}</a></td> <tr class="header">
<td>{{ item.name }}</td> <th>{{ "admin.reports.items.number" | translate }}</th>
<td class="num" *ngFor="let field of queryForm.value['additionalFields']"> <th>{{ "admin.reports.items.id" | translate }}</th>
<span *ngFor="let mdvalue of item.metadata[field]"> <th>{{ "admin.reports.items.collection" | translate }}</th>
{{ mdvalue.value || "" }} <th>{{ "admin.reports.items.handle" | translate }}</th>
</span> <th>{{ "admin.reports.items.title" | translate }}</th>
</td> @for (field of queryForm.value['additionalFields']; track field) {
</tr> <th>{{ field }}</th>
</tbody> }
</table> </tr>
<div> </thead>
{{'admin.reports.commons.page' | translate}} {{ currentPage + 1 }} {{'admin.reports.commons.of' | translate}} {{ pageCount() }} <tbody>
</div> @for (item of results$ | async; track item) {
<div> <tr>
<button id="prev" class="btn btn-light" (click)="prevPage()" [dsBtnDisabled]="!canNavigatePrevious()">{{'admin.reports.commons.previous-page' | translate}}</button> <td class="num">{{ item.index }}</td>
<button id="next" class="btn btn-light" (click)="nextPage()" [dsBtnDisabled]="!canNavigateNext()">{{'admin.reports.commons.next-page' | translate}}</button> <td>{{ item.uuid }}</td>
<!-- <td>@if (item.owningCollection) {
<button id="export">{{'admin.reports.commons.export' | translate}}</button> <a href="/handle/{{ item.owningCollection.handle }}" rel="noopener noreferrer" target="_blank">{{ item.owningCollection.name }}</a>
--> }</td>
</div> <td>@if (item.handle) {
<table id="itemtable" class="sortable"></table> <a href="/handle/{{ item.handle }}" rel="noopener noreferrer" target="_blank">{{ item.handle }}</a>
</ng-template> }</td>
</ngb-panel> <td>{{ item.name }}</td>
</ngb-accordion> @for (field of queryForm.value['additionalFields']; track field) {
</div> <td class="num">
@for (mdvalue of item.metadata[field]; track mdvalue) {
<span>
{{ mdvalue.value || "" }}
</span>
}
</td>
}
</tr>
}
</tbody>
</table>
<div>
{{'admin.reports.commons.page' | translate}} {{ currentPage + 1 }} {{'admin.reports.commons.of' | translate}} {{ pageCount() }}
</div>
<div>
<button id="prev" class="btn btn-light" (click)="prevPage()" [dsBtnDisabled]="!canNavigatePrevious()">{{'admin.reports.commons.previous-page' | translate}}</button>
<button id="next" class="btn btn-light" (click)="nextPage()" [dsBtnDisabled]="!canNavigateNext()">{{'admin.reports.commons.next-page' | translate}}</button>
<!--
<button id="export">{{'admin.reports.commons.export' | translate}}</button>
-->
</div>
<table id="itemtable" class="sortable"></table>
</ng-template>
</ngb-panel>
</ngb-accordion>
</div>
</div>
</div> </div>
</div>
</div> </div>

View File

@@ -1,8 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgForOf,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
OnInit, OnInit,
@@ -38,14 +34,16 @@ import { MetadataField } from 'src/app/core/metadata/metadata-field.model';
import { MetadataSchema } from 'src/app/core/metadata/metadata-schema.model'; import { MetadataSchema } from 'src/app/core/metadata/metadata-schema.model';
import { Collection } from 'src/app/core/shared/collection.model'; import { Collection } from 'src/app/core/shared/collection.model';
import { Community } from 'src/app/core/shared/community.model'; import { Community } from 'src/app/core/shared/community.model';
import { Item } from 'src/app/core/shared/item.model';
import { getFirstSucceededRemoteListPayload } from 'src/app/core/shared/operators'; import { getFirstSucceededRemoteListPayload } from 'src/app/core/shared/operators';
import { isEmpty } from 'src/app/shared/empty.util'; import { isEmpty } from 'src/app/shared/empty.util';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
import { BtnDisabledDirective } from '../../../shared/btn-disabled.directive'; import { BtnDisabledDirective } from '../../../shared/btn-disabled.directive';
import { FiltersComponent } from '../filters-section/filters-section.component'; import { FiltersComponent } from '../filters-section/filters-section.component';
import { FilteredItems } from './filtered-items-model'; import {
FilteredItem,
FilteredItems,
} from './filtered-items-model';
import { OptionVO } from './option-vo.model'; import { OptionVO } from './option-vo.model';
import { PresetQuery } from './preset-query.model'; import { PresetQuery } from './preset-query.model';
import { QueryPredicate } from './query-predicate.model'; import { QueryPredicate } from './query-predicate.model';
@@ -62,8 +60,6 @@ import { QueryPredicate } from './query-predicate.model';
NgbAccordionModule, NgbAccordionModule,
TranslateModule, TranslateModule,
AsyncPipe, AsyncPipe,
NgIf,
NgForOf,
FiltersComponent, FiltersComponent,
BtnDisabledDirective, BtnDisabledDirective,
], ],
@@ -81,7 +77,7 @@ export class FilteredItemsComponent implements OnInit {
queryForm: FormGroup; queryForm: FormGroup;
currentPage = 0; currentPage = 0;
results: FilteredItems = new FilteredItems(); results: FilteredItems = new FilteredItems();
results$: Observable<Item[]>; results$: Observable<FilteredItem[]>;
@ViewChild('acc') accordionComponent: NgbAccordion; @ViewChild('acc') accordionComponent: NgbAccordion;
constructor( constructor(

View File

@@ -9,6 +9,7 @@ export class OptionVO {
id: string; id: string;
name$: Observable<string>; name$: Observable<string>;
disabled = false; disabled = false;
isDefault?: boolean;
static collection(id: string, name: string, disabled: boolean = false): OptionVO { static collection(id: string, name: string, disabled: boolean = false): OptionVO {
const opt = new OptionVO(); const opt = new OptionVO();

View File

@@ -5,6 +5,7 @@ export class PresetQuery {
id: string; id: string;
label: string; label: string;
predicates: QueryPredicate[]; predicates: QueryPredicate[];
isDefault?: boolean;
static of(id: string, label: string, predicates: QueryPredicate[]) { static of(id: string, label: string, predicates: QueryPredicate[]) {
const query = new PresetQuery(); const query = new PresetQuery();

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,4 +1,4 @@
import { NgForOf } from '@angular/common';
import { import {
Component, Component,
Input, Input,
@@ -23,7 +23,6 @@ import { FilterGroup } from './filter-group.model';
templateUrl: './filters-section.component.html', templateUrl: './filters-section.component.html',
styleUrls: ['./filters-section.component.scss'], styleUrls: ['./filters-section.component.scss'],
imports: [ imports: [
NgForOf,
ReactiveFormsModule, ReactiveFormsModule,
TranslateModule, TranslateModule,
], ],

View File

@@ -1,30 +1,52 @@
<div class="space-children-mr my-1"> <div class="space-children-mr my-1">
<a [ngClass]="{'btn-sm': small}" class="btn btn-secondary move-link" [routerLink]="[getMoveRoute()]" [title]="'admin.search.item.move' | translate"> <a [ngClass]="{'btn-sm': small}" class="btn btn-secondary move-link" [routerLink]="[getMoveRoute()]" [title]="'admin.search.item.move' | translate">
<i class="fa fa-arrow-circle-right"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.move" | translate}}</span> <i class="fa fa-arrow-circle-right"></i>@if (!small) {
</a> <span class="d-none d-sm-inline"> {{"admin.search.item.move" | translate}}</span>
}
</a>
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isDiscoverable" class="btn btn-secondary private-link" [routerLink]="[getPrivateRoute()]" [title]="'admin.search.item.make-private' | translate"> @if (item && item.isDiscoverable) {
<i class="fa fa-eye-slash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-private" | translate}}</span> <a [ngClass]="{'btn-sm': small}" class="btn btn-secondary private-link" [routerLink]="[getPrivateRoute()]" [title]="'admin.search.item.make-private' | translate">
</a> <i class="fa fa-eye-slash"></i>@if (!small) {
<span class="d-none d-sm-inline"> {{"admin.search.item.make-private" | translate}}</span>
}
</a>
}
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isDiscoverable" class="btn btn-secondary public-link" [routerLink]="[getPublicRoute()]" [title]="'admin.search.item.make-public' | translate"> @if (item && !item.isDiscoverable) {
<i class="fa fa-eye"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-public" | translate}}</span> <a [ngClass]="{'btn-sm': small}" class="btn btn-secondary public-link" [routerLink]="[getPublicRoute()]" [title]="'admin.search.item.make-public' | translate">
</a> <i class="fa fa-eye"></i>@if (!small) {
<span class="d-none d-sm-inline"> {{"admin.search.item.make-public" | translate}}</span>
}
</a>
}
<a [ngClass]="{'btn-sm': small}" class="btn btn-secondary edit-link" [routerLink]="[getEditRoute()]" [title]="'admin.search.item.edit' | translate"> <a [ngClass]="{'btn-sm': small}" class="btn btn-secondary edit-link" [routerLink]="[getEditRoute()]" [title]="'admin.search.item.edit' | translate">
<i class="fa fa-edit"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.edit" | translate}}</span> <i class="fa fa-edit"></i>@if (!small) {
</a> <span class="d-none d-sm-inline"> {{"admin.search.item.edit" | translate}}</span>
}
</a>
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isWithdrawn" class="btn btn-warning t withdraw-link" [routerLink]="[getWithdrawRoute()]" [title]="'admin.search.item.withdraw' | translate"> @if (item && !item.isWithdrawn) {
<i class="fa fa-ban"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.withdraw" | translate}}</span> <a [ngClass]="{'btn-sm': small}" class="btn btn-warning t withdraw-link" [routerLink]="[getWithdrawRoute()]" [title]="'admin.search.item.withdraw' | translate">
</a> <i class="fa fa-ban"></i>@if (!small) {
<span class="d-none d-sm-inline"> {{"admin.search.item.withdraw" | translate}}</span>
}
</a>
}
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isWithdrawn" class="btn btn-warning reinstate-link" [routerLink]="[getReinstateRoute()]" [title]="'admin.search.item.reinstate' | translate"> @if (item && item.isWithdrawn) {
<i class="fa fa-undo"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.reinstate" | translate}}</span> <a [ngClass]="{'btn-sm': small}" class="btn btn-warning reinstate-link" [routerLink]="[getReinstateRoute()]" [title]="'admin.search.item.reinstate' | translate">
</a> <i class="fa fa-undo"></i>@if (!small) {
<span class="d-none d-sm-inline"> {{"admin.search.item.reinstate" | translate}}</span>
}
</a>
}
<a [ngClass]="{'btn-sm': small}" class="btn btn-danger delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.search.item.delete' | translate"> <a [ngClass]="{'btn-sm': small}" class="btn btn-danger delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.search.item.delete' | translate">
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.delete" | translate}}</span> <i class="fa fa-trash"></i>@if (!small) {
</a> <span class="d-none d-sm-inline"> {{"admin.search.item.delete" | translate}}</span>
}
</a>
</div> </div>

View File

@@ -1,7 +1,4 @@
import { import { NgClass } from '@angular/common';
NgClass,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
Input, Input,
@@ -26,7 +23,7 @@ import { getItemEditRoute } from '../../../item-page/item-page-routing-paths';
styleUrls: ['./item-admin-search-result-actions.component.scss'], styleUrls: ['./item-admin-search-result-actions.component.scss'],
templateUrl: './item-admin-search-result-actions.component.html', templateUrl: './item-admin-search-result-actions.component.html',
standalone: true, standalone: true,
imports: [NgClass, RouterLink, NgIf, TranslateModule], imports: [NgClass, RouterLink, TranslateModule],
}) })
/** /**
* The component for displaying the actions for a list element for an item search result on the admin search page * The component for displaying the actions for a list element for an item search result on the admin search page

View File

@@ -1,64 +1,62 @@
<nav class="navbar navbar-dark p-0 vh-100" @if (menuVisible | async) {
id="admin-sidebar" <nav class="navbar navbar-dark p-0 vh-100"
[attr.aria-label]="'menu.header.nav.description' | translate" id="admin-sidebar"
[ngClass]="{'expanded': sidebarOpen, 'collapsed': sidebarClosed, 'transitioning': sidebarTransitioning}" [attr.aria-label]="'menu.header.nav.description' | translate"
[ngClass]="{'expanded': sidebarOpen, 'collapsed': sidebarClosed, 'transitioning': sidebarTransitioning}"
[@slideSidebar]="{ [@slideSidebar]="{
value: ((sidebarExpanded | async) !== true ? 'collapsed' : 'expanded'), value: ((sidebarExpanded | async) !== true ? 'collapsed' : 'expanded'),
params: { collapsedWidth: (collapsedSidebarWidth$ | async), expandedWidth: (expandedSidebarWidth$ | async) } params: { collapsedWidth: (collapsedSidebarWidth$ | async), expandedWidth: (expandedSidebarWidth$ | async) }
}" (@slideSidebar.done)="finishSlide($event)" (@slideSidebar.start)="startSlide($event)" }" (@slideSidebar.done)="finishSlide($event)" (@slideSidebar.start)="startSlide($event)"
*ngIf="menuVisible | async" (mouseenter)="handleMouseEnter($event)"
(mouseenter)="handleMouseEnter($event)" (mouseleave)="handleMouseLeave($event)">
(mouseleave)="handleMouseLeave($event)"> <!-- HEADER -->
<div class="sidebar-full-width-container" id="sidebar-header-container" aria-hidden="true">
<!-- HEADER --> <div class="sidebar-section-wrapper">
<div class="sidebar-fixed-element-wrapper">
<div class="sidebar-full-width-container" id="sidebar-header-container" aria-hidden="true"> <img id="admin-sidebar-logo" src="assets/images/dspace-logo-mini.svg" [alt]="('menu.header.image.logo') | translate" aria-hidden="true">
<div class="sidebar-section-wrapper"> </div>
<div class="sidebar-fixed-element-wrapper"> <div class="sidebar-collapsible-element-outer-wrapper">
<img id="admin-sidebar-logo" src="assets/images/dspace-logo-mini.svg" [alt]="('menu.header.image.logo') | translate" aria-hidden="true"> <div class="sidebar-collapsible-element-inner-wrapper sidebar-item">
</div> <h4 class="my-1">{{ 'menu.header.admin' | translate }}</h4>
<div class="sidebar-collapsible-element-outer-wrapper"> </div>
<div class="sidebar-collapsible-element-inner-wrapper sidebar-item">
<h4 class="my-1">{{ 'menu.header.admin' | translate }}</h4>
</div> </div>
</div> </div>
</div> </div>
</div> <!-- ITEMS -->
<div class="sidebar-full-width-container" id="sidebar-top-level-items-container">
<!-- ITEMS --> <div class="sidebar-full-width-container" id="sidebar-top-level-items" role="menubar"
<div class="sidebar-full-width-container" id="sidebar-top-level-items-container">
<div class="sidebar-full-width-container" id="sidebar-top-level-items" role="menubar"
[attr.aria-label]="'menu.header.admin.description' |translate"> [attr.aria-label]="'menu.header.admin.description' |translate">
<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> <!-- TOGGLER -->
<div class="sidebar-full-width-container" id="sidebar-collapse-toggle-container">
<!-- TOGGLER --> <a class="sidebar-section-wrapper sidebar-full-width-container"
id="sidebar-collapse-toggle"
<div class="sidebar-full-width-container" id="sidebar-collapse-toggle-container"> [attr.data-test]="'sidebar-collapse-toggle' | dsBrowserOnly"
<a class="sidebar-section-wrapper sidebar-full-width-container" href="javascript:void(0);"
id="sidebar-collapse-toggle" (click)="toggle($event)"
[attr.data-test]="'sidebar-collapse-toggle' | dsBrowserOnly" (keyup.space)="toggle($event)"
href="javascript:void(0);" >
(click)="toggle($event)" <div class="sidebar-fixed-element-wrapper">
(keyup.space)="toggle($event)" @if ((menuCollapsed | async)) {
> <i class="fas fa-fw fa-angle-double-right"
<div class="sidebar-fixed-element-wrapper"> [title]="'menu.section.icon.pin' | translate"></i>
<i *ngIf="(menuCollapsed | async)" class="fas fa-fw fa-angle-double-right" }
[title]="'menu.section.icon.pin' | translate"></i> @if ((menuCollapsed | async) !== true) {
<i *ngIf="(menuCollapsed | async) !== true" class="fas fa-fw fa-angle-double-left" <i class="fas fa-fw fa-angle-double-left"
[title]="'menu.section.icon.unpin' | translate"></i> [title]="'menu.section.icon.unpin' | translate"></i>
</div> }
<div class="sidebar-collapsible-element-outer-wrapper">
<div class="sidebar-collapsible-element-inner-wrapper sidebar-item">
{{ ((menuCollapsed | async) ? 'menu.section.pin' : 'menu.section.unpin' ) | translate }}
</div> </div>
</div> <div class="sidebar-collapsible-element-outer-wrapper">
</a> <div class="sidebar-collapsible-element-inner-wrapper sidebar-item">
</div> {{ ((menuCollapsed | async) ? 'menu.section.pin' : 'menu.section.unpin' ) | translate }}
</div>
</nav> </div>
</a>
</div>
</nav>
}

View File

@@ -2,8 +2,6 @@ import {
AsyncPipe, AsyncPipe,
NgClass, NgClass,
NgComponentOutlet, NgComponentOutlet,
NgFor,
NgIf,
} from '@angular/common'; } from '@angular/common';
import { import {
Component, Component,
@@ -47,7 +45,7 @@ import { BrowserOnlyPipe } from '../../shared/utils/browser-only.pipe';
styleUrls: ['./admin-sidebar.component.scss'], styleUrls: ['./admin-sidebar.component.scss'],
animations: [slideSidebar], animations: [slideSidebar],
standalone: true, standalone: true,
imports: [NgIf, NgbDropdownModule, NgClass, NgFor, NgComponentOutlet, AsyncPipe, TranslateModule, BrowserOnlyPipe], imports: [NgbDropdownModule, NgClass, NgComponentOutlet, AsyncPipe, TranslateModule, BrowserOnlyPipe],
}) })
export class AdminSidebarComponent extends MenuComponent implements OnInit { export class AdminSidebarComponent extends MenuComponent implements OnInit {
/** /**

View File

@@ -4,16 +4,16 @@
params: {endColor: (sidebarActiveBg$ | async)} params: {endColor: (sidebarActiveBg$ | async)}
}"> }">
<a class="sidebar-section-wrapper" <a class="sidebar-section-wrapper"
role="menuitem" tabindex="0" role="menuitem" tabindex="0"
aria-haspopup="menu" aria-haspopup="menu"
[attr.aria-controls]="adminMenuSectionId(section.id)" [attr.aria-controls]="adminMenuSectionId(section.id)"
[attr.aria-expanded]="isExpanded$ | async" [attr.aria-expanded]="isExpanded$ | async"
[attr.aria-label]="('menu.section.toggle.' + section.id) | translate" [attr.aria-label]="('menu.section.toggle.' + section.id) | translate"
[class.disabled]="section.model?.disabled" [class.disabled]="section.model?.disabled"
(click)="toggleSection($event)" (click)="toggleSection($event)"
(keyup.space)="toggleSection($event)" (keyup.space)="toggleSection($event)"
href="javascript:void(0);" href="javascript:void(0);"
> >
<div class="sidebar-fixed-element-wrapper" data-test="sidebar-section-icon" aria-hidden="true"> <div class="sidebar-fixed-element-wrapper" data-test="sidebar-section-icon" aria-hidden="true">
<i class="fas fa-{{section.icon}} fa-fw"></i> <i class="fas fa-{{section.icon}} fa-fw"></i>
</div> </div>
@@ -21,25 +21,29 @@
<div class="sidebar-collapsible-element-inner-wrapper sidebar-item toggler-wrapper"> <div class="sidebar-collapsible-element-inner-wrapper sidebar-item toggler-wrapper">
<span [id]="adminMenuSectionTitleId(section.id)" [attr.data-test]="adminMenuSectionTitleId(section.id) | dsBrowserOnly"> <span [id]="adminMenuSectionTitleId(section.id)" [attr.data-test]="adminMenuSectionTitleId(section.id) | dsBrowserOnly">
<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>
</span> </span>
<i class="fas fa-chevron-right fa-xs" aria-hidden="true" <i class="fas fa-chevron-right fa-xs" aria-hidden="true"
[@rotate]="(isExpanded$ | async) ? 'expanded' : 'collapsed'" [@rotate]="(isExpanded$ | async) ? 'expanded' : 'collapsed'"
></i> ></i>
</div> </div>
</div> </div>
</a> </a>
<div class="sidebar-section-wrapper subsection" @slide *ngIf="(isExpanded$ | async)"> @if ((isExpanded$ | async)) {
<div class="sidebar-fixed-element-wrapper"></div> <div class="sidebar-section-wrapper subsection" @slide>
<div class="sidebar-collapsible-element-outer-wrapper"> <div class="sidebar-fixed-element-wrapper"></div>
<div class="sidebar-collapsible-element-inner-wrapper"> <div class="sidebar-collapsible-element-outer-wrapper">
<div class="sidebar-sub-level-item-list" role="menu" [id]="adminMenuSectionId(section.id)" [attr.aria-label]="('menu.section.' + section.id) | translate"> <div class="sidebar-collapsible-element-inner-wrapper">
<div class="sidebar-item" *ngFor="let subSection of (subSections$ | async)"> <div class="sidebar-sub-level-item-list" role="menu" [id]="adminMenuSectionId(section.id)" [attr.aria-label]="('menu.section.' + section.id) | translate">
<ng-container @for (subSection of (subSections$ | async); track subSection) {
*ngComponentOutlet="(sectionMap$ | async).get(subSection.id).component; injector: (sectionMap$ | async).get(subSection.id).injector;"></ng-container> <div class="sidebar-item">
<ng-container
*ngComponentOutlet="(sectionMap$ | async).get(subSection.id).component; injector: (sectionMap$ | async).get(subSection.id).injector;"></ng-container>
</div>
}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> }
</div> </div>

View File

@@ -2,8 +2,6 @@ import {
AsyncPipe, AsyncPipe,
NgClass, NgClass,
NgComponentOutlet, NgComponentOutlet,
NgFor,
NgIf,
} from '@angular/common'; } from '@angular/common';
import { import {
Component, Component,
@@ -37,7 +35,7 @@ import { AdminSidebarSectionComponent } from '../admin-sidebar-section/admin-sid
styleUrls: ['./expandable-admin-sidebar-section.component.scss'], styleUrls: ['./expandable-admin-sidebar-section.component.scss'],
animations: [rotate, slide, bgColor], animations: [rotate, slide, bgColor],
standalone: true, standalone: true,
imports: [NgClass, NgComponentOutlet, NgIf, NgFor, AsyncPipe, TranslateModule, BrowserOnlyPipe], imports: [NgClass, NgComponentOutlet, AsyncPipe, TranslateModule, BrowserOnlyPipe],
}) })
export class ExpandableAdminSidebarSectionComponent extends AdminSidebarSectionComponent implements OnInit { export class ExpandableAdminSidebarSectionComponent extends AdminSidebarSectionComponent implements OnInit {

View File

@@ -1,8 +1,12 @@
<div class="space-children-mr"> <div class="space-children-mr">
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.workflow.item.delete' | translate"> <a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.workflow.item.delete' | translate">
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.delete" | translate}}</span> <i class="fa fa-trash"></i>@if (!small) {
</a> <span class="d-none d-sm-inline"> {{"admin.workflow.item.delete" | translate}}</span>
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 send-back-link" [routerLink]="[getSendBackRoute()]" [title]="'admin.workflow.item.send-back' | translate"> }
<i class="fa fa-hand-point-left"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.send-back" | translate}}</span> </a>
</a> <a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 send-back-link" [routerLink]="[getSendBackRoute()]" [title]="'admin.workflow.item.send-back' | translate">
<i class="fa fa-hand-point-left"></i>@if (!small) {
<span class="d-none d-sm-inline"> {{"admin.workflow.item.send-back" | translate}}</span>
}
</a>
</div> </div>

View File

@@ -1,7 +1,4 @@
import { import { NgClass } from '@angular/common';
NgClass,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
Input, Input,
@@ -20,7 +17,7 @@ import {
styleUrls: ['./workflow-item-admin-workflow-actions.component.scss'], styleUrls: ['./workflow-item-admin-workflow-actions.component.scss'],
templateUrl: './workflow-item-admin-workflow-actions.component.html', templateUrl: './workflow-item-admin-workflow-actions.component.html',
standalone: true, standalone: true,
imports: [NgClass, RouterLink, NgIf, TranslateModule], imports: [NgClass, RouterLink, TranslateModule],
}) })
/** /**
* The component for displaying the actions for a list element for a workflow-item on the admin workflow search page * The component for displaying the actions for a list element for a workflow-item on the admin workflow search page

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,4 +1,4 @@
import { NgIf } from '@angular/common';
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -34,7 +34,7 @@ import { ErrorComponent } from '../../../../../../shared/error/error.component';
styleUrls: ['./supervision-order-group-selector.component.scss'], styleUrls: ['./supervision-order-group-selector.component.scss'],
templateUrl: './supervision-order-group-selector.component.html', templateUrl: './supervision-order-group-selector.component.html',
standalone: true, standalone: true,
imports: [FormsModule, NgIf, ErrorComponent, EpersonGroupListComponent, TranslateModule], imports: [FormsModule, ErrorComponent, EpersonGroupListComponent, TranslateModule],
}) })
export class SupervisionOrderGroupSelectorComponent { export class SupervisionOrderGroupSelectorComponent {

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,8 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgFor,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -42,7 +38,7 @@ export interface SupervisionOrderListEntry {
templateUrl: './supervision-order-status.component.html', templateUrl: './supervision-order-status.component.html',
styleUrls: ['./supervision-order-status.component.scss'], styleUrls: ['./supervision-order-status.component.scss'],
standalone: true, standalone: true,
imports: [VarDirective, NgIf, NgFor, NgbTooltipModule, AsyncPipe, TranslateModule], imports: [VarDirective, NgbTooltipModule, AsyncPipe, TranslateModule],
}) })
export class SupervisionOrderStatusComponent implements OnChanges { export class SupervisionOrderStatusComponent implements OnChanges {

View File

@@ -1,16 +1,22 @@
<div class="my-1"> <div class="my-1">
<ds-supervision-order-status [supervisionOrderList]="supervisionOrderList" <ds-supervision-order-status [supervisionOrderList]="supervisionOrderList"
(delete)="deleteSupervisionOrder($event)"></ds-supervision-order-status> (delete)="deleteSupervisionOrder($event)"></ds-supervision-order-status>
</div> </div>
<div class="space-children-mr"> <div class="space-children-mr">
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.workflow.item.delete' | translate"> <a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.workflow.item.delete' | translate">
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.delete" | translate}}</span> <i class="fa fa-trash"></i>@if (!small) {
</a> <span class="d-none d-sm-inline"> {{"admin.workflow.item.delete" | translate}}</span>
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 policies-link" [routerLink]="resourcePoliciesPageRoute" [title]="'admin.workflow.item.policies' | translate"> }
<i class="fas fa-edit"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{'admin.workflow.item.policies' | translate}}</span> </a>
</a> <a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 policies-link" [routerLink]="resourcePoliciesPageRoute" [title]="'admin.workflow.item.policies' | translate">
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 supervision-group-selector" [title]="'admin.workflow.item.supervision' | translate" (click)="openSupervisionModal()"> <i class="fas fa-edit"></i>@if (!small) {
<i class="fas fa-users-cog"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{'admin.workflow.item.supervision' | translate}}</span> <span class="d-none d-sm-inline"> {{'admin.workflow.item.policies' | translate}}</span>
</a> }
</a>
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 supervision-group-selector" [title]="'admin.workflow.item.supervision' | translate" (click)="openSupervisionModal()">
<i class="fas fa-users-cog"></i>@if (!small) {
<span class="d-none d-sm-inline"> {{'admin.workflow.item.supervision' | translate}}</span>
}
</a>
</div> </div>

View File

@@ -1,7 +1,4 @@
import { import { NgClass } from '@angular/common';
NgClass,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -52,7 +49,7 @@ import {
styleUrls: ['./workspace-item-admin-workflow-actions.component.scss'], styleUrls: ['./workspace-item-admin-workflow-actions.component.scss'],
templateUrl: './workspace-item-admin-workflow-actions.component.html', templateUrl: './workspace-item-admin-workflow-actions.component.html',
standalone: true, standalone: true,
imports: [SupervisionOrderStatusComponent, NgClass, RouterLink, NgIf, TranslateModule], imports: [SupervisionOrderStatusComponent, NgClass, RouterLink, TranslateModule],
}) })
/** /**
* The component for displaying the actions for a list element for a workspace-item on the admin workflow search page * The component for displaying the actions for a list element for a workspace-item on the admin workflow search page

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,4 +1,4 @@
import { NgIf } from '@angular/common';
import { import {
Component, Component,
ComponentRef, ComponentRef,
@@ -43,7 +43,7 @@ import { WorkflowItemAdminWorkflowActionsComponent } from '../../actions/workflo
styleUrls: ['./workflow-item-search-result-admin-workflow-grid-element.component.scss'], styleUrls: ['./workflow-item-search-result-admin-workflow-grid-element.component.scss'],
templateUrl: './workflow-item-search-result-admin-workflow-grid-element.component.html', templateUrl: './workflow-item-search-result-admin-workflow-grid-element.component.html',
standalone: true, standalone: true,
imports: [NgIf, WorkflowItemAdminWorkflowActionsComponent, TranslateModule, DynamicComponentLoaderDirective], imports: [WorkflowItemAdminWorkflowActionsComponent, TranslateModule, DynamicComponentLoaderDirective],
}) })
/** /**
* The component for displaying a grid element for an workflow item on the admin workflow search page * The component for displaying a grid element for an workflow item on the admin workflow search page

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,7 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
Inject, Inject,
@@ -42,7 +39,7 @@ import { WorkflowItemAdminWorkflowActionsComponent } from '../../actions/workflo
styleUrls: ['./workflow-item-search-result-admin-workflow-list-element.component.scss'], styleUrls: ['./workflow-item-search-result-admin-workflow-list-element.component.scss'],
templateUrl: './workflow-item-search-result-admin-workflow-list-element.component.html', templateUrl: './workflow-item-search-result-admin-workflow-list-element.component.html',
standalone: true, standalone: true,
imports: [NgIf, ListableObjectComponentLoaderComponent, WorkflowItemAdminWorkflowActionsComponent, AsyncPipe, TranslateModule], imports: [ListableObjectComponentLoaderComponent, WorkflowItemAdminWorkflowActionsComponent, AsyncPipe, TranslateModule],
}) })
/** /**
* The component for displaying a list element for a workflow item on the admin workflow search page * The component for displaying a list element for a workflow item on the admin workflow search page

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,7 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
Inject, Inject,
@@ -52,7 +49,7 @@ import { WorkspaceItemAdminWorkflowActionsComponent } from '../../actions/worksp
styleUrls: ['./workspace-item-search-result-admin-workflow-list-element.component.scss'], styleUrls: ['./workspace-item-search-result-admin-workflow-list-element.component.scss'],
templateUrl: './workspace-item-search-result-admin-workflow-list-element.component.html', templateUrl: './workspace-item-search-result-admin-workflow-list-element.component.html',
standalone: true, standalone: true,
imports: [NgIf, ListableObjectComponentLoaderComponent, WorkspaceItemAdminWorkflowActionsComponent, AsyncPipe, TranslateModule], imports: [ListableObjectComponentLoaderComponent, WorkspaceItemAdminWorkflowActionsComponent, AsyncPipe, TranslateModule],
}) })
/** /**
* The component for displaying a list element for a workflow item on the admin workflow search page * The component for displaying a list element for a workflow item on the admin workflow search page

View File

@@ -108,7 +108,7 @@ export const APP_ROUTES: Route[] = [
path: COLLECTION_MODULE_PATH, path: COLLECTION_MODULE_PATH,
loadChildren: () => import('./collection-page/collection-page-routes') loadChildren: () => import('./collection-page/collection-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
data: { showBreadcrumbs: false, enableRSS: true }, data: { enableRSS: true },
canActivate: [endUserAgreementCurrentUserGuard], canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {

View File

@@ -1,33 +1,41 @@
<ng-container *ngVar="(bitstreamRD$ | async) as bitstreamRD"> <ng-container *ngVar="(bitstreamRD$ | async) as bitstreamRD">
<div class="container"> <div class="container">
<div class="row" *ngIf="bitstreamRD?.hasSucceeded"> @if (bitstreamRD?.hasSucceeded) {
<div class="col-md-2"> <div class="row">
<ds-thumbnail [thumbnail]="bitstreamRD?.payload"></ds-thumbnail> <div class="col-md-2">
</div> <ds-thumbnail [thumbnail]="bitstreamRD?.payload"></ds-thumbnail>
<div class="col-md-10"> </div>
<div class="container"> <div class="col-md-10">
<div class="row"> <div class="container">
<div class="col-12"> <div class="row">
<h1 class="h2">{{dsoNameService.getName(bitstreamRD?.payload)}} <span class="text-muted">({{bitstreamRD?.payload?.sizeBytes | dsFileSize}})</span></h1> <div class="col-12">
<h1 class="h2">{{dsoNameService.getName(bitstreamRD?.payload)}} <span class="text-muted">({{bitstreamRD?.payload?.sizeBytes | dsFileSize}})</span></h1>
</div>
</div> </div>
</div> </div>
@if (formGroup) {
<ds-form [formId]="'edit-bitstream-form-id'"
[formGroup]="formGroup"
[formModel]="formModel"
[formLayout]="formLayout"
[submitLabel]="'form.save'"
(submitForm)="onSubmit()"
(cancel)="onCancel()"
(dfChange)="onChange($event)">
<div additional class="container py-3">
<a [routerLink]="['/bitstreams', bitstreamRD?.payload?.id, 'authorizations']">{{'bitstream.edit.authorizations.link' | translate}}</a>
</div>
</ds-form>
}
</div> </div>
<ds-form *ngIf="formGroup" [formId]="'edit-bitstream-form-id'"
[formGroup]="formGroup"
[formModel]="formModel"
[formLayout]="formLayout"
[submitLabel]="'form.save'"
(submitForm)="onSubmit()"
(cancel)="onCancel()"
(dfChange)="onChange($event)">
<div additional class="container py-3">
<a [routerLink]="['/bitstreams', bitstreamRD?.payload?.id, 'authorizations']">{{'bitstream.edit.authorizations.link' | translate}}</a>
</div>
</ds-form>
</div> </div>
</div> }
<ds-error *ngIf="bitstreamRD?.hasFailed" message="{{'error.bitstream' | translate}}"></ds-error> @if (bitstreamRD?.hasFailed) {
<ds-loading *ngIf="!bitstreamRD || bitstreamRD?.isLoading" <ds-error message="{{'error.bitstream' | translate}}"></ds-error>
message="{{'loading.bitstream' | translate}}"></ds-loading> }
@if (!bitstreamRD || bitstreamRD?.isLoading) {
<ds-loading
message="{{'loading.bitstream' | translate}}"></ds-loading>
}
</div> </div>
</ng-container> </ng-container>

View File

@@ -1,7 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgIf,
} from '@angular/common';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
@@ -87,7 +84,6 @@ import { ThemedThumbnailComponent } from '../../thumbnail/themed-thumbnail.compo
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ imports: [
FormComponent, FormComponent,
NgIf,
VarDirective, VarDirective,
ThemedThumbnailComponent, ThemedThumbnailComponent,
AsyncPipe, AsyncPipe,

View File

@@ -1,20 +1,22 @@
<ng-container *ngVar="(breadcrumbs$ | async) as breadcrumbs"> <ng-container *ngVar="(breadcrumbs$ | async) as breadcrumbs">
<nav *ngIf="(showBreadcrumbs$ | async)" aria-label="breadcrumb" class="nav-breadcrumb"> @if ((showBreadcrumbs$ | async)) {
<ol class="container breadcrumb my-0"> <nav aria-label="breadcrumb" class="nav-breadcrumb">
<ng-container <ol class="container breadcrumb my-0">
*ngTemplateOutlet="breadcrumbs?.length > 0 ? breadcrumb : activeBreadcrumb; context: {text: 'home.breadcrumbs', url: '/'}"></ng-container> <ng-container
<ng-container *ngFor="let bc of breadcrumbs; let last = last;"> *ngTemplateOutlet="breadcrumbs?.length > 0 ? breadcrumb : activeBreadcrumb; context: {text: 'home.breadcrumbs', url: '/'}"></ng-container>
<ng-container *ngTemplateOutlet="!last ? breadcrumb : activeBreadcrumb; context: bc"></ng-container> @for (bc of breadcrumbs; track bc; let last = $last) {
</ng-container> <ng-container *ngTemplateOutlet="!last ? breadcrumb : activeBreadcrumb; context: bc"></ng-container>
</ol> }
</ol>
</nav> </nav>
}
<ng-template #breadcrumb let-text="text" let-url="url"> <ng-template #breadcrumb let-text="text" let-url="url">
<li class="breadcrumb-item"><div class="breadcrumb-item-limiter"><a [routerLink]="url" class="text-truncate" [ngbTooltip]="text | translate" placement="bottom" >{{text | translate}}</a></div></li> <li class="breadcrumb-item"><div class="breadcrumb-item-limiter"><a [routerLink]="url" class="text-truncate" [ngbTooltip]="text | translate" placement="bottom" >{{text | translate}}</a></div></li>
</ng-template> </ng-template>
<ng-template #activeBreadcrumb let-text="text"> <ng-template #activeBreadcrumb let-text="text">
<li class="breadcrumb-item active" aria-current="page"><div class="breadcrumb-item-limiter"><div class="text-truncate">{{text | translate}}</div></div></li> <li class="breadcrumb-item active" aria-current="page"><div class="breadcrumb-item-limiter"><div class="text-truncate">{{text | translate}}</div></div></li>
</ng-template> </ng-template>
</ng-container> </ng-container>

View File

@@ -1,7 +1,5 @@
import { import {
AsyncPipe, AsyncPipe,
NgFor,
NgIf,
NgTemplateOutlet, NgTemplateOutlet,
} from '@angular/common'; } from '@angular/common';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@@ -22,7 +20,7 @@ import { BreadcrumbsService } from './breadcrumbs.service';
templateUrl: './breadcrumbs.component.html', templateUrl: './breadcrumbs.component.html',
styleUrls: ['./breadcrumbs.component.scss'], styleUrls: ['./breadcrumbs.component.scss'],
standalone: true, standalone: true,
imports: [VarDirective, NgIf, NgTemplateOutlet, NgFor, RouterLink, NgbTooltipModule, AsyncPipe, TranslateModule], imports: [VarDirective, NgTemplateOutlet, RouterLink, NgbTooltipModule, AsyncPipe, TranslateModule],
}) })
export class BreadcrumbsComponent { export class BreadcrumbsComponent {

View File

@@ -1,7 +1,6 @@
import { import {
AsyncPipe, AsyncPipe,
isPlatformServer, isPlatformServer,
NgIf,
} from '@angular/common'; } from '@angular/common';
import { import {
ChangeDetectorRef, ChangeDetectorRef,
@@ -62,7 +61,6 @@ import {
standalone: true, standalone: true,
imports: [ imports: [
AsyncPipe, AsyncPipe,
NgIf,
TranslateModule, TranslateModule,
ThemedLoadingComponent, ThemedLoadingComponent,
ThemedBrowseByComponent, ThemedBrowseByComponent,

View File

@@ -1,21 +1,27 @@
<section class="comcol-page-browse-section" *ngIf="(!ssrRenderingDisabled)"> @if ((!ssrRenderingDisabled)) {
<div class="browse-by-metadata w-100"> <section class="comcol-page-browse-section">
<ds-browse-by *ngIf="(loading$ | async) !== true" class="col-xs-12 w-100" <div class="browse-by-metadata w-100">
@if ((loading$ | async) !== true) {
<ds-browse-by class="col-xs-12 w-100"
title="{{'browse.title' | translate:{ title="{{'browse.title' | translate:{
field: 'browse.metadata.' + browseId | translate, field: 'browse.metadata.' + browseId | translate,
startsWith: (startsWith)? ('browse.startsWith' | translate: { startsWith: '&quot;' + startsWith + '&quot;' }) : '', startsWith: (startsWith)? ('browse.startsWith' | translate: { startsWith: '&quot;' + startsWith + '&quot;' }) : '',
value: (value)? '&quot;' + value + '&quot;': '' value: (value)? '&quot;' + value + '&quot;': ''
} }}" } }}"
[displayTitle]="displayTitle" [displayTitle]="displayTitle"
[objects$]="(items$ !== undefined)? items$ : browseEntries$" [objects$]="(items$ !== undefined)? items$ : browseEntries$"
[paginationConfig]="(currentPagination$ |async)" [paginationConfig]="(currentPagination$ |async)"
[sortConfig]="(currentSort$ |async)" [sortConfig]="(currentSort$ |async)"
[type]="startsWithType" [type]="startsWithType"
[startsWithOptions]="startsWithOptions" [startsWithOptions]="startsWithOptions"
(prev)="goPrev()" (prev)="goPrev()"
(next)="goNext()"> (next)="goNext()">
</ds-browse-by> </ds-browse-by>
<ds-loading *ngIf="loading$ | async" }
message="{{'loading.browse-by-page' | translate}}"></ds-loading> @if (loading$ | async) {
</div> <ds-loading
</section> message="{{'loading.browse-by-page' | translate}}"></ds-loading>
}
</div>
</section>
}

View File

@@ -1,7 +1,6 @@
import { import {
AsyncPipe, AsyncPipe,
isPlatformServer, isPlatformServer,
NgIf,
} from '@angular/common'; } from '@angular/common';
import { import {
Component, Component,
@@ -69,7 +68,6 @@ export const BBM_PAGINATION_ID = 'bbm';
templateUrl: './browse-by-metadata.component.html', templateUrl: './browse-by-metadata.component.html',
imports: [ imports: [
AsyncPipe, AsyncPipe,
NgIf,
TranslateModule, TranslateModule,
ThemedLoadingComponent, ThemedLoadingComponent,
ThemedBrowseByComponent, ThemedBrowseByComponent,

View File

@@ -1,23 +1,25 @@
<section> <section>
<h1 *ngIf="displayTitle"> @if (displayTitle) {
{{ ('browse.title') | translate:{ <h1>
field: 'browse.metadata.' + vocabularyName | translate, {{ ('browse.title') | translate:{
startsWith: '', field: 'browse.metadata.' + vocabularyName | translate,
value: '', startsWith: '',
} }} value: '',
</h1> } }}
</h1>
}
<div class="mb-3"> <div class="mb-3">
<ds-vocabulary-treeview [description]="description" <ds-vocabulary-treeview [description]="description"
[vocabularyOptions]=vocabularyOptions [vocabularyOptions]=vocabularyOptions
[multiSelect]="true" [multiSelect]="true"
[showAdd]="false" [showAdd]="false"
(select)="onSelect($event)" (select)="onSelect($event)"
(deselect)="onDeselect($event)"> (deselect)="onDeselect($event)">
</ds-vocabulary-treeview> </ds-vocabulary-treeview>
</div> </div>
<a class="btn btn-primary" <a class="btn btn-primary"
[routerLink]="['/search']" [routerLink]="['/search']"
[queryParams]="queryParams" [queryParams]="queryParams"
[queryParamsHandling]="'merge'"> [queryParamsHandling]="'merge'">
{{ 'browse.taxonomy.button' | translate }}</a> {{ 'browse.taxonomy.button' | translate }}</a>
</section> </section>

View File

@@ -1,7 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgIf,
} from '@angular/common';
import { import {
Component, Component,
Input, Input,
@@ -52,7 +49,6 @@ import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type';
AsyncPipe, AsyncPipe,
ComcolPageHeaderComponent, ComcolPageHeaderComponent,
ComcolPageLogoComponent, ComcolPageLogoComponent,
NgIf,
ThemedComcolPageHandleComponent, ThemedComcolPageHandleComponent,
ThemedComcolPageContentComponent, ThemedComcolPageContentComponent,
DsoEditMenuComponent, DsoEditMenuComponent,

View File

@@ -1,7 +1,6 @@
import { import {
AsyncPipe, AsyncPipe,
isPlatformServer, isPlatformServer,
NgIf,
} from '@angular/common'; } from '@angular/common';
import { import {
Component, Component,
@@ -39,7 +38,6 @@ import {
standalone: true, standalone: true,
imports: [ imports: [
AsyncPipe, AsyncPipe,
NgIf,
TranslateModule, TranslateModule,
ThemedLoadingComponent, ThemedLoadingComponent,
ThemedBrowseByComponent, ThemedBrowseByComponent,

View File

@@ -1,8 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgClass,
NgIf,
} from '@angular/common';
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
@@ -64,8 +60,6 @@ import {
UploaderComponent, UploaderComponent,
AsyncPipe, AsyncPipe,
ComcolPageLogoComponent, ComcolPageLogoComponent,
NgIf,
NgClass,
VarDirective, VarDirective,
], ],
}) })

View File

@@ -19,7 +19,7 @@
[dangerConfirm]="true" [dangerConfirm]="true"
[hideCollection]="true" [hideCollection]="true"
(confirm)="mapItems($event, true)" (confirm)="mapItems($event, true)"
(cancel)="onCancel()"></ds-item-select> (cancel)="onCancel()"></ds-item-select>
</div> </div>
</ng-template> </ng-template>
</li> </li>
@@ -29,30 +29,34 @@
<div class="row mt-2"> <div class="row mt-2">
<div class="col-12 col-lg-6"> <div class="col-12 col-lg-6">
<ds-search-form id="search-form" <ds-search-form id="search-form"
[query]="(searchOptions$ | async)?.query" [query]="(searchOptions$ | async)?.query"
[scope]="(searchOptions$ | async)?.scope" [scope]="(searchOptions$ | async)?.scope"
[currentUrl]="'./'" [currentUrl]="'./'"
[inPlaceSearch]="true" [inPlaceSearch]="true"
[searchPlaceholder]="'collection.edit.item-mapper.search-form.placeholder' | translate" [searchPlaceholder]="'collection.edit.item-mapper.search-form.placeholder' | translate"
(submitSearch)="performedSearch = true"> (submitSearch)="performedSearch = true">
</ds-search-form> </ds-search-form>
</div> </div>
</div> </div>
<div *ngIf="performedSearch"> @if (performedSearch) {
<ds-item-select class="mt-2" <div>
[key]="'map'" <ds-item-select class="mt-2"
[dsoRD$]="mappedItemsRD$" [key]="'map'"
[paginationOptions]="(searchOptions$ | async)?.pagination" [dsoRD$]="mappedItemsRD$"
[featureId]="FeatureIds.CanManageMappings" [paginationOptions]="(searchOptions$ | async)?.pagination"
[confirmButton]="'collection.edit.item-mapper.confirm'" [featureId]="FeatureIds.CanManageMappings"
[cancelButton]="'collection.edit.item-mapper.cancel'" [confirmButton]="'collection.edit.item-mapper.confirm'"
(confirm)="mapItems($event)" [cancelButton]="'collection.edit.item-mapper.cancel'"
(confirm)="mapItems($event)"
(cancel)="onCancel()"></ds-item-select> (cancel)="onCancel()"></ds-item-select>
</div> </div>
<div *ngIf="!performedSearch" class="alert alert-info w-100" role="alert"> }
{{'collection.edit.item-mapper.no-search' | translate}} @if (!performedSearch) {
</div> <div class="alert alert-info w-100" role="alert">
{{'collection.edit.item-mapper.no-search' | translate}}
</div>
}
</ng-template> </ng-template>
</li> </li>
</ul> </ul>

View File

@@ -1,7 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgIf,
} from '@angular/common';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
@@ -86,7 +83,6 @@ import { followLink } from '../../shared/utils/follow-link-config.model';
TranslateModule, TranslateModule,
AsyncPipe, AsyncPipe,
ItemSelectComponent, ItemSelectComponent,
NgIf,
BrowserOnlyPipe, BrowserOnlyPipe,
], ],
standalone: true, standalone: true,

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,7 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgIf,
} from '@angular/common';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
@@ -65,7 +62,6 @@ import { getCollectionPageRoute } from './collection-page-routing-paths';
imports: [ imports: [
ThemedComcolPageContentComponent, ThemedComcolPageContentComponent,
ErrorComponent, ErrorComponent,
NgIf,
ThemedLoadingComponent, ThemedLoadingComponent,
TranslateModule, TranslateModule,
ViewTrackerComponent, ViewTrackerComponent,

View File

@@ -1,15 +1,19 @@
<div class="container" *ngIf="(isLoading$ | async) === false"> @if ((isLoading$ | async) === false) {
<div class="row"> <div class="container">
<div class="col-12 pb-4"> <div class="row">
<h1 id="sub-header" <div class="col-12 pb-4">
class="border-bottom pb-2">{{ 'collection.create.sub-head' | translate:{ parent: dsoNameService.getName((parentRD$| async)?.payload) } }}</h1> <h1 id="sub-header"
class="border-bottom pb-2">{{ 'collection.create.sub-head' | translate:{ parent: dsoNameService.getName((parentRD$| async)?.payload) } }}</h1>
</div>
</div> </div>
<ds-collection-form (submitForm)="onSubmit($event)"
[isCreation]="true"
(back)="navigateToHome()"></ds-collection-form>
</div> </div>
<ds-collection-form (submitForm)="onSubmit($event)" }
[isCreation]="true"
(back)="navigateToHome()"></ds-collection-form>
</div>
<div class="container"> <div class="container">
<ds-loading *ngIf="isLoading$ | async"></ds-loading> @if (isLoading$ | async) {
<ds-loading></ds-loading>
}
</div> </div>

View File

@@ -1,7 +1,4 @@
import { import { AsyncPipe } from '@angular/common';
AsyncPipe,
NgIf,
} from '@angular/common';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { import {
@@ -32,7 +29,6 @@ import { CollectionFormComponent } from '../collection-form/collection-form.comp
TranslateModule, TranslateModule,
AsyncPipe, AsyncPipe,
ThemedLoadingComponent, ThemedLoadingComponent,
NgIf,
], ],
standalone: true, standalone: true,
}) })

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