From 2ad97d2845c4043128b5e7e1cf8ddaea5b408014 Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Mon, 29 Jan 2024 09:08:46 +0100 Subject: [PATCH 01/17] 107873: Set process overview table column widths --- .../process-overview-table.component.html | 10 +++++----- .../process-overview-table.component.scss | 20 +++++++++++++++++++ src/styles/_custom_variables.scss | 6 ++++++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/app/process-page/overview/table/process-overview-table.component.html b/src/app/process-page/overview/table/process-overview-table.component.html index 8a3bd55dab..3aa335ab50 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.html +++ b/src/app/process-page/overview/table/process-overview-table.component.html @@ -18,11 +18,11 @@ - - - - - + + + + + diff --git a/src/app/process-page/overview/table/process-overview-table.component.scss b/src/app/process-page/overview/table/process-overview-table.component.scss index 6d92c1df6d..96a80599d2 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.scss +++ b/src/app/process-page/overview/table/process-overview-table.component.scss @@ -1,3 +1,23 @@ .toggle-icon { font-size: calc(var(--bs-small-font-size) * 0.6); } + +.status-header { + width: var(--ds-process-overview-table-status-column-width); +} + +.name-header { + width: var(--ds-process-overview-table-name-column-width); +} + +.user-header { + width: var(--ds-process-overview-table-user-column-width); +} + +.info-header { + width: var(--ds-process-overview-table-info-column-width); +} + +.actions-header { + width: var(--ds-process-overview-table-actions-column-width); +} diff --git a/src/styles/_custom_variables.scss b/src/styles/_custom_variables.scss index ddf490c7a7..1898cc46e2 100644 --- a/src/styles/_custom_variables.scss +++ b/src/styles/_custom_variables.scss @@ -97,4 +97,10 @@ --ds-dso-edit-lang-width: 90px; --ds-dso-edit-actions-width: 173px; --ds-dso-edit-virtual-tooltip-min-width: 300px; + + --ds-process-overview-table-status-column-width: 150px; + --ds-process-overview-table-name-column-width: auto; + --ds-process-overview-table-user-column-width: 200px; + --ds-process-overview-table-info-column-width: 250px; + --ds-process-overview-table-actions-column-width: 80px; } From 536c02a078ab29a38a3989027e78c6336f3b87da Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Mon, 29 Jan 2024 09:12:50 +0100 Subject: [PATCH 02/17] 107873: Remove pagination gear --- .../overview/table/process-overview-table.component.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/process-page/overview/table/process-overview-table.component.html b/src/app/process-page/overview/table/process-overview-table.component.html index 3aa335ab50..65cb18f5cb 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.html +++ b/src/app/process-page/overview/table/process-overview-table.component.html @@ -12,7 +12,8 @@ + [retainScrollPosition]="true" + [hideGear]="true">
{{'process.overview.table.status' | translate}}{{'process.overview.table.name' | translate}}{{'process.overview.table.user' | translate}}{{'process.overview.table.' + processStatus.toLowerCase() + '.info' | translate}}{{'process.overview.table.actions' | translate}}{{'process.overview.table.status' | translate}}{{'process.overview.table.name' | translate}}{{'process.overview.table.user' | translate}}{{'process.overview.table.' + processStatus.toLowerCase() + '.info' | translate}}{{'process.overview.table.actions' | translate}}
From 73e823e969d708afc91d95b5d43cf33ccf14f9aa Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Mon, 29 Jan 2024 09:30:13 +0100 Subject: [PATCH 03/17] 107873: Add number of processes badge --- .../table/process-overview-table.component.html | 12 ++++++++++-- .../table/process-overview-table.component.scss | 5 +++++ src/styles/_custom_variables.scss | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/app/process-page/overview/table/process-overview-table.component.html b/src/app/process-page/overview/table/process-overview-table.component.html index 65cb18f5cb..e1caf69c89 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.html +++ b/src/app/process-page/overview/table/process-overview-table.component.html @@ -1,7 +1,15 @@
-

{{'process.overview.table.' + processStatus.toLowerCase() + '.title' | translate}}

+

+ {{'process.overview.table.' + processStatus.toLowerCase() + '.title' | translate}} + + {{processesRD?.payload?.totalElements}} + + + + +

