30999726e5
Retire the Tracks list view; promote the Releases view to /releases with medium tabs (ALL/CUTS/SESSIONS/MIXES). Migrate bulk profile/high-res backfill and per-track waveform columns into the releases grids. Point catalogue cards at the three mediums. Remove dead BrowseMode/ViewModel.
104 lines
4.4 KiB
Plaintext
104 lines
4.4 KiB
Plaintext
@page "/catalogue"
|
|
@using DeepDrftManager.Services
|
|
@using DeepDrftModels.Enums
|
|
@attribute [Authorize]
|
|
@layout Layout.CmsLayout
|
|
@inject NavigationManager Nav
|
|
@inject ICmsReleaseService CmsReleaseService
|
|
@inject ILogger<Index> Logger
|
|
|
|
<PageTitle>DeepDrft CMS</PageTitle>
|
|
|
|
<MudContainer MaxWidth="MaxWidth.Large" Class="mt-8">
|
|
<MudText Typo="Typo.h3" Class="mb-6">Catalogue</MudText>
|
|
|
|
<MudGrid Spacing="4">
|
|
@foreach (var card in Cards)
|
|
{
|
|
<MudItem xs="12" sm="4">
|
|
@SummaryCard(card)
|
|
</MudItem>
|
|
}
|
|
</MudGrid>
|
|
</MudContainer>
|
|
|
|
@code {
|
|
// One card per release medium. Each deep-links to /releases with the medium tab pre-selected via the
|
|
// same ?medium= convention the Add Track buttons use. The count is that medium's release total.
|
|
private sealed record MediumCard(ReleaseMedium Medium, string Label, string Icon, Color Color);
|
|
|
|
private static readonly IReadOnlyList<MediumCard> Cards = new[]
|
|
{
|
|
new MediumCard(ReleaseMedium.Cut, "CUTS", Icons.Material.Filled.Album, Color.Primary),
|
|
new MediumCard(ReleaseMedium.Session, "SESSIONS", Icons.Material.Filled.Mic, Color.Secondary),
|
|
new MediumCard(ReleaseMedium.Mix, "MIXES", Icons.Material.Filled.GraphicEq, Color.Tertiary),
|
|
};
|
|
|
|
// Medium → release count (null while loading or on failure). Each medium's count is one cheap paged
|
|
// read (pageSize 1) for its TotalCount, run concurrently.
|
|
private readonly Dictionary<ReleaseMedium, int?> _counts = new();
|
|
private readonly HashSet<ReleaseMedium> _loading = Cards.Select(c => c.Medium).ToHashSet();
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
// Each loader calls StateHasChanged in its finally block so its card updates as soon as its own
|
|
// fetch returns, rather than blocking on the slowest of the three.
|
|
await Task.WhenAll(Cards.Select(c => LoadCountAsync(c.Medium)));
|
|
}
|
|
|
|
private async Task LoadCountAsync(ReleaseMedium medium)
|
|
{
|
|
try
|
|
{
|
|
// pageSize 1 — we only need TotalCount, not the rows. Sort column is required by the API but
|
|
// immaterial to the count.
|
|
var result = await CmsReleaseService.GetPagedAsync(
|
|
medium, page: 1, pageSize: 1, sortColumn: "Title", sortDescending: false);
|
|
_counts[medium] = result.Success && result.Value is not null ? result.Value.TotalCount : null;
|
|
if (!result.Success)
|
|
{
|
|
Logger.LogWarning("Dashboard {Medium} count failed: {Error}",
|
|
medium, result.Messages.FirstOrDefault()?.Message ?? "Unknown error");
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_loading.Remove(medium);
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
private RenderFragment SummaryCard(MediumCard card) => __builder =>
|
|
{
|
|
var loading = _loading.Contains(card.Medium);
|
|
var count = _counts.GetValueOrDefault(card.Medium);
|
|
<MudCard Elevation="8" Style="height: 100%;">
|
|
<MudCardContent>
|
|
<MudStack AlignItems="AlignItems.Center" Spacing="2" Class="py-4">
|
|
<MudIcon Icon="@card.Icon" Color="@card.Color" Size="Size.Large" />
|
|
@if (loading)
|
|
{
|
|
<MudProgressCircular Color="@card.Color" Indeterminate="true" Size="Size.Small" />
|
|
}
|
|
else
|
|
{
|
|
<MudText Typo="Typo.h3" Color="@card.Color">@(count?.ToString() ?? "—")</MudText>
|
|
}
|
|
<MudText Typo="Typo.subtitle1" Class="mud-text-secondary text-uppercase">@card.Label</MudText>
|
|
</MudStack>
|
|
</MudCardContent>
|
|
<MudCardActions Class="justify-center pb-4">
|
|
<MudButton Variant="Variant.Text" Color="@card.Color" EndIcon="@Icons.Material.Filled.ArrowForward"
|
|
OnClick="@(() => Nav.NavigateTo(ReleasesHref(card.Medium)))">
|
|
View
|
|
</MudButton>
|
|
</MudCardActions>
|
|
</MudCard>
|
|
};
|
|
|
|
// Deep-link to the Releases page with this medium's tab pre-selected. Mirrors the ?medium= seed the
|
|
// Add Track buttons use; the Releases page reads it to set the active tab.
|
|
private static string ReleasesHref(ReleaseMedium medium) =>
|
|
$"/releases?medium={medium.ToString().ToLowerInvariant()}";
|
|
}
|