mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-18 23:43:01 +00:00
107873: Add process overview page tables
This commit is contained in:
@@ -13,46 +13,26 @@
|
||||
</button>
|
||||
<button class="btn btn-success" routerLink="/processes/new"><i
|
||||
class="fas fa-plus pr-2"></i>{{'process.overview.new' | translate}}</button>
|
||||
|
||||
</div>
|
||||
<ds-pagination *ngIf="(processesRD$ | async)?.payload?.totalElements > 0"
|
||||
[paginationOptions]="pageConfig"
|
||||
[pageInfoState]="(processesRD$ | async)?.payload"
|
||||
[collectionSize]="(processesRD$ | async)?.payload?.totalElements"
|
||||
[hideGear]="true"
|
||||
[hidePagerWhenSinglePage]="true">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{'process.overview.table.id' | translate}}</th>
|
||||
<th scope="col">{{'process.overview.table.name' | translate}}</th>
|
||||
<th scope="col">{{'process.overview.table.user' | translate}}</th>
|
||||
<th scope="col">{{'process.overview.table.start' | translate}}</th>
|
||||
<th scope="col">{{'process.overview.table.finish' | translate}}</th>
|
||||
<th scope="col">{{'process.overview.table.status' | translate}}</th>
|
||||
<th scope="col">{{'process.overview.table.actions' | translate}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let process of (processesRD$ | async)?.payload?.page"
|
||||
[class.table-danger]="processBulkDeleteService.isToBeDeleted(process.processId)">
|
||||
<td><a [routerLink]="['/processes/', process.processId]">{{process.processId}}</a></td>
|
||||
<td><a [routerLink]="['/processes/', process.processId]">{{process.scriptName}}</a></td>
|
||||
<td *ngVar="(getEpersonName(process.userId) | async) as ePersonName">{{ePersonName}}</td>
|
||||
<td>{{process.startTime | date:dateFormat:'UTC'}}</td>
|
||||
<td>{{process.endTime | date:dateFormat:'UTC'}}</td>
|
||||
<td>{{process.processStatus}}</td>
|
||||
<td>
|
||||
<button class="btn btn-outline-danger"
|
||||
(click)="processBulkDeleteService.toggleDelete(process.processId)"><i
|
||||
class="fas fa-trash"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</ds-pagination>
|
||||
|
||||
<div class="sections">
|
||||
<ds-process-overview-table
|
||||
[processStatus]="ProcessStatus.RUNNING"
|
||||
[useAutoRefreshingSearchBy]="true"
|
||||
[getInfoValueMethod]="processOverviewService.timeStarted"/>
|
||||
<ds-process-overview-table
|
||||
[processStatus]="ProcessStatus.SCHEDULED"
|
||||
[useAutoRefreshingSearchBy]="true"
|
||||
[getInfoValueMethod]="processOverviewService.timeStarted"/>
|
||||
<ds-process-overview-table
|
||||
[processStatus]="ProcessStatus.COMPLETED"
|
||||
[useAutoRefreshingSearchBy]="true"
|
||||
[getInfoValueMethod]="processOverviewService.timeCompleted"/>
|
||||
<ds-process-overview-table
|
||||
[processStatus]="ProcessStatus.FAILED"
|
||||
[useAutoRefreshingSearchBy]="true"
|
||||
[getInfoValueMethod]="processOverviewService.timeCompleted"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #deleteModal>
|
||||
@@ -88,4 +68,3 @@
|
||||
|
||||
|
||||
</ng-template>
|
||||
|
||||
|
@@ -5,9 +5,7 @@ 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 { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
|
||||
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
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';
|
||||
@@ -15,6 +13,8 @@ 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 { ProcessStatus } from '../processes/process-status.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-process-overview',
|
||||
@@ -25,6 +25,8 @@ import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
||||
*/
|
||||
export class ProcessOverviewComponent implements OnInit, OnDestroy {
|
||||
|
||||
protected readonly ProcessStatus = ProcessStatus;
|
||||
|
||||
/**
|
||||
* List of all processes
|
||||
*/
|
||||
@@ -56,6 +58,7 @@ export class ProcessOverviewComponent implements OnInit, OnDestroy {
|
||||
isProcessingSub: Subscription;
|
||||
|
||||
constructor(protected processService: ProcessDataService,
|
||||
protected processOverviewService: ProcessOverviewService,
|
||||
protected paginationService: PaginationService,
|
||||
protected ePersonService: EPersonDataService,
|
||||
protected modalService: NgbModal,
|
||||
@@ -78,17 +81,6 @@ export class ProcessOverviewComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of an EPerson by ID
|
||||
* @param id ID of the EPerson
|
||||
*/
|
||||
getEpersonName(id: string): Observable<string> {
|
||||
return this.ePersonService.findById(id).pipe(
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
map((eperson: EPerson) => this.dsoNameService.getName(eperson)),
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.paginationService.clearPagination(this.pageConfig.id);
|
||||
if (hasValue(this.isProcessingSub)) {
|
||||
|
72
src/app/process-page/overview/process-overview.service.ts
Normal file
72
src/app/process-page/overview/process-overview.service.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ProcessDataService } from '../../core/data/processes/process-data.service';
|
||||
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../core/data/paginated-list.model';
|
||||
import { Process } from '../processes/process.model';
|
||||
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';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Service to retrieve
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ProcessOverviewService {
|
||||
|
||||
constructor(protected processDataService: ProcessDataService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Date format to use for start and end time of processes
|
||||
*/
|
||||
dateFormat = 'yyyy-MM-dd HH:mm:ss';
|
||||
|
||||
datePipe = new DatePipe('en-US');
|
||||
|
||||
|
||||
timeCompleted = (process: Process) => this.datePipe.transform(process.endTime, this.dateFormat, 'UTC');
|
||||
timeStarted = (process: Process) => this.datePipe.transform(process.startTime, this.dateFormat, 'UTC');
|
||||
|
||||
/**
|
||||
* Retrieve processes by their status
|
||||
* @param processStatus The status for which to retrieve processes
|
||||
* @param findListOptions The FindListOptions object
|
||||
* @param autoRefreshingIntervalInMs Optional: The interval by which to automatically refresh the retrieved processes.
|
||||
* Leave empty or set to null to only retrieve the processes once.
|
||||
*/
|
||||
getProcessesByProcessStatus(processStatus: ProcessStatus, findListOptions?: FindListOptions, autoRefreshingIntervalInMs: number = null): Observable<RemoteData<PaginatedList<Process>>> {
|
||||
let requestParam = new RequestParam('processStatus', processStatus);
|
||||
let options: FindListOptions = Object.assign(new FindListOptions(), {
|
||||
searchParams: [requestParam],
|
||||
elementsPerPage: 5,
|
||||
}, findListOptions);
|
||||
|
||||
if (autoRefreshingIntervalInMs !== null && autoRefreshingIntervalInMs > 0) {
|
||||
return this.processDataService.autoRefreshingSearchBy('byProperty', options, autoRefreshingIntervalInMs);
|
||||
} else {
|
||||
return this.processDataService.searchBy('byProperty', options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the provided paginationOptions to FindListOptions
|
||||
* @param paginationOptions the PaginationComponentOptions to map
|
||||
*/
|
||||
getFindListOptions(paginationOptions: PaginationComponentOptions): FindListOptions {
|
||||
return Object.assign(
|
||||
new FindListOptions(),
|
||||
{
|
||||
currentPage: paginationOptions.currentPage,
|
||||
elementsPerPage: paginationOptions.pageSize,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
<div class="container">
|
||||
<div class="d-flex dropdown-toggle" (click)="collapse.toggle()" [attr.aria-expanded]="!collapse.collapsed" role="button">
|
||||
<h2 class="flex-grow-1">{{'process.overview.table.' + processStatus.toLowerCase() + '.title' | translate}}</h2>
|
||||
</div>
|
||||
|
||||
<div ngbCollapse #collapse="ngbCollapse">
|
||||
|
||||
<ng-container *ngVar="(processesRD$ | async) as processesRD">
|
||||
<ds-themed-loading *ngIf="!processesRD || processesRD.isLoading"/>
|
||||
|
||||
<ds-pagination *ngIf="processesRD?.payload?.totalElements > 0"
|
||||
[paginationOptions]="(paginationOptions$ | async)"
|
||||
[collectionSize]="processesRD?.payload?.totalElements"
|
||||
[retainScrollPosition]="true">
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{'process.overview.table.status' | translate}}</th>
|
||||
<th scope="col">{{'process.overview.table.name' | translate}}</th>
|
||||
<th scope="col">{{'process.overview.table.user' | translate}}</th>
|
||||
<th scope="col">{{'process.overview.table.' + processStatus.toLowerCase() + '.info' | translate}}</th>
|
||||
<th scope="col">{{'process.overview.table.actions' | translate}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr *ngFor="let process of processesRD?.payload?.page"
|
||||
[class.table-danger]="processBulkDeleteService.isToBeDeleted(process.processId)">
|
||||
<td>{{process.processStatus}}</td>
|
||||
<td><a [routerLink]="['/processes/', process.processId]">{{process.scriptName}}</a></td>
|
||||
<td *ngVar="(getEPersonName(process.userId) | async) as ePersonName">{{ePersonName}}</td>
|
||||
<td>{{getInfoValueMethod(process)}}</td>
|
||||
<td>
|
||||
<button class="btn btn-outline-danger"
|
||||
(click)="processBulkDeleteService.toggleDelete(process.processId)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</ds-pagination>
|
||||
|
||||
<div *ngIf="processesRD?.payload?.totalElements == 0">
|
||||
<p>{{'process.overview.table.empty' | translate}}</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,99 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { ProcessStatus } from '../../processes/process-status.model';
|
||||
import { Observable } 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 { 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 } 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';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-process-overview-table',
|
||||
templateUrl: './process-overview-table.component.html'
|
||||
})
|
||||
export class ProcessOverviewTableComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* The status of the processes this sections should show
|
||||
*/
|
||||
@Input() processStatus: ProcessStatus;
|
||||
|
||||
/**
|
||||
* Whether to use auto refresh for the processes shown in this table.
|
||||
*/
|
||||
@Input() useAutoRefreshingSearchBy = false;
|
||||
|
||||
/**
|
||||
* The interval by which to refresh if autoRefreshing is enabled
|
||||
*/
|
||||
@Input() autoRefreshInterval = 5000;
|
||||
|
||||
/**
|
||||
* The function used to retrieve the value that will be shown in the 'info' column of the table.
|
||||
* {@Link ProcessOverviewService} contains some predefined functions.
|
||||
*/
|
||||
@Input() getInfoValueMethod: (process: Process) => string;
|
||||
|
||||
/**
|
||||
* List of processes to be shown in this table
|
||||
*/
|
||||
processesRD$: Observable<RemoteData<PaginatedList<Process>>>;
|
||||
|
||||
/**
|
||||
* The pagination ID for this overview section
|
||||
*/
|
||||
paginationId: string;
|
||||
|
||||
/**
|
||||
* The current pagination options for the overview section
|
||||
*/
|
||||
paginationOptions$: Observable<PaginationComponentOptions>;
|
||||
|
||||
constructor(protected processOverviewService: ProcessOverviewService,
|
||||
protected processBulkDeleteService: ProcessBulkDeleteService,
|
||||
protected ePersonDataService: EPersonDataService,
|
||||
protected dsoNameService: DSONameService,
|
||||
protected paginationService: PaginationService) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.paginationId = 'processOverviewTable' + this.processStatus;
|
||||
|
||||
let defaultPaginationOptions = Object.assign(new PaginationComponentOptions(), {
|
||||
id: this.paginationId,
|
||||
pageSize: 5,
|
||||
});
|
||||
|
||||
this.paginationOptions$ = this.paginationService.getCurrentPagination(this.paginationId, defaultPaginationOptions);
|
||||
this.processesRD$ = this.paginationOptions$
|
||||
.pipe(
|
||||
map((paginationOptions: PaginationComponentOptions) =>
|
||||
this.processOverviewService.getFindListOptions(paginationOptions)),
|
||||
switchMap(
|
||||
(findListOptions: FindListOptions) => {
|
||||
return this.processOverviewService.getProcessesByProcessStatus(
|
||||
this.processStatus, findListOptions, this.useAutoRefreshingSearchBy ? this.autoRefreshInterval : null);
|
||||
}
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of an EPerson by ID
|
||||
* @param id ID of the EPerson
|
||||
*/
|
||||
getEPersonName(id: string): Observable<string> {
|
||||
return this.ePersonDataService.findById(id).pipe(
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
map((eperson: EPerson) => this.dsoNameService.getName(eperson)),
|
||||
);
|
||||
}
|
||||
}
|
@@ -16,10 +16,14 @@ import { ProcessDetailFieldComponent } from './detail/process-detail-field/proce
|
||||
import { ProcessBreadcrumbsService } from './process-breadcrumbs.service';
|
||||
import { ProcessBreadcrumbResolver } from './process-breadcrumb.resolver';
|
||||
import { ProcessFormComponent } from './form/process-form.component';
|
||||
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ProcessOverviewTableComponent } from './overview/table/process-overview-table.component';
|
||||
import { DatePipe } from '@angular/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
SharedModule,
|
||||
NgbCollapseModule,
|
||||
],
|
||||
declarations: [
|
||||
NewProcessComponent,
|
||||
@@ -33,13 +37,15 @@ import { ProcessFormComponent } from './form/process-form.component';
|
||||
BooleanValueInputComponent,
|
||||
DateValueInputComponent,
|
||||
ProcessOverviewComponent,
|
||||
ProcessOverviewTableComponent,
|
||||
ProcessDetailComponent,
|
||||
ProcessDetailFieldComponent,
|
||||
ProcessFormComponent
|
||||
],
|
||||
providers: [
|
||||
ProcessBreadcrumbResolver,
|
||||
ProcessBreadcrumbsService
|
||||
ProcessBreadcrumbsService,
|
||||
DatePipe,
|
||||
]
|
||||
})
|
||||
|
||||
|
@@ -3218,12 +3218,30 @@
|
||||
|
||||
"process.detail.refreshing": "Auto-refreshing…",
|
||||
|
||||
"process.overview.table.completed.info": "Finish time (UTC)",
|
||||
|
||||
"process.overview.table.completed.title": "Completed processes",
|
||||
|
||||
"process.overview.table.empty": "No matching processes found.",
|
||||
|
||||
"process.overview.table.failed.info": "Finish time (UTC)",
|
||||
|
||||
"process.overview.table.failed.title": "Failed processes",
|
||||
|
||||
"process.overview.table.finish": "Finish time (UTC)",
|
||||
|
||||
"process.overview.table.id": "Process ID",
|
||||
|
||||
"process.overview.table.name": "Name",
|
||||
|
||||
"process.overview.table.running.info": "Start time (UTC)",
|
||||
|
||||
"process.overview.table.running.title": "Running processes",
|
||||
|
||||
"process.overview.table.scheduled.info": "Start time (UTC)",
|
||||
|
||||
"process.overview.table.scheduled.title": "Scheduled processes",
|
||||
|
||||
"process.overview.table.start": "Start time (UTC)",
|
||||
|
||||
"process.overview.table.status": "Status",
|
||||
|
Reference in New Issue
Block a user