Переглянути джерело

Improve runner jobs UX

Use badges with same colors for type/runner
Adds processed/finished information
Chocobozzz 2 місяців тому
батько
коміт
d4a09f9ce2

+ 13 - 3
client/src/app/+admin/system/jobs/jobs.component.html

@@ -53,7 +53,8 @@
       <th scope="col" style="width: 200px" class="job-priority" i18n>Priority <small>(1 = highest priority)</small></th>
       <th scope="col" style="width: 200px" class="job-state" i18n *ngIf="jobState === 'all'">State</th>
       <th scope="col" style="width: 100px" class="job-progress" i18n *ngIf="hasGlobalProgress()">Progress</th>
-      <th scope="col" style="width: 150px" class="job-date" i18n [ngbTooltip]="sortTooltip" container="body" pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
+      <th scope="col" style="width: 200px" class="job-date" i18n [ngbTooltip]="sortTooltip" container="body" pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
+      <th scope="col" style="width: 230px;" i18n>Processed/Finished</th>
     </tr>
   </ng-template>
 
@@ -64,11 +65,15 @@
       </td>
 
       <td class="job-id c-hand" [pRowToggler]="job" [title]="job.id">{{ job.id }}</td>
-      <td class="job-type c-hand" [pRowToggler]="job">{{ job.type }}</td>
+
+      <td class="job-type c-hand" [pRowToggler]="job">
+        <span class="pt-badge ellipsis" [ngClass]="getRandomJobTypeBadge(job.type)">{{ job.type }}</span>
+      </td>
+
       <td class="job-priority c-hand" [pRowToggler]="job">{{ job.priority }}</td>
 
       <td class="job-state c-hand" [pRowToggler]="job" *ngIf="jobState === 'all'">
-        <span class="pt-badge" [ngClass]="getJobStateClass(job.state)">{{ job.state }}</span>
+        <span class="pt-badge ellipsis" [ngClass]="getJobStateClass(job.state)">{{ job.state }}</span>
       </td>
 
       <td *ngIf="hasGlobalProgress()" class="job-progress c-hand" [pRowToggler]="job">
@@ -76,6 +81,11 @@
       </td>
 
       <td class="job-date c-hand" [pRowToggler]="job">{{ job.createdAt }}</td>
+
+      <td class="fs-7">
+        <div>{{ job.processedOn }}</div>
+        <div>{{ job.finishedOn}}</div>
+      </td>
     </tr>
   </ng-template>
 

+ 1 - 1
client/src/app/+admin/system/jobs/jobs.component.scss

@@ -27,7 +27,7 @@
   }
 
   .job-date {
-    width: 170px !important;
+    width: 200px !important;
   }
 }
 

+ 4 - 0
client/src/app/+admin/system/jobs/jobs.component.ts

@@ -144,6 +144,10 @@ export class JobsComponent extends RestTable implements OnInit {
     this.reloadData()
   }
 
