diff --git a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs index c1ba76a7c..91cbb9e72 100644 --- a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs @@ -17,7 +17,6 @@ namespace Ombi.Core.Engine.Interfaces Task ApproveMovie(MovieRequests request); Task ApproveMovieById(int requestId); Task DenyMovieById(int modelId); - Task> Filter(FilterViewModel vm); } diff --git a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs index 553ad79dd..740428ec7 100644 --- a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Ombi.Core.Models.Requests; +using Ombi.Core.Models.UI; using Ombi.Store.Entities; namespace Ombi.Core.Engine.Interfaces @@ -12,7 +13,7 @@ namespace Ombi.Core.Engine.Interfaces //Task> GetNewRequests(); //Task> GetAvailableRequests(); RequestCountModel RequestCount(); - Task> GetRequests(int count, int position); + Task> GetRequests(int count, int position, OrderFilterModel model); Task> GetRequests(); Task UserHasRequest(string userId); diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index 460752bb1..15383ed12 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -13,6 +13,7 @@ using Microsoft.Extensions.Logging; using Ombi.Api.TheMovieDb.Models; using Ombi.Core.Authentication; using Ombi.Core.Engine.Interfaces; +using Ombi.Core.Models.UI; using Ombi.Core.Rule.Interfaces; using Ombi.Core.Settings; using Ombi.Settings.Settings.Models; @@ -25,7 +26,8 @@ namespace Ombi.Core.Engine { public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user, INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger log, - OmbiUserManager manager, IRepository rl, ICacheService cache, ISettingsService ombiSettings, IRepository sub) + OmbiUserManager manager, IRepository rl, ICacheService cache, + ISettingsService ombiSettings, IRepository sub) : base(user, requestService, r, manager, cache, ombiSettings, sub) { MovieApi = movieApi; @@ -58,6 +60,7 @@ namespace Ombi.Core.Engine ErrorMessage = $"Please try again later" }; } + var fullMovieName = $"{movieInfo.Title}{(!string.IsNullOrEmpty(movieInfo.ReleaseDate) ? $" ({DateTime.Parse(movieInfo.ReleaseDate).Year})" : string.Empty)}"; @@ -82,7 +85,8 @@ namespace Ombi.Core.Engine }; var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US"); - requestModel.DigitalReleaseDate = usDates?.ReleaseDate?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate; + requestModel.DigitalReleaseDate = usDates?.ReleaseDate + ?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate; var ruleResults = (await RunRequestRules(requestModel)).ToList(); if (ruleResults.Any(x => !x.Success)) @@ -125,25 +129,93 @@ namespace Ombi.Core.Engine /// /// The count. /// The position. + /// The order/filter type. /// - public async Task> GetRequests(int count, int position) + public async Task> GetRequests(int count, int position, + OrderFilterModel orderFilter) { var shouldHide = await HideFromOtherUsers(); - List allRequests; + IQueryable allRequests; if (shouldHide.Hide) { - allRequests = await MovieRepository.GetWithUser(shouldHide.UserId).Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); + allRequests = + MovieRepository.GetWithUser(shouldHide + .UserId); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); } else { - allRequests = await MovieRepository.GetWithUser().Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); + allRequests = + MovieRepository + .GetWithUser(); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); } - allRequests.ForEach(async x => + + switch (orderFilter.AvailabilityFilter) + { + case FilterType.None: + break; + case FilterType.Available: + allRequests = allRequests.Where(x => x.Available); + break; + case FilterType.NotAvailable: + allRequests = allRequests.Where(x => !x.Available); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + switch (orderFilter.StatusFilter) + { + case FilterType.None: + break; + case FilterType.Approved: + allRequests = allRequests.Where(x => x.Approved); + break; + case FilterType.Processing: + allRequests = allRequests.Where(x => x.Approved && !x.Available); + break; + case FilterType.PendingApproval: + allRequests = allRequests.Where(x => !x.Approved && !x.Available && !(x.Denied ?? false)); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + var total = allRequests.Count(); + + var requests = await (OrderMovies(allRequests, orderFilter.OrderType)).Skip(position).Take(count) + .ToListAsync(); + + requests.ForEach(async x => { x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath); await CheckForSubscription(shouldHide, x); }); - return allRequests; + return new RequestsViewModel + { + Collection = requests, + Total = total + }; + } + + private IQueryable OrderMovies(IQueryable allRequests, OrderType type) + { + switch (type) + { + case OrderType.RequestedDateAsc: + return allRequests.OrderBy(x => x.RequestedDate); + case OrderType.RequestedDateDesc: + return allRequests.OrderByDescending(x => x.RequestedDate); + case OrderType.TitleAsc: + return allRequests.OrderBy(x => x.Title); + case OrderType.TitleDesc: + return allRequests.OrderByDescending(x => x.Title); + case OrderType.StatusAsc: + return allRequests.OrderBy(x => x.Status); + case OrderType.StatusDesc: + return allRequests.OrderByDescending(x => x.Status); + default: + throw new ArgumentOutOfRangeException(nameof(type), type, null); + } } public async Task GetTotal() @@ -216,6 +288,7 @@ namespace Ombi.Core.Engine { allRequests = await MovieRepository.GetWithUser().ToListAsync(); } + var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList(); results.ForEach(async x => { @@ -241,6 +314,7 @@ namespace Ombi.Core.Engine ErrorMessage = "Request does not exist" }; } + request.Denied = true; // We are denying a request NotificationHelper.Notify(request, NotificationType.RequestDeclined); @@ -261,6 +335,7 @@ namespace Ombi.Core.Engine ErrorMessage = "Request does not exist" }; } + request.Approved = true; request.Denied = false; await MovieRepository.Update(request); @@ -281,6 +356,7 @@ namespace Ombi.Core.Engine Result = true }; } + if (!result.Success) { Logger.LogWarning("Tried auto sending movie but failed. Message: {0}", result.Message); @@ -291,6 +367,7 @@ namespace Ombi.Core.Engine Result = false }; } + // If there are no providers then it's successful but movie has not been sent } @@ -352,6 +429,7 @@ namespace Ombi.Core.Engine ErrorMessage = "Request does not exist" }; } + request.Available = false; await MovieRepository.Update(request); @@ -372,6 +450,7 @@ namespace Ombi.Core.Engine ErrorMessage = "Request does not exist" }; } + request.Available = true; NotificationHelper.Notify(request, NotificationType.RequestAvailable); await MovieRepository.Update(request); @@ -401,55 +480,7 @@ namespace Ombi.Core.Engine RequestType = RequestType.Movie, }); - return new RequestEngineResult { Result = true, Message = $"{movieName} has been successfully added!" }; - } - - public async Task> Filter(FilterViewModel vm) - { - var shouldHide = await HideFromOtherUsers(); - var requests = shouldHide.Hide - ? MovieRepository.GetWithUser(shouldHide.UserId) - : MovieRepository.GetWithUser(); - - switch (vm.AvailabilityFilter) - { - case FilterType.None: - break; - case FilterType.Available: - requests = requests.Where(x => x.Available); - break; - case FilterType.NotAvailable: - requests = requests.Where(x => !x.Available); - break; - default: - throw new ArgumentOutOfRangeException(); - } - - switch (vm.StatusFilter) - { - case FilterType.None: - break; - case FilterType.Approved: - requests = requests.Where(x => x.Approved); - break; - case FilterType.Processing: - requests = requests.Where(x => x.Approved && !x.Available); - break; - case FilterType.PendingApproval: - requests = requests.Where(x => !x.Approved && !x.Available && !(x.Denied ?? false)); - break; - default: - throw new ArgumentOutOfRangeException(); - } - - var count = await requests.CountAsync(); - requests = requests.Skip(vm.Position).Take(vm.Count); - var retVal = new FilterResult - { - Total = count, - Collection = requests - }; - return retVal; + return new RequestEngineResult {Result = true, Message = $"{movieName} has been successfully added!"}; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index ddf84376e..8bb6d86e6 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -15,6 +15,7 @@ using Microsoft.EntityFrameworkCore; using Ombi.Core.Authentication; using Ombi.Core.Engine.Interfaces; using Ombi.Core.Helpers; +using Ombi.Core.Models.UI; using Ombi.Core.Rule; using Ombi.Core.Rule.Interfaces; using Ombi.Core.Senders; @@ -132,7 +133,7 @@ namespace Ombi.Core.Engine return await AddRequest(newRequest.NewRequest); } - public async Task> GetRequests(int count, int position) + public async Task> GetRequests(int count, int position, OrderFilterModel type) { var shouldHide = await HideFromOtherUsers(); List allRequests; @@ -161,7 +162,10 @@ namespace Ombi.Core.Engine allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); - return allRequests; + return new RequestsViewModel + { + Collection = allRequests + }; } public async Task>>> GetRequestsTreeNode(int count, int position) diff --git a/src/Ombi.Core/Models/UI/OrderFilterModel.cs b/src/Ombi.Core/Models/UI/OrderFilterModel.cs new file mode 100644 index 000000000..3e3192829 --- /dev/null +++ b/src/Ombi.Core/Models/UI/OrderFilterModel.cs @@ -0,0 +1,11 @@ +using Ombi.Core.Models.Requests; + +namespace Ombi.Core.Models.UI +{ + public class OrderFilterModel + { + public FilterType AvailabilityFilter { get; set; } + public FilterType StatusFilter { get; set; } + public OrderType OrderType { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/UI/OrderType.cs b/src/Ombi.Core/Models/UI/OrderType.cs new file mode 100644 index 000000000..67d20b127 --- /dev/null +++ b/src/Ombi.Core/Models/UI/OrderType.cs @@ -0,0 +1,12 @@ +namespace Ombi.Core.Models.UI +{ + public enum OrderType + { + RequestedDateAsc =1, + RequestedDateDesc =2, + TitleAsc =3, + TitleDesc=4, + StatusAsc=5, + StatusDesc=6 + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/UI/RequestsViewModel.cs b/src/Ombi.Core/Models/UI/RequestsViewModel.cs new file mode 100644 index 000000000..9d772bc53 --- /dev/null +++ b/src/Ombi.Core/Models/UI/RequestsViewModel.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Ombi.Core.Models.UI +{ + public class RequestsViewModel + { + public IEnumerable Collection { get; set; } + public int Total { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts b/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts index 1895914c3..236e694fc 100644 --- a/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts +++ b/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts @@ -20,7 +20,7 @@ export interface IMovieRequests extends IFullBaseRequest { qualityOverrideTitle: string; } -export interface IFilterResult { +export interface IRequestsViewModel { total: number; collection: T[]; } @@ -87,6 +87,15 @@ export interface ITvUpdateModel { id: number; } +export enum OrderType { + RequestedDateAsc = 1, + RequestedDateDesc = 2, + TitleAsc = 3, + TitleDesc = 4, + StatusAsc = 5, + StatusDesc = 6, +} + export interface INewSeasonRequests { id: number; seasonNumber: number; @@ -112,8 +121,6 @@ export interface IMovieRequestModel { export interface IFilter { availabilityFilter: FilterType; statusFilter: FilterType; - position: number; - count: number; } export enum FilterType { diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.html b/src/Ombi/ClientApp/app/requests/movierequests.component.html index b2ea68db2..241e0af6b 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.html +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.html @@ -1,57 +1,45 @@ - + +
@@ -59,9 +47,7 @@
-
- - +
@@ -99,7 +85,7 @@ + [translate]="'Common.PendingApproval'">
@@ -125,11 +111,15 @@
-
+
@@ -171,7 +161,7 @@
- +
- @@ -198,7 +190,7 @@
- +
diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.ts b/src/Ombi/ClientApp/app/requests/movierequests.component.ts index eaab1f4f8..0848daf53 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.ts @@ -9,7 +9,7 @@ import { Subject } from "rxjs/Subject"; import { AuthService } from "../auth/auth.service"; import { NotificationService, RadarrService, RequestService } from "../services"; -import { FilterType, IFilter, IIssueCategory, IMovieRequests, IPagenator, IRadarrProfile, IRadarrRootFolder } from "../interfaces"; +import { FilterType, IFilter, IIssueCategory, IMovieRequests, IPagenator, IRadarrProfile, IRadarrRootFolder, OrderType } from "../interfaces"; @Component({ selector: "movie-requests", @@ -38,9 +38,9 @@ export class MovieRequestsComponent implements OnInit { public filter: IFilter; public filterType = FilterType; - public order: string = "requestedDate"; - public reverse = true; - + public orderType: OrderType = OrderType.RequestedDateDesc; + public OrderType = OrderType; + public totalMovies: number = 100; private currentlyLoaded: number; private amountToLoad: number; @@ -75,20 +75,17 @@ export class MovieRequestsComponent implements OnInit { public ngOnInit() { this.amountToLoad = 10; - this.currentlyLoaded = 10; - this.loadInit(); - this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + this.currentlyLoaded = 10; this.filter = { availabilityFilter: FilterType.None, statusFilter: FilterType.None, - count: this.amountToLoad, - position: 0, }; + this.loadInit(); + this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); } public paginate(event: IPagenator) { - const skipAmount = event.first; - + const skipAmount = event.first; this.loadRequests(this.amountToLoad, skipAmount); } @@ -189,27 +186,21 @@ export class MovieRequestsComponent implements OnInit { public filterStatus(filter: FilterType, el: any) { this.filterActiveStyle(el); this.filter.statusFilter = filter; - this.requestService.filterMovies(this.filter) - .subscribe(x => { - this.totalMovies = x.total; - this.setOverrides(x.collection); - this.movieRequests = x.collection; - }); + this.loadInit(); } - public setOrder(value: string, el: any) { + public setOrder(value: OrderType, el: any) { el = el.toElement || el.relatedTarget || el.target || el.srcElement; const parent = el.parentElement; const previousFilter = parent.querySelector(".active"); - if (this.order === value) { - this.reverse = !this.reverse; - } else { - previousFilter.className = ""; - el.className = "active"; - } - this.order = value; + previousFilter.className = ""; + el.className = "active"; + + this.orderType = value; + + this.loadInit(); } public subscribe(request: IMovieRequests) { @@ -247,26 +238,16 @@ export class MovieRequestsComponent implements OnInit { } private loadRequests(amountToLoad: number, currentlyLoaded: number) { - if(this.filter.availabilityFilter === FilterType.None && this.filter.statusFilter === FilterType.None) { - this.requestService.getMovieRequests(amountToLoad, currentlyLoaded + 1) + this.requestService.getMovieRequests(amountToLoad, currentlyLoaded, this.orderType, this.filter) .subscribe(x => { - this.setOverrides(x); + this.setOverrides(x.collection); if(!this.movieRequests) { this.movieRequests = []; } - this.movieRequests = x; + this.movieRequests = x.collection; + this.totalMovies = x.total; this.currentlyLoaded = currentlyLoaded + amountToLoad; }); - } else { - this.filter.position = currentlyLoaded; - this.requestService.filterMovies(this.filter) - .subscribe(x => { - this.setOverrides(x.collection); - this.totalMovies = x.total; - this.movieRequests = x.collection; - this.currentlyLoaded = currentlyLoaded + amountToLoad; - }); - } } private updateRequest(request: IMovieRequests) { @@ -305,23 +286,25 @@ export class MovieRequestsComponent implements OnInit { } private loadInit() { - this.requestService.getTotalMovies().subscribe(x => this.totalMovies = x); - this.requestService.getMovieRequests(this.amountToLoad, 0) + this.requestService.getMovieRequests(this.amountToLoad, 0, this.orderType, this.filter) .subscribe(x => { - this.movieRequests = x; + this.movieRequests = x.collection; + this.totalMovies = x.total; this.movieRequests.forEach((req) => { - this.setBackground(req); - this.setPoster(req); - }); - this.radarrService.getQualityProfilesFromSettings().subscribe(c => { - this.radarrProfiles = c; - this.movieRequests.forEach((req) => this.setQualityOverrides(req)); - }); - this.radarrService.getRootFoldersFromSettings().subscribe(c => { - this.radarrRootFolders = c; - this.movieRequests.forEach((req) => this.setRootFolderOverrides(req)); + this.setBackground(req); + this.setPoster(req); }); + if (this.isAdmin) { + this.radarrService.getQualityProfilesFromSettings().subscribe(c => { + this.radarrProfiles = c; + this.movieRequests.forEach((req) => this.setQualityOverrides(req)); + }); + this.radarrService.getRootFoldersFromSettings().subscribe(c => { + this.radarrRootFolders = c; + this.movieRequests.forEach((req) => this.setRootFolderOverrides(req)); + }); + } }); } diff --git a/src/Ombi/ClientApp/app/services/request.service.ts b/src/Ombi/ClientApp/app/services/request.service.ts index 2da0c1a8a..e52ab3057 100644 --- a/src/Ombi/ClientApp/app/services/request.service.ts +++ b/src/Ombi/ClientApp/app/services/request.service.ts @@ -6,7 +6,7 @@ import { Observable } from "rxjs/Rx"; import { TreeNode } from "primeng/primeng"; import { IRequestEngineResult } from "../interfaces"; -import { IChildRequests, IFilter, IFilterResult, IMovieRequestModel, IMovieRequests, IMovieUpdateModel, ITvRequests,ITvUpdateModel } from "../interfaces"; +import { IChildRequests, IFilter, IMovieRequestModel, IMovieRequests, IMovieUpdateModel, IRequestsViewModel, ITvRequests,ITvUpdateModel, OrderType } from "../interfaces"; import { ITvRequestViewModel } from "../interfaces"; import { ServiceHelpers } from "./service.helpers"; @@ -48,8 +48,8 @@ export class RequestService extends ServiceHelpers { return this.http.post(`${this.url}Movie/unavailable`, JSON.stringify(movie), {headers: this.headers}); } - public getMovieRequests(count: number, position: number): Observable { - return this.http.get(`${this.url}movie/${count}/${position}`, {headers: this.headers}); + public getMovieRequests(count: number, position: number, order: OrderType, filter: IFilter): Observable> { + return this.http.get>(`${this.url}movie/${count}/${position}/${order}/${filter.statusFilter}/${filter.availabilityFilter}`, {headers: this.headers}); } public searchMovieRequests(search: string): Observable { @@ -114,10 +114,7 @@ export class RequestService extends ServiceHelpers { public deleteChild(child: IChildRequests): Observable { return this.http.delete(`${this.url}tv/child/${child.id}`, {headers: this.headers}); } - public filterMovies(filter: IFilter): Observable> { - return this.http.post>(`${this.url}movie/filter`, JSON.stringify(filter), {headers: this.headers}); - } - + public subscribeToMovie(requestId: number): Observable { return this.http.post(`${this.url}movie/subscribe/${requestId}`, {headers: this.headers}); } diff --git a/src/Ombi/Controllers/RequestController.cs b/src/Ombi/Controllers/RequestController.cs index 0a56c32b6..47329a0ec 100644 --- a/src/Ombi/Controllers/RequestController.cs +++ b/src/Ombi/Controllers/RequestController.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Ombi.Store.Entities.Requests; using System.Diagnostics; +using Ombi.Core.Models.UI; using Ombi.Models; using Ombi.Store.Entities; @@ -32,10 +33,18 @@ namespace Ombi.Controllers /// /// The count of items you want to return. /// The position. - [HttpGet("movie/{count:int}/{position:int}")] - public async Task> GetRequests(int count, int position) + /// The way we want to order. + /// + /// + [HttpGet("movie/{count:int}/{position:int}/{orderType:int}/{statusType:int}/{availabilityType:int}")] + public async Task> GetRequests(int count, int position, int orderType, int statusType, int availabilityType) { - return await MovieRequestEngine.GetRequests(count, position); + return await MovieRequestEngine.GetRequests(count, position, new OrderFilterModel + { + OrderType = (OrderType)orderType, + AvailabilityFilter = (FilterType)availabilityType, + StatusFilter = (FilterType)statusType, + }); } /// @@ -170,11 +179,19 @@ namespace Ombi.Controllers /// /// The count of items you want to return. /// The position. + /// + /// + /// /// - [HttpGet("tv/{count:int}/{position:int}")] - public async Task> GetTvRequests(int count, int position) + [HttpGet("tv/{count:int}/{position:int}/{orderType:int}/{statusFilterType:int}/{availabilityFilterType:int}")] + public async Task> GetTvRequests(int count, int position, int orderType, int statusType, int availabilityType) { - return await TvRequestEngine.GetRequests(count, position); + return await TvRequestEngine.GetRequests(count, position, new OrderFilterModel + { + OrderType = (OrderType)orderType, + AvailabilityFilter = (FilterType) availabilityType, + StatusFilter = (FilterType) statusType, + }); } /// @@ -347,17 +364,6 @@ namespace Ombi.Controllers return movies || tv; } - /// - /// Returns a filtered list - /// - /// - /// - [HttpPost("movie/filter")] - public async Task> Filter([FromBody] FilterViewModel vm) - { - return await MovieRequestEngine.Filter(vm); - } - /// /// Subscribes for notifications to a movie request /// diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index cb7c0fe8b..289db8be5 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -138,10 +138,12 @@ "Filter":"Filter", "Sort":"Sort", "SeasonNumberHeading":"Season: {seasonNumber}", - "SortTitle":"Title", - "SortRequestDate": "Request Date", - "SortRequestedBy":"Requested By", - "SortStatus":"Status" + "SortTitleAsc":"Title ▲", + "SortTitleDesc":"Title ▼", + "SortRequestDateAsc": "Request Date ▲", + "SortRequestDateDesc": "Request Date ▼", + "SortStatusAsc":"Status ▲", + "SortStatusDesc":"Status ▼" }, "Issues":{ "Title":"Issues",