diff --git a/src/app/process-page/overview/table/process-overview-table.component.scss b/src/app/process-page/overview/table/process-overview-table.component.scss index 96a80599d2..e83f1f268a 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.scss +++ b/src/app/process-page/overview/table/process-overview-table.component.scss @@ -2,6 +2,11 @@ font-size: calc(var(--bs-small-font-size) * 0.6); } +.badge-nb-processes { + font-size: var(--ds-process-overview-table-nb-processes-badge-size); + vertical-align: middle; +} + .status-header { width: var(--ds-process-overview-table-status-column-width); } diff --git a/src/styles/_custom_variables.scss b/src/styles/_custom_variables.scss index 1898cc46e2..14279246ba 100644 --- a/src/styles/_custom_variables.scss +++ b/src/styles/_custom_variables.scss @@ -98,6 +98,7 @@ --ds-dso-edit-actions-width: 173px; --ds-dso-edit-virtual-tooltip-min-width: 300px; + --ds-process-overview-table-nb-processes-badge-size: 0.5em; --ds-process-overview-table-status-column-width: 150px; --ds-process-overview-table-name-column-width: auto; --ds-process-overview-table-user-column-width: 200px; From 1c73a0c5094ced9b923b1044e0a21f3850a2ff83 Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Mon, 5 Feb 2024 13:20:00 +0100 Subject: [PATCH 04/17] 111638: Improve accessibility --- .../overview/table/process-overview-table.component.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/process-page/overview/table/process-overview-table.component.html b/src/app/process-page/overview/table/process-overview-table.component.html index e1caf69c89..3c70730fe5 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.html +++ b/src/app/process-page/overview/table/process-overview-table.component.html @@ -43,8 +43,10 @@
From e25ce44a479b576e7df9ab02b6a4eb343609b165 Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Mon, 5 Feb 2024 14:27:05 +0100 Subject: [PATCH 05/17] 111638: Collapse table when it contains no processes --- .../process-overview-table.component.html | 2 +- .../table/process-overview-table.component.ts | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/app/process-page/overview/table/process-overview-table.component.html b/src/app/process-page/overview/table/process-overview-table.component.html index 3c70730fe5..cb3b2f1739 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.html +++ b/src/app/process-page/overview/table/process-overview-table.component.html @@ -12,7 +12,7 @@ -
+
diff --git a/src/app/process-page/overview/table/process-overview-table.component.ts b/src/app/process-page/overview/table/process-overview-table.component.ts index 193e7f3bcc..cbf475386c 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.ts +++ b/src/app/process-page/overview/table/process-overview-table.component.ts @@ -9,8 +9,8 @@ import { ProcessOverviewService } from '../process-overview.service'; import { ProcessBulkDeleteService } from '../process-bulk-delete.service'; import { EPersonDataService } from '../../../core/eperson/eperson-data.service'; import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; -import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators'; -import { map, switchMap, toArray } from 'rxjs/operators'; +import { getFirstSucceededRemoteDataPayload, getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { map, switchMap, toArray, take } from 'rxjs/operators'; import { EPerson } from '../../../core/eperson/models/eperson.model'; import { PaginationService } from 'src/app/core/pagination/pagination.service'; import { FindListOptions } from '../../../core/data/find-list-options.model'; @@ -71,6 +71,11 @@ export class ProcessOverviewTableComponent implements OnInit { */ paginationOptions$: Observable; + /** + * Whether the table is collapsed + */ + isCollapsed = false; + constructor(protected processOverviewService: ProcessOverviewService, protected processBulkDeleteService: ProcessBulkDeleteService, protected ePersonDataService: EPersonDataService, @@ -152,6 +157,15 @@ export class ProcessOverviewTableComponent implements OnInit { ); + // Collapse this section when the number of processes is zero the first time processes are retrieved + this.processesRD$.pipe(getFirstCompletedRemoteData()).subscribe( + (processesRD: RemoteData>) => { + if (!(processesRD.payload.totalElements > 0)) { + this.isCollapsed = true; + } + } + ); + } /** From e96a7582f47e0ffe12bd3637d5928805d0ad523f Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Mon, 5 Feb 2024 14:47:43 +0100 Subject: [PATCH 06/17] 111638: Duplicate buttons at bottom of page --- .../overview/process-overview.component.html | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/app/process-page/overview/process-overview.component.html b/src/app/process-page/overview/process-overview.component.html index 57d4a5346e..0c5a27145e 100644 --- a/src/app/process-page/overview/process-overview.component.html +++ b/src/app/process-page/overview/process-overview.component.html @@ -2,18 +2,7 @@

{{'process.overview.title' | translate}}

