Added more UI styling. Updated voice work search provider to send back English tag names, if applicable.
All checks were successful
ci / build-test (push) Successful in 2m15s
ci / publish-image (push) Has been skipped

This commit is contained in:
2025-11-15 22:37:15 -05:00
parent 634050c06f
commit 75900a90ef
10 changed files with 245 additions and 12 deletions

View File

@@ -1,5 +1,5 @@
<div class="@ContainerClassees">
<div class="j-image-overlay"></div>
<div class="@OverlayClasses"></div>
<img class="@ImageClasses" loading="@LoadingAttribute" src="@Source" @onload="OnImageLoaded">
</div>
@@ -16,6 +16,9 @@
[Parameter]
public string? ContainerClass { get; set; }
[Parameter]
public string? OverlayClass { get; set; }
[Parameter]
public string? ImageClass { get; set; }
@@ -23,6 +26,7 @@
private string? _lastSource;
private string ContainerClassees => GetContainerClasses();
private string OverlayClasses => GetOverlayClasses();
private string ImageClasses => GetImageClasses();
private string? LoadingAttribute => LazyLoading ? "lazy" : null;
@@ -54,6 +58,24 @@
return string.Join(" ", classNames);
}
private string GetOverlayClasses()
{
List<string> classNames = ["j-image-overlay"];
if (!string.IsNullOrEmpty(OverlayClass))
{
List<string> customClassNames = OverlayClass
.Split(" ")
.Select(className => className.Trim())
.Where(className => !string.IsNullOrWhiteSpace(className))
.ToList();
classNames.AddRange(customClassNames);
}
return string.Join(" ", classNames);
}
private string GetImageClasses()
{
List<string> classNames = ["j-image"];

View File

@@ -1,12 +1,30 @@
@using JSMR.Application.VoiceWorks.Queries.Search
@using JSMR.UI.Blazor.Services
@using System.Globalization
<div class="j-card j-voice-work-card">
<div class="j-voice-work-image-container">
<JImage ImageClass="j-voice-work-image" Source="@ImageUrlProvider.GetImageURL(Product.ProductId, Product.HasImage, Product.SalesDate, "main")"></JImage>
<JImage OverlayClass="j-voice-work-image-overlay" ImageClass="j-voice-work-image" Source="@ImageUrlProvider.GetImageUrl(Product, "main")"></JImage>
</div>
<div class="j-voice-work-content">
<div class="j-product-title">@Product.ProductName</div>
<div class="j-product-contributors">
<span class="j-circle">
<MudChip T="string"
Href="https://github.com/MudBlazor/MudBlazor"
Target="_blank"
Variant="Variant.Filled"
Icon="@Icons.Material.Outlined.Circle">@Product.Maker</MudChip>
@foreach (var creator in Product.Creators)
{
<MudChip T="string"
Href="https://github.com/MudBlazor/MudBlazor"
Target="_blank"
Variant="Variant.Filled"
Icon="@Icons.Material.Filled.Person">@creator.Name</MudChip>
}
</span>
</div>
<div class="j-product-description">@Product.Description</div>
<div class="j-tags">
@foreach (var tag in Product.Tags)
@@ -16,10 +34,52 @@
</div>
</div>
<div class="j-voice-work-info">
<div class="j-release-date-container">
<span class="j-icon j-icon-calendar"></span>
<span>@GetSomething(Product)</span>
</div>
<div class="j-wishlist-container">
<span class="j-icon j-icon-star j-icon-color-yellow"></span>
<span>@((Product.WishlistCount ?? 0).ToString("n0"))</span>
</div>
@if (Product.SalesDate is not null)
{
<div class="j-downloads-container">
<span class="j-icon j-icon-bag-fill j-icon-color-green"></span>
<span>@((Product.Downloads ?? 0).ToString("n0"))</span>
</div>
}
</div>
</div>
@code {
[Parameter]
public required VoiceWorkSearchResult Product { get; set; }
private string GetSomething(VoiceWorkSearchResult voiceWork)
{
if (voiceWork.SalesDate.HasValue)
{
return voiceWork.SalesDate.Value.ToString("MMMM d, yyyy", CultureInfo.CurrentCulture);
}
if (voiceWork.PlannedReleaseDate.HasValue)
{
return voiceWork.PlannedReleaseDate.Value.ToString("MMMM d, yyyy", CultureInfo.CurrentCulture);
}
if (voiceWork.ExpectedDate.HasValue)
{
string part = voiceWork.ExpectedDate.Value.Day switch
{
21 => "Late",
11 => "Middle",
_ => "Early"
};
return $"{part} {voiceWork.ExpectedDate.Value.ToString("MMMM yyyy")}";
}
return "Unknown";
}
}

View File

@@ -1,4 +1,5 @@
@page "/voiceworks"
@using JSMR.Application.Common.Search
@using JSMR.Application.VoiceWorks.Queries.Search
@using JSMR.UI.Blazor.Components
@using JSMR.UI.Blazor.Services
@@ -6,21 +7,61 @@
<PageTitle>Voice Works</PageTitle>
<h3>VoiceWorks</h3>
<h3>Voice Works</h3>
<JProductCollection Products="items"></JProductCollection>
<JProductCollection Products="searchResults?.Items"></JProductCollection>
@if (searchResults is not null)
{
<JPagination PageNumber="PageNumber" PageNumberChanged="OnPageNumberChanged" PageSize="PageSize" PageSizeChanged="OnPageSizeChanged" @bind-TotalItems="searchResults.TotalItems" />
}
@code {
VoiceWorkSearchResult[]? items;
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 100;
SearchResult<VoiceWorkSearchResult>? searchResults;
protected override async Task OnInitializedAsync()
{
await UpdateDataAsync(true);
}
private async Task UpdateDataAsync(bool resetPageNumber)
{
if (resetPageNumber)
PageNumber = 1;
SearchVoiceWorksRequest request = new(
Options: new()
{
Criteria = new()
{
SupportedLanguages = [Domain.Enums.Language.English]
},
SortOptions =
[
new(VoiceWorkSortField.ReleaseDate, Application.Common.Search.SortDirection.Descending)
],
PageNumber = PageNumber,
PageSize = PageSize
}
);
var result = await Client.SearchAsync(request);
SearchVoiceWorksResponse? response = await Client.SearchAsync(request);
items = result?.Results.Items ?? [];
searchResults = response?.Results;
}
public async Task OnPageNumberChanged(int newPageNumber)
{
PageNumber = newPageNumber;
await UpdateDataAsync(false);
}
public async Task OnPageSizeChanged(int newPageSize)
{
PageSize = newPageSize;
await UpdateDataAsync(true);
}
}

View File

@@ -1,7 +1,14 @@
namespace JSMR.UI.Blazor.Services;
using JSMR.Application.VoiceWorks.Queries.Search;
namespace JSMR.UI.Blazor.Services;
public static class ImageUrlProvider
{
public static string GetImageUrl(VoiceWorkSearchResult voiceWork, string size)
{
return GetImageURL(voiceWork.OriginalProductId ?? voiceWork.ProductId, voiceWork.HasImage, voiceWork.SalesDate, size);
}
public static string GetImageURL(string? productId, bool hasImage, DateTime? salesDate, string size)
{
string folder = "modpub";

View File

@@ -355,6 +355,10 @@ code {
flex-shrink: 0;
}
.j-voice-work-image-overlay {
border-radius: 20px;
}
.j-voice-work-image {
border-radius: 20px;
}
@@ -377,6 +381,11 @@ code {
text-shadow: 1px 1px 2px black;
}
.j-product-contributors {
font-size: 1rem;
font-family: "Poppins", "M+ 1p";
}
.j-product-description {
/* color: #7C8099; */
font-size: 1rem;
@@ -389,15 +398,96 @@ code {
.j-voice-work-info {
width: 240px;
align-items: flex-end;
display: flex;
flex-direction: column;
padding: .5rem;
/*background: black;*/
border-top-right-radius: 20px;
border-bottom-right-radius: 20px;
gap: .5rem;
font-size: 1rem;
text-shadow: 1px 1px 2px rgb(16, 36, 50);
}
.j-voice-work-card > .j-voice-work-info {
flex-shrink: 0;
}
.j-release-date-container {
display: flex;
align-items: center;
gap: .5rem;
font-size: 1rem;
font-weight: 500;
}
.j-wishlist-container {
display: flex;
align-items: center;
gap: .5rem;
font-size: 1rem;
font-weight: 500;
color: #ffe073;
}
.j-downloads-container {
display: flex;
align-items: center;
gap: .5rem;
font-size: 1rem;
font-weight: 500;
}
/* Tags */
.j-tags {
display: flex;
gap: 1rem;
gap: .75rem .5rem;
flex-wrap: wrap;
/*gap: .5rem 1rem;*/
}
.j-tag {
background-color: var(--tag-background-color);
color: var(--tag-text-color);
border: var(--tag-border);
border-radius: var(--tag-border-radius);
padding: var(--tag-padding);
/*padding: 0;
background-color: transparent;
text-shadow: 1px 1px 2px black;
font-weight: 500;*/
}
/* Icons */
.j-icon {
mask-size: auto;
align-self: center;
background: rgb(180,200, 214);
height: 16px;
width: 16px;
}
.j-icon-color-yellow {
background: #ffe073;
}
.j-icon-color-green {
background: #388E3C;
}
.j-icon-calendar {
mask-image: url("../svg/calendar.svg");
}
.j-icon-star {
mask-image: url("../svg/star-fill.svg");
}
.j-icon-bag {
mask-image: url("../svg/bag.svg");
}
.j-icon-bag-fill {
mask-image: url("../svg/bag-fill.svg");
}

View File

@@ -13,7 +13,8 @@
--tag-text-color: rgb(210,220,230);
--tag-border: none;
--tag-border-radius: 0.8em;
--tag-padding: 0.3em 1em;
--tag-padding-old: 0.3em 1em;
--tag-padding: 0.5em 1em;
--tag-margin: 1em 0.5em 0 0;
--tag-hover-background-color: #596f7e;
--tag-hover-text-color: rgb(240,250,254);

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-bag-fill" viewBox="0 0 16 16">
<path d="M8 1a2.5 2.5 0 0 1 2.5 2.5V4h-5v-.5A2.5 2.5 0 0 1 8 1m3.5 3v-.5a3.5 3.5 0 1 0-7 0V4H1v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4z"/>
</svg>

After

Width:  |  Height:  |  Size: 269 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-bag" viewBox="0 0 16 16">
<path d="M8 1a2.5 2.5 0 0 1 2.5 2.5V4h-5v-.5A2.5 2.5 0 0 1 8 1m3.5 3v-.5a3.5 3.5 0 1 0-7 0V4H1v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4zM2 5h12v9a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1z"/>
</svg>

After

Width:  |  Height:  |  Size: 304 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-calendar3" viewBox="0 0 16 16">
<path d="M14 0H2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2M1 3.857C1 3.384 1.448 3 2 3h12c.552 0 1 .384 1 .857v10.286c0 .473-.448.857-1 .857H2c-.552 0-1-.384-1-.857z"/>
<path d="M6.5 7a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m-9 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m-9 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2"/>
</svg>

After

Width:  |  Height:  |  Size: 664 B