Skip to content

Commit

Permalink
Added filtering on status to the requests page
Browse files Browse the repository at this point in the history
  • Loading branch information
Jamie Rees committed May 4, 2020
1 parent 1ea6a6d commit 4a8c1cd
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 78 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -248,3 +248,4 @@ _Pvt_Extensions
*.vscode
/src/Ombi/database.json
/src/Ombi/healthchecksdb
/src/Ombi/ClientApp/package-lock.json
15 changes: 7 additions & 8 deletions src/Ombi.Core/Engine/MovieRequestEngine.cs
Expand Up @@ -267,10 +267,10 @@ public async Task<RequestsViewModel<MovieRequests>> GetRequestsByStatus(int coun
allRequests = allRequests.Where(x => x.Approved && !x.Available && (!x.Denied.HasValue || !x.Denied.Value));
break;
case RequestStatus.Available:
allRequests = allRequests.Where(x => x.Available && (!x.Denied.HasValue || !x.Denied.Value));
allRequests = allRequests.Where(x => x.Available);
break;
case RequestStatus.Denied:
allRequests = allRequests.Where(x => x.Denied.HasValue && x.Denied.Value);
allRequests = allRequests.Where(x => x.Denied.HasValue && x.Denied.Value && !x.Available);
break;
default:
break;
Expand Down Expand Up @@ -332,12 +332,11 @@ public async Task<RequestsViewModel<MovieRequests>> GetUnavailableRequests(int c
//var secondProp = TypeDescriptor.GetProperties(propType).Find(properties[1], true);
}

allRequests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase)
? allRequests.OrderBy(x => prop.GetValue(x))
: allRequests.OrderByDescending(x => prop.GetValue(x));
var total = await allRequests.CountAsync();
var requests = await allRequests.Skip(position).Take(count)
.ToListAsync();
var requests = (sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase)
? allRequests.ToList().OrderBy(x => prop.GetValue(x))
: allRequests.ToList().OrderByDescending(x => prop.GetValue(x))).ToList();
var total = requests.Count();
requests = requests.Skip(position).Take(count).ToList();

await CheckForSubscription(shouldHide, requests);
return new RequestsViewModel<MovieRequests>
Expand Down
7 changes: 5 additions & 2 deletions src/Ombi.Store/Context/OmbiContext.cs
Expand Up @@ -93,7 +93,7 @@ public void Seed()
}

needToSave = true;
NotificationTemplates notificationToAdd;
NotificationTemplates notificationToAdd = null;
switch (notificationType)
{
case NotificationType.NewRequest:
Expand Down Expand Up @@ -207,7 +207,10 @@ public void Seed()
default:
throw new ArgumentOutOfRangeException();
}
NotificationTemplates.Add(notificationToAdd);
if (notificationToAdd != null)
{
NotificationTemplates.Add(notificationToAdd);
}
}
}

Expand Down
@@ -1,57 +1,73 @@
<div class="mat-elevation-z8">
<grid-spinner [loading]="isLoadingResults"></grid-spinner>

<mat-form-field>
<mat-select placeholder="Requests to Display" [(value)]="gridCount" (selectionChange)="ngAfterViewInit()">
<mat-option value="10">10</mat-option>
<mat-option value="15">15</mat-option>
<mat-option value="30">30</mat-option>
<mat-option value="100">100</mat-option>
</mat-select>
</mat-form-field>

<table mat-table [dataSource]="dataSource" class="table" matSort [matSortActive]="defaultSort"
matSortDisableClear [matSortDirection]="defaultOrder">


<ng-container matColumnDef="requestedUser.requestedBy">
<th mat-header-cell *matHeaderCellDef > Requested By </th>
<td mat-cell *matCellDef="let element"> {{element.requestedUser?.userAlias}} </td>
</ng-container>

<ng-container matColumnDef="title">
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> Title </th>
<td mat-cell *matCellDef="let element"> {{element.title}} ({{element.releaseDate | amLocal | amDateFormat:
'YYYY'}}) </td>
</ng-container>