-
- - - -
+
+ +
+ +
+ + + +
+
+
From d64f521f0c699987906386a757d81c27d22349cd Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Mon, 5 Feb 2024 15:55:43 +0100 Subject: [PATCH 07/17] 111638: Add creationTime to process model --- src/app/process-page/processes/process.model.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/app/process-page/processes/process.model.ts b/src/app/process-page/processes/process.model.ts index 609182d6ca..5ce4ae6340 100644 --- a/src/app/process-page/processes/process.model.ts +++ b/src/app/process-page/processes/process.model.ts @@ -44,6 +44,12 @@ export class Process implements CacheableObject { @autoserialize userId: string; + /** + * The creation time for this process + */ + @autoserialize + creationTime: string; + /** * The start time for this process */ From 5569d7fd320910e13485bccf9d6cd3163af2f4a5 Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Mon, 5 Feb 2024 16:09:17 +0100 Subject: [PATCH 08/17] 111638: Use creation time for scheduled processes --- src/app/process-page/overview/process-overview.component.html | 2 +- src/app/process-page/overview/process-overview.service.ts | 1 + src/assets/i18n/en.json5 | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/process-page/overview/process-overview.component.html b/src/app/process-page/overview/process-overview.component.html index 0c5a27145e..11f28bd290 100644 --- a/src/app/process-page/overview/process-overview.component.html +++ b/src/app/process-page/overview/process-overview.component.html @@ -12,7 +12,7 @@ + [getInfoValueMethod]="processOverviewService.timeCreated"/> this.datePipe.transform(process.creationTime, this.dateFormat, 'UTC'); timeCompleted = (process: Process) => this.datePipe.transform(process.endTime, this.dateFormat, 'UTC'); timeStarted = (process: Process) => this.datePipe.transform(process.startTime, this.dateFormat, 'UTC'); diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index cc3c13f909..1328fe6311 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3238,7 +3238,7 @@ "process.overview.table.running.title": "Running processes", - "process.overview.table.scheduled.info": "Start time (UTC)", + "process.overview.table.scheduled.info": "Creation time (UTC)", "process.overview.table.scheduled.title": "Scheduled processes", From 72c04cfc7798c7e5588a4b59354d8e5da5ee64ee Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Mon, 5 Feb 2024 16:11:36 +0100 Subject: [PATCH 09/17] 111638: Sort processes with creationTime as default field --- .../overview/process-overview.component.html | 2 ++ .../overview/process-overview.component.ts | 4 +++- .../overview/process-overview.service.ts | 17 ++++++++++++++++- .../table/process-overview-table.component.ts | 11 +++++++++-- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/app/process-page/overview/process-overview.component.html b/src/app/process-page/overview/process-overview.component.html index 11f28bd290..b1181c3606 100644 --- a/src/app/process-page/overview/process-overview.component.html +++ b/src/app/process-page/overview/process-overview.component.html @@ -15,10 +15,12 @@ [getInfoValueMethod]="processOverviewService.timeCreated"/>
diff --git a/src/app/process-page/overview/process-overview.component.ts b/src/app/process-page/overview/process-overview.component.ts index d3f55dbb67..6d9cea1777 100644 --- a/src/app/process-page/overview/process-overview.component.ts +++ b/src/app/process-page/overview/process-overview.component.ts @@ -13,7 +13,7 @@ import { ProcessBulkDeleteService } from './process-bulk-delete.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { hasValue } from '../../shared/empty.util'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; -import { ProcessOverviewService } from './process-overview.service'; +import { ProcessOverviewService, ProcessSortField } from './process-overview.service'; import { ProcessStatus } from '../processes/process-status.model'; @Component({ @@ -25,7 +25,9 @@ import { ProcessStatus } from '../processes/process-status.model'; */ export class ProcessOverviewComponent implements OnInit, OnDestroy { + // Enums are redeclared here so they can be used in the template protected readonly ProcessStatus = ProcessStatus; + protected readonly ProcessSortField = ProcessSortField; /** * List of all processes diff --git a/src/app/process-page/overview/process-overview.service.ts b/src/app/process-page/overview/process-overview.service.ts index 762376c85a..1d71e0d1fc 100644 --- a/src/app/process-page/overview/process-overview.service.ts +++ b/src/app/process-page/overview/process-overview.service.ts @@ -9,6 +9,18 @@ import { RequestParam } from '../../core/cache/models/request-param.model'; import { ProcessStatus } from '../processes/process-status.model'; import { DatePipe } from '@angular/common'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; +import { SortOptions, SortDirection } from '../../core/cache/models/sort-options.model'; + +/** + * The sortable fields for processes + * See [the endpoint documentation]{@link https://github.com/DSpace/RestContract/blob/main/processes-endpoint.md#search-processes-by-property} + * for details. + */ +export enum ProcessSortField { + creationTime = 'creationTime', + startTime = 'startTime', + endTime = 'endTime', +} /** * Service to manage the processes displayed in the @@ -58,13 +70,16 @@ export class ProcessOverviewService { /** * Map the provided paginationOptions to FindListOptions * @param paginationOptions the PaginationComponentOptions to map + * @param sortField the field on which the processes are sorted */ - getFindListOptions(paginationOptions: PaginationComponentOptions): FindListOptions { + getFindListOptions(paginationOptions: PaginationComponentOptions, sortField: ProcessSortField): FindListOptions { + let sortOptions = new SortOptions(sortField, SortDirection.DESC); return Object.assign( new FindListOptions(), { currentPage: paginationOptions.currentPage, elementsPerPage: paginationOptions.pageSize, + sort: sortOptions, } ); } diff --git a/src/app/process-page/overview/table/process-overview-table.component.ts b/src/app/process-page/overview/table/process-overview-table.component.ts index cbf475386c..2386480413 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.ts +++ b/src/app/process-page/overview/table/process-overview-table.component.ts @@ -5,7 +5,7 @@ import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list.model'; import { Process } from '../../processes/process.model'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; -import { ProcessOverviewService } from '../process-overview.service'; +import { ProcessOverviewService, ProcessSortField } from '../process-overview.service'; import { ProcessBulkDeleteService } from '../process-bulk-delete.service'; import { EPersonDataService } from '../../../core/eperson/eperson-data.service'; import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; @@ -40,6 +40,13 @@ export class ProcessOverviewTableComponent implements OnInit { */ @Input() processStatus: ProcessStatus; + /** + * The field on which the processes in this table are sorted + * {@link ProcessSortField.creationTime} by default as every single process has a creation time, + * but not every process has a start or end time + */ + @Input() sortField: ProcessSortField = ProcessSortField.creationTime; + /** * Whether to use auto refresh for the processes shown in this table. */ @@ -118,7 +125,7 @@ export class ProcessOverviewTableComponent implements OnInit { .pipe( // Map the paginationOptions to findListOptions map((paginationOptions: PaginationComponentOptions) => - this.processOverviewService.getFindListOptions(paginationOptions)), + this.processOverviewService.getFindListOptions(paginationOptions, this.sortField)), // Use the findListOptions to retrieve the relevant processes every interval switchMap((findListOptions: FindListOptions) => this.processOverviewService.getProcessesByProcessStatus( From 4af303d0f2e5c83dc9d880af4cd45202cefa43bf Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Mon, 5 Feb 2024 16:18:14 +0100 Subject: [PATCH 10/17] 111638: Use hasValue instead of nullCheck --- src/app/process-page/overview/process-overview.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/process-page/overview/process-overview.service.ts b/src/app/process-page/overview/process-overview.service.ts index 1d71e0d1fc..73c7c1529f 100644 --- a/src/app/process-page/overview/process-overview.service.ts +++ b/src/app/process-page/overview/process-overview.service.ts @@ -10,6 +10,7 @@ import { ProcessStatus } from '../processes/process-status.model'; import { DatePipe } from '@angular/common'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortOptions, SortDirection } from '../../core/cache/models/sort-options.model'; +import { hasValue } from '../../shared/empty.util'; /** * The sortable fields for processes @@ -60,7 +61,7 @@ export class ProcessOverviewService { elementsPerPage: 5, }, findListOptions); - if (autoRefreshingIntervalInMs !== null && autoRefreshingIntervalInMs > 0) { + if (hasValue(autoRefreshingIntervalInMs) && autoRefreshingIntervalInMs > 0) { return this.processDataService.autoRefreshingSearchBy('byProperty', options, autoRefreshingIntervalInMs); } else { return this.processDataService.searchBy('byProperty', options); From 8e11777e5b0eb113a46668035b9c893582a407a3 Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Mon, 5 Feb 2024 16:19:27 +0100 Subject: [PATCH 11/17] 111638: Change completed processes header to 'Succeeded processes' --- src/assets/i18n/en.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 1328fe6311..1971a093dd 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3220,7 +3220,7 @@ "process.overview.table.completed.info": "Finish time (UTC)", - "process.overview.table.completed.title": "Completed processes", + "process.overview.table.completed.title": "Succeeded processes", "process.overview.table.empty": "No matching processes found.", From 545b17fc9eeb2b79aa59bb5c35173e3ba383a6d0 Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Mon, 5 Feb 2024 16:51:23 +0100 Subject: [PATCH 12/17] 111638: Clean up process-overview component Removed all unused code & reduced the tests to the minimum necessary without removing testcases. --- .../process-overview.component.spec.ts | 81 ++----------------- .../overview/process-overview.component.ts | 60 +------------- 2 files changed, 11 insertions(+), 130 deletions(-) diff --git a/src/app/process-page/overview/process-overview.component.spec.ts b/src/app/process-page/overview/process-overview.component.spec.ts index 15b0a587d8..39f50bb1a9 100644 --- a/src/app/process-page/overview/process-overview.component.spec.ts +++ b/src/app/process-page/overview/process-overview.component.spec.ts @@ -3,86 +3,27 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { VarDirective } from '../../shared/utils/var.directive'; import { TranslateModule } from '@ngx-translate/core'; import { RouterTestingModule } from '@angular/router/testing'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { NO_ERRORS_SCHEMA, TemplateRef } from '@angular/core'; import { ProcessDataService } from '../../core/data/processes/process-data.service'; -import { Process } from '../processes/process.model'; -import { EPersonDataService } from '../../core/eperson/eperson-data.service'; -import { EPerson } from '../../core/eperson/models/eperson.model'; import { By } from '@angular/platform-browser'; -import { ProcessStatus } from '../processes/process-status.model'; -import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { createPaginatedList } from '../../shared/testing/utils.test'; -import { PaginationService } from '../../core/pagination/pagination.service'; -import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; -import { DatePipe } from '@angular/common'; import { BehaviorSubject } from 'rxjs'; import { ProcessBulkDeleteService } from './process-bulk-delete.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { ProcessOverviewService } from './process-overview.service'; describe('ProcessOverviewComponent', () => { let component: ProcessOverviewComponent; let fixture: ComponentFixture; let processService: ProcessDataService; - let ePersonService: EPersonDataService; - let paginationService; - - let processes: Process[]; - let ePerson: EPerson; let processBulkDeleteService; let modalService; - const pipe = new DatePipe('en-US'); - function init() { - processes = [ - Object.assign(new Process(), { - processId: 1, - scriptName: 'script-name', - startTime: '2020-03-19 00:30:00', - endTime: '2020-03-19 23:30:00', - processStatus: ProcessStatus.COMPLETED - }), - Object.assign(new Process(), { - processId: 2, - scriptName: 'script-name', - startTime: '2020-03-20 00:30:00', - endTime: '2020-03-20 23:30:00', - processStatus: ProcessStatus.FAILED - }), - Object.assign(new Process(), { - processId: 3, - scriptName: 'another-script-name', - startTime: '2020-03-21 00:30:00', - endTime: '2020-03-21 23:30:00', - processStatus: ProcessStatus.RUNNING - }) - ]; - ePerson = Object.assign(new EPerson(), { - metadata: { - 'eperson.firstname': [ - { - value: 'John', - language: null - } - ], - 'eperson.lastname': [ - { - value: 'Doe', - language: null - } - ] - } + processService = jasmine.createSpyObj('processOverviewService', { + timeStarted: '2024-02-05 16:43:32', }); - processService = jasmine.createSpyObj('processService', { - findAll: createSuccessfulRemoteDataObject$(createPaginatedList(processes)) - }); - ePersonService = jasmine.createSpyObj('ePersonService', { - findById: createSuccessfulRemoteDataObject$(ePerson) - }); - - paginationService = new PaginationServiceStub(); processBulkDeleteService = jasmine.createSpyObj('processBulkDeleteService', { clearAllProcesses: {}, @@ -96,11 +37,7 @@ describe('ProcessOverviewComponent', () => { }); (processBulkDeleteService.isToBeDeleted as jasmine.Spy).and.callFake((id) => { - if (id === 2) { - return true; - } else { - return false; - } + return id === 2; }); modalService = jasmine.createSpyObj('modalService', { @@ -114,9 +51,7 @@ describe('ProcessOverviewComponent', () => { declarations: [ProcessOverviewComponent, VarDirective], imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([])], providers: [ - { provide: ProcessDataService, useValue: processService }, - { provide: EPersonDataService, useValue: ePersonService }, - { provide: PaginationService, useValue: paginationService }, + { provide: ProcessOverviewService, useValue: processService }, { provide: ProcessBulkDeleteService, useValue: processBulkDeleteService }, { provide: NgbModal, useValue: modalService }, ], @@ -165,7 +100,7 @@ describe('ProcessOverviewComponent', () => { describe('openDeleteModal', () => { it('should open the modal', () => { - component.openDeleteModal({}); + component.openDeleteModal({} as TemplateRef); expect(modalService.open).toHaveBeenCalledWith({}); }); }); @@ -173,13 +108,11 @@ describe('ProcessOverviewComponent', () => { describe('deleteSelected', () => { it('should call the deleteSelectedProcesses method on the processBulkDeleteService and close the modal when processing is done', () => { spyOn(component, 'closeModal'); - spyOn(component, 'setProcesses'); component.deleteSelected(); expect(processBulkDeleteService.deleteSelectedProcesses).toHaveBeenCalled(); expect(component.closeModal).toHaveBeenCalled(); - expect(component.setProcesses).toHaveBeenCalled(); }); }); }); diff --git a/src/app/process-page/overview/process-overview.component.ts b/src/app/process-page/overview/process-overview.component.ts index 6d9cea1777..3f8c2b4bfb 100644 --- a/src/app/process-page/overview/process-overview.component.ts +++ b/src/app/process-page/overview/process-overview.component.ts @@ -1,18 +1,8 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Observable, Subscription } from 'rxjs'; -import { RemoteData } from '../../core/data/remote-data'; -import { PaginatedList } from '../../core/data/paginated-list.model'; -import { Process } from '../processes/process.model'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { EPersonDataService } from '../../core/eperson/eperson-data.service'; -import { switchMap } from 'rxjs/operators'; -import { ProcessDataService } from '../../core/data/processes/process-data.service'; -import { PaginationService } from '../../core/pagination/pagination.service'; -import { FindListOptions } from '../../core/data/find-list-options.model'; +import { Component, OnDestroy, OnInit, TemplateRef } from '@angular/core'; +import { Subscription } from 'rxjs'; import { ProcessBulkDeleteService } from './process-bulk-delete.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { hasValue } from '../../shared/empty.util'; -import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; import { ProcessOverviewService, ProcessSortField } from './process-overview.service'; import { ProcessStatus } from '../processes/process-status.model'; @@ -29,62 +19,21 @@ export class ProcessOverviewComponent implements OnInit, OnDestroy { protected readonly ProcessStatus = ProcessStatus; protected readonly ProcessSortField = ProcessSortField; - /** - * List of all processes - */ - processesRD$: Observable>>; - - /** - * The current pagination configuration for the page used by the FindAll method - */ - config: FindListOptions = Object.assign(new FindListOptions(), { - elementsPerPage: 20 - }); - - /** - * The current pagination configuration for the page - */ - pageConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'po', - pageSize: 20 - }); - - /** - * Date format to use for start and end time of processes - */ - dateFormat = 'yyyy-MM-dd HH:mm:ss'; - - processesToDelete: string[] = []; private modalRef: any; isProcessingSub: Subscription; - constructor(protected processService: ProcessDataService, - protected processOverviewService: ProcessOverviewService, - protected paginationService: PaginationService, - protected ePersonService: EPersonDataService, + constructor(protected processOverviewService: ProcessOverviewService, protected modalService: NgbModal, public processBulkDeleteService: ProcessBulkDeleteService, - protected dsoNameService: DSONameService, ) { } ngOnInit(): void { - this.setProcesses(); this.processBulkDeleteService.clearAllProcesses(); } - /** - * Send a request to fetch all processes for the current page - */ - setProcesses() { - this.processesRD$ = this.paginationService.getFindListOptions(this.pageConfig.id, this.config).pipe( - switchMap((config) => this.processService.findAll(config, true, false)) - ); - } - ngOnDestroy(): void { - this.paginationService.clearPagination(this.pageConfig.id); if (hasValue(this.isProcessingSub)) { this.isProcessingSub.unsubscribe(); } @@ -94,7 +43,7 @@ export class ProcessOverviewComponent implements OnInit, OnDestroy { * Open a given modal. * @param content - the modal content. */ - openDeleteModal(content) { + openDeleteModal(content: TemplateRef) { this.modalRef = this.modalService.open(content); } @@ -120,7 +69,6 @@ export class ProcessOverviewComponent implements OnInit, OnDestroy { .subscribe((isProcessing) => { if (!isProcessing) { this.closeModal(); - this.setProcesses(); } }); } From 14294e20ee47aebb39c0834bb3b0213f9d391526 Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Tue, 6 Feb 2024 09:43:32 +0100 Subject: [PATCH 13/17] 111638: Disable autorefreshing on non-browser platforms --- .../overview/table/process-overview-table.component.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/app/process-page/overview/table/process-overview-table.component.ts b/src/app/process-page/overview/table/process-overview-table.component.ts index 2386480413..df2b7bbc2f 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.ts +++ b/src/app/process-page/overview/table/process-overview-table.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, Inject, PLATFORM_ID } from '@angular/core'; import { ProcessStatus } from '../../processes/process-status.model'; import { Observable, mergeMap, from as observableFrom } from 'rxjs'; import { RemoteData } from '../../../core/data/remote-data'; @@ -17,6 +17,7 @@ import { FindListOptions } from '../../../core/data/find-list-options.model'; import { redirectOn4xx } from '../../../core/shared/authorized.operators'; import { Router } from '@angular/router'; import { AuthService } from '../../../core/auth/auth.service'; +import { isPlatformBrowser } from '@angular/common'; /** * An interface to store a process and extra information related to the process @@ -90,10 +91,16 @@ export class ProcessOverviewTableComponent implements OnInit { protected paginationService: PaginationService, protected router: Router, protected auth: AuthService, + @Inject(PLATFORM_ID) protected platformId: object, ) { } ngOnInit() { + // Only auto refresh on browsers + if (!isPlatformBrowser(this.platformId)) { + this.useAutoRefreshingSearchBy = false; + } + // Creates an ID from the first 2 characters of the process status. // Should two process status values ever start with the same substring, // increase the number of characters until the ids are distinct. From 8d473eaead358daf151b93b3f192faeac1643a2c Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Tue, 6 Feb 2024 10:10:26 +0100 Subject: [PATCH 14/17] 111638: Show process ID instead of status --- .../overview/table/process-overview-table.component.html | 4 ++-- .../overview/table/process-overview-table.component.scss | 4 ++-- .../overview/table/process-overview-table.component.spec.ts | 4 ++-- src/styles/_custom_variables.scss | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/process-page/overview/table/process-overview-table.component.html b/src/app/process-page/overview/table/process-overview-table.component.html index cb3b2f1739..531bb367a9 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.html +++ b/src/app/process-page/overview/table/process-overview-table.component.html @@ -27,7 +27,7 @@
{{tableEntry.user}} {{tableEntry.info}} -
- + @@ -38,7 +38,7 @@ - + diff --git a/src/app/process-page/overview/table/process-overview-table.component.scss b/src/app/process-page/overview/table/process-overview-table.component.scss index e83f1f268a..a9567be4c7 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.scss +++ b/src/app/process-page/overview/table/process-overview-table.component.scss @@ -7,8 +7,8 @@ vertical-align: middle; } -.status-header { - width: var(--ds-process-overview-table-status-column-width); +.id-header { + width: var(--ds-process-overview-table-id-column-width); } .name-header { diff --git a/src/app/process-page/overview/table/process-overview-table.component.spec.ts b/src/app/process-page/overview/table/process-overview-table.component.spec.ts index eb9bff959c..20217a799a 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.spec.ts +++ b/src/app/process-page/overview/table/process-overview-table.component.spec.ts @@ -143,10 +143,10 @@ describe('ProcessOverviewTableComponent', () => { expect(rowElements.length).toEqual(3); }); - it('should display the process\' status in the first column', () => { + it('should display the process\' ID in the first column', () => { rowElements.forEach((rowElement, index) => { const el = rowElement.query(By.css('td:nth-child(1)')).nativeElement; - expect(el.textContent).toContain(processes[index].processStatus); + expect(el.textContent).toContain(processes[index].processId); }); }); diff --git a/src/styles/_custom_variables.scss b/src/styles/_custom_variables.scss index 14279246ba..1e4ac2570e 100644 --- a/src/styles/_custom_variables.scss +++ b/src/styles/_custom_variables.scss @@ -99,7 +99,7 @@ --ds-dso-edit-virtual-tooltip-min-width: 300px; --ds-process-overview-table-nb-processes-badge-size: 0.5em; - --ds-process-overview-table-status-column-width: 150px; + --ds-process-overview-table-id-column-width: 120px; --ds-process-overview-table-name-column-width: auto; --ds-process-overview-table-user-column-width: 200px; --ds-process-overview-table-info-column-width: 250px; From 4da63cc30ef1cc91356acf37f00a78e14500f9e5 Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Tue, 6 Feb 2024 13:20:52 +0100 Subject: [PATCH 15/17] 111638: Serialize processId as string to match type processId was originally of type 'number' even though it has 'string' as type annotation. With this change it actually is a string. --- src/app/process-page/processes/process.model.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/process-page/processes/process.model.ts b/src/app/process-page/processes/process.model.ts index 5ce4ae6340..8468b4e43d 100644 --- a/src/app/process-page/processes/process.model.ts +++ b/src/app/process-page/processes/process.model.ts @@ -3,7 +3,7 @@ import { PROCESS_OUTPUT_TYPE } from '../../core/shared/process-output.resource-t import { ProcessStatus } from './process-status.model'; import { ProcessParameter } from './process-parameter.model'; import { HALLink } from '../../core/shared/hal-link.model'; -import { autoserialize, deserialize } from 'cerialize'; +import { autoserialize, deserialize, autoserializeAs } from 'cerialize'; import { PROCESS } from './process.resource-type'; import { excludeFromEquals } from '../../core/utilities/equals.decorators'; import { ResourceType } from '../../core/shared/resource-type'; @@ -35,7 +35,7 @@ export class Process implements CacheableObject { /** * The identifier for this process */ - @autoserialize + @autoserializeAs(String) processId: string; /** From ca18d5ff9021a8bfe69ac479a6c12e9d7119951d Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Tue, 6 Feb 2024 13:28:27 +0100 Subject: [PATCH 16/17] 111638: Highlight newly created processes --- .../form/process-form.component.ts | 22 ++++++++------- .../process-overview-table.component.html | 2 +- .../process-overview-table.component.spec.ts | 5 ++++ .../table/process-overview-table.component.ts | 27 +++++++++++++++++++ 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/app/process-page/form/process-form.component.ts b/src/app/process-page/form/process-form.component.ts index 70eb3160a8..1c5d6b9516 100644 --- a/src/app/process-page/form/process-form.component.ts +++ b/src/app/process-page/form/process-form.component.ts @@ -7,8 +7,7 @@ import { ControlContainer, NgForm } from '@angular/forms'; import { ScriptParameter } from '../scripts/script-parameter.model'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; -import { RequestService } from '../../core/data/request.service'; -import { Router } from '@angular/router'; +import { Router, NavigationExtras } from '@angular/router'; import { getFirstCompletedRemoteData } from '../../core/shared/operators'; import { RemoteData } from '../../core/data/remote-data'; import { getProcessListRoute } from '../process-page-routing.paths'; @@ -57,7 +56,6 @@ export class ProcessFormComponent implements OnInit { private scriptService: ScriptDataService, private notificationsService: NotificationsService, private translationService: TranslateService, - private requestService: RequestService, private router: Router) { } @@ -91,7 +89,7 @@ export class ProcessFormComponent implements OnInit { const title = this.translationService.get('process.new.notification.success.title'); const content = this.translationService.get('process.new.notification.success.content'); this.notificationsService.success(title, content); - this.sendBack(); + this.sendBack(rd.payload); } else { const title = this.translationService.get('process.new.notification.error.title'); const content = this.translationService.get('process.new.notification.error.content'); @@ -143,11 +141,17 @@ export class ProcessFormComponent implements OnInit { return this.missingParameters.length > 0; } - private sendBack() { - this.requestService.removeByHrefSubstring('/processes'); - /* should subscribe on the previous method to know the action is finished and then navigate, - will fix this when the removeByHrefSubstring changes are merged */ - this.router.navigateByUrl(getProcessListRoute()); + /** + * Redirect the user to the processes overview page with the new process' ID, + * so it can be highlighted in the overview table. + * @param newProcess The newly created process + * @private + */ + private sendBack(newProcess: Process) { + const extras: NavigationExtras = { + queryParams: { new_process_id: newProcess.processId }, + }; + void this.router.navigate([getProcessListRoute()], extras); } } diff --git a/src/app/process-page/overview/table/process-overview-table.component.html b/src/app/process-page/overview/table/process-overview-table.component.html index 531bb367a9..2565a79618 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.html +++ b/src/app/process-page/overview/table/process-overview-table.component.html @@ -37,7 +37,7 @@ + [class]="getRowClass(tableEntry.process)"> diff --git a/src/app/process-page/overview/table/process-overview-table.component.spec.ts b/src/app/process-page/overview/table/process-overview-table.component.spec.ts index 20217a799a..aa073f291c 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.spec.ts +++ b/src/app/process-page/overview/table/process-overview-table.component.spec.ts @@ -19,6 +19,8 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { AuthService } from '../../../core/auth/auth.service'; import { AuthServiceMock } from '../../../shared/mocks/auth.service.mock'; +import { RouteService } from '../../../core/services/route.service'; +import { routeServiceStub } from '../../../shared/testing/route-service.stub'; describe('ProcessOverviewTableComponent', () => { @@ -31,6 +33,7 @@ describe('ProcessOverviewTableComponent', () => { let processBulkDeleteService: ProcessBulkDeleteService; let modalService: NgbModal; let authService; // : AuthService; Not typed as the mock does not fully implement AuthService + let routeService: RouteService; let processes: Process[]; let ePerson: EPerson; @@ -104,6 +107,7 @@ describe('ProcessOverviewTableComponent', () => { }); authService = new AuthServiceMock(); + routeService = routeServiceStub; } beforeEach(waitForAsync(() => { @@ -119,6 +123,7 @@ describe('ProcessOverviewTableComponent', () => { { provide: ProcessBulkDeleteService, useValue: processBulkDeleteService }, { provide: NgbModal, useValue: modalService }, { provide: AuthService, useValue: authService }, + { provide: RouteService, useValue: routeService }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/process-page/overview/table/process-overview-table.component.ts b/src/app/process-page/overview/table/process-overview-table.component.ts index df2b7bbc2f..9fc2946670 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.ts +++ b/src/app/process-page/overview/table/process-overview-table.component.ts @@ -18,6 +18,9 @@ import { redirectOn4xx } from '../../../core/shared/authorized.operators'; import { Router } from '@angular/router'; import { AuthService } from '../../../core/auth/auth.service'; import { isPlatformBrowser } from '@angular/common'; +import { RouteService } from '../../../core/services/route.service'; + +const NEW_PROCESS_PARAM = 'new_process_id'; /** * An interface to store a process and extra information related to the process @@ -84,11 +87,17 @@ export class ProcessOverviewTableComponent implements OnInit { */ isCollapsed = false; + /** + * The id of the process to highlight + */ + newProcessId: string; + constructor(protected processOverviewService: ProcessOverviewService, protected processBulkDeleteService: ProcessBulkDeleteService, protected ePersonDataService: EPersonDataService, protected dsoNameService: DSONameService, protected paginationService: PaginationService, + protected routeService: RouteService, protected router: Router, protected auth: AuthService, @Inject(PLATFORM_ID) protected platformId: object, @@ -101,6 +110,10 @@ export class ProcessOverviewTableComponent implements OnInit { this.useAutoRefreshingSearchBy = false; } + this.routeService.getQueryParameterValue(NEW_PROCESS_PARAM).pipe(take(1)).subscribe((id) => { + this.newProcessId = id; + }); + // Creates an ID from the first 2 characters of the process status. // Should two process status values ever start with the same substring, // increase the number of characters until the ids are distinct. @@ -193,4 +206,18 @@ export class ProcessOverviewTableComponent implements OnInit { ); } + /** + * Get the css class for a row depending on the state of the process + * @param process + */ + getRowClass(process: Process): string { + if (this.processBulkDeleteService.isToBeDeleted(process.processId)) { + return 'table-danger'; + } else if (this.newProcessId === process.processId) { + return 'table-info'; + } else { + return ''; + } + } + } From 4651d3a0d01d258a3acd1dd98fd6edb5b4998949 Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Tue, 6 Feb 2024 14:20:50 +0100 Subject: [PATCH 17/17] 111638: Fix typeError when useAutoRefreshing is set to false --- .../overview/table/process-overview-table.component.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app/process-page/overview/table/process-overview-table.component.ts b/src/app/process-page/overview/table/process-overview-table.component.ts index 9fc2946670..36003a2a89 100644 --- a/src/app/process-page/overview/table/process-overview-table.component.ts +++ b/src/app/process-page/overview/table/process-overview-table.component.ts @@ -9,7 +9,11 @@ import { ProcessOverviewService, ProcessSortField } from '../process-overview.se import { ProcessBulkDeleteService } from '../process-bulk-delete.service'; import { EPersonDataService } from '../../../core/eperson/eperson-data.service'; import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; -import { getFirstSucceededRemoteDataPayload, getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { + getFirstSucceededRemoteDataPayload, + getFirstCompletedRemoteData, + getAllCompletedRemoteData +} from '../../../core/shared/operators'; import { map, switchMap, toArray, take } from 'rxjs/operators'; import { EPerson } from '../../../core/eperson/models/eperson.model'; import { PaginationService } from 'src/app/core/pagination/pagination.service'; @@ -153,6 +157,7 @@ export class ProcessOverviewTableComponent implements OnInit { ), // Redirect the user when he is logged out redirectOn4xx(this.router, this.auth), + getAllCompletedRemoteData(), // Map RemoteData> to RemoteData> switchMap((processesRD: RemoteData>) => { // Create observable emitting all processes one by one
{{'process.overview.table.status' | translate}}{{'process.overview.table.id' | translate}} {{'process.overview.table.name' | translate}} {{'process.overview.table.user' | translate}} {{'process.overview.table.' + processStatus.toLowerCase() + '.info' | translate}}
{{tableEntry.process.processStatus}}{{tableEntry.process.processId}} {{tableEntry.process.scriptName}} {{tableEntry.user}} {{tableEntry.info}}
{{tableEntry.process.processId}} {{tableEntry.process.scriptName}} {{tableEntry.user}}