Updated Blazor UI tag/creator views. Altered logic for sorting favorite/blacklisted fields for tags/creators.
All checks were successful
ci / build-test (push) Successful in 2m45s
ci / publish-image (push) Successful in 1m58s

This commit is contained in:
2026-04-18 21:39:58 -04:00
parent 1f91e46527
commit c203b2cbdb
13 changed files with 308 additions and 120 deletions

View File

@@ -44,8 +44,8 @@ public class CreatorSearchProvider(AppDbContext context) : SearchProvider<Creato
Expression<Func<CreatorSearchItem, object?>> selector = field switch Expression<Func<CreatorSearchItem, object?>> selector = field switch
{ {
CreatorSortField.VoiceWorkCount => x => x.VoiceWorkCount, CreatorSortField.VoiceWorkCount => x => x.VoiceWorkCount,
CreatorSortField.Favorite => x => !x.Favorite, CreatorSortField.Favorite => x => x.Favorite,
CreatorSortField.Blacklisted => x => !x.Blacklisted, CreatorSortField.Blacklisted => x => x.Blacklisted,
_ => x => x.Name _ => x => x.Name
}; };

View File

@@ -54,7 +54,7 @@ public class CreatorSearchProviderTests(CreatorSearchProviderFixture fixture) :
{ {
var options = new SearchOptions<CreatorSearchCriteria, CreatorSortField>() var options = new SearchOptions<CreatorSearchCriteria, CreatorSortField>()
{ {
SortOptions = [new(CreatorSortField.Favorite, SortDirection.Ascending)] SortOptions = [new(CreatorSortField.Favorite, SortDirection.Descending)]
}; };
var result = await SearchAsync(options); var result = await SearchAsync(options);
@@ -67,7 +67,7 @@ public class CreatorSearchProviderTests(CreatorSearchProviderFixture fixture) :
{ {
var options = new SearchOptions<CreatorSearchCriteria, CreatorSortField>() var options = new SearchOptions<CreatorSearchCriteria, CreatorSortField>()
{ {
SortOptions = [new(CreatorSortField.Blacklisted, SortDirection.Ascending)] SortOptions = [new(CreatorSortField.Blacklisted, SortDirection.Descending)]
}; };
var result = await SearchAsync(options); var result = await SearchAsync(options);

View File

@@ -84,4 +84,30 @@ public class TagSearchProviderTests(TagSearchProviderFixture fixture) : IClassFi
result.TotalItems.ShouldBe(1); result.TotalItems.ShouldBe(1);
result.Items.ShouldContain(tagView => tagView.EnglishName == "Heartwarming"); result.Items.ShouldContain(tagView => tagView.EnglishName == "Heartwarming");
} }
[Fact]
public async Task Filter_None_Sort_Favorite_Descending()
{
var options = new SearchOptions<TagSearchCriteria, TagSortField>()
{
SortOptions = [new(TagSortField.Favorite, SortDirection.Descending)]
};
var result = await SearchAsync(options);
result.Items[0].EnglishName.ShouldBe("Heartwarming");
}
[Fact]
public async Task Filter_None_Sort_Blacklisted_Descending()
{
var options = new SearchOptions<TagSearchCriteria, TagSortField>()
{
SortOptions = [new(TagSortField.Blacklisted, SortDirection.Descending)]
};
var result = await SearchAsync(options);
result.Items[0].EnglishName.ShouldBe("Tsundere");
}
} }

View File

