Enhance metadata fetching and UI improvements
Build and Push Docker Image / build-and-push (push) Successful in 25s
Build and Push Docker Image / build-and-push (push) Successful in 25s
- Added `MetadataFetchSucceeded` and `MetadataFetchFailed` enums. - Improved metadata fetch logging with detailed outcomes. - Enhanced toolbar formatting options in `RegistryAdmin`. - Made `LanguageOptions` dynamic based on the environment. - Styled Quill text-size classes in public header content. - Improved error handling in `RegistryMetadataService`. - Adjusted supported cultures for development environments. - Updated localization for new metadata fetch statuses.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using BirthList.Domain.Entities;
|
||||
using BirthList.Web.Authorization;
|
||||
using BirthList.Web.Features.Registries;
|
||||
using BirthList.Web.Services;
|
||||
@@ -41,10 +42,11 @@ public partial class RegistryAdmin : ComponentBase
|
||||
protected string ActiveTab { get; set; } = "items";
|
||||
protected RenderFragment ToolbarContent => builder =>
|
||||
{
|
||||
builder.AddMarkupContent(0, "<span class='ql-formats'><select class='ql-header'><option selected></option><option value='1'></option><option value='2'></option></select></span>");
|
||||
builder.AddMarkupContent(1, "<span class='ql-formats'><button class='ql-bold'></button><button class='ql-italic'></button><button class='ql-underline'></button></span>");
|
||||
builder.AddMarkupContent(2, "<span class='ql-formats'><button class='ql-list' value='ordered'></button><button class='ql-list' value='bullet'></button></span>");
|
||||
builder.AddMarkupContent(3, "<span class='ql-formats'><button class='ql-link'></button><button class='ql-clean'></button></span>");
|
||||
builder.AddMarkupContent(0, @"<span class='ql-formats'><select class='ql-size'><option value='small'></option><option selected></option><option value='large'></option><option value='huge'></option></select><select class='ql-header'><option selected></option><option value='1'></option><option value='2'></option><option value='3'></option><option value='4'></option><option value='5'></option></select></span>");
|
||||
builder.AddMarkupContent(1, @"<span class='ql-formats'><button class='ql-bold'></button><button class='ql-italic'></button><button class='ql-underline'></button><button class='ql-strike'></button></span>");
|
||||
builder.AddMarkupContent(2, @"<span class='ql-formats'><select class=""ql-color""></select><select class=""ql-background""></select></span>");
|
||||
builder.AddMarkupContent(3, @"<span class='ql-formats'><button class='ql-list' value='ordered'></button><button class='ql-list' value='bullet'></button></span>");
|
||||
builder.AddMarkupContent(4, @"<span class='ql-formats'><button class='ql-link'></button><button class='ql-clean'></button></span>");
|
||||
};
|
||||
|
||||
private bool _pendingEditorLoad;
|
||||
@@ -583,41 +585,95 @@ public partial class RegistryAdmin : ComponentBase
|
||||
return;
|
||||
}
|
||||
|
||||
var metadata = await RegistryMetadataService.FetchAsync(ItemModel.ProductUrl, CancellationToken.None).ConfigureAwait(false);
|
||||
if (metadata is null)
|
||||
var userId = await RegistryUserContext.GetUserIdAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
var actorUserId = string.IsNullOrWhiteSpace(userId) ? "unknown-user" : userId;
|
||||
|
||||
async Task LogMetadataAsync(UserActionType actionType, string details)
|
||||
{
|
||||
return;
|
||||
try
|
||||
{
|
||||
await RegistryService.LogUserActionAsync(
|
||||
RegistryId,
|
||||
actorUserId,
|
||||
actionType,
|
||||
ItemModel.Id,
|
||||
TruncateLogDetails(details),
|
||||
CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// Keep autofetch UX stable even if logging cannot be persisted.
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(metadata.NormalizedUrl))
|
||||
try
|
||||
{
|
||||
ItemModel.ProductUrl = metadata.NormalizedUrl;
|
||||
var metadata = await RegistryMetadataService.FetchAsync(ItemModel.ProductUrl, CancellationToken.None).ConfigureAwait(false);
|
||||
if (metadata is null)
|
||||
{
|
||||
await LogMetadataAsync(UserActionType.MetadataFetchFailed, $"metadata-fetch failed; url={ItemModel.ProductUrl}; reason=no-metadata").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(metadata.NormalizedUrl))
|
||||
{
|
||||
ItemModel.ProductUrl = metadata.NormalizedUrl;
|
||||
}
|
||||
|
||||
if ((string.IsNullOrWhiteSpace(ItemModel.Name) || string.Equals(ItemModel.Name, "Amazon", StringComparison.OrdinalIgnoreCase)) && !string.IsNullOrWhiteSpace(metadata.Title))
|
||||
{
|
||||
ItemModel.Name = metadata.Title;
|
||||
}
|
||||
|
||||
if ((string.IsNullOrWhiteSpace(ItemModel.Description) || string.Equals(ItemModel.Description, "Amazon", StringComparison.OrdinalIgnoreCase)) && !string.IsNullOrWhiteSpace(metadata.Description))
|
||||
{
|
||||
ItemModel.Description = metadata.Description;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ItemModel.PictureUrl) && !string.IsNullOrWhiteSpace(metadata.ImageUrl))
|
||||
{
|
||||
ItemModel.PictureUrl = metadata.ImageUrl;
|
||||
}
|
||||
|
||||
if (!ItemModel.PriceAmount.HasValue && metadata.PriceAmount.HasValue)
|
||||
{
|
||||
ItemModel.PriceAmount = metadata.PriceAmount;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(metadata.CurrencyCode))
|
||||
{
|
||||
ItemModel.CurrencyCode = metadata.CurrencyCode;
|
||||
}
|
||||
|
||||
var summary = $"metadata-fetch success; url={ItemModel.ProductUrl}; title={(string.IsNullOrWhiteSpace(metadata.Title) ? "-" : metadata.Title)}; image={(string.IsNullOrWhiteSpace(metadata.ImageUrl) ? "no" : "yes")}; price={(metadata.PriceAmount.HasValue ? metadata.PriceAmount.Value.ToString("0.00") : "-")}; currency={(string.IsNullOrWhiteSpace(metadata.CurrencyCode) ? "-" : metadata.CurrencyCode)}";
|
||||
await LogMetadataAsync(UserActionType.MetadataFetchSucceeded, summary).ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
await LogMetadataAsync(UserActionType.MetadataFetchFailed, $"metadata-fetch failed; url={ItemModel.ProductUrl}; reason=http; message={ex.Message}").ConfigureAwait(false);
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
await LogMetadataAsync(UserActionType.MetadataFetchFailed, $"metadata-fetch failed; url={ItemModel.ProductUrl}; reason=timeout-or-cancel; message={ex.Message}").ConfigureAwait(false);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
await LogMetadataAsync(UserActionType.MetadataFetchFailed, $"metadata-fetch failed; url={ItemModel.ProductUrl}; reason=invalid-operation; message={ex.Message}").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await LogMetadataAsync(UserActionType.MetadataFetchFailed, $"metadata-fetch failed; url={ItemModel.ProductUrl}; reason=unexpected-{ex.GetType().Name}; message={ex.Message}").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static string TruncateLogDetails(string details)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(details))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if ((string.IsNullOrWhiteSpace(ItemModel.Name) || string.Equals(ItemModel.Name, "Amazon", StringComparison.OrdinalIgnoreCase)) && !string.IsNullOrWhiteSpace(metadata.Title))
|
||||
{
|
||||
ItemModel.Name = metadata.Title;
|
||||
}
|
||||
|
||||
if ((string.IsNullOrWhiteSpace(ItemModel.Description) || string.Equals(ItemModel.Description, "Amazon", StringComparison.OrdinalIgnoreCase)) && !string.IsNullOrWhiteSpace(metadata.Description))
|
||||
{
|
||||
ItemModel.Description = metadata.Description;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ItemModel.PictureUrl) && !string.IsNullOrWhiteSpace(metadata.ImageUrl))
|
||||
{
|
||||
ItemModel.PictureUrl = metadata.ImageUrl;
|
||||
}
|
||||
|
||||
if (!ItemModel.PriceAmount.HasValue && metadata.PriceAmount.HasValue)
|
||||
{
|
||||
ItemModel.PriceAmount = metadata.PriceAmount;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(metadata.CurrencyCode))
|
||||
{
|
||||
ItemModel.CurrencyCode = metadata.CurrencyCode;
|
||||
}
|
||||
return details.Length <= 500 ? details : details[..500];
|
||||
}
|
||||
|
||||
protected async Task CreateInviteAsync()
|
||||
|
||||
Reference in New Issue
Block a user