+  getRandomJobTypeBadge (type: string) {
+    return this.getRandomBadge('type', type)
+  }
+
   protected reloadDataInternal () {
     let jobState = this.jobState as JobState
     if (this.jobState === 'all') jobState = null

+ 20 - 5
client/src/app/+admin/system/runners/runner-job-list/runner-job-list.component.html

@@ -30,11 +30,12 @@
       </th>
       <th scope="col" i18n>UUID</th>
       <th scope="col" i18n>Type</th>
-      <th scope="col" i18n [ngbTooltip]="sortTooltip" container="body" pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th>
+      <th scope="col" style="width: 150px" i18n [ngbTooltip]="sortTooltip" container="body" pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th>
       <th scope="col" style="width: 100px" i18n [ngbTooltip]="sortTooltip" container="body" pSortableColumn="priority">Priority <p-sortIcon field="priority"></p-sortIcon></th>
       <th scope="col" style="width: 100px" i18n [ngbTooltip]="sortTooltip" container="body" pSortableColumn="progress">Progress <p-sortIcon field="progress"></p-sortIcon></th>
-      <th scope="col" i18n>Runner</th>
+      <th scope="col" style="width: 100px;" i18n>Runner</th>
       <th scope="col" style="width: 200px;" i18n [ngbTooltip]="sortTooltip" container="body" pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
+      <th scope="col" style="width: 230px;" i18n>Processed/Finished</th>
     </tr>
   </ng-template>
 
@@ -75,18 +76,32 @@
       </td>
 
       <td>{{ runnerJob.uuid }}</td>
-      <td>{{ runnerJob.type }}</td>
+
       <td>
-        <span class="pt-badge" [ngClass]="getStateBadgeColor(runnerJob)">{{ runnerJob.state.label }}</span>
+        <span class="pt-badge ellipsis" [ngClass]="getRandomRunnerTypeBadge(runnerJob.type)">{{ runnerJob.type }}</span>
       </td>
+
+      <td>
+        <span class="pt-badge ellipsis" [ngClass]="getStateBadgeColor(runnerJob)">{{ runnerJob.state.label }}</span>
+      </td>
+
       <td>{{ runnerJob.priority }}</td>
 
       <td>
         <ng-container *ngIf="runnerJob.progress">{{ runnerJob.progress }}%</ng-container>
       </td>
 
-      <td>{{ runnerJob.runner?.name }}</td>
+      <td>
+        <div *ngIf="runnerJob.runner?.name" class="pt-badge" [ngClass]="getRandomRunnerNameBadge(runnerJob.runner.name)">
+          {{ runnerJob.runner.name }}
+        </div>
+      </td>
       <td>{{ runnerJob.createdAt }}</td>
+
+      <td class="fs-7">
+        <div>{{ runnerJob.startedAt }}</div>
+        <div>{{ runnerJob.finishedAt}}</div>
+      </td>
     </tr>
   </ng-template>
 

+ 18 - 10
client/src/app/+admin/system/runners/runner-job-list/runner-job-list.component.ts

@@ -1,19 +1,19 @@
-import { SortMeta, SharedModule } from 'primeng/api'
+import { NgClass, NgIf } from '@angular/common'
 import { Component, OnInit } from '@angular/core'
+import { RouterLink } from '@angular/router'
 import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
 import { formatICU } from '@app/helpers'
-import { RunnerJob, RunnerJobState } from '@peertube/peertube-models'
-import { RunnerJobFormatted, RunnerService } from '../runner.service'
-import { AutoColspanDirective } from '../../../../shared/shared-main/angular/auto-colspan.directive'
-import { TableExpanderIconComponent } from '../../../../shared/shared-tables/table-expander-icon.component'
-import { ButtonComponent } from '../../../../shared/shared-main/buttons/button.component'
-import { AdvancedInputFilter, AdvancedInputFilterComponent } from '../../../../shared/shared-forms/advanced-input-filter.component'
-import { ActionDropdownComponent, DropdownAction } from '../../../../shared/shared-main/buttons/action-dropdown.component'
-import { NgIf, NgClass } from '@angular/common'
 import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'
+import { RunnerJob, RunnerJobState } from '@peertube/peertube-models'
+import { SharedModule, SortMeta } from 'primeng/api'
 import { TableModule } from 'primeng/table'
-import { RouterLink } from '@angular/router'
+import { AdvancedInputFilter, AdvancedInputFilterComponent } from '../../../../shared/shared-forms/advanced-input-filter.component'
 import { GlobalIconComponent } from '../../../../shared/shared-icons/global-icon.component'
+import { AutoColspanDirective } from '../../../../shared/shared-main/angular/auto-colspan.directive'
+import { ActionDropdownComponent, DropdownAction } from '../../../../shared/shared-main/buttons/action-dropdown.component'
+import { ButtonComponent } from '../../../../shared/shared-main/buttons/button.component'
+import { TableExpanderIconComponent } from '../../../../shared/shared-tables/table-expander-icon.component'
+import { RunnerJobFormatted, RunnerService } from '../runner.service'
 
 @Component({
   selector: 'my-runner-job-list',
@@ -176,6 +176,14 @@ export class RunnerJobListComponent extends RestTable <RunnerJob> implements OnI
     }
   }
 
+  getRandomRunnerNameBadge (value: string) {
+    return this.getRandomBadge('runner', value)
+  }
+
+  getRandomRunnerTypeBadge (value: string) {
+    return this.getRandomBadge('type', value)
+  }
+
   protected reloadDataInternal () {
     this.runnerService.listRunnerJobs({ pagination: this.pagination, sort: this.sort, search: this.search })
       .subscribe({

+ 40 - 3
client/src/app/core/rest/rest-table.ts

@@ -1,9 +1,9 @@
-import debug from 'debug'
-import { SortMeta } from 'primeng/api'
-import { TableLazyLoadEvent } from 'primeng/table'
 import { ActivatedRoute, Router } from '@angular/router'
 import { logger } from '@root-helpers/logger'
 import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
+import debug from 'debug'
+import { SortMeta } from 'primeng/api'
+import { TableLazyLoadEvent } from 'primeng/table'
 import { RestPagination } from './rest-pagination'
 
 const debugLogger = debug('peertube:tables:RestTable')
@@ -27,6 +27,11 @@ export abstract class RestTable <T = unknown> {
   protected route: ActivatedRoute
   protected router: Router
 
+  // First string is badge column type
+  // Inner Map is value -> badge name
+  private valueToBadge = new Map<string, Map<string, string>>()
+  private badgesUsed = new Set<string>()
+
   abstract getIdentifier (): string
 
   initialize () {
@@ -96,6 +101,38 @@ export abstract class RestTable <T = unknown> {
     this.reloadDataInternal()
   }
 
+  protected getRandomBadge (type: string, value: string): string {
+    if (!this.valueToBadge.has(type)) {
+      this.valueToBadge.set(type, new Map())
+    }
+
+    const badges = this.valueToBadge.get(type)
+    const badge = badges.get(value)
+    if (badge) return badge
+
+    const toTry = [
+      'badge-yellow',
+      'badge-purple',
+      'badge-blue',
+      'badge-brown',
+      'badge-green',
+      'badge-secondary'
+    ]
+
+    for (const badge of toTry) {
+      if (!this.badgesUsed.has(badge)) {
+        this.badgesUsed.add(badge)
+        badges.set(value, badge)
+        return badge
+      }
+    }
+
+    // Reset, we used all available badges
+    this.badgesUsed.clear()
+
+    return this.getRandomBadge(type, value)
+  }
+
   private getSortLocalStorageKey () {
     return 'rest-table-sort-' + this.getIdentifier()
   }

+ 1 - 0
client/src/sass/include/_badges.scss

@@ -8,6 +8,7 @@
   font-size: 75%;
   font-weight: $font-semibold;
   line-height: 1.1;
+  max-width: 100%;
 
   &.badge-fs-normal {
     font-size: 100%;