Added image deletion functionality to product creation
continuous-integration/drone/pr Build is passing

This commit is contained in:
Khwezi Mngoma
2026-05-20 16:12:10 +02:00
parent 5c34663617
commit 530b8ffea2
2 changed files with 50 additions and 31 deletions
+42 -25
View File
@@ -1,6 +1,4 @@
using LiteCharms.Features.S3.Abstractions; using LiteCharms.Features.S3.Abstractions;
using Microsoft.AspNetCore.Components.Forms;
using System.ComponentModel.DataAnnotations;
using static LiteCharms.Features.S3.Constants; using static LiteCharms.Features.S3.Constants;
namespace ShopAdmin.Components; namespace ShopAdmin.Components;
@@ -16,6 +14,8 @@ public partial class CreateProduct([FromKeyedServices(BookshopBucketName)] IS3Se
private const long MaxAllowedFileSize = 1024 * 1024 * 5; private const long MaxAllowedFileSize = 1024 * 1024 * 5;
private readonly Func<string, string> GetFileKeyFromUrl = url => url.Split('/').Last();
protected override void OnInitialized() protected override void OnInitialized()
{ {
base.OnInitialized(); base.OnInitialized();
@@ -26,15 +26,11 @@ public partial class CreateProduct([FromKeyedServices(BookshopBucketName)] IS3Se
ProductModel.Thumbnails = [.. Enumerable.Repeat(string.Empty, 5)]; ProductModel.Thumbnails = [.. Enumerable.Repeat(string.Empty, 5)];
} }
// Your saving logic goes here when the ledger button is clicked
public Task HandleValidSubmit() => Task.CompletedTask; public Task HandleValidSubmit() => Task.CompletedTask;
// Checks if a valid URL asset exists at the specified position public bool HasAssetAt(int index) => (ProductModel?.Thumbnails) != null && index < ProductModel.Thumbnails.Count &&
public bool HasAssetAt(int index) => ProductModel?.Thumbnails == null || index >= ProductModel.Thumbnails.Count !string.IsNullOrWhiteSpace(ProductModel.Thumbnails[index]);
? false
: !string.IsNullOrWhiteSpace(ProductModel.Thumbnails[index]);
// Handles uploading the primary image node
private async Task HandleMainImageUpload(InputFileChangeEventArgs e) private async Task HandleMainImageUpload(InputFileChangeEventArgs e)
{ {
try try
@@ -47,6 +43,8 @@ public partial class CreateProduct([FromKeyedServices(BookshopBucketName)] IS3Se
await file.OpenReadStream(MaxAllowedFileSize).CopyToAsync(stream, cancellationToken); await file.OpenReadStream(MaxAllowedFileSize).CopyToAsync(stream, cancellationToken);
stream.Seek(0, SeekOrigin.Begin);
var result = await s3Service.UploadFileAsync(file.Name, stream, var result = await s3Service.UploadFileAsync(file.Name, stream,
MimeTypes.GetMimeType(file.Name), cancellationToken); MimeTypes.GetMimeType(file.Name), cancellationToken);
@@ -65,20 +63,20 @@ public partial class CreateProduct([FromKeyedServices(BookshopBucketName)] IS3Se
public void SetPreviewActive(string? url) public void SetPreviewActive(string? url)
{ {
if (!string.IsNullOrWhiteSpace(url)) if (string.IsNullOrWhiteSpace(url)) return;
{
ActivePreviewUrl = url; ActivePreviewUrl = url;
StateHasChanged(); StateHasChanged();
} }
}
public void ClosePreviewDrawer() public void ClosePreviewDrawer()
{ {
ActivePreviewUrl = null; ActivePreviewUrl = null;
StateHasChanged(); StateHasChanged();
} }
// Handles uploading a thumbnail image into its specific slot index
private async Task HandleThumbnailUpload(InputFileChangeEventArgs e, int index) private async Task HandleThumbnailUpload(InputFileChangeEventArgs e, int index)
{ {
try try
@@ -91,6 +89,8 @@ public partial class CreateProduct([FromKeyedServices(BookshopBucketName)] IS3Se
await file.OpenReadStream(MaxAllowedFileSize, cancellationToken).CopyToAsync(stream, cancellationToken); await file.OpenReadStream(MaxAllowedFileSize, cancellationToken).CopyToAsync(stream, cancellationToken);
stream.Seek(0, SeekOrigin.Begin);
var result = await s3Service.UploadFileAsync(file.Name, stream, var result = await s3Service.UploadFileAsync(file.Name, stream,
MimeTypes.GetMimeType(file.Name), cancellationToken); MimeTypes.GetMimeType(file.Name), cancellationToken);
@@ -107,25 +107,42 @@ public partial class CreateProduct([FromKeyedServices(BookshopBucketName)] IS3Se
} }
} }
public void ClearMainImage() public async Task ClearMainImage()
{ {
if (ActivePreviewUrl == ProductModel.ImageUrl) if (string.IsNullOrEmpty(ProductModel.ImageUrl)) return;
{
ActivePreviewUrl = null; var targetUrl = ProductModel.ImageUrl;
}
if (ActivePreviewUrl == targetUrl) ActivePreviewUrl = null;
ProductModel.ImageUrl = null; ProductModel.ImageUrl = null;
StateHasChanged();
var result = await s3Service.DeleteFileAsync(GetFileKeyFromUrl(targetUrl));
if (!result.IsSuccess)
Console.WriteLine($"[S3 Orphan Cleanup Failure]: {result.Errors[0].Message}");
} }
public void RemoveThumbnailAt(int index) public async Task RemoveThumbnailAt(int index)
{ {
if (index >= 0 && index < ProductModel.Thumbnails.Count) if (index < 0 || index >= ProductModel.Thumbnails.Count) return;
{
if (ActivePreviewUrl == ProductModel.Thumbnails[index]) var targetUrl = ProductModel.Thumbnails[index];
{
ActivePreviewUrl = null; if (string.IsNullOrEmpty(targetUrl)) return;
}
if (ActivePreviewUrl == targetUrl) ActivePreviewUrl = null;
ProductModel.Thumbnails[index] = string.Empty; ProductModel.Thumbnails[index] = string.Empty;
}
StateHasChanged();
var result = await s3Service.DeleteFileAsync(GetFileKeyFromUrl(targetUrl));
if (result.IsFailed)
Console.WriteLine($"[S3 Thumbnail Cleanup Failure]: {result.Errors[0].Message}");
} }
} }
+3 -1
View File
@@ -16,7 +16,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="LiteCharms.Features" Version="1.39.0" /> <PackageReference Include="LiteCharms.Features" Version="1.40.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid" Version="10.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid" Version="10.0.8" />
<PackageReference Include="Polly" Version="8.6.6" /> <PackageReference Include="Polly" Version="8.6.6" />
</ItemGroup> </ItemGroup>
@@ -59,6 +59,8 @@
<!-- Shared Global Usings --> <!-- Shared Global Usings -->
<ItemGroup> <ItemGroup>
<Using Include="MimeKit" /> <Using Include="MimeKit" />
<Using Include="System.ComponentModel.DataAnnotations" />
<Using Include="Microsoft.AspNetCore.Components.Forms" />
<Using Include="Microsoft.AspNetCore.Components.QuickGrid" /> <Using Include="Microsoft.AspNetCore.Components.QuickGrid" />
<Using Include="Microsoft.AspNetCore.HttpOverrides" /> <Using Include="Microsoft.AspNetCore.HttpOverrides" />
<Using Include="Microsoft.AspNetCore.Authentication" /> <Using Include="Microsoft.AspNetCore.Authentication" />