@@ -5,6 +5,7 @@
@using JSMR.UI.Blazor.Enums @using JSMR.UI.Blazor.Enums
@using JSMR.UI.Blazor.Filters @using JSMR.UI.Blazor.Filters
@using JSMR.UI.Blazor.Services @using JSMR.UI.Blazor.Services
@using AntDesign
<div class="search-toolbar"> <div class="search-toolbar">
<BitMenuButton TItem="BitMenuButtonItem" Text="Presets" Items="presets" Variant="BitVariant.Outline" OnClick="test" /> <BitMenuButton TItem="BitMenuButtonItem" Text="Presets" Items="presets" Variant="BitVariant.Outline" OnClick="test" />
@@ -59,9 +60,18 @@
Value="@Value.SaleStatus" Value="@Value.SaleStatus"
ValueChanged="@(value => Update(Value with { SaleStatus = value }))"> ValueChanged="@(value => Update(Value with { SaleStatus = value }))">
<PrefixTemplate> <PrefixTemplate>
<InputPrefix Graphic="Graphic.Cart" Tooltip="Keywords"></InputPrefix> <InputPrefix Graphic="Graphic.Cart" Tooltip="Sale Status"></InputPrefix>
</PrefixTemplate> </PrefixTemplate>
</BitDropdown> </BitDropdown>
@* <Select TItem="SelectOption<SaleStatus?>"
TItemValue="SaleStatus?"
Placeholder="All (Placeholder)"
ValueName="Value"
LabelName="Label"
DataSource="saleStatuses2"
Value="@Value.SaleStatus"
ValueChanged="@(value => Update(Value with { SaleStatus = value }))">
</Select> *@
</div> </div>
<div class="search-filter-control-span-1"> <div class="search-filter-control-span-1">
<BitDropdown Prefix="Circles" <BitDropdown Prefix="Circles"
@@ -219,6 +229,7 @@
List<BitDropdownItem<Locale>> locales = []; List<BitDropdownItem<Locale>> locales = [];
List<BitDropdownItem<SaleStatus?>> saleStatuses = []; List<BitDropdownItem<SaleStatus?>> saleStatuses = [];
List<SelectOption<SaleStatus?>> saleStatuses2 = [];
List<BitDropdownItem<CircleStatus?>> circleStatuses = []; List<BitDropdownItem<CircleStatus?>> circleStatuses = [];
List<BitDropdownItem<TagStatus?>> tagStatuses = []; List<BitDropdownItem<TagStatus?>> tagStatuses = [];
List<BitDropdownItem<CreatorStatus?>> creatorStatuses = []; List<BitDropdownItem<CreatorStatus?>> creatorStatuses = [];
@@ -242,6 +253,7 @@
{ {
locales = Lookups.GetLocales(); locales = Lookups.GetLocales();
saleStatuses = Lookups.GetSaleStatuses(); saleStatuses = Lookups.GetSaleStatuses();
saleStatuses2 = Lookups.GetSaleStatuses2();
circleStatuses = Lookups.GetCircleStatuses(); circleStatuses = Lookups.GetCircleStatuses();
tagStatuses = Lookups.GetTagStatuses(); tagStatuses = Lookups.GetTagStatuses();
creatorStatuses = Lookups.GetCreatorStatuses(); creatorStatuses = Lookups.GetCreatorStatuses();

View File

@@ -10,6 +10,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AntDesign" Version="1.6.1" />
<PackageReference Include="Bit.BlazorUI" Version="10.4.3" /> <PackageReference Include="Bit.BlazorUI" Version="10.4.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.6" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.6" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.6" PrivateAssets="all" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.6" PrivateAssets="all" />

View File

@@ -8,7 +8,7 @@
<MudLayout> <MudLayout>
<MudAppBar Elevation="1" Dense="@_dense"> <MudAppBar Elevation="1" Dense="@_dense">
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@ToggleDrawer" /> <MudIconButton Icon="@Icons.Material.Filled.Menu" Color="MudBlazor.Color.Inherit" Edge="Edge.Start" OnClick="@ToggleDrawer" />
<MudText>JSMR</MudText> <MudText>JSMR</MudText>
<MudSpacer /> <MudSpacer />
@* <MudIconButton Icon="@Icons.Custom.Brands.GitHub" Color="Color.Inherit" Href="https://github.com/MudBlazor/MudBlazor" Target="_blank" /> *@ @* <MudIconButton Icon="@Icons.Custom.Brands.GitHub" Color="Color.Inherit" Href="https://github.com/MudBlazor/MudBlazor" Target="_blank" /> *@
@@ -52,6 +52,7 @@
</MudLayout> </MudLayout>
<RadzenComponents @rendermode="RenderMode.InteractiveAuto" /> <RadzenComponents @rendermode="RenderMode.InteractiveAuto" />
<AntContainer />
@code{ @code{
private bool _open = false; private bool _open = false;

View File

@@ -39,15 +39,15 @@ else
@if (item.Favorite) @if (item.Favorite)
{ {
<MudChip T="string" Label="true" Color="Color.Info" Style="width: 100%" Variant="MudBlazor.Variant.Outlined">Favorite</MudChip> <MudChip T="string" Label="true" Color="MudBlazor.Color.Info" Style="width: 100%" Variant="MudBlazor.Variant.Outlined">Favorite</MudChip>
} }
else if (item.Blacklisted) else if (item.Blacklisted)
{ {
<MudChip T="string" Label="true" Color="Color.Warning" Style="width: 100%" Variant="MudBlazor.Variant.Outlined">Blacklisted</MudChip> <MudChip T="string" Label="true" Color="MudBlazor.Color.Warning" Style="width: 100%" Variant="MudBlazor.Variant.Outlined">Blacklisted</MudChip>
} }
else if (item.Spam) else if (item.Spam)
{ {
<MudChip T="string" Label="true" Color="Color.Error" Style="width: 100%" Variant="MudBlazor.Variant.Outlined">Spam</MudChip> <MudChip T="string" Label="true" Color="MudBlazor.Color.Error" Style="width: 100%" Variant="MudBlazor.Variant.Outlined">Spam</MudChip>
} }
else else
{ {

View File

@@ -13,37 +13,48 @@
<MudTextField T="string" Value="Keywords" ValueChanged="OnKeywordsChanged" Immediate="true" DebounceInterval="500" Label="Filter" Variant="MudBlazor.Variant.Text" Adornment="@Adornment.Start" AdornmentIcon="@Icons.Material.Outlined.Search" /> <MudTextField T="string" Value="Keywords" ValueChanged="OnKeywordsChanged" Immediate="true" DebounceInterval="500" Label="Filter" Variant="MudBlazor.Variant.Text" Adornment="@Adornment.Start" AdornmentIcon="@Icons.Material.Outlined.Search" />
@if (searchResults is null)
{
<p>Loading…</p>
}
else if (searchResults.Items.Length == 0)
{
<p>No results.</p>
}
else
{
<MudTable Items="@searchResults.Items" Style="table-layout: fixed" Virtualize="true">
<HeaderContent>
<MudTh>Name</MudTh>
<MudTh>Voice Works</MudTh>
<MudTh>Favorite</MudTh>
<MudTh>Blacklisted</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Name">@context.Name</MudTd>
<MudTd DataLabel="Voice Works">@context.VoiceWorkCount</MudTd>
<MudTd DataLabel="Favorite">
<MudIconButton Size="@Size.Small" Icon="@Icons.Material.Filled.Favorite" Color="@(context.Favorite? Color.Secondary: Color.Dark)" />
</MudTd>
<MudTd DataLabel="Blacklisted">
<MudIconButton Size="@Size.Small" Icon="@Icons.Material.Filled.Block" Color="@(context.Blacklisted? Color.Secondary: Color.Dark)" />
</MudTd>
</RowTemplate>
</MudTable>
<JPagination PageNumber="PageNumber" PageNumberChanged="OnPageNumberChanged" PageSize="PageSize" PageSizeChanged="OnPageSizeChanged" @bind-TotalItems="searchResults.TotalItems" /> <AntDesign.Table DataSource="@(searchResults?.Items ?? Enumerable.Empty<CreatorSearchItem>())"
Total="@(searchResults?.TotalItems ?? 0)"
TItem="CreatorSearchItem"
Loading="LoadingData"
HidePagination="@true"
RemoteDataSource="@true"
RowKey="x=>x.CreatorId"
OnChange="HandleTableChange">
<ColumnDefinitions>
<AntDesign.PropertyColumn Property="c => c.Name" Title="Name" Sortable="true" SorterMultiple="4" />
<AntDesign.PropertyColumn Property="c => c.VoiceWorkCount" Title="Voice Works" Format="n0" HeaderStyle="width: 14em" Sortable="true" SorterMultiple="4" />
<AntDesign.PropertyColumn Property="c => c.Favorite" Title="Favorite" HeaderStyle="width: 8em" Sortable="true" SortDirections="new[] { AntDesign.SortDirection.Descending }" SorterMultiple="4">
@if (context.Favorite)
{
<AntDesign.Tag Color="AntDesign.TagColor.PurpleInverse">Favorite</AntDesign.Tag>
} }
</AntDesign.PropertyColumn>
<AntDesign.PropertyColumn Property="c => c.Blacklisted" Title="Blacklisted" HeaderStyle="width: 8em" Sortable="true" SortDirections="new[] { AntDesign.SortDirection.Descending }" SorterMultiple="4">
@if (context.Blacklisted)
{
<AntDesign.Tag Color="AntDesign.TagColor.RedInverse">Blacklisted</AntDesign.Tag>
}
</AntDesign.PropertyColumn>
@* <AntDesign.ActionColumn HeaderStyle="width: 2em">
<AntDesign.Dropdown Trigger="@(new AntDesign.Trigger[] { AntDesign.Trigger.Click })">
<Overlay>
<AntDesign.Menu>
<AntDesign.MenuItem>
<span>Some Menu Item</span>
</AntDesign.MenuItem>
</AntDesign.Menu>
</Overlay>
<ChildContent>
<AntDesign.Button Shape="AntDesign.ButtonShape.Circle" Icon="@AntDesign.IconType.Outline.More" Type="AntDesign.ButtonType.Default"></AntDesign.Button>
</ChildContent>
</AntDesign.Dropdown>
</AntDesign.ActionColumn> *@
</ColumnDefinitions>
</AntDesign.Table>
<JPagination PageNumber="PageNumber" PageNumberChanged="OnPageNumberChanged" PageSize="PageSize" PageSizeChanged="OnPageSizeChanged" TotalItems="TotalItems" />
<style> <style>
.mud-table-root { .mud-table-root {
@@ -66,12 +77,15 @@ else
public string? Keywords { get; set; } public string? Keywords { get; set; }
public int PageNumber { get; set; } = 1; public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 100; public int PageSize { get; set; } = 100;
public int TotalItems => searchResults?.TotalItems ?? 0;
public bool LoadingData { get; set; }
SearchResult<CreatorSearchItem>? searchResults; SearchResult<CreatorSearchItem>? searchResults;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await UpdateDataAsync(true); //await UpdateDataAsync(true);
} }
public async Task OnKeywordsChanged(string? newKeywords) public async Task OnKeywordsChanged(string? newKeywords)
@@ -102,6 +116,8 @@ else
private async Task LoadCreatorsAsync() private async Task LoadCreatorsAsync()
{ {
LoadingData = true;
SearchCreatorsRequest request = new( SearchCreatorsRequest request = new(
Options: new() Options: new()
{ {
@@ -111,10 +127,7 @@ else
{ {
Name = Keywords Name = Keywords
}, },
SortOptions = SortOptions = [.. _sortOptions]
[
new(CreatorSortField.Name, Application.Common.Search.SortDirection.Ascending)
]
} }
); );
@@ -123,6 +136,68 @@ else
searchResults = result?.Results ?? new(); searchResults = result?.Results ?? new();
LoadingData = false;
//await InvokeAsync(StateHasChanged); //await InvokeAsync(StateHasChanged);
} }
private List<SortOption<CreatorSortField>> _sortOptions =
[
new(CreatorSortField.Name, Application.Common.Search.SortDirection.Ascending)
];
private async Task HandleTableChange(AntDesign.TableModels.QueryModel<CreatorSearchItem> queryModel)
{
//PageNumber = queryModel.PageIndex;
//PageSize = queryModel.PageSize;
_sortOptions = MapSortOptions(queryModel);
await LoadCreatorsAsync();
}
private List<SortOption<CreatorSortField>> MapSortOptions(AntDesign.TableModels.QueryModel<CreatorSearchItem> queryModel)
{
var requestedSorts = queryModel.SortModel
.Select(sort => new
{
Field = sort.FieldName switch
{
nameof(CreatorSearchItem.Favorite) => CreatorSortField.Favorite,
nameof(CreatorSearchItem.Blacklisted) => CreatorSortField.Blacklisted,
nameof(CreatorSearchItem.VoiceWorkCount) => CreatorSortField.VoiceWorkCount,
nameof(CreatorSearchItem.Name) => CreatorSortField.Name,
_ => (CreatorSortField?)null
},
Direction = sort.SortDirection switch
{
AntDesign.SortDirection.Ascending => Application.Common.Search.SortDirection.Ascending,
AntDesign.SortDirection.Descending => Application.Common.Search.SortDirection.Descending,
_ => (Application.Common.Search.SortDirection?)null
}
})
.Where(x => x.Field is not null && x.Direction is not null)
.ToDictionary(x => x.Field!.Value, x => x.Direction!.Value);
var finalSorts = new List<SortOption<CreatorSortField>>();
// Force your preferred precedence
if (requestedSorts.TryGetValue(CreatorSortField.Favorite, out var favoriteDir))
finalSorts.Add(new(CreatorSortField.Favorite, favoriteDir));
if (requestedSorts.TryGetValue(CreatorSortField.Blacklisted, out var blacklistedDir))
finalSorts.Add(new(CreatorSortField.Blacklisted, blacklistedDir));
if (requestedSorts.TryGetValue(CreatorSortField.VoiceWorkCount, out var countDir))
finalSorts.Add(new(CreatorSortField.VoiceWorkCount, countDir));
if (requestedSorts.TryGetValue(CreatorSortField.Name, out var nameDir))
finalSorts.Add(new(CreatorSortField.Name, nameDir));
// Default fallback
if (finalSorts.Count == 0)
finalSorts.Add(new(CreatorSortField.Name, Application.Common.Search.SortDirection.Ascending));
return finalSorts;
}
} }

View File

@@ -11,44 +11,54 @@
<h1>Tags</h1> <h1>Tags</h1>
<MudTextField @bind-Value="Keywords" Immediate="true" DebounceInterval="500" Label="Filter" Variant="MudBlazor.Variant.Text" Adornment="@Adornment.Start" AdornmentIcon="@Icons.Material.Outlined.Search" /> <MudTextField T="string" Value="Keywords" ValueChanged="OnKeywordsChanged" Immediate="true" DebounceInterval="500" Label="Filter" Variant="MudBlazor.Variant.Text" Adornment="@Adornment.Start" AdornmentIcon="@Icons.Material.Outlined.Search" />
@if (searchResults is null) @* <AntDesign.Input @bind-Value="Keywords" DebounceMilliseconds="500" Placeholder="Filter">
{ <Prefix>
<p>Loading…</p> <AntDesign.Icon Type="@AntDesign.IconType.Outline.Search" />
} </Prefix>
else if (searchResults.Items.Length == 0) </AntDesign.Input> *@
{
<p>No results.</p>
}
else
{
<MudDataGrid Items="@searchResults.Items" Style="table-layout: fixed">
<Columns>
<PropertyColumn Property="x => x.Name" Title="Name" />
<PropertyColumn Property="x => x.EnglishName" Title="English Name" />
<PropertyColumn Property="x => x.VoiceWorkCount" Title="Voice Works" HeaderStyle="width: 14em" />
<TemplateColumn Title="Favorite" HeaderStyle="width: 8em">
<CellTemplate>
<MudIconButton Size="@Size.Small" Icon="@Icons.Material.Filled.Favorite" Color="@(context.Item.Favorite ? Color.Secondary : Color.Dark)" @onclick="@(e => IncrementCount(context.Item))" />
</CellTemplate>
</TemplateColumn>
<TemplateColumn Title="Blacklisted" HeaderStyle="width: 8em">
<CellTemplate>
<MudIconButton Size="@Size.Small" Icon="@Icons.Material.Filled.Block" Color="@(context.Item.Blacklisted? Color.Secondary: Color.Dark)" />
</CellTemplate>
</TemplateColumn>
</Columns>
</MudDataGrid>
<JPagination @bind-PageNumber="PageNumber" @bind-PageSize="PageSize" @bind-TotalItems="searchResults.TotalItems" /> <AntDesign.Table
Responsive
DataSource="@(searchResults?.Items ?? Enumerable.Empty<TagSearchItem>())"
Total="@(searchResults?.TotalItems ?? 0)"
TItem="TagSearchItem"
Loading="LoadingData"
HidePagination="@true"
RemoteDataSource="@true"
RowKey="x=>x.TagId"
OnChange="HandleTableChange">
<ColumnDefinitions>
<AntDesign.PropertyColumn Property="c => c.Name" Title="Name" Sortable SorterMultiple="5" />
<AntDesign.PropertyColumn Property="c => c.EnglishName" Title="English Name" Sortable SorterMultiple="5" />
<AntDesign.PropertyColumn Property="c => c.VoiceWorkCount" Title="Voice Works" Format="n0" HeaderStyle="width: 14em" Sortable SorterMultiple="5" />
<AntDesign.PropertyColumn Property="c => c.Favorite" Title="Favorite" HeaderStyle="width: 8em" Sortable SortDirections="new[] { AntDesign.SortDirection.Descending }" SorterMultiple="5">
@if (context.Favorite)
{
<AntDesign.Tag Color="AntDesign.TagColor.PurpleInverse">Favorite</AntDesign.Tag>
} }
</AntDesign.PropertyColumn>
<AntDesign.PropertyColumn Property="c => c.Blacklisted" Title="Blacklisted" HeaderStyle="width: 9em" Sortable SortDirections="new[] { AntDesign.SortDirection.Descending }" SorterMultiple="5">
@if (context.Blacklisted)
{
<AntDesign.Tag Color="AntDesign.TagColor.RedInverse">Blacklisted</AntDesign.Tag>
}
</AntDesign.PropertyColumn>
</ColumnDefinitions>
</AntDesign.Table>
<JPagination PageNumber="PageNumber" PageNumberChanged="OnPageNumberChanged" PageSize="PageSize" PageSizeChanged="OnPageSizeChanged" TotalItems="TotalItems" />
<style> <style>
.mud-table-root { .mud-table-root {
table-layout: fixed; table-layout: fixed;
} }
.ant-table table {
table-layout: fixed;
}
.j-pager { .j-pager {
position: fixed; position: fixed;
bottom: 0; bottom: 0;
@@ -62,74 +72,60 @@ else
</style> </style>
@code { @code {
private string? keywords; public string? Keywords { get; set; }
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 100;
public int TotalItems => searchResults?.TotalItems ?? 0;
public string? Keywords public bool LoadingData { get; set; }
{
get { return keywords; }
set
{
keywords = value;
_ = UpdateDataAsync(true);
}
}
private int pageNumber = 1;
public int PageNumber
{
get { return pageNumber; }
set
{
pageNumber = value;
_ = UpdateDataAsync(false);
}
}
int pageSize = 100;
public int PageSize
{
get { return pageSize; }
set
{
pageSize = value;
_ = UpdateDataAsync(true);
}
}
SearchResult<TagSearchItem>? searchResults; SearchResult<TagSearchItem>? searchResults;
protected override Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
_ = LoadTagsAsync(); //await UpdateDataAsync(true);
}
return Task.CompletedTask; public async Task OnKeywordsChanged(string? newKeywords)
{
Keywords = newKeywords;
await UpdateDataAsync(true);
}
public async Task OnPageNumberChanged(int newPageNumber)
{
PageNumber = newPageNumber;
await UpdateDataAsync(false);
}
public async Task OnPageSizeChanged(int newPageSize)
{
PageSize = newPageSize;
await UpdateDataAsync(true);
} }
private async Task UpdateDataAsync(bool resetPageNumber) private async Task UpdateDataAsync(bool resetPageNumber)
{ {
if (resetPageNumber) if (resetPageNumber)
pageNumber = 1; PageNumber = 1;
await LoadTagsAsync(); await LoadTagsAsync();
} }
private async Task LoadTagsAsync() private async Task LoadTagsAsync()
{ {
LoadingData = true;
SearchTagsRequest request = new( SearchTagsRequest request = new(
Options: new() Options: new()
{ {
PageNumber = PageNumber, PageNumber = PageNumber,
PageSize = pageSize, PageSize = PageSize,
Criteria = new() Criteria = new()
{ {
Name = Keywords Name = Keywords
}, },
SortOptions = SortOptions = [.. _sortOptions]
[
new(TagSortField.Name, Application.Common.Search.SortDirection.Ascending)
]
} }
); );
@@ -138,11 +134,72 @@ else
searchResults = result?.Results ?? new(); searchResults = result?.Results ?? new();
await InvokeAsync(StateHasChanged); LoadingData = false;
//await InvokeAsync(StateHasChanged);
} }
private void IncrementCount(TagSearchItem item) private List<SortOption<TagSortField>> _sortOptions =
[
new(TagSortField.Name, Application.Common.Search.SortDirection.Ascending)
];
private async Task HandleTableChange(AntDesign.TableModels.QueryModel<TagSearchItem> queryModel)
{ {
//item.Favorite = !item.Favorite; //PageNumber = queryModel.PageIndex;
//PageSize = queryModel.PageSize;
_sortOptions = MapSortOptions(queryModel);
await LoadTagsAsync();
}
private List<SortOption<TagSortField>> MapSortOptions(AntDesign.TableModels.QueryModel<TagSearchItem> queryModel)
{
var requestedSorts = queryModel.SortModel
.Select(sort => new
{
Field = sort.FieldName switch
{
nameof(TagSearchItem.Favorite) => TagSortField.Favorite,
nameof(TagSearchItem.Blacklisted) => TagSortField.Blacklisted,
nameof(TagSearchItem.VoiceWorkCount) => TagSortField.VoiceWorkCount,
nameof(TagSearchItem.EnglishName) => TagSortField.EnglishName,
nameof(TagSearchItem.Name) => TagSortField.Name,
_ => (TagSortField?)null
},
Direction = sort.SortDirection switch
{
AntDesign.SortDirection.Ascending => Application.Common.Search.SortDirection.Ascending,
AntDesign.SortDirection.Descending => Application.Common.Search.SortDirection.Descending,
_ => (Application.Common.Search.SortDirection?)null
}
})
.Where(x => x.Field is not null && x.Direction is not null)
.ToDictionary(x => x.Field!.Value, x => x.Direction!.Value);
var finalSorts = new List<SortOption<TagSortField>>();
// Force your preferred precedence
if (requestedSorts.TryGetValue(TagSortField.Favorite, out var favoriteDir))
finalSorts.Add(new(TagSortField.Favorite, favoriteDir));
if (requestedSorts.TryGetValue(TagSortField.Blacklisted, out var blacklistedDir))
finalSorts.Add(new(TagSortField.Blacklisted, blacklistedDir));
if (requestedSorts.TryGetValue(TagSortField.VoiceWorkCount, out var countDir))
finalSorts.Add(new(TagSortField.VoiceWorkCount, countDir));
if (requestedSorts.TryGetValue(TagSortField.EnglishName, out var englishNameDir))
finalSorts.Add(new(TagSortField.EnglishName, englishNameDir));
if (requestedSorts.TryGetValue(TagSortField.Name, out var nameDir))
finalSorts.Add(new(TagSortField.Name, nameDir));
// Default fallback
if (finalSorts.Count == 0)
finalSorts.Add(new(TagSortField.Name, Application.Common.Search.SortDirection.Ascending));
return finalSorts;
} }
} }

View File

@@ -47,6 +47,7 @@ builder.Services.AddScoped<SessionState>();
builder.Services.AddMudServices(); builder.Services.AddMudServices();
builder.Services.AddRadzenComponents(); builder.Services.AddRadzenComponents();
builder.Services.AddBitBlazorUIServices(); builder.Services.AddBitBlazorUIServices();
builder.Services.AddAntDesign();
builder.Services.AddScoped<VoiceWorksClient>(); builder.Services.AddScoped<VoiceWorksClient>();
builder.Services.AddScoped<ILookupDataService, LookupDataService>(); builder.Services.AddScoped<ILookupDataService, LookupDataService>();

View File

@@ -10,6 +10,7 @@ public interface ILookupDataService
{ {
List<BitDropdownItem<Locale>> GetLocales(); List<BitDropdownItem<Locale>> GetLocales();
List<BitDropdownItem<SaleStatus?>> GetSaleStatuses(); List<BitDropdownItem<SaleStatus?>> GetSaleStatuses();
List<SelectOption<SaleStatus?>> GetSaleStatuses2();
List<BitDropdownItem<CircleStatus?>> GetCircleStatuses(); List<BitDropdownItem<CircleStatus?>> GetCircleStatuses();
List<BitDropdownItem<TagStatus?>> GetTagStatuses(); List<BitDropdownItem<TagStatus?>> GetTagStatuses();
List<BitDropdownItem<CreatorStatus?>> GetCreatorStatuses(); List<BitDropdownItem<CreatorStatus?>> GetCreatorStatuses();

View File

@@ -29,6 +29,13 @@ public sealed class LookupDataService(VoiceWorksClient client) : ILookupDataServ
new() { Text = "All", Value = null } new() { Text = "All", Value = null }
]; ];
public List<SelectOption<SaleStatus?>> GetSaleStatuses2() =>
[
new() { Label = "Available", Value = SaleStatus.Available },
new() { Label = "Upcoming", Value = SaleStatus.Upcoming },
new() { Label = "All", Value = null }
];
public List<BitDropdownItem<CircleStatus?>> GetCircleStatuses() => public List<BitDropdownItem<CircleStatus?>> GetCircleStatuses() =>
[ [
new() { Text = "Not Blacklisted", Value = CircleStatus.NotBlacklisted }, new() { Text = "Not Blacklisted", Value = CircleStatus.NotBlacklisted },
@@ -132,3 +139,9 @@ public sealed class LookupDataService(VoiceWorksClient client) : ILookupDataServ
return _creators; return _creators;
} }
} }
public class SelectOption<T>
{
public string? Label { get; set; }
public T? Value { get; set; }
}

View File

@@ -23,6 +23,7 @@
<link rel="stylesheet" href="css/mud-blazor.css" /> <link rel="stylesheet" href="css/mud-blazor.css" />
<link rel="stylesheet" href="css/radzen.css" /> <link rel="stylesheet" href="css/radzen.css" />
<link href="_content/Bit.BlazorUI/styles/bit.blazorui.css" rel="stylesheet" /> <link href="_content/Bit.BlazorUI/styles/bit.blazorui.css" rel="stylesheet" />
<link href="_content/AntDesign/css/ant-design-blazor.dark.css" rel="stylesheet" />
<link rel="stylesheet" href="css/bit-blazor.css" /> <link rel="stylesheet" href="css/bit-blazor.css" />
<link rel="stylesheet" href="css/font.css" /> <link rel="stylesheet" href="css/font.css" />
<link rel="stylesheet" href="css/app.css" /> <link rel="stylesheet" href="css/app.css" />