AppMenu : Implement Sidebar Menu with Permission-based Access Control #112
214
Marco.Pms.Helpers/CacheHelper/SidebarMenu.cs
Normal file
214
Marco.Pms.Helpers/CacheHelper/SidebarMenu.cs
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
using Marco.Pms.Model.AppMenu;
|
||||||
|
using Marco.Pms.Model.Dtos.AppMenu;
|
||||||
|
using Marco.Pms.Model.ViewModels.AppMenu;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MongoDB.Bson;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using static System.Collections.Specialized.BitVector32;
|
||||||
|
|
||||||
|
namespace Marco.Pms.CacheHelper
|
||||||
|
{
|
||||||
|
public class SideBarMenu
|
||||||
|
{
|
||||||
|
private readonly IMongoCollection<MenuSection> _collection;
|
||||||
|
private readonly ILogger<SideBarMenu> _logger;
|
||||||
|
|
||||||
|
public SideBarMenu(IConfiguration configuration, ILogger<SideBarMenu> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
var connectionString = configuration["MongoDB:ConnectionMenu"];
|
||||||
|
var mongoUrl = new MongoUrl(connectionString);
|
||||||
|
var client = new MongoClient(mongoUrl);
|
||||||
|
var database = client.GetDatabase(mongoUrl.DatabaseName);
|
||||||
|
_collection = database.GetCollection<MenuSection>("Menus");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MenuSection?> CreateMenuSectionAsync(MenuSection section)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _collection.InsertOneAsync(section);
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error occurred while adding MenuSection.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MenuSection?> UpdateMenuSectionAsync(Guid sectionId, MenuSection updatedSection)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var filter = Builders<MenuSection>.Filter.Eq(s => s.Id, sectionId);
|
||||||
|
|
||||||
|
var update = Builders<MenuSection>.Update
|
||||||
|
.Set(s => s.Header, updatedSection.Header)
|
||||||
|
.Set(s => s.Title, updatedSection.Title)
|
||||||
|
.Set(s => s.Items, updatedSection.Items);
|
||||||
|
|
||||||
|
var result = await _collection.UpdateOneAsync(filter, update);
|
||||||
|
|
||||||
|
if (result.ModifiedCount > 0)
|
||||||
|
{
|
||||||
|
return await _collection.Find(s => s.Id == sectionId).FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error updating MenuSection.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MenuSection?> AddMenuItemAsync(Guid sectionId, MenuItem newItem)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
newItem.Id = Guid.NewGuid();
|
||||||
|
|
||||||
|
var filter = Builders<MenuSection>.Filter.Eq(s => s.Id, sectionId);
|
||||||
|
|
||||||
|
var update = Builders<MenuSection>.Update.Push(s => s.Items, newItem);
|
||||||
|
|
||||||
|
var result = await _collection.UpdateOneAsync(filter, update);
|
||||||
|
|
||||||
|
if (result.ModifiedCount > 0)
|
||||||
|
{
|
||||||
|
return await _collection.Find(s => s.Id == sectionId).FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error adding menu item.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MenuItem?> UpdateMenuItemAsync(Guid sectionId, Guid itemId, MenuItem updatedItem)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var filter = Builders<MenuSection>.Filter.And(
|
||||||
|
Builders<MenuSection>.Filter.Eq(s => s.Id, sectionId),
|
||||||
|
Builders<MenuSection>.Filter.ElemMatch(s => s.Items, i => i.Id == itemId)
|
||||||
|
);
|
||||||
|
|
||||||
|
var update = Builders<MenuSection>.Update
|
||||||
|
.Set("Items.$.Text", updatedItem.Text)
|
||||||
|
.Set("Items.$.Icon", updatedItem.Icon)
|
||||||
|
.Set("Items.$.Available", updatedItem.Available)
|
||||||
|
.Set("Items.$.Link", updatedItem.Link)
|
||||||
|
.Set("Items.$.PermissionKeys", updatedItem.PermissionKeys); // <-- updated
|
||||||
|
|
||||||
|
var result = await _collection.UpdateOneAsync(filter, update);
|
||||||
|
|
||||||
|
if (result.ModifiedCount > 0)
|
||||||
|
{
|
||||||
|
// Re-fetch section and return the updated item
|
||||||
|
var section = await _collection.Find(s => s.Id == sectionId).FirstOrDefaultAsync();
|
||||||
|
return section?.Items.FirstOrDefault(i => i.Id == itemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error updating MenuItem.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MenuSection?> AddSubMenuItemAsync(Guid sectionId, Guid itemId, SubMenuItem newSubItem)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
newSubItem.Id = Guid.NewGuid();
|
||||||
|
|
||||||
|
// Match the MenuSection and the specific MenuItem inside it
|
||||||
|
var filter = Builders<MenuSection>.Filter.And(
|
||||||
|
Builders<MenuSection>.Filter.Eq(s => s.Id, sectionId),
|
||||||
|
Builders<MenuSection>.Filter.ElemMatch(s => s.Items, i => i.Id == itemId)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Use positional operator `$` to target matched MenuItem and push into its Submenu
|
||||||
|
var update = Builders<MenuSection>.Update.Push("Items.$.Submenu", newSubItem);
|
||||||
|
|
||||||
|
var result = await _collection.UpdateOneAsync(filter, update);
|
||||||
|
|
||||||
|
if (result.ModifiedCount > 0)
|
||||||
|
{
|
||||||
|
return await _collection.Find(s => s.Id == sectionId).FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error adding submenu item.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SubMenuItem?> UpdateSubmenuItemAsync(Guid sectionId, Guid itemId, Guid subItemId, SubMenuItem updatedSub)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var filter = Builders<MenuSection>.Filter.Eq(s => s.Id, sectionId);
|
||||||
|
|
||||||
|
var arrayFilters = new List<ArrayFilterDefinition>
|
||||||
|
{
|
||||||
|
new BsonDocumentArrayFilterDefinition<BsonDocument>(
|
||||||
|
new BsonDocument("item._id", itemId.ToString())),
|
||||||
|
new BsonDocumentArrayFilterDefinition<BsonDocument>(
|
||||||
|
new BsonDocument("sub._id", subItemId.ToString()))
|
||||||
|
};
|
||||||
|
|
||||||
|
var update = Builders<MenuSection>.Update
|
||||||
|
.Set("Items.$[item].Submenu.$[sub].Text", updatedSub.Text)
|
||||||
|
.Set("Items.$[item].Submenu.$[sub].Available", updatedSub.Available)
|
||||||
|
.Set("Items.$[item].Submenu.$[sub].Link", updatedSub.Link)
|
||||||
|
.Set("Items.$[item].Submenu.$[sub].PermissionKeys", updatedSub.PermissionKeys); // <-- updated
|
||||||
|
|
||||||
|
var options = new UpdateOptions { ArrayFilters = arrayFilters };
|
||||||
|
|
||||||
|
var result = await _collection.UpdateOneAsync(filter, update, options);
|
||||||
|
|
||||||
|
if (result.ModifiedCount == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var updatedSection = await _collection.Find(x => x.Id == sectionId).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
var subItem = updatedSection?.Items
|
||||||
|
.FirstOrDefault(i => i.Id == itemId)?
|
||||||
|
.Submenu
|
||||||
|
.FirstOrDefault(s => s.Id == subItemId);
|
||||||
|
|
||||||
|
return subItem;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error updating SubMenuItem.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<List<MenuSection>> GetAllMenuSectionsAsync()
|
||||||
|
{
|
||||||
|
return await _collection.Find(_ => true).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
51
Marco.Pms.Model/AppMenu/SideBarMenu.cs
Normal file
51
Marco.Pms.Model/AppMenu/SideBarMenu.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using MongoDB.Bson;
|
||||||
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.AppMenu
|
||||||
|
{
|
||||||
|
public class MenuSection
|
||||||
|
{
|
||||||
|
[BsonId]
|
||||||
|
[BsonRepresentation(BsonType.String)]
|
||||||
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
public string? Header { get; set; }
|
||||||
|
public string? Title { get; set; }
|
||||||
|
public List<MenuItem> Items { get; set; } = new List<MenuItem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MenuItem
|
||||||
|
{
|
||||||
|
[BsonId]
|
||||||
|
[BsonRepresentation(BsonType.String)]
|
||||||
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
public string? Text { get; set; }
|
||||||
|
public string? Icon { get; set; }
|
||||||
|
public bool Available { get; set; } = true;
|
||||||
|
|
||||||
|
public string? Link { get; set; }
|
||||||
|
|
||||||
|
// Changed from string → List<string>
|
||||||
|
public List<string> PermissionKeys { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
public List<SubMenuItem> Submenu { get; set; } = new List<SubMenuItem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SubMenuItem
|
||||||
|
{
|
||||||
|
[BsonId]
|
||||||
|
[BsonRepresentation(BsonType.String)]
|
||||||
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
public string? Text { get; set; }
|
||||||
|
public bool Available { get; set; } = true;
|
||||||
|
|
||||||
|
public string Link { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
// Changed from string → List<string>
|
||||||
|
public List<string> PermissionKeys { get; set; } = new List<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
42
Marco.Pms.Model/Dtos/AppMenu/SideBarMenuDtco.cs
Normal file
42
Marco.Pms.Model/Dtos/AppMenu/SideBarMenuDtco.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using MongoDB.Bson;
|
||||||
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.Dtos.AppMenu
|
||||||
|
{
|
||||||
|
public class MenuSectionDto
|
||||||
|
{
|
||||||
|
public string? Header { get; set; }
|
||||||
|
public string? Title { get; set; }
|
||||||
|
public List<MenuItemDto> Items { get; set; } = new List<MenuItemDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MenuItemDto
|
||||||
|
{
|
||||||
|
public string? Text { get; set; }
|
||||||
|
public string? Icon { get; set; }
|
||||||
|
public bool Available { get; set; } = true;
|
||||||
|
|
||||||
|
public string? Link { get; set; }
|
||||||
|
|
||||||
|
// Changed from string → List<string>
|
||||||
|
public List<string> PermissionKeys { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
public List<SubMenuItemDto> Submenu { get; set; } = new List<SubMenuItemDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SubMenuItemDto
|
||||||
|
{
|
||||||
|
public string? Text { get; set; }
|
||||||
|
public bool Available { get; set; } = true;
|
||||||
|
|
||||||
|
public string Link { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
// Changed from string → List<string>
|
||||||
|
public List<string> PermissionKeys { get; set; } = new List<string>();
|
||||||
|
}
|
||||||
|
}
|
28
Marco.Pms.Model/ViewModels/AppMenu/AppMenuVM.cs
Normal file
28
Marco.Pms.Model/ViewModels/AppMenu/AppMenuVM.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.ViewModels.AppMenu
|
||||||
|
{
|
||||||
|
public class MenuSectionVm
|
||||||
|
{
|
||||||
|
public string? Header { get; set; }
|
||||||
|
public string? Title { get; set; }
|
||||||
|
public List<MenuItemVm> Items { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MenuItemVm
|
||||||
|
{
|
||||||
|
public string? Text { get; set; }
|
||||||
|
public string? Icon { get; set; }
|
||||||
|
public bool Available { get; set; } = true;
|
||||||
|
public string? Link { get; set; }
|
||||||
|
public List<SubMenuItemVm> Submenu { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SubMenuItemVm
|
||||||
|
{
|
||||||
|
public string? Text { get; set; }
|
||||||
|
public bool Available { get; set; } = true;
|
||||||
|
public string Link { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
325
Marco.Pms.Services/Controllers/AppMenuController.cs
Normal file
325
Marco.Pms.Services/Controllers/AppMenuController.cs
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using Azure;
|
||||||
|
using Marco.Pms.CacheHelper;
|
||||||
|
using Marco.Pms.Model.AppMenu;
|
||||||
|
using Marco.Pms.Model.Dtos.AppMenu;
|
||||||
|
using Marco.Pms.Model.Employees;
|
||||||
|
using Marco.Pms.Model.Entitlements;
|
||||||
|
using Marco.Pms.Model.Utilities;
|
||||||
|
using Marco.Pms.Model.ViewModels.AppMenu;
|
||||||
|
using Marco.Pms.Services.Service;
|
||||||
|
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||||
|
using MarcoBMS.Services.Helpers;
|
||||||
|
using MarcoBMS.Services.Service;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http.HttpResults;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
using Org.BouncyCastle.Asn1.Ocsp;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using static System.Collections.Specialized.BitVector32;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Services.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class AppMenuController : ControllerBase
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly UserHelper _userHelper;
|
||||||
|
private readonly EmployeeHelper _employeeHelper;
|
||||||
|
private readonly RolesHelper _rolesHelper;
|
||||||
|
private readonly SideBarMenu _sideBarMenuHelper;
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
private readonly ILoggingService _logger;
|
||||||
|
private readonly PermissionServices _permissions;
|
||||||
|
|
||||||
|
public AppMenuController(EmployeeHelper employeeHelper, IProjectServices projectServices, UserHelper userHelper, RolesHelper rolesHelper, SideBarMenu sideBarMenuHelper, IMapper mapper, ILoggingService logger, PermissionServices permissions)
|
||||||
|
{
|
||||||
|
|
||||||
|
_userHelper = userHelper;
|
||||||
|
_employeeHelper = employeeHelper;
|
||||||
|
_rolesHelper = rolesHelper;
|
||||||
|
_sideBarMenuHelper = sideBarMenuHelper;
|
||||||
|
_mapper = mapper;
|
||||||
|
_logger = logger;
|
||||||
|
_permissions = permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpPost("sidebar/menu-section")]
|
||||||
|
public async Task<IActionResult> CreateAppSideBarMenu([FromBody] MenuSectionDto MenuSecetion)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
var user = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
|
|
||||||
|
if (!(user.ApplicationUser?.IsRootUser ?? false))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Access Denied while creating side menu");
|
||||||
|
return StatusCode(403, ApiResponse<object>.ErrorResponse("access denied", "User haven't permission", 403));
|
||||||
|
}
|
||||||
|
|
||||||
|
var sideMenuSection = _mapper.Map<MenuSection>(MenuSecetion);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sideMenuSection = await _sideBarMenuHelper.CreateMenuSectionAsync(sideMenuSection);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error Occurred while creating Menu");
|
||||||
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("Server Error", ex, 500));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sideMenuSection == null) {
|
||||||
|
_logger.LogWarning("Error Occurred while creating Menu");
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid MenuSection", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInfo("Error Occurred while creating Menu");
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(sideMenuSection, "Sidebar menu created successfully.", 201));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("sidebar/menu-section/{sectionId}")]
|
||||||
|
public async Task<IActionResult> UpdateMenuSection(Guid sectionId, [FromBody] MenuSection updatedSection)
|
||||||
|
{
|
||||||
|
if (sectionId == Guid.Empty || updatedSection == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Error Occurred while Updating Menu Item");
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid section ID, item ID, or menu item payload.", 400));
|
||||||
|
}
|
||||||
|
var UpdatedMenuSection = _mapper.Map<MenuSection>(updatedSection);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UpdatedMenuSection = await _sideBarMenuHelper.UpdateMenuSectionAsync(sectionId, UpdatedMenuSection);
|
||||||
|
|
||||||
|
if (UpdatedMenuSection == null)
|
||||||
|
return NotFound(ApiResponse<object>.ErrorResponse("Menu section not found", 404));
|
||||||
|
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(UpdatedMenuSection, "Menu section updated successfully"));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to update menu section");
|
||||||
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("Server error", ex, 500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("sidebar/menus/{sectionId}/items")]
|
||||||
|
public async Task<IActionResult> AddMenuItem(Guid sectionId, [FromBody] MenuItemDto newItemDto)
|
||||||
|
{
|
||||||
|
if (sectionId == Guid.Empty || newItemDto == null)
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid input", 400));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var menuItem = _mapper.Map<MenuItem>(newItemDto);
|
||||||
|
|
||||||
|
var result = await _sideBarMenuHelper.AddMenuItemAsync(sectionId, menuItem);
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
|
return NotFound(ApiResponse<object>.ErrorResponse("Menu section not found", 404));
|
||||||
|
|
||||||
|
_logger.LogInfo("Added MenuItem in Section: {SectionId}");
|
||||||
|
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(result, "Menu item added successfully"));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error occurred while adding MenuItem inside MenuSection: {SectionId}", sectionId);
|
||||||
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("Server error", ex, 500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("sidebar/{sectionId}/items/{itemId}")]
|
||||||
|
public async Task<IActionResult> UpdateMenuItem(Guid sectionId, Guid itemId, [FromBody] MenuItemDto updatedMenuItem)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (sectionId == Guid.Empty || itemId == Guid.Empty || updatedMenuItem == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Error Occurred while Updating Menu Item");
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid section ID, item ID, or menu item payload.", 400));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var sideMenuItem = _mapper.Map<MenuItem>(updatedMenuItem);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
sideMenuItem = await _sideBarMenuHelper.UpdateMenuItemAsync(sectionId, itemId, sideMenuItem);
|
||||||
|
|
||||||
|
if (sideMenuItem == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Error Occurred while Updating SidBar Section:{SectionId} MenuItem:{itemId} ");
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse("Menu creation failed", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInfo("SidBar Section{SectionId} MenuItem {itemId} Updated ");
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(sideMenuItem, "Sidebar MenuItem Updated successfully.", 201));
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
_logger.LogError(ex, "Error Occurred while creating MenuItem");
|
||||||
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("Server Error", ex, 500));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("sidebar/menus/{sectionId}/items/{itemId}/subitems")]
|
||||||
|
public async Task<IActionResult> AddSubMenuItem(Guid sectionId, Guid itemId, [FromBody] SubMenuItemDto newSubItem)
|
||||||
|
{
|
||||||
|
if (sectionId == Guid.Empty || itemId == Guid.Empty || newSubItem == null)
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid input", 400));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var subMenuItem = _mapper.Map<SubMenuItem>(newSubItem);
|
||||||
|
|
||||||
|
var result = await _sideBarMenuHelper.AddSubMenuItemAsync(sectionId, itemId, subMenuItem);
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return NotFound(ApiResponse<object>.ErrorResponse("Menu item not found", 404));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInfo("Added SubMenuItem in Section: {SectionId}, MenuItem: {ItemId}");
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(result, "Submenu item added successfully"));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to add submenu item");
|
||||||
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("Server error", ex, 500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpPut("sidebar/{sectionId}/items/{itemId}/subitems/{subItemId}")]
|
||||||
|
public async Task<IActionResult> UpdateSubmenuItem(Guid sectionId, Guid itemId, Guid subItemId, [FromBody] SubMenuItemDto updatedSubMenuItem)
|
||||||
|
{
|
||||||
|
if (sectionId == Guid.Empty || itemId == Guid.Empty || subItemId == Guid.Empty || updatedSubMenuItem == null)
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid input", 400));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var SubMenuItem = _mapper.Map<SubMenuItem>(updatedSubMenuItem);
|
||||||
|
SubMenuItem = await _sideBarMenuHelper.UpdateSubmenuItemAsync(sectionId, itemId, subItemId, SubMenuItem);
|
||||||
|
|
||||||
|
if (SubMenuItem == null)
|
||||||
|
return NotFound(ApiResponse<object>.ErrorResponse("Submenu item not found", 404));
|
||||||
|
|
||||||
|
_logger.LogInfo("SidBar Section{SectionId} MenuItem {itemId} SubMenuItem {subItemId} Updated");
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(SubMenuItem, "Submenu item updated successfully"));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error Occurred while Updating Sub-MenuItem");
|
||||||
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("Server Error", ex, 500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("sidebar/menu-section")]
|
||||||
|
public async Task<IActionResult> GetAppSideBarMenu()
|
||||||
|
{
|
||||||
|
var loggedUser = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
|
var employeeId = loggedUser.Id;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
var menus = await _sideBarMenuHelper.GetAllMenuSectionsAsync();
|
||||||
|
|
||||||
|
foreach (var menu in menus)
|
||||||
|
{
|
||||||
|
var allowedItems = new List<MenuItem>();
|
||||||
|
|
||||||
|
foreach (var item in menu.Items)
|
||||||
|
{
|
||||||
|
bool isAllowed = false;
|
||||||
|
|
||||||
|
if (item.PermissionKeys == null || !item.PermissionKeys.Any())
|
||||||
|
{
|
||||||
|
isAllowed = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var pk in item.PermissionKeys)
|
||||||
|
{
|
||||||
|
if (Guid.TryParse(pk, out var permissionId))
|
||||||
|
{
|
||||||
|
if (await _permissions.HasPermission(permissionId, employeeId))
|
||||||
|
{
|
||||||
|
isAllowed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAllowed)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (item.Submenu != null && item.Submenu.Any())
|
||||||
|
{
|
||||||
|
var allowedSubmenus = new List<SubMenuItem>();
|
||||||
|
foreach (var sm in item.Submenu)
|
||||||
|
{
|
||||||
|
if (sm.PermissionKeys == null || !sm.PermissionKeys.Any())
|
||||||
|
{
|
||||||
|
allowedSubmenus.Add(sm);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var pk in sm.PermissionKeys)
|
||||||
|
{
|
||||||
|
if (Guid.TryParse(pk, out var permissionId))
|
||||||
|
{
|
||||||
|
if (await _permissions.HasPermission(permissionId, employeeId))
|
||||||
|
{
|
||||||
|
allowedSubmenus.Add(sm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.Submenu = allowedSubmenus;
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedItems.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.Items = allowedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInfo("Fetched Sidebar Menu");
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(menus, "SideBar Menu Fetched successfully"));
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
|
||||||
|
_logger.LogError(ex, "Error Occurred while Updating Fetching Menu");
|
||||||
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("Server Error", ex, 500));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Marco.Pms.Model.Dtos.Expenses;
|
using Marco.Pms.Model.Dtos.Expenses;
|
||||||
using Marco.Pms.Model.Dtos.Master;
|
using Marco.Pms.Model.Dtos.Master;
|
||||||
|
using Marco.Pms.Model.AppMenu;
|
||||||
|
using Marco.Pms.Model.Dtos.AppMenu;
|
||||||
using Marco.Pms.Model.Dtos.Project;
|
using Marco.Pms.Model.Dtos.Project;
|
||||||
using Marco.Pms.Model.Dtos.Tenant;
|
using Marco.Pms.Model.Dtos.Tenant;
|
||||||
using Marco.Pms.Model.Employees;
|
using Marco.Pms.Model.Employees;
|
||||||
@ -305,6 +307,12 @@ namespace Marco.Pms.Services.MappingProfiles
|
|||||||
opt => opt.MapFrom(src => Guid.Parse(src.DocumentId)));
|
opt => opt.MapFrom(src => Guid.Parse(src.DocumentId)));
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region ======================================================= AppMenu =======================================================
|
||||||
|
CreateMap<MenuSectionDto, MenuSection>();
|
||||||
|
CreateMap<MenuItemDto, MenuItem>();
|
||||||
|
CreateMap<SubMenuItemDto, SubMenuItem>();
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,6 +197,7 @@ builder.Services.AddScoped<ProjectCache>();
|
|||||||
builder.Services.AddScoped<EmployeeCache>();
|
builder.Services.AddScoped<EmployeeCache>();
|
||||||
builder.Services.AddScoped<ReportCache>();
|
builder.Services.AddScoped<ReportCache>();
|
||||||
builder.Services.AddScoped<ExpenseCache>();
|
builder.Services.AddScoped<ExpenseCache>();
|
||||||
|
builder.Services.AddScoped<SideBarMenu>();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
// Singleton services (one instance for the app's lifetime)
|
// Singleton services (one instance for the app's lifetime)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user