<ng-container matColumnDef="requestedDate">
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> Request Date </th>
<td mat-cell *matCellDef="let element"> {{element.requestedDate | amLocal | amDateFormat: 'LL'}} </td>
</ng-container>

<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> Status </th>
<td mat-cell *matCellDef="let element"> {{element.status}} </td>
</ng-container>


<ng-container matColumnDef="requestStatus">
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> Request Status </th>
<td mat-cell *matCellDef="let element"> {{element.requestStatus | translate}} </td>
</ng-container>

<ng-container matColumnDef="actions" >
<th mat-header-cell *matHeaderCellDef> </th>
<td mat-cell *matCellDef="let element" >
<button mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">Details</button>
<button mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin">Options</button>
</td>
</ng-container>

<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

<mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator>
</div>

<!-- <div class="row"> -->
<div class="row justify-content-md-center top-spacing">
<div class="btn-group" role="group">
<button type="button" (click)="switchFilter(RequestFilter.All)" [attr.color]="currentFilter === RequestFilter.All ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.All ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.AllRequests' | translate}}</button>
<button type="button" (click)="switchFilter(RequestFilter.Pending)" [attr.color]="currentFilter === RequestFilter.Pending ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Pending ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.PendingRequests' | translate}}</button>
<button type="button" (click)="switchFilter(RequestFilter.Processing)" [attr.color]="currentFilter === RequestFilter.Processing ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Processing ? 'mat-accent' : 'mat-primary'" mat-raised-button
class="btn grow">{{'Requests.ProcessingRequests' | translate}}</button>
<button type="button" (click)="switchFilter(RequestFilter.Available)" [attr.color]="currentFilter === RequestFilter.Available ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Available ? 'mat-accent' : 'mat-primary'" mat-raised-button
class="btn grow">{{'Requests.AvailableRequests' | translate}}</button>
<button type="button" (click)="switchFilter(RequestFilter.Denied)" [attr.color]="currentFilter === RequestFilter.Denied ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Denied ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.DeniedRequests' | translate}}</button>
</div>
</div>

<div class="row">
<div class="col-md-2 offset-md-10">
<mat-form-field>
<mat-select placeholder="Requests to Display" [(value)]="gridCount" (selectionChange)="ngAfterViewInit()">
<mat-option value="10">10</mat-option>
<mat-option value="15">15</mat-option>
<mat-option value="30">30</mat-option>
<mat-option value="100">100</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<!-- </div> -->
<table mat-table [dataSource]="dataSource" class="table" matSort [matSortActive]="defaultSort" matSortDisableClear [matSortDirection]="defaultOrder">


<ng-container matColumnDef="requestedUser.requestedBy">
<th mat-header-cell *matHeaderCellDef> Requested By </th>
<td mat-cell *matCellDef="let element"> {{element.requestedUser?.userAlias}} </td>
</ng-container>

<ng-container matColumnDef="title">
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> Title </th>
<td mat-cell *matCellDef="let element"> {{element.title}} ({{element.releaseDate | amLocal | amDateFormat: 'YYYY'}}) </td>
</ng-container>

<ng-container matColumnDef="requestedDate">
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> Request Date </th>
<td mat-cell *matCellDef="let element"> {{element.requestedDate | amLocal | amDateFormat: 'LL'}} </td>
</ng-container>

<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> Status </th>
<td mat-cell *matCellDef="let element"> {{element.status}} </td>
</ng-container>


<ng-container matColumnDef="requestStatus">
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> Request Status </th>
<td mat-cell *matCellDef="let element"> {{element.requestStatus | translate}} </td>
</ng-container>

<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> </th>
<td mat-cell *matCellDef="let element">
<button mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">Details</button>
<button mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin">Options</button>
</td>
</ng-container>

<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

<mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator>
</div>
Expand Up @@ -7,6 +7,7 @@ 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";

