[value]="runnerJobs" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order"
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
- [showCurrentPageReport]="true" i18n-currentPageReportTemplate
+ [(selection)]="selectedRows" [showCurrentPageReport]="true" i18n-currentPageReportTemplate
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} runner jobs"
[expandedRowKeys]="expandedRows" dataKey="uuid"
>
<ng-template pTemplate="header">
<tr>
+ <th style="width: 40px">
+ <p-tableHeaderCheckbox ariaLabel="Select all rows" i18n-ariaLabel></p-tableHeaderCheckbox>
+ </th>
<th style="width: 40px"></th>
<th style="width: 120px;"></th>
<th i18n>UUID</th>
</ng-template>
<ng-template pTemplate="caption">
+
<div class="caption">
+ <div class="left-buttons">
+ <my-action-dropdown
+ *ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
+ [actions]="bulkActions" [entry]="selectedRows"
+ >
+ </my-action-dropdown>
+ </div>
+
<div class="ms-auto d-flex">
<my-advanced-input-filter class="me-2" (search)="onSearch($event)"></my-advanced-input-filter>
</ng-template>
<ng-template pTemplate="body" let-expanded="expanded" let-runnerJob>
- <tr>
+ <tr [pSelectableRow]="runnerJob">
+ <td class="checkbox-cell">
+ <p-tableCheckbox [value]="runnerJob" ariaLabel="Select this row" i18n-ariaLabel></p-tableCheckbox>
+ </td>
+
<td class="expand-cell" [pRowToggler]="runnerJob">
<my-table-expander-icon [expanded]="expanded"></my-table-expander-icon>
</td>
import { SortMeta } from 'primeng/api'
import { Component, OnInit } from '@angular/core'
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
+import { prepareIcu } from '@app/helpers'
import { DropdownAction } from '@app/shared/shared-main'
-import { RunnerJob } from '@shared/models'
+import { RunnerJob, RunnerJobState } from '@shared/models'
import { RunnerJobFormatted, RunnerService } from '../runner.service'
@Component({
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
actions: DropdownAction<RunnerJob>[][] = []
+ bulkActions: DropdownAction<RunnerJob[]>[][] = []
constructor (
private runnerService: RunnerService,
[
{
label: $localize`Cancel this job`,
- handler: job => this.cancelJob(job)
+ handler: job => this.cancelJobs([ job ]),
+ isDisplayed: job => this.canCancelJob(job)
+ }
+ ]
+ ]
+
+ this.bulkActions = [
+ [
+ {
+ label: $localize`Cancel`,
+ handler: jobs => this.cancelJobs(jobs),
+ isDisplayed: jobs => jobs.every(j => this.canCancelJob(j))
}
]
]
return 'RunnerJobListComponent'
}
- async cancelJob (job: RunnerJob) {
- const res = await this.confirmService.confirm(
- $localize`Do you really want to cancel this job? Children won't be processed.`,
- $localize`Cancel job`
- )
+ async cancelJobs (jobs: RunnerJob[]) {
+ const message = prepareIcu(
+ $localize`Do you really want to cancel {count, plural, =1 {this job} other {{count} jobs}}? Children jobs will also be cancelled.`
+ )({ count: jobs.length }, $localize`Do you really want to cancel these jobs? Children jobs will also be cancelled.`)
+
+ const res = await this.confirmService.confirm(message, $localize`Cancel`)
if (res === false) return
- this.runnerService.cancelJob(job)
+ this.runnerService.cancelJobs(jobs)
.subscribe({
next: () => {
this.reloadData()
- this.notifier.success($localize`Job cancelled.`)
+ this.notifier.success($localize`Job(s) cancelled.`)
},
error: err => this.notifier.error(err.message)
error: err => this.notifier.error(err.message)
})
}
+
+ private canCancelJob (job: RunnerJob) {
+ return job.state.id === RunnerJobState.PENDING ||
+ job.state.id === RunnerJobState.PROCESSING ||
+ job.state.id === RunnerJobState.WAITING_FOR_PARENT_JOB
+ }
}
import { SortMeta } from 'primeng/api'
-import { catchError, forkJoin, map } from 'rxjs'
+import { catchError, concatMap, forkJoin, from, map, toArray } from 'rxjs'
import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { RestExtractor, RestPagination, RestService, ServerService } from '@app/core'
-import { peertubeTranslate } from '@shared/core-utils'
+import { arrayify, peertubeTranslate } from '@shared/core-utils'
import { ResultList } from '@shared/models/common'
import { Runner, RunnerJob, RunnerJobAdmin, RunnerRegistrationToken } from '@shared/models/runners'
import { environment } from '../../../../environments/environment'
)
}
- cancelJob (job: RunnerJob) {
- return this.authHttp.post(RunnerService.BASE_RUNNER_URL + '/jobs/' + job.uuid + '/cancel', {})
- .pipe(catchError(res => this.restExtractor.handleError(res)))
+ cancelJobs (jobsArg: RunnerJob | RunnerJob[]) {
+ const jobs = arrayify(jobsArg)
+
+ return from(jobs)
+ .pipe(
+ concatMap(job => this.authHttp.post(RunnerService.BASE_RUNNER_URL + '/jobs/' + job.uuid + '/cancel', {})),
+ toArray(),
+ catchError(err => this.restExtractor.handleError(err))
+ )
}
// ---------------------------------------------------------------------------