Simplified and enhnaced voice work page.

This commit is contained in:
2025-11-25 12:27:19 -05:00
parent 8eb5a22031
commit f39a7697d7
7 changed files with 680 additions and 411 deletions

View File

@@ -0,0 +1,208 @@
@using JSMR.Application.Enums
@using JSMR.Application.VoiceWorks.Queries.Search
@using JSMR.Domain.Enums
@using JSMR.Domain.ValueObjects
@using JSMR.UI.Blazor.Enums
@using JSMR.UI.Blazor.Filters
@using JSMR.UI.Blazor.Services
<div class="search-filter-control-container">
<div class="search-filter-control-span-2">
<BitTextField Prefix="Keywords"
Immediate="true" DebounceTime="500"
Value="@Value.Keywords"
ValueChanged="@(value => Update(Value with { Keywords = value }))" />
</div>
<div class="search-filter-control-span-1">
<BitDropdown Prefix="Languages"
MultiSelect
Items="languages"
Placeholder="Select..."
TItem="BitDropdownItem<Language>"
TValue="Language"
Values="@Value.SupportedLanguages"
ValuesChanged="@(values => Update(Value with { SupportedLanguages = [.. values] }))" />
</div>
<div class="search-filter-control-span-1">
<BitDropdown Prefix="Locale"
Items="locales"
Placeholder="Select..."
TItem="BitDropdownItem<Locale>"
TValue="Locale"
Value="@Value.Locale"
ValueChanged="@(value => Update(Value with { Locale = value }))" />
</div>
<!-- Row 2 -->
<div class="search-filter-control-span-1">
<BitDropdown Prefix="Sale Status"
Items="saleStatuses"
Placeholder="Select..."
TItem="BitDropdownItem<SaleStatus?>"
TValue="SaleStatus ?"
Value="@Value.SaleStatus"
ValueChanged="@(value => Update(Value with { SaleStatus = value }))" />
</div>
<div class="search-filter-control-span-1">
<BitDropdown Prefix="Circles"
Items="circleStatuses"
Placeholder="Select..."
TItem="BitDropdownItem<CircleStatus?>"
TValue="CircleStatus ?"
Value="@Value.CircleStatus"
ValueChanged="@(value => Update(Value with { CircleStatus = value }))" />
</div>
<div class="search-filter-control-span-1">
<BitDropdown Prefix="Tags"
Items="tagStatuses"
Placeholder="Select..."
TItem="BitDropdownItem<TagStatus?>"
TValue="TagStatus ?"
Value="@Value.TagStatus"
ValueChanged="@(value => Update(Value with { TagStatus = value }))" />
</div>
<div class="search-filter-control-span-1">
<BitDropdown Prefix="Creators"
Items="creatorStatuses"
Placeholder="Select..."
TItem="BitDropdownItem<CreatorStatus?>"
TValue="CreatorStatus ?"
Value="@Value.CreatorStatus"
ValueChanged="@(value => Update(Value with { CreatorStatus = value }))" />
</div>
<!-- Row 3 -->
<div class="search-filter-control-span-2">
<BitDropdown Prefix="Tags"
Items="tags"
MultiSelect
Virtualize
ShowSearchBox
AutoFocusSearchBox
Chips
Placeholder="Select..."
TItem="BitDropdownItem<int>"
TValue="int"
Values="@Value.TagIds"
ValuesChanged="@(values => Update(Value with { TagIds = [.. values] }))" />
</div>
<div class="search-filter-control-span-1">
<BitCheckbox Label="Include All Tags"
Value="@Value.IncludeAllTags"
ValueChanged="@(value => Update(Value with { IncludeAllTags = value }))" />
</div>
<div class="search-filter-control-span-1"></div>
<!-- Row 4 -->
<div class="search-filter-control-span-2">
<BitDropdown Prefix="Creators"
Items="creators"
MultiSelect
Virtualize
ShowSearchBox
AutoFocusSearchBox
Chips
Placeholder="Select..."
TItem="BitDropdownItem<int>"
TValue="int"
Values="@Value.CreatorIds"
ValuesChanged="@(values => Update(Value with { CreatorIds = [.. values] }))" />
</div>
<div class="search-filter-control-span-1">
<BitCheckbox Label="Include All Creators"
Value="@Value.IncludeAllCreators"
ValueChanged="@(value => Update(Value with { IncludeAllCreators = value }))" />
</div>
<div class="search-filter-control-span-1"></div>
<!-- Row 5 -->
<div class="search-filter-control-span-1">
<BitCheckbox Label="Show Only Favorite Works"
Value="@Value.ShowFavorite"
ValueChanged="@(value => Update(Value with { ShowFavorite = value }))" />
</div>
<div class="search-filter-control-span-1">
<BitCheckbox Label="Show Only Invalid Works"
Value="@Value.ShowInvalid"
ValueChanged="@(value => Update(Value with { ShowInvalid = value }))" />
</div>
<div class="search-filter-control-span-2"></div>
<div class="search-filter-control-span-1">
<BitDropdown Label="Age Ratings"
MultiSelect
Items="ageRatings"
Placeholder="Select..."
TItem="BitDropdownItem<AgeRating>"
TValue="AgeRating"
Values="@Value.AgeRatings"
ValuesChanged="@(values => Update(Value with { AgeRatings = [.. values] }))" />
</div>
<div class="search-filter-control-span-1">
<BitDatePicker Label="Release Date Start"
ShowClearButton="true"
Value="@ToDto(Value.ReleaseDateStart)"
ValueChanged="@(value => Update(Value with { ReleaseDateStart = FromDto(value) }))" />
</div>
<div class="search-filter-control-span-1">
<BitDatePicker Label="Release Date End"
ShowClearButton="true"
Value="@ToDto(Value.ReleaseDateEnd)"
ValueChanged="@(value => Update(Value with { ReleaseDateEnd = FromDto(value) }))" />
</div>
<div class="search-filter-control-span-2">
<BitDropdown Prefix="Sort"
Items="sortOptions"
Placeholder="Select..."
TItem="BitDropdownItem<VoiceWorkSort>"
TValue="VoiceWorkSort"
Value="@Value.Sort"
ValueChanged="@(value => Update(Value with { Sort = value }))" />
</div>
@* <div class="search-filter-control-span-1">
<BitSlider Label="Downloads" Min="0" Max="100000" Value="MinDownloads" ValueChanged="OnMinDownloadsChanged" />
</div> *@
</div>
@code {
[Inject]
ILookupDataService Lookups { get; set; } = default!;
[Parameter]
public VoiceWorkFilterState Value { get; set; } = new();
[Parameter]
public EventCallback<VoiceWorkFilterState> ValueChanged { get; set; }
List<BitDropdownItem<Locale>> locales = [];
List<BitDropdownItem<SaleStatus?>> saleStatuses = [];
List<BitDropdownItem<CircleStatus?>> circleStatuses = [];
List<BitDropdownItem<TagStatus?>> tagStatuses = [];
List<BitDropdownItem<CreatorStatus?>> creatorStatuses = [];
List<BitDropdownItem<Language>> languages = [];
List<BitDropdownItem<AgeRating>> ageRatings = [];
List<BitDropdownItem<VoiceWorkSort>> sortOptions = [];
List<BitDropdownItem<int>> tags = [];
List<BitDropdownItem<int>> creators = [];
protected override async Task OnInitializedAsync()
{
locales = Lookups.GetLocales();
saleStatuses = Lookups.GetSaleStatuses();
circleStatuses = Lookups.GetCircleStatuses();
tagStatuses = Lookups.GetTagStatuses();
creatorStatuses = Lookups.GetCreatorStatuses();
languages = Lookups.GetLanguages();
ageRatings = Lookups.GetAgeRatings();
sortOptions = Lookups.GetSortOptions();
(tags, creators) = (await Lookups.GetTagsAsync(), await Lookups.GetCreatorsAsync());
}
private Task Update(VoiceWorkFilterState next)
=> ValueChanged.InvokeAsync(next with { PageNumber = 1 });
// Map DateOnly? -> DateTimeOffset? (UTC midnight to avoid TZ drift)
private static DateTimeOffset? ToDto(DateOnly? d) =>
d is null ? null
: new DateTimeOffset(d.Value.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified), TimeSpan.Zero);
// Map DateTimeOffset? -> DateOnly?
private static DateOnly? FromDto(DateTimeOffset? dto) =>
dto is null ? null : DateOnly.FromDateTime(dto.Value.Date);
}