feat: add BatchEdit page and extract reusable batch sub-components from BatchUpload
fix: TrackNumber sort case, stale _imagePath reset, skip Done rows on retry in BatchEdit
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
@using DeepDrftModels.Enums
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@inject IHttpClientFactory HttpClientFactory
|
||||
|
||||
<MudPaper Class="pa-6 mb-4" Elevation="2">
|
||||
<MudGrid>
|
||||
<MudItem xs="12" sm="6">
|
||||
<MudTextField Value="AlbumName" ValueChanged="@((string v) => AlbumNameChanged.InvokeAsync(v))"
|
||||
T="string" Label="Album Name" Required="true" RequiredError="Album Name is required"
|
||||
Variant="Variant.Outlined" Disabled="Disabled" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6">
|
||||
<MudTextField Value="Artist" ValueChanged="@((string v) => ArtistChanged.InvokeAsync(v))"
|
||||
T="string" Label="Artist" Required="true" RequiredError="Artist is required"
|
||||
Variant="Variant.Outlined" Disabled="Disabled" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6">
|
||||
<MudTextField Value="Genre" ValueChanged="@((string v) => GenreChanged.InvokeAsync(v))"
|
||||
T="string" Label="Genre" Variant="Variant.Outlined" Disabled="Disabled" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6">
|
||||
<MudTextField Value="ReleaseDate" ValueChanged="@((string v) => ReleaseDateChanged.InvokeAsync(v))"
|
||||
T="string" Label="Release Date (YYYY-MM-DD)" Placeholder="2024-01-15"
|
||||
Variant="Variant.Outlined" Disabled="Disabled" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6">
|
||||
<MudSelect T="ReleaseType" Value="ReleaseType" ValueChanged="@((ReleaseType v) => ReleaseTypeChanged.InvokeAsync(v))"
|
||||
Label="Release Type" Variant="Variant.Outlined" Disabled="Disabled">
|
||||
@foreach (var rt in Enum.GetValues<ReleaseType>())
|
||||
{
|
||||
<MudSelectItem T="ReleaseType" Value="rt">@rt</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6">
|
||||
<MudField Label="Cover Art" Variant="Variant.Outlined" InnerPadding="false">
|
||||
<MudStack Spacing="3">
|
||||
@if (SelectedImageFile is { } selectedImage)
|
||||
{
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||
<MudText Typo="Typo.body2" Color="Color.Default">Selected: @selectedImage.Name</MudText>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Clear"
|
||||
Color="Color.Error"
|
||||
Size="Size.Small"
|
||||
Disabled="Disabled"
|
||||
OnClick="ClearSelectedFile"
|
||||
aria-label="Cancel image selection" />
|
||||
</MudStack>
|
||||
}
|
||||
else if (ExistingImagePreviewUrl is { } previewUrl)
|
||||
{
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||
<MudImage Src="@previewUrl"
|
||||
Alt="Current cover art"
|
||||
Elevation="1"
|
||||
Style="max-width: 120px; height: auto; border-radius: 4px;" />
|
||||
<MudText Typo="Typo.body2" Color="Color.Default">Current cover art.</MudText>
|
||||
</MudStack>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudText Typo="Typo.body2" Color="Color.Default">No cover art — optional.</MudText>
|
||||
}
|
||||
|
||||
<InputFile OnChange="HandleImageFileSelected" accept="image/*" disabled="@Disabled" />
|
||||
@if (SelectedImageFile is not null)
|
||||
{
|
||||
<MudText Typo="Typo.caption">Will upload on submit.</MudText>
|
||||
}
|
||||
</MudStack>
|
||||
</MudField>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</MudPaper>
|
||||
|
||||
@code {
|
||||
[Parameter] public string AlbumName { get; set; } = string.Empty;
|
||||
[Parameter] public EventCallback<string> AlbumNameChanged { get; set; }
|
||||
[Parameter] public string Artist { get; set; } = string.Empty;
|
||||
[Parameter] public EventCallback<string> ArtistChanged { get; set; }
|
||||
[Parameter] public string Genre { get; set; } = string.Empty;
|
||||
[Parameter] public EventCallback<string> GenreChanged { get; set; }
|
||||
[Parameter] public string ReleaseDate { get; set; } = string.Empty;
|
||||
[Parameter] public EventCallback<string> ReleaseDateChanged { get; set; }
|
||||
[Parameter] public ReleaseType ReleaseType { get; set; } = ReleaseType.Single;
|
||||
[Parameter] public EventCallback<ReleaseType> ReleaseTypeChanged { get; set; }
|
||||
[Parameter] public IBrowserFile? SelectedImageFile { get; set; }
|
||||
[Parameter] public EventCallback<IBrowserFile?> SelectedImageFileChanged { get; set; }
|
||||
|
||||
// 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.
|
||||
[Parameter] public string? ExistingImagePath { get; set; }
|
||||
|
||||
[Parameter] public bool Disabled { get; set; }
|
||||
|
||||
// The image endpoint (GET api/image/{entryKey}) is unauthenticated, so the browser hits
|
||||
// DeepDrftAPI directly. Base address comes from the same named client the CMS uses.
|
||||
private string? ExistingImagePreviewUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(ExistingImagePath)) return null;
|
||||
var baseAddress = HttpClientFactory.CreateClient("DeepDrft.Content.Cms").BaseAddress;
|
||||
return baseAddress is null
|
||||
? null
|
||||
: new Uri(baseAddress, $"api/image/{Uri.EscapeDataString(ExistingImagePath)}").ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private Task HandleImageFileSelected(InputFileChangeEventArgs e) =>
|
||||
SelectedImageFileChanged.InvokeAsync(e.File);
|
||||
|
||||
private Task ClearSelectedFile() =>
|
||||
SelectedImageFileChanged.InvokeAsync(null);
|
||||
}
|
||||
Reference in New Issue
Block a user