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>
|
||||||
<button class="btn btn-success" routerLink="/processes/new"><i
|
<button class="btn btn-success" routerLink="/processes/new"><i
|
||||||
class="fas fa-plus pr-2"></i>{{'process.overview.new' | translate}}</button>
|
class="fas fa-plus pr-2"></i>{{'process.overview.new' | translate}}</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<ds-pagination *ngIf="(processesRD$ | async)?.payload?.totalElements > 0"
|
|
||||||
[paginationOptions]="pageConfig"
|
<div class="sections">
|
||||||
[pageInfoState]="(processesRD$ | async)?.payload"
|
<ds-process-overview-table
|
||||||
[collectionSize]="(processesRD$ | async)?.payload?.totalElements"
|
[processStatus]="ProcessStatus.RUNNING"
|
||||||
[hideGear]="true"
|
[useAutoRefreshingSearchBy]="true"
|
||||||
[hidePagerWhenSinglePage]="true">
|
[getInfoValueMethod]="processOverviewService.timeStarted"/>
|
||||||
<div class="table-responsive">
|
<ds-process-overview-table
|
||||||
<table class="table table-striped table-hover">
|
[processStatus]="ProcessStatus.SCHEDULED"
|
||||||
<thead>
|
[useAutoRefreshingSearchBy]="true"
|
||||||
<tr>
|
[getInfoValueMethod]="processOverviewService.timeStarted"/>
|
||||||
<th scope="col">{{'process.overview.table.id' | translate}}</th>
|
<ds-process-overview-table
|
||||||
<th scope="col">{{'process.overview.table.name' | translate}}</th>
|
[processStatus]="ProcessStatus.COMPLETED"
|
||||||
<th scope="col">{{'process.overview.table.user' | translate}}</th>
|
[useAutoRefreshingSearchBy]="true"
|
||||||
<th scope="col">{{'process.overview.table.start' | translate}}</th>
|
[getInfoValueMethod]="processOverviewService.timeCompleted"/>
|
||||||
<th scope="col">{{'process.overview.table.finish' | translate}}</th>
|
<ds-process-overview-table
|
||||||
<th scope="col">{{'process.overview.table.status' | translate}}</th>
|
[processStatus]="ProcessStatus.FAILED"
|
||||||
<th scope="col">{{'process.overview.table.actions' | translate}}</th>
|
[useAutoRefreshingSearchBy]="true"
|
||||||
</tr>
|
[getInfoValueMethod]="processOverviewService.timeCompleted"/>
|
||||||
</thead>
|
</div>
|
||||||
<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>
|
</div>
|
||||||
|
|
||||||
<ng-template #deleteModal>
|
<ng-template #deleteModal>
|
||||||
@@ -88,4 +68,3 @@
|
|||||||
|
|
||||||
|
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
@@ -5,9 +5,7 @@ import { PaginatedList } from '../../core/data/paginated-list.model';
|
|||||||
import { Process } from '../processes/process.model';
|
import { Process } from '../processes/process.model';
|
||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
|
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
|
||||||
import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
|
import { switchMap } from 'rxjs/operators';
|
||||||
import { EPerson } from '../../core/eperson/models/eperson.model';
|
|
||||||
import { map, switchMap } from 'rxjs/operators';
|
|
||||||
import { ProcessDataService } from '../../core/data/processes/process-data.service';
|
import { ProcessDataService } from '../../core/data/processes/process-data.service';
|
||||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||||
import { FindListOptions } from '../../core/data/find-list-options.model';
|
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 { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
||||||
|
import { ProcessOverviewService } from './process-overview.service';
|
||||||
|
import { ProcessStatus } from '../processes/process-status.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-process-overview',
|
selector: 'ds-process-overview',
|
||||||
@@ -25,6 +25,8 @@ import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
|||||||
*/
|
*/
|
||||||
export class ProcessOverviewComponent implements OnInit, OnDestroy {
|
export class ProcessOverviewComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
protected readonly ProcessStatus = ProcessStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of all processes
|
* List of all processes
|
||||||
*/
|
*/
|
||||||
@@ -56,6 +58,7 @@ export class ProcessOverviewComponent implements OnInit, OnDestroy {
|
|||||||
isProcessingSub: Subscription;
|
isProcessingSub: Subscription;
|
||||||
|
|
||||||
constructor(protected processService: ProcessDataService,
|
constructor(protected processService: ProcessDataService,
|
||||||
|
protected processOverviewService: ProcessOverviewService,
|
||||||
protected paginationService: PaginationService,
|
protected paginationService: PaginationService,
|
||||||
protected ePersonService: EPersonDataService,
|
protected ePersonService: EPersonDataService,
|
||||||
protected modalService: NgbModal,
|
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 {
|
ngOnDestroy(): void {
|
||||||
this.paginationService.clearPagination(this.pageConfig.id);
|
this.paginationService.clearPagination(this.pageConfig.id);
|
||||||
if (hasValue(this.isProcessingSub)) {
|
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 { ProcessBreadcrumbsService } from './process-breadcrumbs.service';
|
||||||
import { ProcessBreadcrumbResolver } from './process-breadcrumb.resolver';
|
import { ProcessBreadcrumbResolver } from './process-breadcrumb.resolver';
|
||||||
import { ProcessFormComponent } from './form/process-form.component';
|
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({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
NgbCollapseModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
NewProcessComponent,
|
NewProcessComponent,
|
||||||
@@ -33,13 +37,15 @@ import { ProcessFormComponent } from './form/process-form.component';
|
|||||||
BooleanValueInputComponent,
|
BooleanValueInputComponent,
|
||||||
DateValueInputComponent,
|
DateValueInputComponent,
|
||||||
ProcessOverviewComponent,
|
ProcessOverviewComponent,
|
||||||
|
ProcessOverviewTableComponent,
|
||||||
ProcessDetailComponent,
|
ProcessDetailComponent,
|
||||||
ProcessDetailFieldComponent,
|
ProcessDetailFieldComponent,
|
||||||
ProcessFormComponent
|
ProcessFormComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ProcessBreadcrumbResolver,
|
ProcessBreadcrumbResolver,
|
||||||
ProcessBreadcrumbsService
|
ProcessBreadcrumbsService,
|
||||||
|
DatePipe,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -3218,12 +3218,30 @@
|
|||||||
|
|
||||||
"process.detail.refreshing": "Auto-refreshing…",
|
"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.finish": "Finish time (UTC)",
|
||||||
|
|
||||||
"process.overview.table.id": "Process ID",
|
"process.overview.table.id": "Process ID",
|
||||||
|
|
||||||
"process.overview.table.name": "Name",
|
"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.start": "Start time (UTC)",
|
||||||
|
|
||||||
"process.overview.table.status": "Status",
|
"process.overview.table.status": "Status",
|
||||||
|
Reference in New Issue
Block a user