diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts index f0a2423e5..06a1cf0e4 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts @@ -12,7 +12,7 @@ import { NewIssueComponent } from "../shared/new-issue/new-issue.component"; import { MovieAdvancedOptionsComponent } from "./panels/movie-advanced-options/movie-advanced-options.component"; import { RequestServiceV2 } from "../../../services/requestV2.service"; import { RequestBehalfComponent } from "../shared/request-behalf/request-behalf.component"; -import { Observable, forkJoin } from "rxjs"; +import { forkJoin } from "rxjs"; @Component({ templateUrl: "./movie-details.component.html", diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html index 90a3e8784..4a8c17f7c 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html +++ b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html @@ -30,6 +30,20 @@ + + + + @@ -71,4 +85,11 @@
+ + + + + + {{ 'Requests.RequestsTitle' | translate}}
- \ No newline at end of file + + + + + + + \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts index 93cf37a64..c73084c2b 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts +++ b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts @@ -1,14 +1,18 @@ import { Component, AfterViewInit, ViewChild, EventEmitter, Output, ChangeDetectorRef, OnInit } from "@angular/core"; -import { IMovieRequests, IRequestsViewModel } from "../../../interfaces"; +import { IMovieRequests, IRequestEngineResult, IRequestsViewModel } from "../../../interfaces"; import { MatPaginator } from "@angular/material/paginator"; import { MatSort } from "@angular/material/sort"; -import { merge, Observable, of as observableOf } from 'rxjs'; +import { merge, Observable, of as observableOf, forkJoin } from 'rxjs'; import { catchError, map, startWith, switchMap } from 'rxjs/operators'; import { RequestServiceV2 } from "../../../services/requestV2.service"; import { AuthService } from "../../../auth/auth.service"; import { StorageService } from "../../../shared/storage/storage-service"; import { RequestFilterType } from "../../models/RequestFilterType"; +import { SelectionModel } from "@angular/cdk/collections"; +import { NotificationService, RequestService } from "../../../services"; +import { TranslateService } from "@ngx-translate/core"; +import { MatTableDataSource } from "@angular/material/table"; @Component({ templateUrl: "./movies-grid.component.html", @@ -16,7 +20,7 @@ import { RequestFilterType } from "../../models/RequestFilterType"; styleUrls: ["./movies-grid.component.scss"] }) export class MoviesGridComponent implements OnInit, AfterViewInit { - public dataSource: IMovieRequests[] = []; + public dataSource: MatTableDataSource; public resultsLength: number; public isLoadingResults = true; public displayedColumns: string[] = ['title', 'requestedUser.requestedBy', 'status', 'requestStatus','requestedDate', 'actions']; @@ -25,6 +29,7 @@ export class MoviesGridComponent implements OnInit, AfterViewInit { public defaultSort: string = "requestedDate"; public defaultOrder: string = "desc"; public currentFilter: RequestFilterType = RequestFilterType.All; + public selection = new SelectionModel(true, []); public RequestFilter = RequestFilterType; @@ -40,13 +45,17 @@ export class MoviesGridComponent implements OnInit, AfterViewInit { @ViewChild(MatSort) sort: MatSort; constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef, - private auth: AuthService, private storageService: StorageService) { + private auth: AuthService, private storageService: StorageService, + private requestServiceV1: RequestService, private notification: NotificationService, + private translateService: TranslateService) { } - - public ngOnInit() { - this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + public ngOnInit() { + this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + if (this.isAdmin) { + this.displayedColumns.unshift('select'); + } const defaultCount = this.storageService.get(this.storageKeyGridCount); const defaultSort = this.storageService.get(this.storageKey); const defaultOrder = this.storageService.get(this.storageKeyOrder); @@ -96,7 +105,7 @@ export class MoviesGridComponent implements OnInit, AfterViewInit { this.isLoadingResults = false; return observableOf([]); }) - ).subscribe(data => this.dataSource = data); + ).subscribe(data => this.dataSource = new MatTableDataSource(data)); } public loadData(): Observable> { @@ -117,9 +126,9 @@ export class MoviesGridComponent implements OnInit, AfterViewInit { public openOptions(request: IMovieRequests) { const filter = () => { - this.dataSource = this.dataSource.filter((req) => { - return req.id !== request.id; - }) + this.dataSource.data = this.dataSource.data.filter((req) => { + return req.id !== request.id; + }); }; const onChange = () => { @@ -133,4 +142,56 @@ export class MoviesGridComponent implements OnInit, AfterViewInit { this.currentFilter = type; 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.removeMovieRequestAsync(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>(); + this.selection.selected.forEach((selected) => { + tasks.push(this.requestServiceV1.approveMovie({ 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(); + }) + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.scss b/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.scss index 1c6a9f617..78fd3f91f 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.scss +++ b/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.scss @@ -62,4 +62,11 @@ ::ng-deep table.requests button{ margin:5px; -} \ No newline at end of file +} + +::ng-deep .floating-fab { + position: fixed; + right: 3%; + bottom: 6%; + z-index: 10; +} diff --git a/src/Ombi/ClientApp/src/styles/shared.scss b/src/Ombi/ClientApp/src/styles/shared.scss index 2dd922173..7184d96fb 100644 --- a/src/Ombi/ClientApp/src/styles/shared.scss +++ b/src/Ombi/ClientApp/src/styles/shared.scss @@ -137,4 +137,4 @@ table { ::ng-deep .mat-form-field.mat-focused .mat-form-field-label { color: $accent; -} +} \ No newline at end of file diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index c75a74a99..0efb3fe88 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -180,7 +180,9 @@ "RequestPanel": { "Delete":"Delete Request", "Approve":"Approve Request", - "ChangeAvailability":"Mark Available" + "ChangeAvailability":"Mark Available", + "Deleted": "Successfully deleted selected items", + "Approved": "Successfully approved selected items" } }, "Issues": {