Init
This commit is contained in:
129
Prefab.Web/Gateway/Shared/NavMenu.cs
Normal file
129
Prefab.Web/Gateway/Shared/NavMenu.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using FluentValidation;
|
||||
using Prefab.Endpoints;
|
||||
using Prefab.Shared.Catalog;
|
||||
using Prefab.Shared.Catalog.Products;
|
||||
using Prefab.Web.Client.Models.Shared;
|
||||
using Prefab.Web.Client.Services;
|
||||
using UrlBuilder = Prefab.Web.UrlPolicy.UrlPolicy;
|
||||
|
||||
namespace Prefab.Web.Gateway.Shared;
|
||||
|
||||
public static class NavMenu
|
||||
{
|
||||
public class Endpoints : IEndpointRegistrar
|
||||
{
|
||||
public void MapEndpoints(IEndpointRouteBuilder endpoints)
|
||||
{
|
||||
endpoints.MapGet("/bff/nav-menu/{depth:int}", async (INavMenuService service, int depth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
var result = await service.Get(depth, cancellationToken);
|
||||
var status = result.Problem?.StatusCode ?? (result.IsSuccess
|
||||
? StatusCodes.Status200OK
|
||||
: StatusCodes.Status500InternalServerError);
|
||||
return Results.Json(result, statusCode: status);
|
||||
})
|
||||
.WithName(nameof(NavMenu));
|
||||
}
|
||||
}
|
||||
|
||||
public class Service(IModuleClient moduleClient) : INavMenuService
|
||||
{
|
||||
private const int MaxItemsPerColumn = 8;
|
||||
|
||||
public async Task<Result<NavMenuModel>> Get(int depth, CancellationToken cancellationToken)
|
||||
{
|
||||
var normalizedDepth = Math.Clamp(depth, 1, 2);
|
||||
|
||||
try
|
||||
{
|
||||
var response = await moduleClient.Product.GetCategoryTree(normalizedDepth, cancellationToken);
|
||||
var columns = BuildColumns(response.Categories);
|
||||
var model = new NavMenuModel(normalizedDepth, MaxItemsPerColumn, columns);
|
||||
return Result<NavMenuModel>.Success(model);
|
||||
}
|
||||
catch (ValidationException exception)
|
||||
{
|
||||
var problem = ResultProblem.FromValidation(exception);
|
||||
return Result<NavMenuModel>.Failure(problem);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var problem = ResultProblem.Unexpected("Failed to load navigation menu.", exception);
|
||||
return Result<NavMenuModel>.Failure(problem);
|
||||
}
|
||||
}
|
||||
|
||||
private static IReadOnlyList<NavMenuColumnModel> BuildColumns(IReadOnlyList<GetCategoryModels.CategoryNodeDto>? roots)
|
||||
{
|
||||
if (roots is null || roots.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var columns = new List<NavMenuColumnModel>(roots.Count);
|
||||
columns.AddRange(roots.Select(BuildColumn));
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
private static NavMenuColumnModel BuildColumn(GetCategoryModels.CategoryNodeDto root)
|
||||
{
|
||||
var rootSlug = NormalizeSlug(root.Slug);
|
||||
var isRootLeaf = root.IsLeaf;
|
||||
var rootUrl = isRootLeaf
|
||||
? UrlBuilder.BrowseProducts(rootSlug)
|
||||
: UrlBuilder.BrowseCategory(rootSlug);
|
||||
|
||||
var rootLink = new NavMenuLinkModel(
|
||||
root.Id.ToString(),
|
||||
root.Name,
|
||||
rootSlug,
|
||||
isRootLeaf,
|
||||
rootUrl);
|
||||
|
||||
var children = (root.Children ?? [])
|
||||
.OrderBy(child => child.DisplayOrder)
|
||||
.ThenBy(child => child.Name, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
var featured = children
|
||||
.Where(child => child.IsFeatured)
|
||||
.ToList();
|
||||
|
||||
var menuCandidates = featured.Count > 0 ? featured : children;
|
||||
|
||||
var items = menuCandidates
|
||||
.Take(MaxItemsPerColumn)
|
||||
.Select(ToLink)
|
||||
.ToList();
|
||||
|
||||
var hasMore = menuCandidates.Count > items.Count;
|
||||
var seeAllUrl = isRootLeaf
|
||||
? UrlBuilder.BrowseProducts(rootSlug)
|
||||
: UrlBuilder.BrowseCategory(rootSlug);
|
||||
|
||||
return new NavMenuColumnModel(rootLink, items, hasMore, seeAllUrl);
|
||||
}
|
||||
|
||||
private static NavMenuLinkModel ToLink(GetCategoryModels.CategoryNodeDto node)
|
||||
{
|
||||
var slug = NormalizeSlug(node.Slug);
|
||||
var isLeaf = node.IsLeaf;
|
||||
var url = isLeaf
|
||||
? UrlBuilder.BrowseProducts(slug)
|
||||
: UrlBuilder.BrowseCategory(slug);
|
||||
|
||||
return new NavMenuLinkModel(
|
||||
node.Id.ToString(),
|
||||
node.Name,
|
||||
slug,
|
||||
isLeaf,
|
||||
url);
|
||||
}
|
||||
|
||||
private static string NormalizeSlug(string slug) =>
|
||||
string.IsNullOrWhiteSpace(slug)
|
||||
? string.Empty
|
||||
: slug.Trim().ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user