From ef394f80674bee2411ffc7a18e783f5d8a4fd824 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Mon, 11 May 2026 11:32:25 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20Api+App:=20Chunk=20B=20=E2=80=94=20P?= =?UTF-8?q?ATCH=20/menus/{key}=20+=20DTO=20extend=20isVisible/displayLabel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Session 20 turn 7 Chunk B. BE API cho admin Ẩn/Hiện + Đổi tên menu fe-user. DTO (MenuDtos.cs): - MenuNodeDto +IsVisible bool +DisplayLabel string? - MenuItemDto +IsVisible bool +DisplayLabel string? GetMyMenuTreeQueryHandler: - Pass m.IsVisible + m.DisplayLabel vào MenuNodeDto record - KHÔNG filter IsVisible server-side (FE 2 app tự filter — fe-admin render hết, fe-user filter ẩn). Lý do: 1 endpoint serve cả 2 FE. ListMenuItemsQueryHandler: +IsVisible +DisplayLabel trong Select projection. NEW UpdateMenuItemCommand + Validator + Handler (PermissionFeatures.cs): - Body: { Key, IsVisible, DisplayLabel? } - Validator: Key required + max 50, DisplayLabel max 200 - Handler: load MenuItem by Key (NotFoundException nếu missing), set IsVisible + DisplayLabel (whitespace → null normalize), SaveChangesAsync MenusController +PATCH /api/menus/{key}: - [Authorize(Policy = "Permissions.Update")] — reuse policy admin matrix - Body: UpdateMenuItemRequest { IsVisible, DisplayLabel? } - Send UpdateMenuItemCommand qua MediatR - Return 204 NoContent Verify: - dotnet build SolutionErp.slnx — 0 err (1 warn cũ DocxRenderer không liên quan) Pending Chunk C: FE Admin MenuVisibilityPage Pending Chunk D: FE Layout fe-user filter + render displayLabel Pending Chunk E: Docs S20 turn 7 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Controllers/MenusController.cs | 13 +++++++ .../Permissions/Dtos/MenuDtos.cs | 6 +++- .../Permissions/PermissionFeatures.cs | 35 ++++++++++++++++++- .../GetMyMenuTree/GetMyMenuTreeQuery.cs | 1 + 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/Backend/SolutionErp.Api/Controllers/MenusController.cs b/src/Backend/SolutionErp.Api/Controllers/MenusController.cs index 1131a6a..aa2a153 100644 --- a/src/Backend/SolutionErp.Api/Controllers/MenusController.cs +++ b/src/Backend/SolutionErp.Api/Controllers/MenusController.cs @@ -19,4 +19,17 @@ public class MenusController(IMediator mediator) : ControllerBase [HttpGet] public async Task>> List(CancellationToken ct) => Ok(await mediator.Send(new ListMenuItemsQuery(), ct)); + + // [Mig 27] Admin Ẩn/Hiện + Đổi tên hiển thị menu cho fe-user. Body shape khớp + // UpdateMenuItemCommand. Authorize policy "Permissions.Update" (admin matrix + // quản lý phân quyền — cùng scope). + public record UpdateMenuItemRequest(bool IsVisible, string? DisplayLabel); + + [HttpPatch("{key}")] + [Authorize(Policy = "Permissions.Update")] + public async Task Update(string key, [FromBody] UpdateMenuItemRequest body, CancellationToken ct) + { + await mediator.Send(new UpdateMenuItemCommand(key, body.IsVisible, body.DisplayLabel), ct); + return NoContent(); + } } diff --git a/src/Backend/SolutionErp.Application/Permissions/Dtos/MenuDtos.cs b/src/Backend/SolutionErp.Application/Permissions/Dtos/MenuDtos.cs index f34e525..dfb00d1 100644 --- a/src/Backend/SolutionErp.Application/Permissions/Dtos/MenuDtos.cs +++ b/src/Backend/SolutionErp.Application/Permissions/Dtos/MenuDtos.cs @@ -10,6 +10,8 @@ public record MenuNodeDto( bool CanCreate, bool CanUpdate, bool CanDelete, + bool IsVisible, // [Mig 27] admin ẩn menu cho fe-user; fe-admin ignore filter này + string? DisplayLabel, // [Mig 27] override label cho fe-user; fe-admin luôn render Label gốc List Children); public record PermissionDto( @@ -33,4 +35,6 @@ public record MenuItemDto( string Label, string? ParentKey, int Order, - string? Icon); + string? Icon, + bool IsVisible, + string? DisplayLabel); diff --git a/src/Backend/SolutionErp.Application/Permissions/PermissionFeatures.cs b/src/Backend/SolutionErp.Application/Permissions/PermissionFeatures.cs index 7a1de2b..aa5f8a1 100644 --- a/src/Backend/SolutionErp.Application/Permissions/PermissionFeatures.cs +++ b/src/Backend/SolutionErp.Application/Permissions/PermissionFeatures.cs @@ -19,11 +19,44 @@ public class ListMenuItemsQueryHandler(IApplicationDbContext db) : IRequestHandl { return await db.MenuItems.AsNoTracking() .OrderBy(m => m.Order) - .Select(m => new MenuItemDto(m.Key, m.Label, m.ParentKey, m.Order, m.Icon)) + .Select(m => new MenuItemDto(m.Key, m.Label, m.ParentKey, m.Order, m.Icon, m.IsVisible, m.DisplayLabel)) .ToListAsync(ct); } } +// ========== Update menu visibility + display label (admin only) ========== +// Session 20 Mig 27: admin Ẩn/Hiện + Đổi tên hiển thị menu cho fe-user (eOffice). +// fe-admin sidebar luôn render Label gốc (KHÔNG dùng DisplayLabel) — user Q2=b. +public record UpdateMenuItemCommand( + string Key, + bool IsVisible, + string? DisplayLabel) : IRequest; + +public class UpdateMenuItemCommandValidator : AbstractValidator +{ + public UpdateMenuItemCommandValidator() + { + RuleFor(x => x.Key).NotEmpty().MaximumLength(50); + RuleFor(x => x.DisplayLabel).MaximumLength(200); + } +} + +public class UpdateMenuItemCommandHandler(IApplicationDbContext db) : IRequestHandler +{ + public async Task Handle(UpdateMenuItemCommand request, CancellationToken ct) + { + var menu = await db.MenuItems.FirstOrDefaultAsync(m => m.Key == request.Key, ct) + ?? throw new NotFoundException("MenuItem", request.Key); + + menu.IsVisible = request.IsVisible; + menu.DisplayLabel = string.IsNullOrWhiteSpace(request.DisplayLabel) + ? null + : request.DisplayLabel.Trim(); + + await db.SaveChangesAsync(ct); + } +} + // ========== List permissions by role (matrix edit) ========== public record ListPermissionsByRoleQuery(Guid RoleId) : IRequest>; diff --git a/src/Backend/SolutionErp.Application/Permissions/Queries/GetMyMenuTree/GetMyMenuTreeQuery.cs b/src/Backend/SolutionErp.Application/Permissions/Queries/GetMyMenuTree/GetMyMenuTreeQuery.cs index e3c2099..6008340 100644 --- a/src/Backend/SolutionErp.Application/Permissions/Queries/GetMyMenuTree/GetMyMenuTreeQuery.cs +++ b/src/Backend/SolutionErp.Application/Permissions/Queries/GetMyMenuTree/GetMyMenuTreeQuery.cs @@ -85,6 +85,7 @@ public class GetMyMenuTreeQueryHandler( return new MenuNodeDto(m.Key, m.Label, m.ParentKey, m.Order, m.Icon, flags.Item1, flags.Item2, flags.Item3, flags.Item4, + m.IsVisible, m.DisplayLabel, BuildChildren(m.Key, nextInherit)); }) .ToList();