579 lines
29 KiB
C#
579 lines
29 KiB
C#
using AutoMapper;
|
|
using Marco.Pms.CacheHelper;
|
|
using Marco.Pms.Model.AppMenu;
|
|
using Marco.Pms.Model.Dtos.AppMenu;
|
|
using Marco.Pms.Model.Utilities;
|
|
using Marco.Pms.Model.ViewModels.AppMenu;
|
|
using Marco.Pms.Model.ViewModels.DocumentManager;
|
|
using Marco.Pms.Services.Helpers;
|
|
using Marco.Pms.Services.Service;
|
|
using MarcoBMS.Services.Helpers;
|
|
using MarcoBMS.Services.Service;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace Marco.Pms.Services.Controllers
|
|
{
|
|
[Authorize]
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
public class AppMenuController : ControllerBase
|
|
{
|
|
|
|
private readonly UserHelper _userHelper;
|
|
private readonly SidebarMenuHelper _sideBarMenuHelper;
|
|
private readonly IMapper _mapper;
|
|
private readonly ILoggingService _logger;
|
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
|
private readonly PermissionServices _permissionService;
|
|
|
|
private readonly Guid tenantId;
|
|
private static readonly Guid superTenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26");
|
|
private static readonly Guid ProjectManagement = Guid.Parse("53176ebf-c75d-42e5-839f-4508ffac3def");
|
|
private static readonly Guid ExpenseManagement = Guid.Parse("a4e25142-449b-4334-a6e5-22f70e4732d7");
|
|
private static readonly Guid TaskManagement = Guid.Parse("9d4b5489-2079-40b9-bd77-6e1bf90bc19f");
|
|
private static readonly Guid EmployeeManagement = Guid.Parse("81ab8a87-8ccd-4015-a917-0627cee6a100");
|
|
private static readonly Guid AttendanceManagement = Guid.Parse("52c9cf54-1eb2-44d2-81bb-524cf29c0a94");
|
|
private static readonly Guid MastersMangent = Guid.Parse("be3b3afc-6ccf-4566-b9b6-aafcb65546be");
|
|
private static readonly Guid DirectoryManagement = Guid.Parse("39e66f81-efc6-446c-95bd-46bff6cfb606");
|
|
private static readonly Guid TenantManagement = Guid.Parse("2f3509b7-160d-410a-b9b6-daadd96c986d");
|
|
|
|
public AppMenuController(UserHelper userHelper,
|
|
SidebarMenuHelper sideBarMenuHelper,
|
|
IMapper mapper,
|
|
ILoggingService logger,
|
|
IServiceScopeFactory serviceScopeFactory,
|
|
PermissionServices permissionService)
|
|
{
|
|
|
|
_userHelper = userHelper;
|
|
_sideBarMenuHelper = sideBarMenuHelper;
|
|
_mapper = mapper;
|
|
_logger = logger;
|
|
_serviceScopeFactory = serviceScopeFactory;
|
|
tenantId = userHelper.GetTenantId();
|
|
_permissionService = permissionService;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the sidebar menu for the current tenant and logged-in employee,
|
|
/// filtered by permission and structured for the web application UI.
|
|
/// </summary>
|
|
[HttpGet("get/menu")]
|
|
public async Task<IActionResult> GetAppSideBarMenuAsync()
|
|
{
|
|
// Correlation ID enables tracing this request across services and logs.
|
|
var correlationId = HttpContext.TraceIdentifier;
|
|
|
|
// Log the high-level intent and core context up front (no PII, no secrets).
|
|
_logger.LogInfo("GetAppSideBarMenuAsync started. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
try
|
|
{
|
|
// 1. Validate tenant context
|
|
if (tenantId == Guid.Empty)
|
|
{
|
|
_logger.LogWarning("GetAppSideBarMenuAsync rejected due to invalid tenant. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
var error = ApiResponse<object>.ErrorResponse("Invalid Tenant Identifier", "The tenant identifier is missing or invalid. Please verify the tenant context and try again.", 400);
|
|
|
|
return BadRequest(error);
|
|
}
|
|
|
|
// 2. Resolve current employee context
|
|
// - This call should throw or return a known result if the user is not authenticated.
|
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
|
|
|
if (loggedInEmployee is null)
|
|
{
|
|
_logger.LogWarning("GetAppSideBarMenuAsync failed: current employee not resolved. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
var error = ApiResponse<object>.ErrorResponse("User Context Not Found", "The current user context could not be resolved. Please re-authenticate and try again.", 403);
|
|
|
|
return StatusCode(403, error);
|
|
}
|
|
|
|
var employeeId = loggedInEmployee.Id;
|
|
|
|
_logger.LogDebug("GetAppSideBarMenuAsync resolved employee context. EmployeeId: {EmployeeId}, TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
employeeId, tenantId, correlationId);
|
|
|
|
// 3. Preload all permission ids for the employee for efficient in-memory checks
|
|
var permissionIds = await _permissionService.GetPermissionIdsByEmployeeId(employeeId);
|
|
|
|
_logger.LogDebug("GetAppSideBarMenuAsync loaded {PermissionCount} permissions for EmployeeId: {EmployeeId}, CorrelationId: {CorrelationId}",
|
|
permissionIds.Count, employeeId, correlationId);
|
|
|
|
// 4. Fetch all menu entries configured for this tenant
|
|
var menus = await _sideBarMenuHelper.GetAllWebMenuSectionsAsync(tenantId);
|
|
|
|
if (menus == null || !menus.Any())
|
|
{
|
|
_logger.LogInfo("GetAppSideBarMenuAsync: No menu sections found for tenant. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
var emptyResponse = new List<WebMenuSectionVM>();
|
|
|
|
return Ok(ApiResponse<object>.SuccessResponse(emptyResponse, "No sidebar menu sections are configured for the current tenant.", 200));
|
|
}
|
|
|
|
_logger.LogDebug("GetAppSideBarMenuAsync loaded {MenuSectionCount} raw menu records. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
menus.Count, tenantId, correlationId);
|
|
|
|
// 5. Build logical menu sections (root + children) and apply permission filtering
|
|
var responseSections = new List<WebMenuSectionVM>();
|
|
|
|
// Root container section for the web UI.
|
|
var rootSection = new WebMenuSectionVM
|
|
{
|
|
Id = Guid.Parse("4885d9f4-89b8-447d-9a95-7434b343dfda"),
|
|
Header = "Main Navigation",
|
|
Name = "Main Menu"
|
|
};
|
|
|
|
// To avoid multiple enumerations and improve readability, materialize once.
|
|
var menusList = menus.ToList();
|
|
|
|
foreach (var menu in menusList)
|
|
{
|
|
// Skip any non-root menu entry; these will be attached as children.
|
|
if (menu.ParentMenuId.HasValue)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var itemVm = _mapper.Map<WebSideMenuItemVM>(menu);
|
|
|
|
// If the menu requires any permission, check now.
|
|
if (menu.PermissionIds.Any())
|
|
{
|
|
var hasMenuPermission = _permissionService.HasPermissionAny(permissionIds, menu.PermissionIds, employeeId);
|
|
|
|
if (!hasMenuPermission)
|
|
{
|
|
_logger.LogDebug("Menu item skipped due to insufficient permissions. MenuId: {MenuId}, EmployeeId: {EmployeeId}, CorrelationId: {CorrelationId}",
|
|
menu.Id, employeeId, correlationId);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Resolve submenus only for eligible root menu entries.
|
|
var subMenus = menusList
|
|
.Where(m => m.ParentMenuId.HasValue && m.ParentMenuId == menu.Id)
|
|
.ToList();
|
|
|
|
foreach (var subMenu in subMenus)
|
|
{
|
|
var subMenuVm = _mapper.Map<WebSideMenuItemVM>(subMenu);
|
|
|
|
// If the submenu requires permissions, validate before adding.
|
|
if (subMenu.PermissionIds.Any())
|
|
{
|
|
var hasSubPermission = _permissionService.HasPermissionAny(permissionIds, subMenu.PermissionIds, employeeId);
|
|
|
|
if (!hasSubPermission)
|
|
{
|
|
_logger.LogDebug(
|
|
"Submenu item skipped due to insufficient permissions. MenuId: {MenuId}, ParentMenuId: {ParentMenuId}, EmployeeId: {EmployeeId}, CorrelationId: {CorrelationId}",
|
|
subMenu.Id, subMenu.ParentMenuId!, employeeId, correlationId);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Add the submenu to the root section
|
|
itemVm.Submenu.Add(subMenuVm);
|
|
}
|
|
// Add the root menu item
|
|
rootSection.Items.Add(itemVm);
|
|
}
|
|
|
|
if (rootSection.Items.Any())
|
|
{
|
|
responseSections.Add(rootSection);
|
|
}
|
|
|
|
_logger.LogInfo(
|
|
"GetAppSideBarMenuAsync completed successfully. TenantId: {TenantId}, EmployeeId: {EmployeeId}, OriginalMenuCount: {OriginalCount}, ReturnedSectionCount: {SectionCount}, CorrelationId: {CorrelationId}",
|
|
tenantId, employeeId, menusList.Count, responseSections.Count, correlationId);
|
|
|
|
return Ok(ApiResponse<object>.SuccessResponse(responseSections,
|
|
responseSections.Any()
|
|
? $"Sidebar menu fetched successfully. {responseSections.Count} section(s) returned."
|
|
: "No accessible sidebar menu items were found for the current user.", 200));
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// This typically indicates client disconnected or explicit cancellation.
|
|
_logger.LogWarning("GetAppSideBarMenuAsync was cancelled. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
var error = ApiResponse<object>.ErrorResponse("Request Cancelled", "The operation was cancelled, likely due to a client disconnection or explicit cancellation request.", 499); // Non-standard but commonly used in APIs for client closed request.
|
|
|
|
return StatusCode(499, error);
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
// Handle authorization-related errors explicitly to avoid leaking details.
|
|
_logger.LogError(ex, "GetAppSideBarMenuAsync authorization failure. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
var error = ApiResponse<object>.ErrorResponse("Access Denied", "You do not have sufficient permissions to access the sidebar menu.", 403);
|
|
|
|
return StatusCode(403, error);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Fallback handler for unexpected failures. Keep log detailed, response generic.
|
|
_logger.LogError(ex, "Unhandled exception in GetAppSideBarMenuAsync. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
var error = ApiResponse<object>.ErrorResponse("Internal Server Error", "An unexpected error occurred while processing the sidebar menu request. Please try again later.", 500);
|
|
|
|
return StatusCode(500, error);
|
|
}
|
|
}
|
|
|
|
[HttpPost("add/side-menu")]
|
|
public async Task<IActionResult> AddWebMenuItemAsync([FromBody] List<CreateWebSideMenuItemDto> model)
|
|
{
|
|
// Step 1: Validate tenant context
|
|
if (tenantId == Guid.Empty)
|
|
{
|
|
_logger.LogWarning("GetAppSideBarMenuAsync rejected: Invalid TenantId.");
|
|
|
|
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid TenantId", "The tenant identifier provided is invalid or missing.", 400));
|
|
}
|
|
|
|
// Step 2: Fetch logged-in user
|
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
|
var isRootUser = loggedInEmployee.ApplicationUser?.IsRootUser ?? false;
|
|
|
|
// Step 3: Authorization check
|
|
if (!isRootUser && tenantId != superTenantId)
|
|
{
|
|
_logger.LogWarning("Access denied: User {UserId} attempted to add menu item in Tenant {TenantId}",
|
|
loggedInEmployee.Id, tenantId);
|
|
|
|
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access Denied", "User does not have permission.", 403));
|
|
}
|
|
|
|
// Step 4: Input validation
|
|
if (model == null)
|
|
{
|
|
_logger.LogWarning("Invalid AddMenuItem request. Tenant: {TenantId}, UserId: {UserId}",
|
|
tenantId, loggedInEmployee.Id);
|
|
|
|
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid section ID or menu item payload.", 400));
|
|
}
|
|
|
|
try
|
|
{
|
|
// Step 5: Map DTO to entity
|
|
var menuItemEntity = _mapper.Map<List<WebSideMenuItem>>(model);
|
|
|
|
menuItemEntity.ForEach(m => m.TenantId = tenantId);
|
|
|
|
// Step 6: Perform Add operation
|
|
var result = await _sideBarMenuHelper.AddWebMenuItemAsync(menuItemEntity);
|
|
|
|
if (result == null)
|
|
{
|
|
_logger.LogWarning("Menu section not found. Unable to add menu item. TenantId: {TenantId}, UserId: {UserId}", tenantId, loggedInEmployee.Id);
|
|
|
|
return NotFound(ApiResponse<object>.ErrorResponse("Menu section not found", 404));
|
|
}
|
|
|
|
// Step 7: Successful addition
|
|
_logger.LogInfo("Menu items added successfully TenantId: {TenantId}, UserId: {UserId}",
|
|
tenantId, loggedInEmployee.Id);
|
|
|
|
return Ok(ApiResponse<object>.SuccessResponse(result, "Menu item added successfully"));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Step 8: Handle unexpected errors
|
|
_logger.LogError(ex, "Error occurred while adding menu item. TenantId: {TenantId}, UserId: {UserId}, Payload: {@NewItemDto}",
|
|
tenantId, loggedInEmployee.Id, model);
|
|
|
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("Server error", "An unexpected error occurred while adding the menu item.", 500));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the mobile sidebar menu sections for the authenticated employee within the current tenant,
|
|
/// filtered by employee permissions and structured for mobile application consumption.
|
|
/// Supports permission-based access control and tenant isolation.
|
|
/// </summary>
|
|
/// <returns>A filtered list of accessible mobile menu sections or appropriate error response.</returns>
|
|
/// <response code="200">Returns filtered mobile menu sections successfully.</response>
|
|
/// <response code="400">Invalid tenant identifier provided.</response>
|
|
/// <response code="403">Employee context not resolved or insufficient permissions.</response>
|
|
/// <response code="500">Internal server error during menu retrieval or processing.</response>
|
|
|
|
[HttpGet("get/menu-mobile")]
|
|
public async Task<IActionResult> GetAppSideBarMenuForMobileAsync()
|
|
{
|
|
// Correlation ID enables distributed tracing across services, middleware, and structured logs.
|
|
var correlationId = HttpContext.TraceIdentifier;
|
|
|
|
_logger.LogInfo("GetAppSideBarMenuForMobileAsync started. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
try
|
|
{
|
|
// 1. Validate tenant isolation - critical for multi-tenant security
|
|
if (tenantId == Guid.Empty)
|
|
{
|
|
_logger.LogWarning("GetAppSideBarMenuForMobileAsync rejected: invalid tenant context. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
var error = ApiResponse<object>.ErrorResponse(
|
|
"InvalidTenantContext",
|
|
"Tenant identifier is missing or invalid. Verify tenant context and retry.",
|
|
400);
|
|
|
|
return BadRequest(error);
|
|
}
|
|
|
|
// 2. Resolve authenticated employee context with tenant isolation
|
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
|
if (loggedInEmployee is null)
|
|
{
|
|
_logger.LogWarning("GetAppSideBarMenuForMobileAsync failed: employee context not resolved. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
var error = ApiResponse<object>.ErrorResponse("EmployeeContextNotFound", "Current employee context could not be resolved. Please authenticate and retry.", 403);
|
|
|
|
return StatusCode(403, error);
|
|
}
|
|
|
|
var employeeId = loggedInEmployee.Id;
|
|
_logger.LogDebug("GetAppSideBarMenuForMobileAsync resolved employee: EmployeeId: {EmployeeId}, TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
employeeId, tenantId, correlationId);
|
|
|
|
// 3. Bulk-load employee permissions for efficient in-memory permission checks (avoids N+1 queries)
|
|
var permissionIds = await _permissionService.GetPermissionIdsByEmployeeId(employeeId);
|
|
_logger.LogDebug("GetAppSideBarMenuForMobileAsync loaded {PermissionCount} permissions for EmployeeId: {EmployeeId}, TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
permissionIds?.Count ?? 0, employeeId, tenantId, correlationId);
|
|
|
|
// 4. Fetch tenant-specific mobile menu configuration
|
|
var allMenus = await _sideBarMenuHelper.GetAllMobileMenuSectionsAsync(tenantId);
|
|
if (allMenus == null || !allMenus.Any())
|
|
{
|
|
_logger.LogInfo("GetAppSideBarMenuForMobileAsync: no mobile menu sections configured. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
var emptyResponse = new List<MenuSectionApplicationVM>();
|
|
return Ok(ApiResponse<object>.SuccessResponse(emptyResponse,
|
|
"No mobile sidebar menu sections configured for this tenant.", 200));
|
|
}
|
|
|
|
_logger.LogDebug("GetAppSideBarMenuForMobileAsync loaded {MenuCount} raw sections. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
allMenus.Count, tenantId, correlationId);
|
|
|
|
// 5. Filter menus by employee permissions (in-memory for performance)
|
|
var accessibleMenus = new List<MobileMenu>();
|
|
foreach (var menuSection in allMenus)
|
|
{
|
|
// Skip permission check for public menu items
|
|
if (!menuSection.PermissionIds.Any())
|
|
{
|
|
accessibleMenus.Add(menuSection);
|
|
continue;
|
|
}
|
|
|
|
// Perform permission intersection check
|
|
var hasAccess = _permissionService.HasPermissionAny(permissionIds ?? new List<Guid>(),
|
|
menuSection.PermissionIds, employeeId);
|
|
|
|
if (hasAccess)
|
|
{
|
|
accessibleMenus.Add(menuSection);
|
|
_logger.LogDebug("GetAppSideBarMenuForMobileAsync granted menu access. MenuId: {MenuId}, EmployeeId: {EmployeeId}, CorrelationId: {CorrelationId}",
|
|
menuSection.Id, employeeId, correlationId);
|
|
}
|
|
else
|
|
{
|
|
_logger.LogDebug("GetAppSideBarMenuForMobileAsync denied menu access. MenuId: {MenuId}, EmployeeId: {EmployeeId}, CorrelationId: {CorrelationId}",
|
|
menuSection.Id, employeeId, correlationId);
|
|
}
|
|
}
|
|
|
|
// 6. Defensive mapping with null-safety
|
|
var response = _mapper.Map<List<MenuSectionApplicationVM>>(accessibleMenus);
|
|
_logger.LogInfo("GetAppSideBarMenuForMobileAsync completed successfully. AccessibleMenus: {AccessibleCount}/{TotalCount}, EmployeeId: {EmployeeId}, TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
accessibleMenus.Count, allMenus.Count, employeeId, tenantId, correlationId);
|
|
|
|
return Ok(ApiResponse<object>.SuccessResponse(response,
|
|
$"Mobile sidebar menu fetched successfully ({response?.Count ?? 0} sections accessible).", 200));
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
_logger.LogWarning("GetAppSideBarMenuForMobileAsync cancelled. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
return StatusCode(499, ApiResponse<object>.ErrorResponse("RequestCancelled",
|
|
"Request was cancelled by client or timeout.", 499));
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
_logger.LogError(ex, "GetAppSideBarMenuForMobileAsync authorization failed. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
return StatusCode(403, ApiResponse<object>.ErrorResponse("AuthorizationFailed",
|
|
"Insufficient permissions to access mobile menu sections.", 403));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "GetAppSideBarMenuForMobileAsync failed unexpectedly. TenantId: {TenantId}, CorrelationId: {CorrelationId}",
|
|
tenantId, correlationId);
|
|
|
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("InternalServerError",
|
|
"An unexpected error occurred while fetching the mobile sidebar menu. Please contact support if issue persists.", 500));
|
|
}
|
|
}
|
|
|
|
|
|
[HttpPost("add/mobile/side-menu")]
|
|
public async Task<IActionResult> AddMobileMenuItemAsync([FromBody] List<CreateMobileSideMenuItemDto> model)
|
|
{
|
|
// Step 1: Validate tenant context
|
|
if (tenantId == Guid.Empty)
|
|
{
|
|
_logger.LogWarning("GetAppSideBarMenuAsync rejected: Invalid TenantId.");
|
|
|
|
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid TenantId", "The tenant identifier provided is invalid or missing.", 400));
|
|
}
|
|
|
|
// Step 2: Fetch logged-in user
|
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
|
var isRootUser = loggedInEmployee.ApplicationUser?.IsRootUser ?? false;
|
|
|
|
// Step 3: Authorization check
|
|
if (!isRootUser && tenantId != superTenantId)
|
|
{
|
|
_logger.LogWarning("Access denied: User {UserId} attempted to add menu item in Tenant {TenantId}",
|
|
loggedInEmployee.Id, tenantId);
|
|
|
|
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access Denied", "User does not have permission.", 403));
|
|
}
|
|
|
|
// Step 4: Input validation
|
|
if (model == null)
|
|
{
|
|
_logger.LogWarning("Invalid AddMenuItem request. Tenant: {TenantId}, UserId: {UserId}",
|
|
tenantId, loggedInEmployee.Id);
|
|
|
|
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid section ID or menu item payload.", 400));
|
|
}
|
|
|
|
// Step 5: Map DTO to entity
|
|
var menuItemEntity = _mapper.Map<List<MobileMenu>>(model);
|
|
|
|
menuItemEntity.ForEach(m => m.TenantId = tenantId);
|
|
|
|
// Step 6: Perform Add operation
|
|
var result = await _sideBarMenuHelper.AddMobileMenuItemAsync(menuItemEntity);
|
|
|
|
if (result == null)
|
|
{
|
|
_logger.LogWarning("Menu section not found. Unable to add menu item. TenantId: {TenantId}, UserId: {UserId}", tenantId, loggedInEmployee.Id);
|
|
|
|
return NotFound(ApiResponse<object>.ErrorResponse("Menu section not found", 404));
|
|
}
|
|
|
|
// Step 7: Successful addition
|
|
_logger.LogInfo("Menu items added successfully TenantId: {TenantId}, UserId: {UserId}",
|
|
tenantId, loggedInEmployee.Id);
|
|
|
|
return Ok(ApiResponse<object>.SuccessResponse(result, "Menu item added successfully"));
|
|
}
|
|
|
|
[HttpGet("get/master-list")]
|
|
public async Task<IActionResult> GetMasterList()
|
|
{
|
|
// Start logging scope for observability
|
|
|
|
try
|
|
{
|
|
// Get currently logged-in employee
|
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
|
_logger.LogInfo("Fetching master list for EmployeeId: {EmployeeId}", loggedInEmployee.Id);
|
|
|
|
using var scope = _serviceScopeFactory.CreateScope();
|
|
var generalHelper = scope.ServiceProvider.GetRequiredService<GeneralHelper>();
|
|
|
|
// Define static master menus for each feature section
|
|
var featureMenus = new Dictionary<Guid, List<MasterMenuVM>>
|
|
{
|
|
{
|
|
EmployeeManagement, new List<MasterMenuVM>
|
|
{
|
|
new MasterMenuVM { Id = 1, Name = "Application Role" },
|
|
new MasterMenuVM { Id = 2, Name = "Job Role" },
|
|
new MasterMenuVM { Id = 9, Name = "Document Category" },
|
|
new MasterMenuVM { Id = 10, Name = "Document Type" }
|
|
}
|
|
},
|
|
{
|
|
ProjectManagement, new List<MasterMenuVM>
|
|
{
|
|
new MasterMenuVM { Id = 3, Name = "Work Category" },
|
|
new MasterMenuVM { Id = 8, Name = "Services" }
|
|
}
|
|
},
|
|
{
|
|
DirectoryManagement, new List<MasterMenuVM>
|
|
{
|
|
new MasterMenuVM { Id = 4, Name = "Contact Category" },
|
|
new MasterMenuVM { Id = 5, Name = "Contact Tag" }
|
|
}
|
|
},
|
|
{
|
|
ExpenseManagement, new List<MasterMenuVM>
|
|
{
|
|
new MasterMenuVM { Id = 6, Name = "Expense Category" },
|
|
new MasterMenuVM { Id = 7, Name = "Payment Mode" },
|
|
new MasterMenuVM { Id = 10, Name = "Payment Adjustment Head" }
|
|
}
|
|
}
|
|
};
|
|
|
|
if (tenantId == superTenantId)
|
|
{
|
|
var superResponse = featureMenus.Values.SelectMany(list => list).OrderBy(r => r.Name).ToList();
|
|
|
|
_logger.LogInfo("MasterMenu count for TenantId {TenantId}: {Count}", tenantId, superResponse.Count);
|
|
return Ok(ApiResponse<object>.SuccessResponse(superResponse, "Successfully fetched the master table list", 200));
|
|
}
|
|
|
|
// Fetch features enabled for tenant
|
|
var featureIds = await generalHelper.GetFeatureIdsByTenentIdAsync(tenantId);
|
|
_logger.LogInfo("Enabled features for TenantId: {TenantId} -> {FeatureIds}", tenantId, string.Join(",", featureIds));
|
|
|
|
// Aggregate menus based on enabled features
|
|
var response = featureIds
|
|
.Where(id => featureMenus.ContainsKey(id))
|
|
.SelectMany(id => featureMenus[id])
|
|
.OrderBy(r => r.Name)
|
|
.ToList();
|
|
|
|
_logger.LogInfo("MasterMenu count for TenantId {TenantId}: {Count}", tenantId, response.Count);
|
|
return Ok(ApiResponse<object>.SuccessResponse(response, "Successfully fetched the master table list", 200));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Critical error tracking
|
|
_logger.LogError(ex, "Error occurred while fetching master menu list for TenantId: {TenantId}", tenantId);
|
|
return StatusCode(500, ApiResponse<string>.ErrorResponse("An unexpected error occurred while fetching master menu list."));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|