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/process-overview.component.html b/src/app/process-page/overview/process-overview.component.html index 566ca7ad0f..8217cd8994 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}}

-
- - - -
+
+ [getInfoValueMethod]="processOverviewService.timeCreated"/>
+ + + +
+ + + +
+
+
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 d3f55dbb67..3f8c2b4bfb 100644 --- a/src/app/process-page/overview/process-overview.component.ts +++ b/src/app/process-page/overview/process-overview.component.ts @@ -1,19 +1,9 @@ -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 } from './process-overview.service'; +import { ProcessOverviewService, ProcessSortField } from './process-overview.service'; import { ProcessStatus } from '../processes/process-status.model'; @Component({ @@ -25,64 +15,25 @@ 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 - */ - 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(); } @@ -92,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); } @@ -118,7 +69,6 @@ export class ProcessOverviewComponent implements OnInit, OnDestroy { .subscribe((isProcessing) => { if (!isProcessing) { this.closeModal(); - this.setProcesses(); } }); } diff --git a/src/app/process-page/overview/process-overview.service.ts b/src/app/process-page/overview/process-overview.service.ts index 87ac7f8947..73c7c1529f 100644 --- a/src/app/process-page/overview/process-overview.service.ts +++ b/src/app/process-page/overview/process-overview.service.ts @@ -9,6 +9,19 @@ 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'; +import { hasValue } from '../../shared/empty.util'; + +/** + * 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 @@ -30,6 +43,7 @@ export class ProcessOverviewService { datePipe = new DatePipe('en-US'); + timeCreated = (process: Process) => 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'); @@ -47,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); @@ -57,13 +71,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.html b/src/app/process-page/overview/table/process-overview-table.component.html index 34d4288872..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 @@ -12,7 +12,7 @@
-
+
@@ -27,7 +27,7 @@ - + @@ -37,13 +37,14 @@ - + [class]="getRowClass(tableEntry.process)"> +
{{'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}}