Included row bulk selection from Movie Request List into TV request list

This commit is contained in:
ThePheonixGuy 2021-07-28 12:31:31 +02:00
commit 5e737194ca
2 changed files with 95 additions and 12 deletions

View file

@ -30,6 +30,20 @@
<table mat-table [dataSource]="dataSource" class="requests table" matSort [matSortActive]="defaultSort" matSortDisableClear [matSortDirection]="defaultOrder"> <table mat-table [dataSource]="dataSource" class="requests table" matSort [matSortActive]="defaultSort" matSortDisableClear [matSortDirection]="defaultOrder">
<ng-container matColumnDef="select" *ngIf="isAdmin">
<th mat-header-cell *matHeaderCellDef>
<mat-checkbox id="adminMasterCheckbox" (change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()">
</mat-checkbox>
</th>
<td mat-cell *matCellDef="let row">
<mat-checkbox id="adminMasterCheckbox{{row.id}}" (click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null"
[checked]="selection.isSelected(row)">
</mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="series"> <ng-container matColumnDef="series">
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestsTitle' | translate}} </th> <th mat-header-cell *matHeaderCellDef> {{'Requests.RequestsTitle' | translate}} </th>
@ -78,4 +92,11 @@
</table> </table>
<mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator> <mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator>
</div> </div>
<button id="bulkFab" *ngIf="selection.hasValue() && isAdmin" mat-fab color="accent" class="floating-fab" [matMenuTriggerFor]="aboveMenu">
<i class="fas fa-bars"></i></button>
<mat-menu #aboveMenu="matMenu" yPosition="above">
<button id="deleteFabButton" mat-menu-item (click)="bulkDelete()">{{'Requests.RequestPanel.Delete' | translate}}</button>
<button id="approveFabButton" mat-menu-item (click)="bulkApprove()">{{'Requests.RequestPanel.Approve' | translate}}</button>
</mat-menu>

View file

@ -1,14 +1,19 @@
import { Component, AfterViewInit, ViewChild, Output, EventEmitter, ChangeDetectorRef, OnInit } from "@angular/core"; import { Component, AfterViewInit, ViewChild, Output, EventEmitter, ChangeDetectorRef, OnInit } from "@angular/core";
import { IRequestsViewModel, IChildRequests } from "../../../interfaces"; import {IRequestsViewModel, IChildRequests, IMovieRequests, IRequestEngineResult} from "../../../interfaces";
import { MatPaginator } from "@angular/material/paginator"; import { MatPaginator } from "@angular/material/paginator";
import { MatSort } from "@angular/material/sort"; import { MatSort } from "@angular/material/sort";
import { merge, of as observableOf, Observable } from 'rxjs'; import { MatTableDataSource } from "@angular/material/table";
import { SelectionModel } from "@angular/cdk/collections";
import {merge, of as observableOf, Observable, forkJoin} from 'rxjs';
import { catchError, map, startWith, switchMap } from 'rxjs/operators'; import { catchError, map, startWith, switchMap } from 'rxjs/operators';
import { RequestServiceV2 } from "../../../services/requestV2.service"; import { RequestServiceV2 } from "../../../services/requestV2.service";
import { AuthService } from "../../../auth/auth.service"; import { AuthService } from "../../../auth/auth.service";
import { StorageService } from "../../../shared/storage/storage-service"; import { StorageService } from "../../../shared/storage/storage-service";
import { RequestFilterType } from "../../models/RequestFilterType"; import { RequestFilterType } from "../../models/RequestFilterType";
import {NotificationService, RequestService} from "../../../services";
import {TranslateService} from "@ngx-translate/core";
@Component({ @Component({
templateUrl: "./tv-grid.component.html", templateUrl: "./tv-grid.component.html",
@ -16,7 +21,7 @@ import { RequestFilterType } from "../../models/RequestFilterType";
styleUrls: ["../requests-list.component.scss"] styleUrls: ["../requests-list.component.scss"]
}) })
export class TvGridComponent implements OnInit, AfterViewInit { export class TvGridComponent implements OnInit, AfterViewInit {
public dataSource: IChildRequests[] = []; public dataSource: MatTableDataSource<IChildRequests>;
public resultsLength: number; public resultsLength: number;
public isLoadingResults = true; public isLoadingResults = true;
public displayedColumns: string[] = ['series', 'requestedBy', 'status', 'requestStatus', 'requestedDate','actions']; public displayedColumns: string[] = ['series', 'requestedBy', 'status', 'requestStatus', 'requestedDate','actions'];
@ -25,6 +30,8 @@ export class TvGridComponent implements OnInit, AfterViewInit {
public defaultSort: string = "requestedDate"; public defaultSort: string = "requestedDate";
public defaultOrder: string = "desc"; public defaultOrder: string = "desc";
public currentFilter: RequestFilterType = RequestFilterType.All; public currentFilter: RequestFilterType = RequestFilterType.All;
public selection = new SelectionModel<IChildRequests>(true, []);
public RequestFilter = RequestFilterType; public RequestFilter = RequestFilterType;
public manageOwnRequests: boolean; public manageOwnRequests: boolean;
@ -40,12 +47,15 @@ export class TvGridComponent implements OnInit, AfterViewInit {
@ViewChild(MatSort) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
constructor(private requestService: RequestServiceV2, private auth: AuthService, constructor(private requestService: RequestServiceV2, private auth: AuthService,
private ref: ChangeDetectorRef, private storageService: StorageService) { private ref: ChangeDetectorRef, private storageService: StorageService,
private notification: NotificationService,
private translateService: TranslateService,
private requestServiceV1: RequestService) {
} }
public ngOnInit() { public ngOnInit() {
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
const defaultCount = this.storageService.get(this.storageKeyGridCount); const defaultCount = this.storageService.get(this.storageKeyGridCount);
const defaultSort = this.storageService.get(this.storageKey); const defaultSort = this.storageService.get(this.storageKey);
const defaultOrder = this.storageService.get(this.storageKeyOrder); const defaultOrder = this.storageService.get(this.storageKeyOrder);
@ -66,7 +76,7 @@ export class TvGridComponent implements OnInit, AfterViewInit {
public async ngAfterViewInit() { public async ngAfterViewInit() {
this.storageService.save(this.storageKeyGridCount, this.gridCount); this.storageService.save(this.storageKeyGridCount, this.gridCount);
this.storageService.save(this.storageKeyCurrentFilter, (+this.currentFilter).toString()); this.storageService.save(this.storageKeyCurrentFilter, (+this.currentFilter).toString());
this.paginator.showFirstLastButtons = true; this.paginator.showFirstLastButtons = true;
@ -78,7 +88,7 @@ export class TvGridComponent implements OnInit, AfterViewInit {
startWith({}), startWith({}),
switchMap((value: any) => { switchMap((value: any) => {
this.isLoadingResults = true; this.isLoadingResults = true;
if (value.active || value.direction) { if (value.active || value.direction) {
this.storageService.save(this.storageKey, value.active); this.storageService.save(this.storageKey, value.active);
this.storageService.save(this.storageKeyOrder, value.direction); this.storageService.save(this.storageKeyOrder, value.direction);
@ -96,14 +106,14 @@ export class TvGridComponent implements OnInit, AfterViewInit {
this.isLoadingResults = false; this.isLoadingResults = false;
return observableOf([]); return observableOf([]);
}) })
).subscribe(data => this.dataSource = data); ).subscribe(data => this.dataSource = new MatTableDataSource(data));
} }
public openOptions(request: IChildRequests) { public openOptions(request: IChildRequests) {
const filter = () => { this.dataSource = this.dataSource.filter((req) => { const filter = () => { this.dataSource.data = this.dataSource.data.filter((req) => {
return req.id !== request.id; return req.id !== request.id;
})}; })};
const onChange = () => { const onChange = () => {
this.ref.detectChanges(); this.ref.detectChanges();
}; };
@ -131,4 +141,56 @@ export class TvGridComponent implements OnInit, AfterViewInit {
this.currentFilter = type; this.currentFilter = type;
this.ngAfterViewInit(); this.ngAfterViewInit();
} }
public isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
public masterToggle() {
this.isAllSelected() ?
this.selection.clear() :
this.dataSource.data.forEach(row => this.selection.select(row));
}
public async bulkDelete() {
if (this.selection.isEmpty()) {
return;
}
let tasks = new Array();
this.selection.selected.forEach((selected) => {
tasks.push(this.requestServiceV1.deleteChild(selected.id));
});
await Promise.all(tasks);
this.notification.success(this.translateService.instant('Requests.RequestPanel.Deleted'))
this.selection.clear();
this.ngAfterViewInit();
}
public bulkApprove() {
if (this.selection.isEmpty()) {
return;
}
let tasks = new Array<Observable<IRequestEngineResult>>();
this.selection.selected.forEach((selected) => {
tasks.push(this.requestServiceV1.approveChild({ id: selected.id }));
});
this.isLoadingResults = true;
forkJoin(tasks).subscribe((result: IRequestEngineResult[]) => {
this.isLoadingResults = false;
const failed = result.filter(x => !x.result);
if(failed.length > 0) {
this.notification.error("Some requests failed to approve: " + failed[0].errorMessage);
this.selection.clear();
return;
}
this.notification.success(this.translateService.instant('Requests.RequestPanel.Approved'));
this.selection.clear();
this.ngAfterViewInit();
})
}
} }