Files
deepdrft/DeepDrftManager/Components/Pages/Tracks/CmsMixBrowser.razor
T
daniel-c-harvey a7e2335c20 Add Edit action to medium browsers; extract CmsMediumBrowserBase + CmsMediumTable
Session/Mix browsers share base (load/state/thumb) and a shared table shell carrying the per-row Edit link to BatchEdit; subclasses supply only their medium action.
2026-06-13 11:08:43 -04:00

109 lines
4.0 KiB
Plaintext

@page "/tracks/mixes"
@inherits CmsMediumBrowserBase<CmsMixBrowser.MixRow>
@using DeepDrftModels.DTOs
@using DeepDrftModels.Enums
@attribute [Authorize]
@inject ILogger<CmsMixBrowser> Logger
<PageTitle>Mixes — DeepDrft CMS</PageTitle>
<MudContainer MaxWidth="MaxWidth.Large" Class="mt-8">
<MudButton Variant="Variant.Text"
StartIcon="@Icons.Material.Filled.ArrowBack"
Href="/tracks/archive"
Class="mb-4">
Back to Release Archive
</MudButton>
<MudText Typo="Typo.h4" GutterBottom="true">Mixes</MudText>
<CmsMediumTable TRow="MixRow"
Rows="Rows"
Loading="Loading"
ReleaseAccessor="@(row => row.Release)"
ThumbUrl="@(key => ThumbUrl(key))"
TitleHeader="Mix"
EmptyMessage="No mixes found.">
<ActionContent Context="row">
@if (row.HasWaveform)
{
<MudTooltip Text="Waveform generated">
<MudIcon Icon="@Icons.Material.Filled.CheckCircle" Color="Color.Success" Size="Size.Small" />
</MudTooltip>
}
else
{
<MudTooltip Text="No waveform — incomplete">
<MudIcon Icon="@Icons.Material.Filled.Cancel" Color="Color.Warning" Size="Size.Small" />
</MudTooltip>
}
<MudButton Variant="Variant.Outlined"
Size="Size.Small"
StartIcon="@Icons.Material.Filled.GraphicEq"
Disabled="@row.IsGenerating"
OnClick="@(() => GenerateWaveformAsync(row))">
@if (row.IsGenerating)
{
<MudProgressCircular Class="mr-2" Size="Size.Small" Indeterminate="true" />
<span>Generating…</span>
}
else
{
<span>@(row.HasWaveform ? "Regenerate" : "Generate")</span>
}
</MudButton>
</ActionContent>
</CmsMediumTable>
</MudContainer>
@code {
protected override ReleaseMedium Medium => ReleaseMedium.Mix;
protected override string MediumNoun => "mixes";
protected override MixRow ToRow(ReleaseDto release) => new()
{
Release = release,
HasWaveform = !string.IsNullOrEmpty(release.MixMetadata?.WaveformEntryKey)
};
private async Task GenerateWaveformAsync(MixRow row)
{
row.IsGenerating = true;
StateHasChanged();
try
{
var result = await CmsReleaseService.GenerateMixWaveformAsync(row.Release.Id);
if (result.Success)
{
// Optimistic update: the trigger succeeded, so the waveform is stored. Unlike SessionBrowser's
// re-fetch (which retrieves the server-generated HeroImageEntryKey), there is nothing to reflect
// back here — HasWaveform is derived from WaveformEntryKey being non-null, which we know is now set.
row.HasWaveform = true;
Snackbar.Add($"Generated waveform for '{row.Release.Title}'.", Severity.Success);
}
else
{
var error = result.Messages.FirstOrDefault()?.Message ?? "Unknown error";
Snackbar.Add($"Waveform generation failed for '{row.Release.Title}': {error}", Severity.Error);
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Waveform generation failed for release {ReleaseId}", row.Release.Id);
Snackbar.Add($"Waveform generation failed for '{row.Release.Title}' — please try again.", Severity.Error);
}
finally
{
row.IsGenerating = false;
StateHasChanged();
}
}
public sealed class MixRow
{
public required ReleaseDto Release { get; set; }
public bool HasWaveform { get; set; }
public bool IsGenerating { get; set; }
}
}