@Component({
templateUrl: "./movies-grid.component.html",
Expand All @@ -19,14 +20,19 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
public isLoadingResults = true;
public displayedColumns: string[] = ['requestedUser.requestedBy', 'title', 'requestedDate', 'status', 'requestStatus', 'actions'];
public gridCount: string = "15";
public showUnavailableRequests: boolean;
public isAdmin: boolean;
public defaultSort: string = "requestedDate";
public defaultOrder: string = "desc";

public RequestFilter = RequestFilterType;

private currentFilter: RequestFilterType = RequestFilterType.All;

private storageKey = "Movie_DefaultRequestListSort";
private storageKeyOrder = "Movie_DefaultRequestListSortOrder";
private storageKeyGridCount = "Movie_DefaultGridCount";
private storageKeyCurrentFilter = "Movie_DefaultFilter";
private $data: Observable<any>;

@Output() public onOpenOptions = new EventEmitter<{ request: any, filter: any, onChange: any }>();

Expand All @@ -38,19 +44,25 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {

}

public ngOnInit() {
public ngOnInit() {
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");

const defaultCount = this.storageService.get(this.storageKeyGridCount);
const defaultSort = this.storageService.get(this.storageKey);
const defaultOrder = this.storageService.get(this.storageKeyOrder);
const defaultFilter = +this.storageService.get(this.storageKeyCurrentFilter);
if (defaultSort) {
this.defaultSort = defaultSort;
}
if (defaultOrder) {
this.defaultOrder = defaultOrder;
}
if(defaultCount) {
if (defaultCount) {
this.gridCount = defaultCount;
}
if (defaultFilter) {
this.currentFilter = defaultFilter;
}
}

public async ngAfterViewInit() {
Expand All @@ -60,13 +72,12 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
// this.resultsLength = results.total;

this.storageService.save(this.storageKeyGridCount, this.gridCount);

this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
this.storageService.save(this.storageKeyCurrentFilter, (+this.currentFilter).toString());

// If the user changes the sort order, reset back to the first page.
this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);

merge(this.sort.sortChange, this.paginator.page)
merge(this.sort.sortChange, this.paginator.page, this.currentFilter)
.pipe(
startWith({}),
switchMap((value: any) => {
Expand All @@ -92,11 +103,19 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
}

public loadData(): Observable<IRequestsViewModel<IMovieRequests>> {
if (this.showUnavailableRequests) {
return this.requestService.getMovieUnavailableRequests(+this.gridCount, this.paginator.pageIndex * +this.gridCount, this.sort.active, this.sort.direction);
} else {
return this.requestService.getMovieRequests(+this.gridCount, this.paginator.pageIndex * +this.gridCount, this.sort.active, this.sort.direction);
switch(RequestFilterType[RequestFilterType[this.currentFilter]]) {
case RequestFilterType.All:
return this.requestService.getMovieRequests(+this.gridCount, this.paginator.pageIndex * +this.gridCount, this.sort.active, this.sort.direction);
case RequestFilterType.Pending:
return this.requestService.getMoviePendingRequests(+this.gridCount, this.paginator.pageIndex * +this.gridCount, this.sort.active, this.sort.direction);
case RequestFilterType.Available:
return this.requestService.getMovieAvailableRequests(+this.gridCount, this.paginator.pageIndex * +this.gridCount, this.sort.active, this.sort.direction);
case RequestFilterType.Processing:
return this.requestService.getMovieProcessingRequests(+this.gridCount, this.paginator.pageIndex * +this.gridCount, this.sort.active, this.sort.direction);
case RequestFilterType.Denied:
return this.requestService.getMovieDeniedRequests(+this.gridCount, this.paginator.pageIndex * +this.gridCount, this.sort.active, this.sort.direction);
}

}

public openOptions(request: IMovieRequests) {
Expand All @@ -112,4 +131,9 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {

this.onOpenOptions.emit({ request: request, filter: filter, onChange: onChange });
}

public switchFilter(type: RequestFilterType) {
this.currentFilter = type;
this.ngAfterViewInit();
}
}
@@ -0,0 +1,7 @@
export enum RequestFilterType {
All,
Pending,
Processing,
Available,
Denied
}
16 changes: 16 additions & 0 deletions src/Ombi/ClientApp/src/app/services/requestV2.service.ts
Expand Up @@ -17,6 +17,22 @@ export class RequestServiceV2 extends ServiceHelpers {
return this.http.get<IRequestsViewModel<IMovieRequests>>(`${this.url}movie/${count}/${position}/${sortProperty}/${order}`, {headers: this.headers});
}

public getMovieAvailableRequests(count: number, position: number, sortProperty: string , order: string): Observable<IRequestsViewModel<IMovieRequests>> {
return this.http.get<IRequestsViewModel<IMovieRequests>>(`${this.url}movie/available/${count}/${position}/${sortProperty}/${order}`, {headers: this.headers});
}

public getMovieProcessingRequests(count: number, position: number, sortProperty: string , order: string): Observable<IRequestsViewModel<IMovieRequests>> {
return this.http.get<IRequestsViewModel<IMovieRequests>>(`${this.url}movie/processing/${count}/${position}/${sortProperty}/${order}`, {headers: this.headers});
}

public getMoviePendingRequests(count: number, position: number, sortProperty: string , order: string): Observable<IRequestsViewModel<IMovieRequests>> {
return this.http.get<IRequestsViewModel<IMovieRequests>>(`${this.url}movie/pending/${count}/${position}/${sortProperty}/${order}`, {headers: this.headers});
}

public getMovieDeniedRequests(count: number, position: number, sortProperty: string , order: string): Observable<IRequestsViewModel<IMovieRequests>> {
return this.http.get<IRequestsViewModel<IMovieRequests>>(`${this.url}movie/denied/${count}/${position}/${sortProperty}/${order}`, {headers: this.headers});
}

public getTvRequests(count: number, position: number, sortProperty: string , order: string): Observable<IRequestsViewModel<IChildRequests>> {
return this.http.get<IRequestsViewModel<IChildRequests>>(`${this.url}tv/${count}/${position}/${sortProperty}/${order}`, {headers: this.headers});
}
Expand Down
4 changes: 2 additions & 2 deletions src/Ombi/ClientApp/src/app/settings/ombi/ombi.component.html
Expand Up @@ -40,7 +40,7 @@
</div>
<div>
<mat-checkbox formControlName="ignoreCertificateErrors" matTooltip="Enable if you are having connectivity problems over SSL">
Ignore any certificate errors
Ignore any certificate errors (Please restart after changing)
</mat-checkbox>
</div>
<div>
Expand Down Expand Up @@ -71,4 +71,4 @@
</div>
</div>
</form>
</fieldset>
</fieldset>
3 changes: 2 additions & 1 deletion src/Ombi/Controllers/V2/RequestsController.cs
Expand Up @@ -35,8 +35,9 @@ public async Task<RequestsViewModel<MovieRequests>> GetRequests(int count, int p
{
return await _movieRequestEngine.GetRequests(count, position, sort, sortOrder);
}

[HttpGet("movie/availble/{count:int}/{position:int}/{sort}/{sortOrder}")]
[HttpGet("movie/available/{count:int}/{position:int}/{sort}/{sortOrder}")]
public async Task<RequestsViewModel<MovieRequests>> GetAvailableRequests(int count, int position, string sort, string sortOrder)
{
return await _movieRequestEngine.GetRequestsByStatus(count, position, sort, sortOrder, RequestStatus.Available);
Expand Down
7 changes: 6 additions & 1 deletion src/Ombi/wwwroot/translations/en.json
Expand Up @@ -153,7 +153,12 @@
"NextHours": "Another request will be added in {{time}} hours",
"NextMinutes": "Another request will be added in {{time}} minutes",
"NextMinute": "Another request will be added in {{time}} minute"
}
},
"AllRequests": "All Requests",
"PendingRequests": "Pending Requests",
"ProcessingRequests": "Processing Requests",
"AvailableRequests": "Available Requests",
"DeniedRequests": "Denied Requests"
},
"Issues": {
"Title": "Issues",
Expand Down

0 comments on commit 4a8c1cd

Please sign in to comment.