Browse Source

add search fields to filter collections and schedules tables (#2439)

pull/2440/head
Jason Dove 11 months ago committed by GitHub
parent
commit
ecacf3960f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 2
      ErsatzTV.Application/MediaCollections/Queries/GetPagedCollections.cs
  3. 26
      ErsatzTV.Application/MediaCollections/Queries/GetPagedCollectionsHandler.cs
  4. 3
      ErsatzTV.Application/MediaCollections/Queries/GetPagedMultiCollections.cs
  5. 26
      ErsatzTV.Application/MediaCollections/Queries/GetPagedMultiCollectionsHandler.cs
  6. 3
      ErsatzTV.Application/MediaCollections/Queries/GetPagedRerunCollections.cs
  7. 16
      ErsatzTV.Application/MediaCollections/Queries/GetPagedRerunCollectionsHandler.cs
  8. 3
      ErsatzTV.Application/MediaCollections/Queries/GetPagedSmartCollections.cs
  9. 18
      ErsatzTV.Application/MediaCollections/Queries/GetPagedSmartCollectionsHandler.cs
  10. 3
      ErsatzTV.Application/ProgramSchedules/PagedProgramSchedulesViewModel.cs
  11. 4
      ErsatzTV.Application/ProgramSchedules/Queries/GetPagedProgramSchedules.cs
  12. 36
      ErsatzTV.Application/ProgramSchedules/Queries/GetPagedProgramSchedulesHandler.cs
  13. 22
      ErsatzTV/Pages/ManualCollections.razor
  14. 22
      ErsatzTV/Pages/MultiCollections.razor
  15. 22
      ErsatzTV/Pages/RerunCollections.razor
  16. 36
      ErsatzTV/Pages/Schedules.razor
  17. 24
      ErsatzTV/Pages/SmartCollections.razor
  18. 3
      ErsatzTV/wwwroot/css/site.css

1
CHANGELOG.md

@ -37,6 +37,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -37,6 +37,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `loop` - loop forever
- `hold` - hold last frame forever, or `hold_seconds`
- Draw order (`z_index`)
- Add search fields to filter collections and schedules tables
### Fixed
- Fix green output when libplacebo tonemapping is used with NVIDIA acceleration and 10-bit output in FFmpeg Profile

2
ErsatzTV.Application/MediaCollections/Queries/GetPagedCollections.cs

@ -1,3 +1,3 @@ @@ -1,3 +1,3 @@
namespace ErsatzTV.Application.MediaCollections;
public record GetPagedCollections(int PageNum, int PageSize) : IRequest<PagedMediaCollectionsViewModel>;
public record GetPagedCollections(string Query, int PageNum, int PageSize) : IRequest<PagedMediaCollectionsViewModel>;

26
ErsatzTV.Application/MediaCollections/Queries/GetPagedCollectionsHandler.cs

@ -1,24 +1,30 @@ @@ -1,24 +1,30 @@
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Core.Domain;
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using static ErsatzTV.Application.MediaCollections.Mapper;
namespace ErsatzTV.Application.MediaCollections;
public class GetPagedCollectionsHandler : IRequestHandler<GetPagedCollections, PagedMediaCollectionsViewModel>
public class GetPagedCollectionsHandler(IDbContextFactory<TvContext> dbContextFactory)
: IRequestHandler<GetPagedCollections, PagedMediaCollectionsViewModel>
{
private readonly IDbContextFactory<TvContext> _dbContextFactory;
public GetPagedCollectionsHandler(IDbContextFactory<TvContext> dbContextFactory) =>
_dbContextFactory = dbContextFactory;
public async Task<PagedMediaCollectionsViewModel> Handle(
GetPagedCollections request,
CancellationToken cancellationToken)
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
int count = await dbContext.Collections.CountAsync(cancellationToken);
List<MediaCollectionViewModel> page = await dbContext.Collections
.AsNoTracking()
IQueryable<Collection> query = dbContext.Collections.AsNoTracking();
if (!string.IsNullOrWhiteSpace(request.Query))
{
query = query.Where(s => EF.Functions.Like(
EF.Functions.Collate(s.Name, TvContext.CaseInsensitiveCollation),
$"%{request.Query}%"));
}
List<MediaCollectionViewModel> page = await query
.OrderBy(c => EF.Functions.Collate(c.Name, TvContext.CaseInsensitiveCollation))
.Skip(request.PageNum * request.PageSize)
.Take(request.PageSize)

3
ErsatzTV.Application/MediaCollections/Queries/GetPagedMultiCollections.cs

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
namespace ErsatzTV.Application.MediaCollections;
public record GetPagedMultiCollections(int PageNum, int PageSize) : IRequest<PagedMultiCollectionsViewModel>;
public record GetPagedMultiCollections(string Query, int PageNum, int PageSize)
: IRequest<PagedMultiCollectionsViewModel>;

26
ErsatzTV.Application/MediaCollections/Queries/GetPagedMultiCollectionsHandler.cs

@ -1,24 +1,30 @@ @@ -1,24 +1,30 @@
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Core.Domain;
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using static ErsatzTV.Application.MediaCollections.Mapper;
namespace ErsatzTV.Application.MediaCollections;
public class GetPagedMultiCollectionsHandler : IRequestHandler<GetPagedMultiCollections, PagedMultiCollectionsViewModel>
public class GetPagedMultiCollectionsHandler(IDbContextFactory<TvContext> dbContextFactory)
: IRequestHandler<GetPagedMultiCollections, PagedMultiCollectionsViewModel>
{
private readonly IDbContextFactory<TvContext> _dbContextFactory;
public GetPagedMultiCollectionsHandler(IDbContextFactory<TvContext> dbContextFactory) =>
_dbContextFactory = dbContextFactory;
public async Task<PagedMultiCollectionsViewModel> Handle(
GetPagedMultiCollections request,
CancellationToken cancellationToken)
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
int count = await dbContext.MultiCollections.CountAsync(cancellationToken);
List<MultiCollectionViewModel> page = await dbContext.MultiCollections
.AsNoTracking()
IQueryable<MultiCollection> query = dbContext.MultiCollections.AsNoTracking();
if (!string.IsNullOrWhiteSpace(request.Query))
{
query = query.Where(s => EF.Functions.Like(
EF.Functions.Collate(s.Name, TvContext.CaseInsensitiveCollation),
$"%{request.Query}%"));
}
List<MultiCollectionViewModel> page = await query
.OrderBy(f => EF.Functions.Collate(f.Name, TvContext.CaseInsensitiveCollation))
.Skip(request.PageNum * request.PageSize)
.Take(request.PageSize)

3
ErsatzTV.Application/MediaCollections/Queries/GetPagedRerunCollections.cs

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
namespace ErsatzTV.Application.MediaCollections;
public record GetPagedRerunCollections(int PageNum, int PageSize) : IRequest<PagedRerunCollectionsViewModel>;
public record GetPagedRerunCollections(string Query, int PageNum, int PageSize)
: IRequest<PagedRerunCollectionsViewModel>;

16
ErsatzTV.Application/MediaCollections/Queries/GetPagedRerunCollectionsHandler.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Core.Domain;
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using static ErsatzTV.Application.MediaCollections.Mapper;
@ -13,8 +14,17 @@ public class GetPagedRerunCollectionsHandler(IDbContextFactory<TvContext> dbCont @@ -13,8 +14,17 @@ public class GetPagedRerunCollectionsHandler(IDbContextFactory<TvContext> dbCont
{
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
int count = await dbContext.RerunCollections.CountAsync(cancellationToken);
List<RerunCollectionViewModel> page = await dbContext.RerunCollections
.AsNoTracking()
IQueryable<RerunCollection> query = dbContext.RerunCollections.AsNoTracking();
if (!string.IsNullOrWhiteSpace(request.Query))
{
query = query.Where(s => EF.Functions.Like(
EF.Functions.Collate(s.Name, TvContext.CaseInsensitiveCollation),
$"%{request.Query}%"));
}
List<RerunCollectionViewModel> page = await query
.OrderBy(f => EF.Functions.Collate(f.Name, TvContext.CaseInsensitiveCollation))
.Skip(request.PageNum * request.PageSize)
.Take(request.PageSize)

3
ErsatzTV.Application/MediaCollections/Queries/GetPagedSmartCollections.cs

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
namespace ErsatzTV.Application.MediaCollections;
public record GetPagedSmartCollections(int PageNum, int PageSize) : IRequest<PagedSmartCollectionsViewModel>;
public record GetPagedSmartCollections(string Query, int PageNum, int PageSize)
: IRequest<PagedSmartCollectionsViewModel>;

18
ErsatzTV.Application/MediaCollections/Queries/GetPagedSmartCollectionsHandler.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Core.Domain;
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using static ErsatzTV.Application.MediaCollections.Mapper;
@ -13,9 +14,18 @@ public class GetPagedSmartCollectionsHandler(IDbContextFactory<TvContext> dbCont @@ -13,9 +14,18 @@ public class GetPagedSmartCollectionsHandler(IDbContextFactory<TvContext> dbCont
{
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
int count = await dbContext.SmartCollections.CountAsync(cancellationToken);
List<SmartCollectionViewModel> page = await dbContext.SmartCollections
.AsNoTracking()
.OrderBy(f => EF.Functions.Collate(f.Name, TvContext.CaseInsensitiveCollation))
IQueryable<SmartCollection> query = dbContext.SmartCollections.AsNoTracking();
if (!string.IsNullOrWhiteSpace(request.Query))
{
query = query.Where(s => EF.Functions.Like(
EF.Functions.Collate(s.Name, TvContext.CaseInsensitiveCollation),
$"%{request.Query}%"));
}
List<SmartCollectionViewModel> page = await query
.OrderBy(s => EF.Functions.Collate(s.Name, TvContext.CaseInsensitiveCollation))
.Skip(request.PageNum * request.PageSize)
.Take(request.PageSize)
.ToListAsync(cancellationToken)

3
ErsatzTV.Application/ProgramSchedules/PagedProgramSchedulesViewModel.cs

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
namespace ErsatzTV.Application.ProgramSchedules;
public record PagedProgramSchedulesViewModel(int TotalCount, List<ProgramScheduleViewModel> Page);

4
ErsatzTV.Application/ProgramSchedules/Queries/GetPagedProgramSchedules.cs

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
namespace ErsatzTV.Application.ProgramSchedules;
public record GetPagedProgramSchedules(string Query, int PageNum, int PageSize)
: IRequest<PagedProgramSchedulesViewModel>;

36
ErsatzTV.Application/ProgramSchedules/Queries/GetPagedProgramSchedulesHandler.cs

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using static ErsatzTV.Application.ProgramSchedules.Mapper;
namespace ErsatzTV.Application.ProgramSchedules;
public class GetPagedProgramSchedulesHandler(IDbContextFactory<TvContext> dbContextFactory)
: IRequestHandler<GetPagedProgramSchedules, PagedProgramSchedulesViewModel>
{
public async Task<PagedProgramSchedulesViewModel> Handle(
GetPagedProgramSchedules request,
CancellationToken cancellationToken)
{
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
int count = await dbContext.ProgramSchedules.CountAsync(cancellationToken);
IQueryable<ProgramSchedule> query = dbContext.ProgramSchedules.AsNoTracking();
if (!string.IsNullOrWhiteSpace(request.Query))
{
query = query.Where(s => EF.Functions.Like(
EF.Functions.Collate(s.Name, TvContext.CaseInsensitiveCollation),
$"%{request.Query}%"));
}
List<ProgramScheduleViewModel> page = await query
.OrderBy(s => EF.Functions.Collate(s.Name, TvContext.CaseInsensitiveCollation))
.Skip(request.PageNum * request.PageSize)
.Take(request.PageSize)
.ToListAsync(cancellationToken)
.Map(list => list.Map(ProjectToViewModel).ToList());
return new PagedProgramSchedulesViewModel(count, page);
}
}

22
ErsatzTV/Pages/ManualCollections.razor

@ -36,10 +36,15 @@ @@ -36,10 +36,15 @@
<col style="width: 120px;"/>
</MudHidden>
</ColGroup>
<HeaderContent>
<MudTh>Name</MudTh>
<MudTh/>
</HeaderContent>
<ToolBarContent>
<MudTextField T="string"
ValueChanged="@(s => OnSearch(s))"
Placeholder="Search for manual collections"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.FilterList"
Clearable="true">
</MudTextField>
</ToolBarContent>
<RowTemplate>
<MudTd DataLabel="Name">@context.Name</MudTd>
<MudTd>
@ -72,6 +77,7 @@ @@ -72,6 +77,7 @@
private MudTable<MediaCollectionViewModel> _collectionsTable;
private int _collectionsRowsPerPage = 10;
private string _searchString;
public void Dispose()
{
@ -118,7 +124,13 @@ @@ -118,7 +124,13 @@
{
await Mediator.Send(new SaveConfigElementByKey(ConfigElementKey.CollectionsPageSize, state.PageSize.ToString()), cancellationToken);
PagedMediaCollectionsViewModel data = await Mediator.Send(new GetPagedCollections(state.Page, state.PageSize), cancellationToken);
PagedMediaCollectionsViewModel data = await Mediator.Send(new GetPagedCollections(_searchString, state.Page, state.PageSize), cancellationToken);
return new TableData<MediaCollectionViewModel> { TotalItems = data.TotalCount, Items = data.Page };
}
private void OnSearch(string query)
{
_searchString = query;
_collectionsTable.ReloadServerData();
}
}

22
ErsatzTV/Pages/MultiCollections.razor

@ -36,10 +36,15 @@ @@ -36,10 +36,15 @@
<col style="width: 120px;"/>
</MudHidden>
</ColGroup>
<HeaderContent>
<MudTh>Name</MudTh>
<MudTh/>
</HeaderContent>
<ToolBarContent>
<MudTextField T="string"
ValueChanged="@(s => OnSearch(s))"
Placeholder="Search for multi collections"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.FilterList"
Clearable="true">
</MudTextField>
</ToolBarContent>
<RowTemplate>
<MudTd DataLabel="Name">@context.Name</MudTd>
<MudTd>
@ -72,6 +77,7 @@ @@ -72,6 +77,7 @@
private MudTable<MultiCollectionViewModel> _multiCollectionsTable;
private int _multiCollectionsRowsPerPage = 10;
private string _searchString;
public void Dispose()
{
@ -118,7 +124,13 @@ @@ -118,7 +124,13 @@
{
await Mediator.Send(new SaveConfigElementByKey(ConfigElementKey.MultiCollectionsPageSize, state.PageSize.ToString()), cancellationToken);
PagedMultiCollectionsViewModel data = await Mediator.Send(new GetPagedMultiCollections(state.Page, state.PageSize), cancellationToken);
PagedMultiCollectionsViewModel data = await Mediator.Send(new GetPagedMultiCollections(_searchString, state.Page, state.PageSize), cancellationToken);
return new TableData<MultiCollectionViewModel> { TotalItems = data.TotalCount, Items = data.Page };
}
private void OnSearch(string query)
{
_searchString = query;
_multiCollectionsTable.ReloadServerData();
}
}

22
ErsatzTV/Pages/RerunCollections.razor

@ -36,10 +36,15 @@ @@ -36,10 +36,15 @@
<col style="width: 120px;"/>
</MudHidden>
</ColGroup>
<HeaderContent>
<MudTh>Name</MudTh>
<MudTh/>
</HeaderContent>
<ToolBarContent>
<MudTextField T="string"
ValueChanged="@(s => OnSearch(s))"
Placeholder="Search for rerun collections"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.FilterList"
Clearable="true">
</MudTextField>
</ToolBarContent>
<RowTemplate>
<MudTd DataLabel="Name">@context.Name</MudTd>
<MudTd>
@ -72,6 +77,7 @@ @@ -72,6 +77,7 @@
private MudTable<RerunCollectionViewModel> _rerunCollectionsTable;
private int _rerunCollectionsRowsPerPage = 10;
private string _searchString;
public void Dispose()
{
@ -118,7 +124,13 @@ @@ -118,7 +124,13 @@
{
await Mediator.Send(new SaveConfigElementByKey(ConfigElementKey.RerunCollectionsPageSize, state.PageSize.ToString()), cancellationToken);
PagedRerunCollectionsViewModel data = await Mediator.Send(new GetPagedRerunCollections(state.Page, state.PageSize), cancellationToken);
PagedRerunCollectionsViewModel data = await Mediator.Send(new GetPagedRerunCollections(_searchString, state.Page, state.PageSize), cancellationToken);
return new TableData<RerunCollectionViewModel> { TotalItems = data.TotalCount, Items = data.Page };
}
private void OnSearch(string query)
{
_searchString = query;
_rerunCollectionsTable.ReloadServerData();
}
}

36
ErsatzTV/Pages/Schedules.razor

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
@page "/schedules"
@using ErsatzTV.Application.Configuration
@using ErsatzTV.Application.ProgramSchedules
@using NaturalSort.Extension
@implements IDisposable
@inject IDialogService Dialog
@inject IMediator Mediator
@ -30,14 +29,15 @@ @@ -30,14 +29,15 @@
<col style="width: 240px;"/>
</MudHidden>
</ColGroup>
<HeaderContent>
<MudTh>
<MudTableSortLabel SortBy="new Func<ProgramScheduleViewModel, object>(x => x.Name)">
Name
</MudTableSortLabel>
</MudTh>
<MudTh/>
</HeaderContent>
<ToolBarContent>
<MudTextField T="string"
ValueChanged="@(s => OnSearch(s))"
Placeholder="Search for schedules"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.FilterList"
Clearable="true">
</MudTextField>
</ToolBarContent>
<RowTemplate>
<MudTd>@context.Name</MudTd>
<MudTd>
@ -109,6 +109,7 @@ @@ -109,6 +109,7 @@
private MudTable<ProgramScheduleItemViewModel> _detailTable;
private int _rowsPerPage = 10;
private int _detailRowsPerPage = 10;
private string _searchString;
private ProgramScheduleViewModel _selectedSchedule;
public void Dispose()
@ -185,15 +186,8 @@ @@ -185,15 +186,8 @@
{
await Mediator.Send(new SaveConfigElementByKey(ConfigElementKey.SchedulesPageSize, state.PageSize.ToString()), cancellationToken);
List<ProgramScheduleViewModel> schedules = await Mediator.Send(new GetAllProgramSchedules(), cancellationToken);
IOrderedEnumerable<ProgramScheduleViewModel> sorted = schedules.OrderBy(s => s.Name, new NaturalSortComparer(StringComparison.CurrentCultureIgnoreCase));
// TODO: properly page this data
return new TableData<ProgramScheduleViewModel>
{
TotalItems = schedules.Count,
Items = sorted.Skip(state.Page * state.PageSize).Take(state.PageSize)
};
PagedProgramSchedulesViewModel data = await Mediator.Send(new GetPagedProgramSchedules(_searchString, state.Page, state.PageSize), cancellationToken);
return new TableData<ProgramScheduleViewModel> { TotalItems = data.TotalCount, Items = data.Page };
}
private async Task<TableData<ProgramScheduleItemViewModel>> DetailServerReload(TableState state, CancellationToken cancellationToken)
@ -211,4 +205,10 @@ @@ -211,4 +205,10 @@
};
}
private void OnSearch(string query)
{
_selectedSchedule = null;
_searchString = query;
_table.ReloadServerData();
}
}

24
ErsatzTV/Pages/SmartCollections.razor

@ -7,8 +7,6 @@ @@ -7,8 +7,6 @@
@inject IMediator Mediator
<MudForm Style="max-height: 100%">
<MudPaper Square="true" Style="display: flex; height: 64px; min-height: 64px; width: 100%; z-index: 100; align-items: center">
</MudPaper>
<div class="d-flex flex-column" style="height: 100vh; overflow-x: auto">
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8">
<MudText Typo="Typo.h5" Class="mb-2">Smart Collections</MudText>
@ -24,10 +22,15 @@ @@ -24,10 +22,15 @@
<col style="width: 180px;"/>
</MudHidden>
</ColGroup>
<HeaderContent>
<MudTh>Name</MudTh>
<MudTh/>
</HeaderContent>
<ToolBarContent>
<MudTextField T="string"
ValueChanged="@(s => OnSearch(s))"
Placeholder="Search for smart collections"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.FilterList"
Clearable="true">
</MudTextField>
</ToolBarContent>
<RowTemplate>
<MudTd DataLabel="Name">@context.Name</MudTd>
<MudTd>
@ -65,6 +68,7 @@ @@ -65,6 +68,7 @@
private MudTable<SmartCollectionViewModel> _smartCollectionsTable;
private int _smartCollectionsRowsPerPage = 10;
private string _searchString;
public void Dispose()
{
@ -111,7 +115,13 @@ @@ -111,7 +115,13 @@
{
await Mediator.Send(new SaveConfigElementByKey(ConfigElementKey.SmartCollectionsPageSize, state.PageSize.ToString()), cancellationToken);
PagedSmartCollectionsViewModel data = await Mediator.Send(new GetPagedSmartCollections(state.Page, state.PageSize), cancellationToken);
PagedSmartCollectionsViewModel data = await Mediator.Send(new GetPagedSmartCollections(_searchString, state.Page, state.PageSize), cancellationToken);
return new TableData<SmartCollectionViewModel> { TotalItems = data.TotalCount, Items = data.Page };
}
private void OnSearch(string query)
{
_searchString = query;
_smartCollectionsTable.ReloadServerData();
}
}

3
ErsatzTV/wwwroot/css/site.css

@ -180,7 +180,8 @@ div.ersatztv-light { @@ -180,7 +180,8 @@ div.ersatztv-light {
}
.form-field-stack div.mud-input-control,
.form-field-stack ul.mud-treeview {
.form-field-stack ul.mud-treeview,
.mud-toolbar .mud-input-text {
max-width: 500px;
}

Loading…
Cancel
Save