fix(cms): gate Session hero input to upload path; warn (not error) on missing hero
Edit forms (BatchEdit/TrackEdit/TrackNew) show the guidance alert instead of an inert picker, via an AllowHeroUpload flag. Missing-hero nudge is Severity.Warning; null-ReleaseId hero drop is now logged.
This commit is contained in:
@@ -68,6 +68,7 @@
|
|||||||
@bind-ReleaseType="ReleaseTypeBinding"
|
@bind-ReleaseType="ReleaseTypeBinding"
|
||||||
HeroImageFile="HeroImageFile"
|
HeroImageFile="HeroImageFile"
|
||||||
HeroImageFileChanged="HeroImageFileChanged"
|
HeroImageFileChanged="HeroImageFileChanged"
|
||||||
|
AllowHeroUpload="AllowHeroUpload"
|
||||||
Disabled="Disabled" />
|
Disabled="Disabled" />
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
|
||||||
@@ -92,6 +93,10 @@
|
|||||||
[Parameter] public IBrowserFile? HeroImageFile { get; set; }
|
[Parameter] public IBrowserFile? HeroImageFile { get; set; }
|
||||||
[Parameter] public EventCallback<IBrowserFile?> HeroImageFileChanged { get; set; }
|
[Parameter] public EventCallback<IBrowserFile?> HeroImageFileChanged { get; set; }
|
||||||
|
|
||||||
|
// Gates the hero file picker in SessionFields (threaded to MediumFields → SessionFields).
|
||||||
|
// Set true only on the BatchUpload create path; leave false/absent on all edit paths.
|
||||||
|
[Parameter] public bool AllowHeroUpload { get; set; }
|
||||||
|
|
||||||
// BatchEdit only: when set (and no new file picked), preview the release's current cover.
|
// BatchEdit only: when set (and no new file picked), preview the release's current cover.
|
||||||
// The parent nulls this to drop the preview when the admin clears the existing cover.
|
// The parent nulls this to drop the preview when the admin clears the existing cover.
|
||||||
[Parameter] public string? ExistingImagePath { get; set; }
|
[Parameter] public string? ExistingImagePath { get; set; }
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
MediumChanged="OnMediumChanged"
|
MediumChanged="OnMediumChanged"
|
||||||
@bind-SelectedImageFile="_selectedImageFile"
|
@bind-SelectedImageFile="_selectedImageFile"
|
||||||
@bind-HeroImageFile="_heroImageFile"
|
@bind-HeroImageFile="_heroImageFile"
|
||||||
|
AllowHeroUpload="true"
|
||||||
Disabled="_uploading" />
|
Disabled="_uploading" />
|
||||||
|
|
||||||
@if (_medium == ReleaseMedium.Cut)
|
@if (_medium == ReleaseMedium.Cut)
|
||||||
@@ -72,6 +73,11 @@
|
|||||||
<MudAlert Severity="Severity.Error" Class="mt-4">@_errorMessage</MudAlert>
|
<MudAlert Severity="Severity.Error" Class="mt-4">@_errorMessage</MudAlert>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(_warningMessage))
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Warning" Class="mt-4">@_warningMessage</MudAlert>
|
||||||
|
}
|
||||||
|
|
||||||
<MudStack Row="true" Justify="Justify.FlexEnd" Spacing="2" Class="mt-4">
|
<MudStack Row="true" Justify="Justify.FlexEnd" Spacing="2" Class="mt-4">
|
||||||
<MudButton Variant="Variant.Text"
|
<MudButton Variant="Variant.Text"
|
||||||
OnClick="@(() => Navigation.NavigateTo("/tracks"))"
|
OnClick="@(() => Navigation.NavigateTo("/tracks"))"
|
||||||
@@ -105,6 +111,8 @@
|
|||||||
private bool _uploading;
|
private bool _uploading;
|
||||||
private int _uploadedCount;
|
private int _uploadedCount;
|
||||||
private string? _errorMessage;
|
private string? _errorMessage;
|
||||||
|
// Separate from _errorMessage: a soft non-blocking nudge (Severity.Warning), not a hard failure.
|
||||||
|
private string? _warningMessage;
|
||||||
|
|
||||||
private IBrowserFile? _selectedImageFile;
|
private IBrowserFile? _selectedImageFile;
|
||||||
private string? _imagePath;
|
private string? _imagePath;
|
||||||
@@ -205,6 +213,7 @@
|
|||||||
private async Task SubmitAsync()
|
private async Task SubmitAsync()
|
||||||
{
|
{
|
||||||
_errorMessage = null;
|
_errorMessage = null;
|
||||||
|
_warningMessage = null;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(_albumName))
|
if (string.IsNullOrWhiteSpace(_albumName))
|
||||||
{
|
{
|
||||||
@@ -258,7 +267,7 @@
|
|||||||
if (_medium == ReleaseMedium.Session && _heroImageFile is null && !_heroWarningAcknowledged)
|
if (_medium == ReleaseMedium.Session && _heroImageFile is null && !_heroWarningAcknowledged)
|
||||||
{
|
{
|
||||||
_heroWarningAcknowledged = true;
|
_heroWarningAcknowledged = true;
|
||||||
_errorMessage = "No hero image selected. A Session usually needs one — you can add it now, "
|
_warningMessage = "No hero image selected. A Session usually needs one — you can add it now, "
|
||||||
+ "or submit again to create the Session without it (set the hero later from the Sessions browser).";
|
+ "or submit again to create the Session without it (set the hero later from the Sessions browser).";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -382,6 +391,15 @@
|
|||||||
Snackbar.Add("Session uploaded, but the hero image failed. Set it from the Sessions browser.", Severity.Warning);
|
Snackbar.Add("Session uploaded, but the hero image failed. Set it from the Sessions browser.", Severity.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (_medium == ReleaseMedium.Session && _heroImageFile is not null)
|
||||||
|
{
|
||||||
|
// ReleaseId was null on a Session track result — internal inconsistency.
|
||||||
|
// Hero file is held but cannot be uploaded without a release id; log and
|
||||||
|
// surface so the admin can set it from the Sessions browser.
|
||||||
|
Logger.LogWarning("Batch upload: Session track '{TrackName}' (id={Id}) has no ReleaseId — hero image dropped",
|
||||||
|
row.TrackName, result.Value.Id);
|
||||||
|
Snackbar.Add("Session uploaded, but the hero image could not be linked (no release id). Set it from the Sessions browser.", Severity.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
// Mix uploads fire the server-side high-res waveform trigger (§3.4). The CMS
|
// Mix uploads fire the server-side high-res waveform trigger (§3.4). The CMS
|
||||||
// computes nothing — the API derives the datum from the audio it just stored.
|
// computes nothing — the API derives the datum from the audio it just stored.
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
case ReleaseMedium.Session:
|
case ReleaseMedium.Session:
|
||||||
<SessionFields HeroImageFile="HeroImageFile"
|
<SessionFields HeroImageFile="HeroImageFile"
|
||||||
HeroImageFileChanged="HeroImageFileChanged"
|
HeroImageFileChanged="HeroImageFileChanged"
|
||||||
|
AllowHeroUpload="AllowHeroUpload"
|
||||||
Disabled="Disabled" />
|
Disabled="Disabled" />
|
||||||
break;
|
break;
|
||||||
case ReleaseMedium.Mix:
|
case ReleaseMedium.Mix:
|
||||||
@@ -49,5 +50,9 @@
|
|||||||
[Parameter] public IBrowserFile? HeroImageFile { get; set; }
|
[Parameter] public IBrowserFile? HeroImageFile { get; set; }
|
||||||
[Parameter] public EventCallback<IBrowserFile?> HeroImageFileChanged { get; set; }
|
[Parameter] public EventCallback<IBrowserFile?> HeroImageFileChanged { get; set; }
|
||||||
|
|
||||||
|
// Gates the hero file picker in SessionFields. True on the BatchUpload create path;
|
||||||
|
// false/absent on all edit paths so SessionFields falls back to the guidance alert.
|
||||||
|
[Parameter] public bool AllowHeroUpload { get; set; }
|
||||||
|
|
||||||
[Parameter] public bool Disabled { get; set; }
|
[Parameter] public bool Disabled { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Forms
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
|
||||||
@* Session-medium fields. The hero image is resource-addressed (POST api/release/{id}/session/hero-image)
|
@* Session-medium fields. When AllowHeroUpload is true (BatchUpload create path), the hero image
|
||||||
and therefore cannot be uploaded until the release exists. The file is selected here, held by the
|
file picker is shown — the file is held by the parent and POSTed after the release is created.
|
||||||
parent form, and uploaded after the create call returns a release id — the same deferred-upload
|
When false (edit paths: BatchEdit, TrackEdit, TrackNew), the original guidance alert is rendered
|
||||||
pattern AlbumHeaderFields uses for cover art ("Will upload on submit"). The hero is optional; the
|
instead, directing the admin to the Sessions browser per-row replace action. This gate prevents
|
||||||
parent warns (does not block) when a Session is submitted without one. *@
|
a dead/inert control on edit forms that do not wire the hero callbacks. *@
|
||||||
<MudItem xs="12">
|
@if (AllowHeroUpload)
|
||||||
|
{
|
||||||
|
<MudItem xs="12">
|
||||||
<MudField Label="Hero Image" Variant="Variant.Outlined" InnerPadding="false">
|
<MudField Label="Hero Image" Variant="Variant.Outlined" InnerPadding="false">
|
||||||
<MudStack Spacing="3">
|
<MudStack Spacing="3">
|
||||||
<MudText Typo="Typo.body2" Color="Color.Default">
|
<MudText Typo="Typo.body2" Color="Color.Default">
|
||||||
@@ -36,13 +38,28 @@
|
|||||||
}
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
</MudField>
|
</MudField>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudItem xs="12">
|
||||||
|
<MudAlert Severity="Severity.Info" Dense="true" Variant="Variant.Outlined">
|
||||||
|
Sessions are single-track live releases. After upload, set the hero image from the
|
||||||
|
<strong>Release Archive → Sessions</strong> browser.
|
||||||
|
</MudAlert>
|
||||||
|
</MudItem>
|
||||||
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public IBrowserFile? HeroImageFile { get; set; }
|
[Parameter] public IBrowserFile? HeroImageFile { get; set; }
|
||||||
[Parameter] public EventCallback<IBrowserFile?> HeroImageFileChanged { get; set; }
|
[Parameter] public EventCallback<IBrowserFile?> HeroImageFileChanged { get; set; }
|
||||||
[Parameter] public bool Disabled { get; set; }
|
[Parameter] public bool Disabled { get; set; }
|
||||||
|
|
||||||
|
// When true (BatchUpload create path), render the hero file picker.
|
||||||
|
// When false/absent (edit paths), render the guidance alert directing the admin to the
|
||||||
|
// Sessions browser — no dead control where callbacks are unwired.
|
||||||
|
[Parameter] public bool AllowHeroUpload { get; set; }
|
||||||
|
|
||||||
private Task HandleHeroFileSelected(InputFileChangeEventArgs e) =>
|
private Task HandleHeroFileSelected(InputFileChangeEventArgs e) =>
|
||||||
HeroImageFileChanged.InvokeAsync(e.File);
|
HeroImageFileChanged.InvokeAsync(e.File);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user