Change jobs table in admin control to mat-table (#2444)
* Change jobs table in admin control to mat-table * Update changelog
This commit is contained in:
parent
f47c7313af
commit
d8d4d8f001
@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- Supported enter key press to submit the form of the create or update access dialog
|
- Supported enter key press to submit the form of the create or update access dialog
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Changed the queue jobs view in the admin control panel to an `@angular/material` data table
|
||||||
|
|
||||||
## 2.9.0 - 2023-10-08
|
## 2.9.0 - 2023-10-08
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
OnInit
|
OnInit
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
||||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||||
import { QUEUE_JOB_STATUS_LIST } from '@ghostfolio/common/config';
|
import { QUEUE_JOB_STATUS_LIST } from '@ghostfolio/common/config';
|
||||||
@ -24,7 +25,19 @@ import { takeUntil } from 'rxjs/operators';
|
|||||||
export class AdminJobsComponent implements OnDestroy, OnInit {
|
export class AdminJobsComponent implements OnDestroy, OnInit {
|
||||||
public defaultDateTimeFormat: string;
|
public defaultDateTimeFormat: string;
|
||||||
public filterForm: FormGroup;
|
public filterForm: FormGroup;
|
||||||
public jobs: AdminJobs['jobs'] = [];
|
public dataSource: MatTableDataSource<AdminJobs['jobs'][0]> =
|
||||||
|
new MatTableDataSource();
|
||||||
|
public displayedColumns = [
|
||||||
|
'index',
|
||||||
|
'type',
|
||||||
|
'symbol',
|
||||||
|
'dataSource',
|
||||||
|
'attempts',
|
||||||
|
'created',
|
||||||
|
'finished',
|
||||||
|
'status',
|
||||||
|
'actions'
|
||||||
|
];
|
||||||
public statusFilterOptions = QUEUE_JOB_STATUS_LIST;
|
public statusFilterOptions = QUEUE_JOB_STATUS_LIST;
|
||||||
public user: User;
|
public user: User;
|
||||||
|
|
||||||
@ -102,7 +115,7 @@ export class AdminJobsComponent implements OnDestroy, OnInit {
|
|||||||
.fetchJobs({ status: aStatus })
|
.fetchJobs({ status: aStatus })
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe(({ jobs }) => {
|
.subscribe(({ jobs }) => {
|
||||||
this.jobs = jobs;
|
this.dataSource = new MatTableDataSource(jobs);
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
this.changeDetectorRef.markForCheck();
|
||||||
});
|
});
|
||||||
|
@ -13,122 +13,163 @@
|
|||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</form>
|
</form>
|
||||||
<table class="gf-table w-100">
|
<table class="gf-table w-100" mat-table [dataSource]="dataSource">
|
||||||
<thead>
|
<ng-container matColumnDef="index">
|
||||||
<tr class="mat-header-row">
|
<th *matHeaderCellDef class="px-1 py-2 text-right" mat-header-cell>
|
||||||
<th class="mat-header-cell px-1 py-2 text-right">#</th>
|
#
|
||||||
<th class="mat-header-cell px-1 py-2" i18n>Type</th>
|
</th>
|
||||||
<th class="mat-header-cell px-1 py-2" i18n>Symbol</th>
|
<td *matCellDef="let element" class="px-1 py-2 text-right" mat-cell>
|
||||||
<th class="mat-header-cell px-1 py-2" i18n>Data Source</th>
|
{{ element.id }}
|
||||||
<th class="mat-header-cell px-1 py-2 text-right" i18n>Attempts</th>
|
</td>
|
||||||
<th class="mat-header-cell px-1 py-2" i18n>Created</th>
|
</ng-container>
|
||||||
<th class="mat-header-cell px-1 py-2" i18n>Finished</th>
|
|
||||||
<th class="mat-header-cell px-1 py-2" i18n>Status</th>
|
<ng-container matColumnDef="type">
|
||||||
<th class="mat-header-cell px-1 py-2">
|
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
|
||||||
<button
|
<ng-container i18n>Type</ng-container>
|
||||||
class="mx-1 no-min-width px-2"
|
</th>
|
||||||
mat-button
|
<td *matCellDef="let element" class="px-1 py-2" mat-cell>
|
||||||
[matMenuTriggerFor]="jobsActionsMenu"
|
<span class="align-items-center d-flex">
|
||||||
(click)="$event.stopPropagation()"
|
<ion-icon
|
||||||
|
class="mr-1"
|
||||||
|
name="arrow-down-circle-outline"
|
||||||
|
></ion-icon>
|
||||||
|
<ng-container *ngIf="element.name === 'GATHER_ASSET_PROFILE'">
|
||||||
|
<span i18n>Asset Profile</span>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container
|
||||||
|
*ngIf="element.name === 'GATHER_HISTORICAL_MARKET_DATA'"
|
||||||
>
|
>
|
||||||
<ion-icon name="ellipsis-vertical"></ion-icon>
|
<span i18n>Historical Market Data</span>
|
||||||
|
</ng-container>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="symbol">
|
||||||
|
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
|
||||||
|
<ng-container i18n>Symbol</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1 py-2" mat-cell>
|
||||||
|
{{ element.data?.symbol }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="dataSource">
|
||||||
|
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
|
||||||
|
<ng-container i18n>Data Source</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1 py-2" mat-cell>
|
||||||
|
{{ element.data?.dataSource }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="attempts">
|
||||||
|
<th *matHeaderCellDef class="px-1 py-2 text-right" mat-header-cell>
|
||||||
|
<ng-container i18n>Attempts</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1 py-2 text-right" mat-cell>
|
||||||
|
{{ element.attemptsMade }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="created">
|
||||||
|
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
|
||||||
|
<ng-container i18n>Created</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1 py-2" mat-cell>
|
||||||
|
{{ element.timestamp | date: defaultDateTimeFormat }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="finished">
|
||||||
|
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
|
||||||
|
<ng-container i18n>Finished</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1 py-2" mat-cell>
|
||||||
|
{{ element.finishedOn | date: defaultDateTimeFormat }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="status">
|
||||||
|
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
|
||||||
|
<ng-container i18n>Status</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1 py-2" mat-cell>
|
||||||
|
<ion-icon
|
||||||
|
*ngIf="element.state === 'active'"
|
||||||
|
name="play-outline"
|
||||||
|
></ion-icon>
|
||||||
|
<ion-icon
|
||||||
|
*ngIf="element.state === 'completed'"
|
||||||
|
class="text-success"
|
||||||
|
name="checkmark-circle-outline"
|
||||||
|
></ion-icon>
|
||||||
|
<ion-icon
|
||||||
|
*ngIf="element.state === 'delayed'"
|
||||||
|
name="time-outline"
|
||||||
|
[ngClass]="{ 'text-danger': element.stacktrace?.length > 0 }"
|
||||||
|
></ion-icon>
|
||||||
|
<ion-icon
|
||||||
|
*ngIf="element.state === 'failed'"
|
||||||
|
class="text-danger"
|
||||||
|
name="alert-circle-outline"
|
||||||
|
></ion-icon>
|
||||||
|
<ion-icon
|
||||||
|
*ngIf="element.state === 'paused'"
|
||||||
|
name="pause-outline"
|
||||||
|
></ion-icon>
|
||||||
|
<ion-icon
|
||||||
|
*ngIf="element.state === 'waiting'"
|
||||||
|
name="cafe-outline"
|
||||||
|
></ion-icon>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
|
||||||
|
<button
|
||||||
|
class="mx-1 no-min-width px-2"
|
||||||
|
mat-button
|
||||||
|
[matMenuTriggerFor]="jobsActionsMenu"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
>
|
||||||
|
<ion-icon name="ellipsis-vertical"></ion-icon>
|
||||||
|
</button>
|
||||||
|
<mat-menu #jobsActionsMenu="matMenu" xPosition="before">
|
||||||
|
<button mat-menu-item (click)="onDeleteJobs()">
|
||||||
|
<ng-container i18n>Delete Jobs</ng-container>
|
||||||
</button>
|
</button>
|
||||||
<mat-menu #jobsActionsMenu="matMenu" xPosition="before">
|
</mat-menu>
|
||||||
<button mat-menu-item (click)="onDeleteJobs()">
|
</th>
|
||||||
<ng-container i18n>Delete Jobs</ng-container>
|
<td *matCellDef="let element" class="px-1 py-2" mat-cell>
|
||||||
</button>
|
<button
|
||||||
</mat-menu>
|
class="mx-1 no-min-width px-2"
|
||||||
</th>
|
mat-button
|
||||||
</tr>
|
[matMenuTriggerFor]="jobActionsMenu"
|
||||||
</thead>
|
(click)="$event.stopPropagation()"
|
||||||
<tbody>
|
>
|
||||||
<ng-container *ngFor="let job of jobs">
|
<ion-icon name="ellipsis-horizontal"></ion-icon>
|
||||||
<tr class="mat-row">
|
</button>
|
||||||
<td class="mat-cell px-1 py-2 text-right">{{ job.id }}</td>
|
<mat-menu #jobActionsMenu="matMenu" xPosition="before">
|
||||||
<td class="mat-cell px-1 py-2">
|
<button mat-menu-item (click)="onViewData(element.data)">
|
||||||
<span class="align-items-center d-flex">
|
<ng-container i18n>View Data</ng-container>
|
||||||
<ion-icon
|
</button>
|
||||||
class="mr-1"
|
<button
|
||||||
name="arrow-down-circle-outline"
|
mat-menu-item
|
||||||
></ion-icon>
|
[disabled]="element.stacktrace?.length <= 0"
|
||||||
<ng-container *ngIf="job.name === 'GATHER_ASSET_PROFILE'">
|
(click)="onViewStacktrace(element.stacktrace)"
|
||||||
<span i18n>Asset Profile</span>
|
>
|
||||||
</ng-container>
|
<ng-container i18n>View Stacktrace</ng-container>
|
||||||
<ng-container
|
</button>
|
||||||
*ngIf="job.name === 'GATHER_HISTORICAL_MARKET_DATA'"
|
<button mat-menu-item (click)="onDeleteJob(element.id)">
|
||||||
>
|
<ng-container i18n>Delete Job</ng-container>
|
||||||
<span i18n>Historical Market Data</span>
|
</button>
|
||||||
</ng-container>
|
</mat-menu>
|
||||||
</span>
|
</td>
|
||||||
</td>
|
</ng-container>
|
||||||
<td class="mat-cell px-1 py-2">{{ job.data?.symbol }}</td>
|
|
||||||
<td class="mat-cell px-1 py-2">{{ job.data?.dataSource }}</td>
|
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
|
||||||
<td class="mat-cell px-1 py-2 text-right">
|
<tr *matRowDef="let row; columns: displayedColumns" mat-row></tr>
|
||||||
{{ job.attemptsMade }}
|
|
||||||
</td>
|
|
||||||
<td class="mat-cell px-1 py-2">
|
|
||||||
{{ job.timestamp | date: defaultDateTimeFormat }}
|
|
||||||
</td>
|
|
||||||
<td class="mat-cell px-1 py-2">
|
|
||||||
{{ job.finishedOn | date: defaultDateTimeFormat }}
|
|
||||||
</td>
|
|
||||||
<td class="mat-cell px-1 py-2">
|
|
||||||
<ion-icon
|
|
||||||
*ngIf="job.state === 'active'"
|
|
||||||
name="play-outline"
|
|
||||||
></ion-icon>
|
|
||||||
<ion-icon
|
|
||||||
*ngIf="job.state === 'completed'"
|
|
||||||
class="text-success"
|
|
||||||
name="checkmark-circle-outline"
|
|
||||||
></ion-icon>
|
|
||||||
<ion-icon
|
|
||||||
*ngIf="job.state === 'delayed'"
|
|
||||||
name="time-outline"
|
|
||||||
[ngClass]="{ 'text-danger': job.stacktrace?.length > 0 }"
|
|
||||||
></ion-icon>
|
|
||||||
<ion-icon
|
|
||||||
*ngIf="job.state === 'failed'"
|
|
||||||
class="text-danger"
|
|
||||||
name="alert-circle-outline"
|
|
||||||
></ion-icon>
|
|
||||||
<ion-icon
|
|
||||||
*ngIf="job.state === 'paused'"
|
|
||||||
name="pause-outline"
|
|
||||||
></ion-icon>
|
|
||||||
<ion-icon
|
|
||||||
*ngIf="job.state === 'waiting'"
|
|
||||||
name="cafe-outline"
|
|
||||||
></ion-icon>
|
|
||||||
</td>
|
|
||||||
<td class="mat-cell px-1 py-2">
|
|
||||||
<button
|
|
||||||
class="mx-1 no-min-width px-2"
|
|
||||||
mat-button
|
|
||||||
[matMenuTriggerFor]="jobActionsMenu"
|
|
||||||
(click)="$event.stopPropagation()"
|
|
||||||
>
|
|
||||||
<ion-icon name="ellipsis-horizontal"></ion-icon>
|
|
||||||
</button>
|
|
||||||
<mat-menu #jobActionsMenu="matMenu" xPosition="before">
|
|
||||||
<button mat-menu-item (click)="onViewData(job.data)">
|
|
||||||
<ng-container i18n>View Data</ng-container>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
mat-menu-item
|
|
||||||
[disabled]="job.stacktrace?.length <= 0"
|
|
||||||
(click)="onViewStacktrace(job.stacktrace)"
|
|
||||||
>
|
|
||||||
<ng-container i18n>View Stacktrace</ng-container>
|
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="onDeleteJob(job.id)">
|
|
||||||
<ng-container i18n>Delete Job</ng-container>
|
|
||||||
</button>
|
|
||||||
</mat-menu>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</ng-container>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,6 +4,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { MatTableModule } from '@angular/material/table';
|
||||||
|
|
||||||
import { AdminJobsComponent } from './admin-jobs.component';
|
import { AdminJobsComponent } from './admin-jobs.component';
|
||||||
|
|
||||||
@ -15,6 +16,7 @@ import { AdminJobsComponent } from './admin-jobs.component';
|
|||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatMenuModule,
|
MatMenuModule,
|
||||||
MatSelectModule,
|
MatSelectModule,
|
||||||
|
MatTableModule,
|
||||||
ReactiveFormsModule
|
ReactiveFormsModule
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user