diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..136852a6 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,12 @@ +# Copilot Instructions + +## General Guidelines +- First general instruction +- Second general instruction + +## Unit Testing +- Add new unit tests to existing `MerchantUIServiceTests.cs` for: + - `GetRecentMerchants` (success and failure cases) + - `GetMerchantKpis` (success and failure cases) + - `GetMerchantsForDropDown` (success and failure cases) +- Preferred file location: `EstateManagementUI.BlazorServer.Tests\UIServices\MerchantUIServiceTests.cs` \ No newline at end of file diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/Estate/EstateIndexPageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/Estate/EstateIndexPageTests.cs index 55fb673e..df6ff213 100644 --- a/EstateManagementUI.BlazorServer.Tests/Pages/Estate/EstateIndexPageTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/Pages/Estate/EstateIndexPageTests.cs @@ -22,12 +22,12 @@ public void EstateIndex_RendersCorrectly() BlazorServer.Models.EstateModel estate = new(Guid.NewGuid(), "Test Estate", "EST001"); estate = estate with { ContractCount = 0, - RecentContracts = new List(), + RecentContracts = new List(), OperatorCount = 0, AllOperators = new List(), AssignedOperators = new List(), MerchantCount = 0, - RecentMerchants = new List(), + RecentMerchants = new List(), UserCount = 0 }; @@ -48,12 +48,12 @@ public void EstateIndex_DisplaysEstateDetails() estate = estate with { ContractCount = 0, - RecentContracts = new List(), + RecentContracts = new List(), OperatorCount = 0, AllOperators = new List(), AssignedOperators = new List(), MerchantCount = 0, - RecentMerchants = new List(), + RecentMerchants = new List(), UserCount = 0 }; this.EstateUIService.Setup(e => e.LoadEstate(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(estate)); @@ -73,12 +73,12 @@ public void EstateIndex_HasCorrectPageTitle() estate = estate with { ContractCount = 0, - RecentContracts = new List(), + RecentContracts = new List(), OperatorCount = 0, AllOperators = new List(), AssignedOperators = new List(), MerchantCount = 0, - RecentMerchants = new List(), + RecentMerchants = new List(), UserCount = 0 }; this.EstateUIService.Setup(e => e.LoadEstate(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(estate)); @@ -286,8 +286,8 @@ public void EstateIndex_RemoveOperator_Failure_ShowsErrorMessage() public void EstateIndex_DisplaysMerchants_WhenPresent() { // Arrange - List merchants = new() { - new RecentMerchantsModel + List merchants = new() { + new MerchantModels.RecentMerchantsModel { MerchantId = Guid.NewGuid(), Name = "Test Merchant", @@ -325,8 +325,8 @@ public void EstateIndex_DisplaysNoMerchants_WhenEmpty() public void EstateIndex_DisplaysContracts_WhenPresent() { // Arrange - List contracts = new() { - new RecentContractModel + List contracts = new() { + new ContractModels.RecentContractModel { ContractId = Guid.NewGuid(), Description = "Test Contract", @@ -459,8 +459,8 @@ public void EstateIndex_SuccessMessage_ClearsWhenSwitchingTabs() } // Helper methods - private void SetupSuccessfulDataLoad(List? merchants = null, - List? contracts = null, + private void SetupSuccessfulDataLoad(List? merchants = null, + List? contracts = null, List? assignedOperators = null, List? operators = null) { @@ -468,12 +468,12 @@ private void SetupSuccessfulDataLoad(List? merchants = nul estate = estate with { ContractCount = 5, - RecentContracts = contracts ?? new List(), + RecentContracts = contracts ?? new List(), OperatorCount = 3, AllOperators = operators ?? new List(), AssignedOperators = assignedOperators ?? new List(), MerchantCount = 10, - RecentMerchants = merchants ?? new List(), + RecentMerchants = merchants ?? new List(), UserCount = 2 }; this.EstateUIService.Setup(e => e.LoadEstate(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(estate)); @@ -485,9 +485,9 @@ private void SetupSuccessfulDataLoadWithOperators(List assignedOperators) => SetupSuccessfulDataLoad(assignedOperators: assignedOperators); - private void SetupSuccessfulDataLoadWithMerchants(List merchants) + private void SetupSuccessfulDataLoadWithMerchants(List merchants) => SetupSuccessfulDataLoad(merchants: merchants); - private void SetupSuccessfulDataLoadWithContracts(List contracts) + private void SetupSuccessfulDataLoadWithContracts(List contracts) => SetupSuccessfulDataLoad(contracts: contracts); } diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/HomePageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/HomePageTests.cs index cc21d336..dea9a88f 100644 --- a/EstateManagementUI.BlazorServer.Tests/Pages/HomePageTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/Pages/HomePageTests.cs @@ -1,42 +1,43 @@ using Bunit; using EstateManagementUI.BlazorServer.Components.Pages; using EstateManagementUI.BlazorServer.Components.Permissions; +using EstateManagementUI.BlazorServer.Models; using EstateManagementUI.BlazorServer.Permissions; +using EstateManagementUI.BlazorServer.UIServices; +using EstateManagementUI.BusinessLogic.BackendAPI.DataTransferObjects; +using EstateManagementUI.BusinessLogic.Client; +using EstateManagementUI.BusinessLogic.Requests; using MediatR; -using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; using Microsoft.Extensions.DependencyInjection; using Microsoft.JSInterop; using Moq; using Shouldly; -using System.Security.Claims; -using EstateManagementUI.BusinessLogic.Client; -using EstateManagementUI.BusinessLogic.Requests; using SimpleResults; +using System.Security.Claims; namespace EstateManagementUI.BlazorServer.Tests.Pages; -public class HomePageTests : TestContext +public class HomePageTests : BaseTest { - private readonly Mock _mockMediator; - private readonly Mock _mockAuthStateProvider; - private readonly Mock _mockNavigationManager; + private readonly Mock _mockCalenderUiService; + private readonly Mock _mockMerchantUiService; + private readonly Mock _mockTransactionUiService; private readonly Mock _mockJSRuntime; - private readonly Mock _mockPermissionService; - + public HomePageTests() { - _mockMediator = new Mock(); - _mockAuthStateProvider = new Mock(); - _mockNavigationManager = new Mock(); + _mockCalenderUiService = new Mock(); + _mockMerchantUiService = new Mock(); + _mockTransactionUiService = new Mock(); + _mockJSRuntime = new Mock(); - _mockPermissionService = new Mock(); - Services.AddSingleton(_mockMediator.Object); - Services.AddSingleton(_mockAuthStateProvider.Object); - Services.AddSingleton(_mockNavigationManager.Object); + Services.AddSingleton(_mockCalenderUiService.Object); + Services.AddSingleton(_mockMerchantUiService.Object); + Services.AddSingleton(_mockTransactionUiService.Object); Services.AddSingleton(_mockJSRuntime.Object); - Services.AddSingleton(_mockPermissionService.Object); // Add required permission components ComponentFactories.AddStub(); @@ -55,12 +56,63 @@ public void Home_RendersCorrectly() var user = new ClaimsPrincipal(identity); var authState = Task.FromResult(new AuthenticationState(user)); + List comparisonDates = new() + { + new ComparisonDateModel + { + Date = DateTime.Today, + Description = "Today" + }, + new ComparisonDateModel + { + Date = DateTime.Today.AddDays(-1), + Description = "Yesterday" + } + }; + + TransactionModels.MerchantKpiModel merchantKpi = new() { MerchantsWithNoSaleInLast7Days = 5, MerchantsWithNoSaleToday = 12, MerchantsWithSaleInLastHour = 45 }; + + TransactionModels.TodaysSalesModel todaysSales = new() + { + ComparisonSalesCount = 450, + ComparisonSalesValue = 125000.00m, + ComparisonAverageValue = 277.78m, + TodaysSalesCount = 523, + TodaysSalesValue = 145000.00m, + TodaysAverageValue = 277.24m + }; + + List recentMerchants = new() + { + new MerchantModels.RecentMerchantsModel + { + MerchantId = Guid.Parse("22222222-2222-2222-2222-222222222222"), + Name = "Test Merchant 1", + Reference = "MERCH001", + CreatedDateTime = DateTime.Now + }, + new MerchantModels.RecentMerchantsModel + { + MerchantId = Guid.Parse("22222222-2222-2222-2222-222222222223"), + Name = "Test Merchant 2", + Reference = "MERCH002", + CreatedDateTime = DateTime.Now.AddDays(-1) + }, + new MerchantModels.RecentMerchantsModel + { + MerchantId = Guid.Parse("22222222-2222-2222-2222-222222222224"), + Name = "Test Merchant 3", + Reference = "MERCH003", + CreatedDateTime = DateTime.Now.AddDays(-5) + } + }; + _mockAuthStateProvider.Setup(x => x.GetAuthenticationStateAsync()).Returns(authState); - this._mockMediator.Setup(m => m.Send(It.IsAny())).ReturnsAsync(Result.Success(StubTestData.GetMockComparisonDates())); - this._mockMediator.Setup(m => m.Send(It.IsAny())).ReturnsAsync(Result.Success(StubTestData.GetMockMerchantKpi())); - this._mockMediator.Setup(m => m.Send(It.IsAny())).ReturnsAsync(Result.Success(StubTestData.GetMockTodaysSales())); - this._mockMediator.Setup(m => m.Send(It.IsAny())).ReturnsAsync(Result.Success(StubTestData.GetMockTodaysSales())); - this._mockMediator.Setup(m => m.Send(It.IsAny())).ReturnsAsync(Result.Success(StubTestData.GetMockRecentMerchants())); + this._mockCalenderUiService.Setup(m => m.GetComparisonDates(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(comparisonDates)); + this._mockMerchantUiService.Setup(m => m.GetMerchantKpis(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(merchantKpi)); + this._mockMerchantUiService.Setup(m => m.GetRecentMerchants(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(recentMerchants)); + this._mockTransactionUiService.Setup(m => m.GetTodaysSales(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(todaysSales)); + this._mockTransactionUiService.Setup(m => m.GetTodaysFailedSales(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(todaysSales)); // Act var cut = RenderComponent(); @@ -81,13 +133,65 @@ public void Home_HasCorrectPageTitle() var identity = new ClaimsIdentity(claims, "Test"); var user = new ClaimsPrincipal(identity); var authState = Task.FromResult(new AuthenticationState(user)); - + + List comparisonDates = new() + { + new ComparisonDateModel + { + Date = DateTime.Today, + Description = "Today" + }, + new ComparisonDateModel + { + Date = DateTime.Today.AddDays(-1), + Description = "Yesterday" + } + }; + + TransactionModels.MerchantKpiModel merchantKpi = new() { MerchantsWithNoSaleInLast7Days = 5, MerchantsWithNoSaleToday = 12, MerchantsWithSaleInLastHour = 45 }; + + TransactionModels.TodaysSalesModel todaysSales = new() + { + ComparisonSalesCount = 450, + ComparisonSalesValue = 125000.00m, + ComparisonAverageValue = 277.78m, + TodaysSalesCount = 523, + TodaysSalesValue = 145000.00m, + TodaysAverageValue = 277.24m + }; + + List recentMerchants = new() + { + new MerchantModels.RecentMerchantsModel + { + MerchantId = Guid.Parse("22222222-2222-2222-2222-222222222222"), + Name = "Test Merchant 1", + Reference = "MERCH001", + CreatedDateTime = DateTime.Now + }, + new MerchantModels.RecentMerchantsModel + { + MerchantId = Guid.Parse("22222222-2222-2222-2222-222222222223"), + Name = "Test Merchant 2", + Reference = "MERCH002", + CreatedDateTime = DateTime.Now.AddDays(-1) + }, + new MerchantModels.RecentMerchantsModel + { + MerchantId = Guid.Parse("22222222-2222-2222-2222-222222222224"), + Name = "Test Merchant 3", + Reference = "MERCH003", + CreatedDateTime = DateTime.Now.AddDays(-5) + } + }; + _mockAuthStateProvider.Setup(x => x.GetAuthenticationStateAsync()).Returns(authState); - this._mockMediator.Setup(m => m.Send(It.IsAny())).ReturnsAsync(Result.Success( StubTestData.GetMockComparisonDates())); - this._mockMediator.Setup(m => m.Send(It.IsAny())).ReturnsAsync(Result.Success(StubTestData.GetMockMerchantKpi())); - this._mockMediator.Setup(m => m.Send(It.IsAny())).ReturnsAsync(Result.Success(StubTestData.GetMockTodaysSales())); - this._mockMediator.Setup(m => m.Send(It.IsAny())).ReturnsAsync(Result.Success(StubTestData.GetMockTodaysSales())); - this._mockMediator.Setup(m => m.Send(It.IsAny())).ReturnsAsync(Result.Success(StubTestData.GetMockRecentMerchants())); + + this._mockCalenderUiService.Setup(m => m.GetComparisonDates(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(comparisonDates)); + this._mockMerchantUiService.Setup(m => m.GetMerchantKpis(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(merchantKpi)); + this._mockMerchantUiService.Setup(m => m.GetRecentMerchants(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(recentMerchants)); + this._mockTransactionUiService.Setup(m => m.GetTodaysSales(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(todaysSales)); + this._mockTransactionUiService.Setup(m => m.GetTodaysFailedSales(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(todaysSales)); // Act diff --git a/EstateManagementUI.BlazorServer.Tests/UIServices/CalendarUIServiceTests.cs b/EstateManagementUI.BlazorServer.Tests/UIServices/CalendarUIServiceTests.cs new file mode 100644 index 00000000..7a4ab1e3 --- /dev/null +++ b/EstateManagementUI.BlazorServer.Tests/UIServices/CalendarUIServiceTests.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using EstateManagementUI.BlazorServer.UIServices; +using EstateManagementUI.BusinessLogic.Models; +using EstateManagementUI.BusinessLogic.Requests; +using MediatR; +using Moq; +using Shouldly; +using SimpleResults; +using Xunit; + +namespace EstateManagementUI.BlazorServer.Tests.UIServices +{ + public class CalendarUIServiceTests + { + private readonly Mock _mockMediator; + private readonly CalendarUIService _service; + + public CalendarUIServiceTests() + { + _mockMediator = new Mock(); + _service = new CalendarUIService(_mockMediator.Object); + } + + [Fact] + public async Task GetComparisonDates_ReturnsMappedList_WhenMediatorSucceeds() + { + // Arrange + var estateId = Guid.NewGuid(); + var bizList = new List + { + new() { Date = DateTime.UtcNow.Date.AddDays(-7), Description = "Last Week" }, + new() { Date = DateTime.UtcNow.Date.AddDays(-30), Description = "Last Month" } + }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(bizList)); + + // Act + var result = await _service.GetComparisonDates(CorrelationIdHelper.New(), estateId); + + // Assert + result.IsSuccess.ShouldBeTrue(); + result.Data.ShouldNotBeNull(); + result.Data!.Count.ShouldBe(2); + result.Data[0].Description.ShouldBe("Last Week"); + result.Data[1].Description.ShouldBe("Last Month"); + + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetComparisonDates_ReturnsFailure_WhenMediatorFails() + { + // Arrange + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("backend error")); + + // Act + var result = await _service.GetComparisonDates(CorrelationIdHelper.New(), Guid.NewGuid()); + + // Assert + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + } +} \ No newline at end of file diff --git a/EstateManagementUI.BlazorServer.Tests/UIServices/ContractUIServiceTests.cs b/EstateManagementUI.BlazorServer.Tests/UIServices/ContractUIServiceTests.cs index ffec9e89..7eeec6e7 100644 --- a/EstateManagementUI.BlazorServer.Tests/UIServices/ContractUIServiceTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/UIServices/ContractUIServiceTests.cs @@ -345,5 +345,54 @@ public async Task CreateContract_ReturnsFailure_WhenMediatorFails() // Assert result.IsFailed.ShouldBeTrue(); } + + [Fact] + public async Task GetContractsForDropDown_ReturnsMappedList_WhenMediatorSucceeds() + { + // Arrange + var estateId = Guid.NewGuid(); + var correlationId = CorrelationIdHelper.New(); + + var bizList = new List + { + new() { ContractId = Guid.NewGuid(), Description = "Contract A", OperatorName = "Op A" }, + new() { ContractId = Guid.NewGuid(), Description = "Contract B", OperatorName = "Op B" } + }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(bizList)); + + // Act + var result = await _service.GetContractsForDropDown(correlationId, estateId); + + // Assert + result.IsSuccess.ShouldBeTrue(); + result.Data.ShouldNotBeNull(); + result.Data!.Count.ShouldBe(2); + result.Data[0].ContractId.ShouldBe(bizList[0].ContractId); + result.Data[0].Description.ShouldBe("Contract A"); + result.Data[0].OperatorName.ShouldBe("Op A"); + + _mockMediator.Verify(m => + m.Send(It.Is(q => q.EstateId == estateId), It.IsAny()), + Times.Once); + } + + [Fact] + public async Task GetContractsForDropDown_ReturnsFailure_WhenMediatorFails() + { + // Arrange + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("backend error")); + + // Act + var result = await _service.GetContractsForDropDown(CorrelationIdHelper.New(), Guid.NewGuid()); + + // Assert + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } } } \ No newline at end of file diff --git a/EstateManagementUI.BlazorServer.Tests/UIServices/MerchantUIServiceTests.cs b/EstateManagementUI.BlazorServer.Tests/UIServices/MerchantUIServiceTests.cs index 50e6d004..4a197b32 100644 --- a/EstateManagementUI.BlazorServer.Tests/UIServices/MerchantUIServiceTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/UIServices/MerchantUIServiceTests.cs @@ -159,7 +159,7 @@ public async Task GetMerchantContracts_ReturnsMappedList_WhenMediatorSucceeds() var merchantId = Guid.NewGuid(); var bizList = new List { - new() { MerchantId = merchantId, ContractId = Guid.NewGuid(), ContractName = "C1", OperatorName = "Op" , IsDeleted=false, + new() { MerchantId = merchantId, ContractId = Guid.NewGuid(), ContractName = "C1", OperatorName = "Op" , IsDeleted=false, ContractProducts = new List() } }; @@ -407,5 +407,225 @@ public async Task AddSwapDeviceAndMakeDeposit_SendCorrectCommands() c.EstateId == estateId && c.MerchantId == merchantId && c.Amount == depositModel.Amount && c.Reference == depositModel.Reference ), It.IsAny()), Times.Once); } + + [Fact] + public async Task GetRecentMerchants_ReturnsMappedList_WhenMediatorSucceeds() + { + var estateId = Guid.NewGuid(); + var bizList = new List + { + new() { MerchantId = Guid.NewGuid(), Name = "RecentM", Reference = "RM", CreatedDateTime = DateTime.UtcNow } + }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(bizList)); + + var result = await _service.GetRecentMerchants(CorrelationIdHelper.New(), estateId); + + result.IsSuccess.ShouldBeTrue(); + result.Data.ShouldNotBeNull(); + result.Data!.Count.ShouldBe(1); + result.Data[0].Name.ShouldBe("RecentM"); + result.Data[0].Reference.ShouldBe("RM"); + } + + [Fact] + public async Task GetRecentMerchants_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetRecentMerchants(CorrelationIdHelper.New(), Guid.NewGuid()); + + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task GetMerchantKpis_ReturnsMappedModel_WhenMediatorSucceeds() + { + var estateId = Guid.NewGuid(); + var bizKpi = new BusinessLogic.Models.MerchantModels.MerchantKpiModel + { + MerchantsWithNoSaleInLast7Days = 5, + MerchantsWithNoSaleToday = 2, + MerchantsWithSaleInLastHour = 1 + }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(bizKpi)); + + var result = await _service.GetMerchantKpis(CorrelationIdHelper.New(), estateId); + + result.IsSuccess.ShouldBeTrue(); + result.Data!.MerchantsWithNoSaleInLast7Days.ShouldBe(5); + result.Data.MerchantsWithNoSaleToday.ShouldBe(2); + result.Data.MerchantsWithSaleInLastHour.ShouldBe(1); + } + + [Fact] + public async Task GetMerchantKpis_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetMerchantKpis(CorrelationIdHelper.New(), Guid.NewGuid()); + + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task GetMerchantsForDropDown_ReturnsMappedList_WhenMediatorSucceeds() + { + var estateId = Guid.NewGuid(); + var bizList = new List + { + new() { MerchantId = Guid.NewGuid(), MerchantName = "MDrop" } + }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(bizList)); + + var result = await _service.GetMerchantsForDropDown(CorrelationIdHelper.New(), estateId); + + result.IsSuccess.ShouldBeTrue(); + result.Data.ShouldNotBeNull(); + result.Data!.Count.ShouldBe(1); + result.Data[0].MerchantName.ShouldBe("MDrop"); + } + + [Fact] + public async Task GetMerchantsForDropDown_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetMerchantsForDropDown(CorrelationIdHelper.New(), Guid.NewGuid()); + + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task AddOperatorToMerchant_ReturnsFailure_WhenMediatorFails() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + var operatorId = Guid.NewGuid(); + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.AddOperatorToMerchant(CorrelationIdHelper.New(), estateId, merchantId, operatorId, "MN", "TN"); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task RemoveOperatorFromMerchant_ReturnsFailure_WhenMediatorFails() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + var operatorId = Guid.NewGuid(); + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.RemoveOperatorFromMerchant(CorrelationIdHelper.New(), estateId, merchantId, operatorId); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task AssignContractToMerchant_ReturnsFailure_WhenMediatorFails() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + var contractId = Guid.NewGuid(); + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.AssignContractToMerchant(CorrelationIdHelper.New(), estateId, merchantId, contractId); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task RemoveContractFromMerchant_ReturnsFailure_WhenMediatorFails() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + var contractId = Guid.NewGuid(); + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.RemoveContractFromMerchant(CorrelationIdHelper.New(), estateId, merchantId, contractId); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task AddMerchantDevice_ReturnsFailure_WhenMediatorFails() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + var deviceId = "device-123"; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.AddMerchantDevice(CorrelationIdHelper.New(), estateId, merchantId, deviceId); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task SwapMerchantDevice_ReturnsFailure_WhenMediatorFails() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.SwapMerchantDevice(CorrelationIdHelper.New(), estateId, merchantId, "old", "new"); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task MakeMerchantDeposit_ReturnsFailure_WhenMediatorFails() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + var deposit = new BlazorServer.Models.MerchantModels.DepositModel { Amount = 10, Date = DateTime.UtcNow, Reference = "ref" }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.MakeMerchantDeposit(CorrelationIdHelper.New(), estateId, merchantId, deposit); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } } } \ No newline at end of file diff --git a/EstateManagementUI.BlazorServer.Tests/UIServices/OperatorUIServiceTests.cs b/EstateManagementUI.BlazorServer.Tests/UIServices/OperatorUIServiceTests.cs index d5719742..88692105 100644 --- a/EstateManagementUI.BlazorServer.Tests/UIServices/OperatorUIServiceTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/UIServices/OperatorUIServiceTests.cs @@ -188,4 +188,48 @@ public async Task CreateOperator_ReturnsFailure_WhenMediatorFails() // Assert result.IsFailed.ShouldBeTrue(); } - } \ No newline at end of file + + [Fact] + public async Task GetOperatorsForDropDown_ReturnsMappedList_WhenMediatorSucceeds() + { + // Arrange + var estateId = Guid.NewGuid(); + var bizList = new List + { + new() { OperatorId = Guid.NewGuid(), OperatorName = "Op1" } + }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(bizList)); + + // Act + var result = await _service.GetOperatorsForDropDown(CorrelationIdHelper.New(), estateId); + + // Assert + result.IsSuccess.ShouldBeTrue(); + result.Data.ShouldNotBeNull(); + result.Data!.Count.ShouldBe(1); + result.Data[0].OperatorName.ShouldBe("Op1"); + + _mockMediator.Verify(m => + m.Send(It.Is(q => q.EstateId == estateId), + It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetOperatorsForDropDown_ReturnsFailure_WhenMediatorFails() + { + // Arrange + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("backend error")); + + // Act + var result = await _service.GetOperatorsForDropDown(CorrelationIdHelper.New(), Guid.NewGuid()); + + // Assert + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } +} \ No newline at end of file diff --git a/EstateManagementUI.BlazorServer.Tests/UIServices/TransactionUIServiceTests.cs b/EstateManagementUI.BlazorServer.Tests/UIServices/TransactionUIServiceTests.cs new file mode 100644 index 00000000..4e99c0c7 --- /dev/null +++ b/EstateManagementUI.BlazorServer.Tests/UIServices/TransactionUIServiceTests.cs @@ -0,0 +1,325 @@ +using EstateManagementUI.BlazorServer.UIServices; +using EstateManagementUI.BusinessLogic.Models; +using EstateManagementUI.BusinessLogic.Requests; +using MediatR; +using Moq; +using Shouldly; +using SimpleResults; + +namespace EstateManagementUI.BlazorServer.Tests.UIServices; + +public class TransactionUIServiceTests +{ + private readonly Mock _mockMediator; + private readonly TransactionUIService _service; + + public TransactionUIServiceTests() + { + this._mockMediator = new Mock(); + this._service = new TransactionUIService(this._mockMediator.Object); + } + + [Fact] + public async Task GetTodaysSales_CallsMediatorWithCorrectQuery_AndReturnsSuccess() + { + var estateId = Guid.NewGuid(); + var comparisonDate = DateTime.UtcNow.Date; + + var biz = new BusinessLogic.Models.TodaysSalesModel + { + TodaysSalesValue = 123.45m, + TodaysSalesCount = 10 + }; + + this._mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(biz)); + + var result = await this._service.GetTodaysSales(CorrelationIdHelper.New(), estateId, comparisonDate); + + result.IsSuccess.ShouldBeTrue(); + result.Data!.TodaysSalesCount.ShouldBe(10); + + this._mockMediator.Verify(m => + m.Send(It.Is(q => + q.EstateId == estateId && q.ComparisonDate.Date == comparisonDate.Date + ), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetTodaysSales_ReturnsFailure_WhenMediatorFails() + { + this._mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await this._service.GetTodaysSales(CorrelationIdHelper.New(), Guid.NewGuid(), DateTime.UtcNow); + + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task GetTodaysFailedSales_PassesResponseCodeAndReturnsSuccess() + { + var estateId = Guid.NewGuid(); + var comparisonDate = DateTime.UtcNow.Date; + var responseCode = "RC"; + + var biz = new BusinessLogic.Models.TodaysSalesModel + { + TodaysSalesValue = 1.23m, + TodaysSalesCount = 2 + }; + + this._mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(biz)); + + var result = await this._service.GetTodaysFailedSales(CorrelationIdHelper.New(), estateId, responseCode, comparisonDate); + + result.IsSuccess.ShouldBeTrue(); + this._mockMediator.Verify(m => + m.Send(It.Is(q => + q.EstateId == estateId && q.ResponseCode == responseCode && q.ComparisonDate.Date == comparisonDate.Date + ), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetTodaysSalesByHour_ReturnsListAndVerifiesQuery() + { + var estateId = Guid.NewGuid(); + var comparisonDate = DateTime.UtcNow.Date; + + var bizList = new List + { + new() { Hour = 8, TodaysSalesCount = 1, TodaysSalesValue = 10m } + }; + + this._mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(bizList)); + + var result = await this._service.GetTodaysSalesByHour(CorrelationIdHelper.New(), estateId, comparisonDate); + + result.IsSuccess.ShouldBeTrue(); + result.Data!.Count.ShouldBe(1); + + this._mockMediator.Verify(m => + m.Send(It.Is(q => + q.EstateId == estateId && q.ComparisonDate.Date == comparisonDate.Date + ), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetTodaysSettlement_ReturnsMappedModel() + { + var estateId = Guid.NewGuid(); + var comparisonDate = DateTime.UtcNow.Date; + + var biz = new BusinessLogic.Models.TodaysSettlementModel + { + TodaysSettlementValue = 50m + }; + + this._mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(biz)); + + var result = await this._service.GetTodaysSettlement(CorrelationIdHelper.New(), estateId, comparisonDate); + + result.IsSuccess.ShouldBeTrue(); + result.Data!.TodaysSettlementValue.ShouldBe(50m); + } + + [Fact] + public async Task GetProductPerformance_PassesDateRange_AndReturnsSuccess() + { + var estateId = Guid.NewGuid(); + var start = DateTime.UtcNow.AddDays(-7); + var end = DateTime.UtcNow; + + var biz = new TransactionModels.ProductPerformanceResponse() + { + ProductDetails = new(), + Summary = new() + }; + + this._mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(biz)); + + var result = await this._service.GetProductPerformance(CorrelationIdHelper.New(), estateId, start, end); + + result.IsSuccess.ShouldBeTrue(); + this._mockMediator.Verify(m => + m.Send(It.Is(q => + q.EstateId == estateId && q.StartDate == start && q.EndDate == end + ), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetTransactionDetail_AllowsNullAndNonNullFilters() + { + var estateId = Guid.NewGuid(); + var start = DateTime.UtcNow.AddDays(-7); + var end = DateTime.UtcNow; + + var biz = new BusinessLogic.Models.TransactionModels.TransactionDetailReportResponse + { + Transactions = new(), + Summary = new() + }; + + this._mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(biz)); + + // Null filters + var resultNull = await this._service.GetTransactionDetail(CorrelationIdHelper.New(), estateId, start, end, null, null, null); + resultNull.IsSuccess.ShouldBeTrue(); + + // Non-null filters + var merchantIds = new List { 1, 2 }; + var operatorIds = new List { 3 }; + var productIds = new List { 4 }; + + var result = await this._service.GetTransactionDetail(CorrelationIdHelper.New(), estateId, start, end, merchantIds, operatorIds, productIds); + result.IsSuccess.ShouldBeTrue(); + + this._mockMediator.Verify(m => + m.Send(It.Is(q => + q.EstateId == estateId && q.MerchantIds == merchantIds && q.OperatorIds == operatorIds && q.ProductIds == productIds + ), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetMerchantTransactionSummary_And_GetOperatorTransactionSummary_ReturnSuccess() + { + var estateId = Guid.NewGuid(); + var start = DateTime.UtcNow.AddDays(-7); + var end = DateTime.UtcNow; + + var bizMerchant = new BusinessLogic.Models.TransactionModels.TransactionSummaryByMerchantResponse + { + Summary = new(), + Merchants = new() + }; + + var bizOperator = new BusinessLogic.Models.TransactionModels.TransactionSummaryByOperatorResponse + { + Summary = new(), + Operators = new() + }; + + this._mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(bizMerchant)); + + this._mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(bizOperator)); + + var merchantResult = await this._service.GetMerchantTransactionSummary(CorrelationIdHelper.New(), estateId, start, end, 5, 6); + merchantResult.IsSuccess.ShouldBeTrue(); + + var operatorResult = await this._service.GetOperatorTransactionSummary(CorrelationIdHelper.New(), estateId, start, end, 7, 8); + operatorResult.IsSuccess.ShouldBeTrue(); + + this._mockMediator.Verify(m => m.Send(It.Is(q => + q.EstateId == estateId && q.MerchantId == 5 && q.OperatorId == 6 + ), It.IsAny()), Times.Once); + + this._mockMediator.Verify(m => m.Send(It.Is(q => + q.EstateId == estateId && q.MerchantId == 7 && q.OperatorId == 8 + ), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetTodaysFailedSales_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetTodaysFailedSales(CorrelationIdHelper.New(), Guid.NewGuid(), "RC", DateTime.UtcNow); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetTodaysSalesByHour_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetTodaysSalesByHour(CorrelationIdHelper.New(), Guid.NewGuid(), DateTime.UtcNow); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetTodaysSettlement_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetTodaysSettlement(CorrelationIdHelper.New(), Guid.NewGuid(), DateTime.UtcNow); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetProductPerformance_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetProductPerformance(CorrelationIdHelper.New(), Guid.NewGuid(), DateTime.UtcNow.AddDays(-7), DateTime.UtcNow); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetTransactionDetail_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetTransactionDetail(CorrelationIdHelper.New(), Guid.NewGuid(), DateTime.UtcNow.AddDays(-7), DateTime.UtcNow, null, null, null); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetMerchantTransactionSummary_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetMerchantTransactionSummary(CorrelationIdHelper.New(), Guid.NewGuid(), DateTime.UtcNow.AddDays(-7), DateTime.UtcNow, null, null); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetOperatorTransactionSummary_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetOperatorTransactionSummary(CorrelationIdHelper.New(), Guid.NewGuid(), DateTime.UtcNow.AddDays(-7), DateTime.UtcNow, null, null); + + result.IsFailed.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } +} \ No newline at end of file diff --git a/EstateManagementUI.BlazorServer/Common/BoostrapperExtensions.cs b/EstateManagementUI.BlazorServer/Common/BoostrapperExtensions.cs index cc8df6bb..1f1f1d20 100644 --- a/EstateManagementUI.BlazorServer/Common/BoostrapperExtensions.cs +++ b/EstateManagementUI.BlazorServer/Common/BoostrapperExtensions.cs @@ -254,6 +254,9 @@ public static WebApplicationBuilder RegisterUIServices(this WebApplicationBuilde builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + return builder; } public static WebApplicationBuilder RegisterClients(this WebApplicationBuilder builder) { diff --git a/EstateManagementUI.BlazorServer/Common/Helpers.cs b/EstateManagementUI.BlazorServer/Common/Helpers.cs index f3107921..9e742128 100644 --- a/EstateManagementUI.BlazorServer/Common/Helpers.cs +++ b/EstateManagementUI.BlazorServer/Common/Helpers.cs @@ -29,7 +29,7 @@ public static Result GetEstateIdFromClaims(this AuthenticationState authSt return Result.Success(estateId); } - private static decimal GetSalesVariance(TodaysSalesModel todaysSales) + private static decimal GetSalesVariance(TransactionModels.TodaysSalesModel todaysSales) { if (todaysSales == null) return 0; if (todaysSales.ComparisonSalesValue == 0) @@ -43,7 +43,7 @@ private static decimal GetSalesVariance(TodaysSalesModel todaysSales) return (todaysSales.TodaysSalesValue - todaysSales.ComparisonSalesValue) / todaysSales.ComparisonSalesValue; } - public static string GetSalesBackgroundClass(TodaysSalesModel todaysSales) + public static string GetSalesBackgroundClass(TransactionModels.TodaysSalesModel todaysSales) { Decimal variance = GetSalesVariance(todaysSales); @@ -56,7 +56,7 @@ public static string GetSalesBackgroundClass(TodaysSalesModel todaysSales) }; } - public static string GetSalesTextClass(TodaysSalesModel todaysSales, double opacity = 1.0) + public static string GetSalesTextClass(TransactionModels.TodaysSalesModel todaysSales, double opacity = 1.0) { var variance = GetSalesVariance(todaysSales); @@ -73,7 +73,7 @@ public static string GetSalesTextClass(TodaysSalesModel todaysSales, double opac }; } - public static string GetSalesBorderClass(TodaysSalesModel todaysSales) + public static string GetSalesBorderClass(TransactionModels.TodaysSalesModel todaysSales) { Decimal variance = GetSalesVariance(todaysSales); return variance switch @@ -85,7 +85,7 @@ public static string GetSalesBorderClass(TodaysSalesModel todaysSales) }; } - public static string GetSalesVarianceDisplay(TodaysSalesModel todaysSales) + public static string GetSalesVarianceDisplay(TransactionModels.TodaysSalesModel todaysSales) { Decimal variance = GetSalesVariance(todaysSales); // Special case: comparison was 0, now has sales @@ -95,7 +95,7 @@ public static string GetSalesVarianceDisplay(TodaysSalesModel todaysSales) return $"{sign}{percentageChange:F1}%"; } - public static string GetFailedSalesBackgroundClass(TodaysSalesModel todaysSales) + public static string GetFailedSalesBackgroundClass(TransactionModels.TodaysSalesModel todaysSales) { Decimal variance = GetSalesVariance(todaysSales); return variance switch @@ -107,7 +107,7 @@ public static string GetFailedSalesBackgroundClass(TodaysSalesModel todaysSales) }; } - public static string GetFailedSalesTextClass(TodaysSalesModel todaysSales, double opacity = 1.0) + public static string GetFailedSalesTextClass(TransactionModels.TodaysSalesModel todaysSales, double opacity = 1.0) { Decimal variance = GetSalesVariance(todaysSales); @@ -124,7 +124,7 @@ public static string GetFailedSalesTextClass(TodaysSalesModel todaysSales, doubl }; } - public static string GetFailedSalesBorderClass(TodaysSalesModel todaysSales) + public static string GetFailedSalesBorderClass(TransactionModels.TodaysSalesModel todaysSales) { Decimal variance = GetSalesVariance(todaysSales); return variance switch @@ -136,7 +136,7 @@ public static string GetFailedSalesBorderClass(TodaysSalesModel todaysSales) }; } - public static string GetFailedSalesVarianceDisplay(TodaysSalesModel todaysSales) + public static string GetFailedSalesVarianceDisplay(TransactionModels.TodaysSalesModel todaysSales) { Decimal variance = GetSalesVariance(todaysSales); // Special case: comparison was 0, now has failures diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Home.razor b/EstateManagementUI.BlazorServer/Components/Pages/Home.razor index e1a5b8f8..cbcca700 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Home.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Home.razor @@ -1,11 +1,15 @@ @page "/" @using EstateManagementUI.BlazorServer.Common +@using EstateManagementUI.BlazorServer.UIServices @rendermode InteractiveServer -@inject IMediator Mediator -@inject AuthenticationStateProvider AuthenticationStateProvider +@* @inject AuthenticationStateProvider AuthenticationStateProvider *@ @inject NavigationManager NavigationManager @inject IJSRuntime JSRuntime @inject IPermissionService PermissionService +@inject ITransactionUIService TransactionUiService +@inject IMerchantUIService MerchantUiService +@inject ICalendarUIService CalendarUiService; +@inherits AuthorizedComponentBase Dashboard diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Home.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Home.razor.cs index 7a1847a2..a89f8789 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Home.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Home.razor.cs @@ -1,15 +1,13 @@ -using System.Security.Claims; -using EstateManagementUI.BlazorServer.Common; -using EstateManagementUI.BlazorServer.Factories; +using EstateManagementUI.BlazorServer.Common; using EstateManagementUI.BlazorServer.Models; +using EstateManagementUI.BlazorServer.Permissions; using EstateManagementUI.BusinessLogic.Requests; -using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.JSInterop; using Shared.Exceptions; -using Shared.General; using Shared.Results; using SimpleResults; +using System.Security.Claims; namespace EstateManagementUI.BlazorServer.Components.Pages; @@ -20,29 +18,27 @@ public partial class Home private bool isLoading = true; private bool isAdministrator; - private string? errorMessage; - - private MerchantKpiModel? merchantKpi; - private TodaysSalesModel? todaysSales; - private TodaysSalesModel? todaysFailedSales; + + private TransactionModels.MerchantKpiModel? merchantKpi; + private TransactionModels.TodaysSalesModel? todaysSales; + private TransactionModels.TodaysSalesModel? todaysFailedSales; private List? comparisonDates; - private List? recentMerchants; + private List? recentMerchants; private string _selectedComparisonDate = DateTime.Now.AddDays(-7).ToString("yyyy-MM-dd"); private int changeEventCounter = 0; - protected override async Task OnInitializedAsync() - { - // Keep prerender work minimal. This will still run during prerender, - // so avoid doing heavy/interactive-only tasks here. - await this.LogToConsole("OnInitializedAsync (prerender/early) START"); - // Do not call LoadDashboardData() here to avoid double-load when prerendering. - await base.OnInitializedAsync(); - } + //protected override async Task OnInitializedAsync() + //{ + // // Keep prerender work minimal. This will still run during prerender, + // // so avoid doing heavy/interactive-only tasks here. + // await this.LogToConsole("OnInitializedAsync (prerender/early) START"); + // // Do not call LoadDashboardData() here to avoid double-load when prerendering. + // await base.OnInitializedAsync(); + //} protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) { - await base.OnAfterRenderAsync(firstRender); return; } @@ -51,11 +47,12 @@ protected override async Task OnAfterRenderAsync(bool firstRender) try { - AuthenticationState authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - ClaimsPrincipal user = authState.User; + await OnAfterRender(PermissionSection.Dashboard, PermissionFunction.View); + ClaimsPrincipal user = this.AuthState.User; // Redirect unauthenticated users to entry screen - if (!user.Identity?.IsAuthenticated ?? true) { + if (!user.Identity?.IsAuthenticated ?? true) + { NavigationManager.NavigateToEntryPage(); return; } @@ -66,21 +63,20 @@ protected override async Task OnAfterRenderAsync(bool firstRender) await this.LogToConsole($"User role: {role}, isAdministrator: {this.isAdministrator}"); CorrelationId correlationId = new CorrelationId(Guid.NewGuid()); - var estateIdResult = authState.GetEstateIdFromClaims(); - if (estateIdResult.IsFailed) { - this.NavigationManager.NavigateToErrorPage(); - return; - } + var estateId = await this.GetEstateId(); // Only load dashboard data for non-admins - if (this.isAdministrator == false) { - var result = await this.LoadDashboardData(correlationId, estateIdResult.Data); - if (result.IsFailed) { + if (this.isAdministrator == false) + { + var result = await this.LoadDashboardData(correlationId, estateId); + if (result.IsFailed) + { this.NavigationManager.NavigateToErrorPage(); return; } } - else { + else + { this.isLoading = false; this.StateHasChanged(); } @@ -112,35 +108,32 @@ private async Task LoadDashboardData(CorrelationId correlationId, Guid e } // Load all dashboard data in parallel - var kpiTask = Mediator.Send(new MerchantQueries.GetMerchantKpiQuery(correlationId, estateId)); - var salesTask = Mediator.Send(new TransactionQueries.GetTodaysSalesQuery(correlationId, estateId, comparisonDateResult.Data)); - var failedSalesTask = Mediator.Send(new TransactionQueries.GetTodaysFailedSalesQuery(correlationId, estateId, LOW_CREDIT_RESPONSE_CODE, comparisonDateResult.Data)); - var merchantsTask = Mediator.Send(new MerchantQueries.GetRecentMerchantsQuery(correlationId, estateId)); + var kpiTask = this.MerchantUiService.GetMerchantKpis(correlationId, estateId); + var salesTask = this.TransactionUiService.GetTodaysSales(correlationId, estateId, comparisonDateResult.Data); + var failedSalesTask = this.TransactionUiService.GetTodaysFailedSales(correlationId, estateId, LOW_CREDIT_RESPONSE_CODE, comparisonDateResult.Data); + var merchantsTask = this.MerchantUiService.GetRecentMerchants(correlationId, estateId); await Task.WhenAll(kpiTask, salesTask, failedSalesTask, merchantsTask); // Process results if (kpiTask.Result.IsFailed) - return ResultHelpers.CreateFailure(kpiTask.Result); - - this.merchantKpi = ModelFactory.ConvertFrom((BusinessLogic.Models.MerchantModels.MerchantKpiModel)kpiTask.Result.Data); + return ResultHelpers.CreateFailure(kpiTask.Result); + this.merchantKpi = kpiTask.Result.Data; if (salesTask.Result.IsFailed) - return ResultHelpers.CreateFailure(salesTask.Result); - - this.todaysSales = ModelFactory.ConvertFrom((BusinessLogic.Models.TodaysSalesModel)salesTask.Result.Data); + return ResultHelpers.CreateFailure(salesTask.Result); + this.todaysSales = salesTask.Result.Data; if (failedSalesTask.Result.IsFailed) - return ResultHelpers.CreateFailure(failedSalesTask.Result); - - this.todaysFailedSales = ModelFactory.ConvertFrom((BusinessLogic.Models.TodaysSalesModel)failedSalesTask.Result.Data); + return ResultHelpers.CreateFailure(failedSalesTask.Result); + this.todaysFailedSales = failedSalesTask.Result.Data; if (merchantsTask.Result.IsFailed) return ResultHelpers.CreateFailure(merchantsTask.Result); // Note: API returns merchants in creation order (newest first) // If ordering is incorrect, would need CreatedDate field in the model - this.recentMerchants = ModelFactory.ConvertFrom((List)merchantsTask.Result.Data); + this.recentMerchants = merchantsTask.Result.Data; return Result.Success(); } @@ -157,15 +150,13 @@ private async Task LoadDashboardData(CorrelationId correlationId, Guid e private async Task> LoadComparisonDates(CorrelationId correlationId, Guid estateId) { - Result> comparisonDatesResult; if (this.comparisonDates == null || !this.comparisonDates.Any()) { - comparisonDatesResult = await Mediator.Send(new Queries.GetComparisonDatesQuery(correlationId, estateId)); + var comparisonDatesResult = await this.CalendarUiService.GetComparisonDates(correlationId, estateId); if (comparisonDatesResult.IsFailed) { return ResultHelpers.CreateFailure(comparisonDatesResult); } - - - this.comparisonDates = ModelFactory.ConvertFrom(comparisonDatesResult.Data); + + this.comparisonDates = comparisonDatesResult.Data; if (this.comparisonDates != null && this.comparisonDates.Any()) { // Set default comparison date to the first one only on initial load this._selectedComparisonDate = this.comparisonDates.First().Date.ToString("yyyy-MM-dd"); @@ -188,16 +179,11 @@ private async Task OnComparisonDateChanged() // This is called after _selectedComparisonDate is updated by @bind-Value if (this.isAdministrator == false) { - CorrelationId correlationId = new CorrelationId(Guid.NewGuid()); - AuthenticationState authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - Result estateIdResult = authState.GetEstateIdFromClaims(); - if (estateIdResult.IsFailed) { - this.NavigationManager.NavigateToErrorPage(); - return; - } - + CorrelationId correlationId = CorrelationIdHelper.New(); + Guid estateId= await this.GetEstateId(); + await this.LogToConsole($"Loading dashboard data for date: {this._selectedComparisonDate}"); - var loadResult = await this.LoadDashboardData(correlationId,estateIdResult.Data); + var loadResult = await this.LoadDashboardData(correlationId,estateId); if (loadResult.IsFailed) { this.NavigationManager.NavigateToErrorPage(); return; diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/View.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/View.razor.cs index 12a75bde..10e54ca9 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/View.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/View.razor.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Components; using Shared.Results; using SimpleResults; -using TransactionDetailModel = EstateManagementUI.BlazorServer.Models.TransactionDetailModel; +using TransactionDetailModel = EstateManagementUI.BlazorServer.Models.TransactionModels.TransactionDetailModel; namespace EstateManagementUI.BlazorServer.Components.Pages.Merchants { diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/AnalyticalCharts.razor b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/AnalyticalCharts.razor index ab3af6a2..9e744514 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/AnalyticalCharts.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/AnalyticalCharts.razor @@ -1,12 +1,13 @@ @page "/reporting/analytical-charts" @using EstateManagementUI.BlazorServer.Factories +@using EstateManagementUI.BlazorServer.UIServices @using EstateManagementUI.BusinessLogic.Requests @using global::Shared.General @rendermode InteractiveServer @inherits AuthorizedComponentBase -@inject IMediator Mediator +@inject ITransactionUIService TransactionUiService +@inject ICalendarUIService CalendarUiService @inject IJSRuntime JSRuntime -@inject AuthenticationStateProvider AuthenticationStateProvider Analytical Charts (Volume & Value) diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/AnalyticalCharts.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/AnalyticalCharts.razor.cs index dc0bd1e6..868f10e0 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/AnalyticalCharts.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/AnalyticalCharts.razor.cs @@ -22,9 +22,9 @@ public partial class AnalyticalCharts private decimal averageValue = 0; private decimal netSettlement = 0; - private List? salesByHourData; - private TodaysSalesModel? todaysSales; - private TodaysSettlementModel? todaysSettlement; + private List? salesByHourData; + private TransactionModels.TodaysSalesModel? todaysSales; + private TransactionModels.TodaysSettlementModel? todaysSettlement; protected override async Task OnAfterRenderAsync(bool firstRender) { @@ -61,8 +61,6 @@ protected override async Task OnAfterRenderAsync(bool firstRender) return; } } - - } private async Task LoadDashboardData() { @@ -78,10 +76,10 @@ private async Task LoadDashboardData() { // Load comparison dates first (only if not already loaded) if (comparisonDates == null || !comparisonDates.Any()) { - var comparisonDatesResult = await Mediator.Send(new Queries.GetComparisonDatesQuery(correlationId, estateId)); + var comparisonDatesResult = await this.CalendarUiService.GetComparisonDates(correlationId, estateId); if (comparisonDatesResult.IsSuccess) { - comparisonDates = ModelFactory.ConvertFrom(comparisonDatesResult.Data); + comparisonDates = comparisonDatesResult.Data; if (comparisonDates != null && comparisonDates.Any()) { _selectedComparisonDate = comparisonDates.First().Date.ToString("yyyy-MM-dd"); @@ -95,21 +93,21 @@ private async Task LoadDashboardData() { } // Load all data in parallel - var salesByHourTask = Mediator.Send(new TransactionQueries.GetTodaysSalesByHourQuery(correlationId, estateId, comparisonDate)); - var todaysSalesTask = Mediator.Send(new TransactionQueries.GetTodaysSalesQuery(correlationId, estateId, comparisonDate)); - var settlementTask = Mediator.Send(new SettlementQueries.GetTodaysSettlementQuery(correlationId, estateId, comparisonDate)); + var salesByHourTask = this.TransactionUiService.GetTodaysSalesByHour(correlationId, estateId, comparisonDate); + var todaysSalesTask = this.TransactionUiService.GetTodaysSales(correlationId, estateId, comparisonDate); + var settlementTask = this.TransactionUiService.GetTodaysSettlement(correlationId, estateId, comparisonDate); await Task.WhenAll(salesByHourTask, todaysSalesTask, settlementTask); // Process results if (salesByHourTask.Result.IsSuccess) - this.salesByHourData = ModelFactory.ConvertFrom(salesByHourTask.Result.Data); + this.salesByHourData = salesByHourTask.Result.Data; if (todaysSalesTask.Result.IsSuccess) - todaysSales = ModelFactory.ConvertFrom(todaysSalesTask.Result.Data); + todaysSales = todaysSalesTask.Result.Data; if (settlementTask.Result.IsSuccess) - todaysSettlement = ModelFactory.ConvertFrom(settlementTask.Result.Data); + todaysSettlement = settlementTask.Result.Data; // Calculate KPIs CalculateKPIs(); @@ -170,7 +168,7 @@ private async Task UpdateCharts() var comparisonLabel = GetComparisonLabel(); var todayLabel = today.ToString("MMM dd"); - var comparisonDateLabel = compDate.ToString("MMM dd"); + var comparisonDateLabel = comparisonDateParsed.ToString("MMM dd"); // Update Volume Chart await JSRuntime.InvokeVoidAsync("updateOrCreateChart", @@ -204,27 +202,6 @@ await JSRuntime.InvokeVoidAsync("updateOrCreateChart", } } - // Helper to retry checking for element presence - private async Task EnsureCanvasExistsAsync(string elementId, int retries = 5, int delayMs = 200) - { - for (int i = 0; i < retries; i++) - { - try - { - var exists = await JSRuntime.InvokeAsync("elementExists", elementId); - if (exists) return true; - } - catch - { - // elementExists may not be available yet - } - - await Task.Delay(delayMs); - } - - return false; - } - private string GetComparisonLabel() { if (comparisonDates == null) return "Comparison"; diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/ProductPerformance.razor b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/ProductPerformance.razor index 7538fa11..c3d5f5c7 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/ProductPerformance.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/ProductPerformance.razor @@ -1,11 +1,10 @@ @page "/reporting/product-performance" @using EstateManagementUI.BlazorServer.Factories +@using EstateManagementUI.BlazorServer.UIServices @using EstateManagementUI.BusinessLogic.Requests @rendermode InteractiveServer @inherits AuthorizedComponentBase -@inject IMediator Mediator -@inject NavigationManager Navigation -@inject ILogger Logger +@inject ITransactionUIService TransactionUiService Product Performance Report diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/ProductPerformance.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/ProductPerformance.razor.cs index 9ec6c567..18391ab3 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/ProductPerformance.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/ProductPerformance.razor.cs @@ -46,22 +46,17 @@ private async Task LoadData() var startDate = _startDate.ToDateTime(TimeOnly.MinValue); var endDate = _endDate.ToDateTime(TimeOnly.MaxValue); - var result = await Mediator.Send(new TransactionQueries.GetProductPerformanceQuery( - correlationId, - estateId, - startDate, - endDate - )); + var result = await this.TransactionUiService.GetProductPerformance(correlationId, estateId, startDate, endDate); if (result.IsSuccess && result.Data != null) { - performanceData = ModelFactory.ConvertFrom(result.Data); + performanceData = result.Data; } else { errorMessage = result.Message ?? "Failed to load product performance data"; } - + return Result.Success(); } catch (Exception ex) diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionDetail.razor b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionDetail.razor index f3ed02b0..b3ea8198 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionDetail.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionDetail.razor @@ -1,21 +1,18 @@ @page "/reporting/transaction-detail" @rendermode InteractiveServer -@using System.Text -@using EstateManagementUI.BlazorServer.Factories @using EstateManagementUI.BlazorServer.Components.Shared -@using EstateManagementUI.BusinessLogic.Models -@using EstateManagementUI.BusinessLogic.Requests +@using EstateManagementUI.BlazorServer.UIServices @using ContractProductModel = EstateManagementUI.BlazorServer.Models.ContractModels.ContractProductModel @using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantDropDownModel -@using MerchantModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantModel @using OperatorDropDownModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorDropDownModel -@using OperatorModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorModel -@using TransactionDetailModel = EstateManagementUI.BlazorServer.Models.TransactionDetailModel +@using TransactionDetailModel = EstateManagementUI.BlazorServer.Models.TransactionModels.TransactionDetailModel @inherits AuthorizedComponentBase -@inject IMediator Mediator @inject NavigationManager Navigation -@inject ILogger Logger @inject IJSRuntime JSRuntime +@inject ITransactionUIService TransactionUiService +@inject IMerchantUIService MerchantUiService +@inject IOperatorUIService OperatorUiService +@inject IContractUIService ContractUiService Transaction Detail Report diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionDetail.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionDetail.razor.cs index c6fa9120..9f4379d4 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionDetail.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionDetail.razor.cs @@ -131,21 +131,24 @@ private async Task LoadData() { var estateId = await this.GetEstateId(); // Load filter options - var merchantsTask = Mediator.Send(new MerchantQueries.GetMerchantsForDropDownQuery(correlationId, estateId)); - var operatorsTask = Mediator.Send(new OperatorQueries.GetOperatorsForDropDownQuery(correlationId, estateId)); - var contractsTask = Mediator.Send(new ContractQueries.GetContractsQuery(correlationId, estateId)); + var merchantsTask = this.MerchantUiService.GetMerchantsForDropDown(correlationId, estateId); + var operatorsTask = this.OperatorUiService.GetOperatorsForDropDown(correlationId, estateId); + var contractsTask = this.ContractUiService.GetContracts(correlationId, estateId); await Task.WhenAll(merchantsTask, operatorsTask, contractsTask); if (merchantsTask.Result.IsSuccess) - merchants = ModelFactory.ConvertFrom(merchantsTask.Result.Data); + merchants = merchantsTask.Result.Data; if (operatorsTask.Result.IsSuccess) - operators = ModelFactory.ConvertFrom(operatorsTask.Result.Data); + operators = operatorsTask.Result.Data; if (contractsTask.Result.IsSuccess) { // Extract all products from all contracts - products = contractsTask.Result.Data?.SelectMany(c => ModelFactory.ConvertFrom(c.Products) ?? new List()).Where(p => !string.IsNullOrEmpty(p.ProductName)).DistinctBy(p => p.ProductName).ToList(); + products = contractsTask.Result.Data?.SelectMany(c => c.Products ?? + new List()) + .Where(p => !string.IsNullOrEmpty(p.ProductName)) + .DistinctBy(p => p.ProductName).ToList(); } // Load detail data @@ -170,13 +173,13 @@ private async Task LoadDetailData() { var operatorIds = this.ParseInt32List(_selectedOperatorIds); var productIds = this.ParseInt32List(_selectedProductIds); - var result = await Mediator.Send(new TransactionQueries.GetTransactionDetailQuery(correlationId, estateId, - _startDate.ToDateTime(TimeOnly.MinValue), _endDate.ToDateTime(TimeOnly.MinValue), merchantIds, operatorIds, productIds)); + var result = await this.TransactionUiService.GetTransactionDetail(correlationId, estateId, + _startDate.ToDateTime(TimeOnly.MinValue), _endDate.ToDateTime(TimeOnly.MinValue), merchantIds, operatorIds, productIds); if (result.IsFailed) return ResultHelpers.CreateFailure(result); - detailData = ModelFactory.ConvertFrom(result.Data); + detailData = result.Data; CalculateKPIs(); return Result.Success(); } @@ -263,31 +266,31 @@ private void SortBy(string columnName) detailData.Transactions = columnName switch { - nameof(TransactionDetailModel.TransactionId) => _sortAscending + nameof(TransactionModels.TransactionDetailModel.TransactionId) => _sortAscending ? detailData.Transactions.OrderBy(t => t.Id).ToList() : detailData.Transactions.OrderByDescending(t => t.Id).ToList(), - nameof(TransactionDetailModel.TransactionDateTime) => _sortAscending + nameof(TransactionModels.TransactionDetailModel.TransactionDateTime) => _sortAscending ? detailData.Transactions.OrderBy(t => t.DateTime).ToList() : detailData.Transactions.OrderByDescending(t => t.DateTime).ToList(), - nameof(TransactionDetailModel.MerchantName) => _sortAscending + nameof(TransactionModels.TransactionDetailModel.MerchantName) => _sortAscending ? detailData.Transactions.OrderBy(t => t.Merchant).ToList() : detailData.Transactions.OrderByDescending(t => t.Merchant).ToList(), - nameof(TransactionDetailModel.OperatorName) => _sortAscending + nameof(TransactionModels.TransactionDetailModel.OperatorName) => _sortAscending ? detailData.Transactions.OrderBy(t => t.Operator).ToList() : detailData.Transactions.OrderByDescending(t => t.Operator).ToList(), - nameof(TransactionDetailModel.ProductName) => _sortAscending + nameof(TransactionModels.TransactionDetailModel.ProductName) => _sortAscending ? detailData.Transactions.OrderBy(t => t.Product).ToList() : detailData.Transactions.OrderByDescending(t => t.Product).ToList(), - nameof(TransactionDetailModel.TransactionType) => _sortAscending + nameof(TransactionModels.TransactionDetailModel.TransactionType) => _sortAscending ? detailData.Transactions.OrderBy(t => t.Type).ToList() : detailData.Transactions.OrderByDescending(t => t.Type).ToList(), - nameof(TransactionDetailModel.TransactionStatus) => _sortAscending + nameof(TransactionModels.TransactionDetailModel.TransactionStatus) => _sortAscending ? detailData.Transactions.OrderBy(t => t.Status).ToList() : detailData.Transactions.OrderByDescending(t => t.Status).ToList(), - nameof(TransactionDetailModel.GrossAmount) => _sortAscending + nameof(TransactionModels.TransactionDetailModel.GrossAmount) => _sortAscending ? detailData.Transactions.OrderBy(t => t.Value).ToList() : detailData.Transactions.OrderByDescending(t => t.Value).ToList(), - nameof(TransactionDetailModel.FeesCommission) => _sortAscending + nameof(TransactionModels.TransactionDetailModel.FeesCommission) => _sortAscending ? detailData.Transactions.OrderBy(t => t.TotalFees).ToList() : detailData.Transactions.OrderByDescending(t => t.TotalFees).ToList(), //nameof(TransactionDetailModel.NetAmount) => _sortAscending @@ -360,7 +363,6 @@ private async Task ExportToCSV() } catch (Exception ex) { - Logger.LogError(ex, "Error exporting to CSV"); errorMessage = "Failed to export data to CSV"; } } diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryMerchant.razor b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryMerchant.razor index 2e3ec0c3..0bf2341d 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryMerchant.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryMerchant.razor @@ -1,15 +1,11 @@ @page "/reporting/transaction-summary-merchant" -@using EstateManagementUI.BlazorServer.Factories -@using EstateManagementUI.BusinessLogic.Models -@using EstateManagementUI.BusinessLogic.Requests -@using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantDropDownModel -@using MerchantTransactionSummaryModel = EstateManagementUI.BlazorServer.Models.MerchantTransactionSummaryModel -@using OperatorDropDownModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorDropDownModel -@using OperatorModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorModel +@using EstateManagementUI.BlazorServer.UIServices @rendermode InteractiveServer -@inject IMediator Mediator @inject NavigationManager Navigation @inherits AuthorizedComponentBase +@inject ITransactionUIService TransactionUiService +@inject IMerchantUIService MerchantUiService +@inject IOperatorUIService OperatorUiService Transaction Summary by Merchant diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryMerchant.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryMerchant.razor.cs index 4cacf714..0431ba3c 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryMerchant.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryMerchant.razor.cs @@ -11,7 +11,7 @@ public partial class TransactionSummaryMerchant private bool isLoading = true; // Filter states - private DateOnly _startDate = DateOnly.FromDateTime(DateTime.Now.AddDays(-30)); + private DateOnly _startDate = DateOnly.FromDateTime(DateTime.Now.AddDays(-7)); private DateOnly _endDate = DateOnly.FromDateTime(DateTime.Now); private String _selectedMerchant = "-1"; private String _selectedOperator = "-1"; @@ -56,16 +56,16 @@ private async Task LoadData() var estateId = await this.GetEstateId(); // Load filter options - var merchantsTask = Mediator.Send(new MerchantQueries.GetMerchantsForDropDownQuery(correlationId, estateId)); - var operatorsTask = Mediator.Send(new OperatorQueries.GetOperatorsForDropDownQuery(correlationId, estateId)); + var merchantsTask = this.MerchantUiService.GetMerchantsForDropDown(correlationId, estateId); + var operatorsTask = this.OperatorUiService.GetOperatorsForDropDown(correlationId, estateId); await Task.WhenAll(merchantsTask, operatorsTask); if (merchantsTask.Result.IsSuccess) - merchants = ModelFactory.ConvertFrom(merchantsTask.Result.Data); + merchants = merchantsTask.Result.Data; if (operatorsTask.Result.IsSuccess) - operators = ModelFactory.ConvertFrom(operatorsTask.Result.Data); + operators = operatorsTask.Result.Data; // Load summary data return await LoadSummaryData(); @@ -97,19 +97,17 @@ private async Task LoadSummaryData() if (this._selectedMerchant != "-1") { merchant = Int32.Parse(this._selectedMerchant); } - if (this._selectedOperator != "-1") - { + + if (this._selectedOperator != "-1") { @operator = Int32.Parse(this._selectedOperator); } - var result = await Mediator.Send(new TransactionQueries.GetMerchantTransactionSummaryQuery(correlationId, estateId, startDate, endDate, merchant, @operator)); + var result = await this.TransactionUiService.GetMerchantTransactionSummary(correlationId, estateId, startDate, endDate, merchant, @operator); - if (result.IsSuccess && result.Data != null) - { - summaryData = ModelFactory.ConvertFrom(result.Data); + if (result.IsSuccess && result.Data != null) { + summaryData = result.Data; } - else - { + else { errorMessage = result.Message ?? "Failed to load summary data"; } diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryOperator.razor b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryOperator.razor index 33281831..a8ca01f6 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryOperator.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryOperator.razor @@ -1,13 +1,9 @@ @page "/reporting/transaction-summary-operator" -@using EstateManagementUI.BlazorServer.Factories -@using EstateManagementUI.BusinessLogic.Models -@using EstateManagementUI.BusinessLogic.Requests -@using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantDropDownModel -@using OperatorDropDownModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorDropDownModel -@using OperatorModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorModel -@using OperatorTransactionSummaryModel = EstateManagementUI.BlazorServer.Models.OperatorTransactionSummaryModel +@using EstateManagementUI.BlazorServer.UIServices @rendermode InteractiveServer -@inject IMediator Mediator +@inject ITransactionUIService TransactionUiService +@inject IMerchantUIService MerchantUiService +@inject IOperatorUIService OperatorUiService @inject NavigationManager Navigation @inherits AuthorizedComponentBase diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryOperator.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryOperator.razor.cs index 284bb318..e6e76984 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryOperator.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryOperator.razor.cs @@ -50,16 +50,16 @@ private async Task LoadData() var estateId = await this.GetEstateId(); // Load filter options - var merchantsTask = Mediator.Send(new MerchantQueries.GetMerchantsForDropDownQuery(correlationId, estateId)); - var operatorsTask = Mediator.Send(new OperatorQueries.GetOperatorsForDropDownQuery(correlationId, estateId)); + var merchantsTask = this.MerchantUiService.GetMerchantsForDropDown(correlationId, estateId); + var operatorsTask = this.OperatorUiService.GetOperatorsForDropDown(correlationId, estateId); await Task.WhenAll(merchantsTask, operatorsTask); if (merchantsTask.Result.IsSuccess) - merchants = ModelFactory.ConvertFrom(merchantsTask.Result.Data); + merchants = merchantsTask.Result.Data; if (operatorsTask.Result.IsSuccess) - operators = ModelFactory.ConvertFrom(operatorsTask.Result.Data); + operators = operatorsTask.Result.Data; // Load summary data return await LoadSummaryData(); @@ -97,17 +97,11 @@ private async Task LoadSummaryData() @operator = Int32.Parse(this._selectedOperator); } - var result = await Mediator.Send(new TransactionQueries.GetOperatorTransactionSummaryQuery( - correlationId, - estateId, - startDate, - endDate, - merchant, - @operator)); - + var result = await this.TransactionUiService.GetOperatorTransactionSummary(correlationId, estateId, startDate, endDate, merchant, @operator); + if (result.IsSuccess && result.Data != null) { - summaryData = ModelFactory.ConvertFrom(result.Data); + summaryData = result.Data; } else { diff --git a/EstateManagementUI.BlazorServer/Components/Permissions/AuthorizedComponentBase.cs b/EstateManagementUI.BlazorServer/Components/Permissions/AuthorizedComponentBase.cs index 162471bd..75dee498 100644 --- a/EstateManagementUI.BlazorServer/Components/Permissions/AuthorizedComponentBase.cs +++ b/EstateManagementUI.BlazorServer/Components/Permissions/AuthorizedComponentBase.cs @@ -16,8 +16,8 @@ public abstract class AuthorizedComponentBase : CustomComponentBase protected override async Task OnInitializedAsync() { - this.AuthState = await AuthenticationStateTask; - User = this.AuthState.User; + //this.AuthState = await AuthenticationStateTask; + //User = this.AuthState.User; } [Inject] protected IPermissionService PermissionService { get; set; } = default!; @@ -52,6 +52,9 @@ protected async Task OnAfterRender(PermissionSection section, PermissionFunction function, Func>? loadFunc) { + this.AuthState = await AuthenticationStateTask; + User = this.AuthState.User; + Result authResult = await RequirePermission(section, function); if (authResult.IsFailed) return Result.Failure(); diff --git a/EstateManagementUI.BlazorServer/Factories/ModelFactory.cs b/EstateManagementUI.BlazorServer/Factories/ModelFactory.cs index 20790c79..ea20aca7 100644 --- a/EstateManagementUI.BlazorServer/Factories/ModelFactory.cs +++ b/EstateManagementUI.BlazorServer/Factories/ModelFactory.cs @@ -13,21 +13,20 @@ using MerchantContractProductModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantContractProductModel; using MerchantDeviceModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantDeviceModel; using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantDropDownModel; -using MerchantKpiModel = EstateManagementUI.BlazorServer.Models.MerchantKpiModel; +using MerchantKpiModel = EstateManagementUI.BlazorServer.Models.TransactionModels.MerchantKpiModel; using MerchantListModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantListModel; using MerchantModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantModel; using MerchantOperatorModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantOperatorModel; using MerchantSettlementHistoryModel = EstateManagementUI.BlazorServer.Models.MerchantSettlementHistoryModel; -using MerchantTransactionSummaryModel = EstateManagementUI.BlazorServer.Models.MerchantTransactionSummaryModel; using OperatorModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorModel; -using OperatorTransactionSummaryModel = EstateManagementUI.BlazorServer.Models.OperatorTransactionSummaryModel; +//using OperatorTransactionSummaryModel = EstateManagementUI.BlazorServer.Models.OperatorTransactionSummaryModel; using ProductPerformanceResponse = EstateManagementUI.BlazorServer.Models.TransactionModels.ProductPerformanceResponse; -using RecentContractModel = EstateManagementUI.BlazorServer.Models.RecentContractModel; -using RecentMerchantsModel = EstateManagementUI.BlazorServer.Models.RecentMerchantsModel; +using RecentContractModel = EstateManagementUI.BlazorServer.Models.ContractModels.RecentContractModel; +using RecentMerchantsModel = EstateManagementUI.BlazorServer.Models.MerchantModels.RecentMerchantsModel; using SettlementSummaryModel = EstateManagementUI.BlazorServer.Models.SettlementSummaryModel; -using TodaysSalesModel = EstateManagementUI.BlazorServer.Models.TodaysSalesModel; -using TodaysSettlementModel = EstateManagementUI.BlazorServer.Models.TodaysSettlementModel; -using TransactionDetailModel = EstateManagementUI.BlazorServer.Models.TransactionDetailModel; +using TodaysSalesModel = EstateManagementUI.BlazorServer.Models.TransactionModels.TodaysSalesModel; +using TodaysSettlementModel = EstateManagementUI.BlazorServer.Models.TransactionModels.TodaysSettlementModel; +using TransactionDetailModel = EstateManagementUI.BlazorServer.Models.TransactionModels.TransactionDetailModel; using TransactionModels = EstateManagementUI.BlazorServer.Models.TransactionModels; namespace EstateManagementUI.BlazorServer.Factories; @@ -575,10 +574,10 @@ public static ProductPerformanceResponse ConvertFrom(BusinessLogic.Models.Transa return model; } - public static List? ConvertFrom(List resultData) { - List todaysSalesByHourModels = new(); + public static List? ConvertFrom(List resultData) { + List todaysSalesByHourModels = new(); foreach (BusinessLogic.Models.TransactionModels.TodaysSalesByHourModel todaysSalesCountByHourModel in resultData) { - todaysSalesByHourModels.Add(new TodaysSalesByHourModel { + todaysSalesByHourModels.Add(new TransactionModels.TodaysSalesByHourModel { Hour = todaysSalesCountByHourModel.Hour, TodaysSalesCount = todaysSalesCountByHourModel.TodaysSalesCount, ComparisonSalesCount = todaysSalesCountByHourModel.ComparisonSalesCount, diff --git a/EstateManagementUI.BlazorServer/Models/ContractModels.cs b/EstateManagementUI.BlazorServer/Models/ContractModels.cs index a19b384a..d451c6e6 100644 --- a/EstateManagementUI.BlazorServer/Models/ContractModels.cs +++ b/EstateManagementUI.BlazorServer/Models/ContractModels.cs @@ -1,9 +1,17 @@ using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; using TransactionProcessor.DataTransferObjects.Responses.Contract; namespace EstateManagementUI.BlazorServer.Models; +[ExcludeFromCodeCoverage] public class ContractModels { + public class RecentContractModel + { + public Guid ContractId { get; set; } + public string? Description { get; set; } + public string? OperatorName { get; set; } + } public class ContractDropDownModel { public Guid ContractId { get; set; } diff --git a/EstateManagementUI.BlazorServer/Models/MerchantModels.cs b/EstateManagementUI.BlazorServer/Models/MerchantModels.cs index 52b95f66..ac299b98 100644 --- a/EstateManagementUI.BlazorServer/Models/MerchantModels.cs +++ b/EstateManagementUI.BlazorServer/Models/MerchantModels.cs @@ -1,9 +1,18 @@ using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; namespace EstateManagementUI.BlazorServer.Models; +[ExcludeFromCodeCoverage] public class MerchantModels { + public class RecentMerchantsModel + { + public DateTime CreatedDateTime { get; set; } + public Guid MerchantId { get; set; } + public String Name { get; set; } + public String Reference { get; set; } + } public class MerchantModel { public Guid MerchantId { get; set; } diff --git a/EstateManagementUI.BlazorServer/Models/Models.cs b/EstateManagementUI.BlazorServer/Models/Models.cs index 0f9f91f5..cc237d34 100644 --- a/EstateManagementUI.BlazorServer/Models/Models.cs +++ b/EstateManagementUI.BlazorServer/Models/Models.cs @@ -1,17 +1,20 @@ +using System.Diagnostics.CodeAnalysis; + namespace EstateManagementUI.BlazorServer.Models; +[ExcludeFromCodeCoverage] public record EstateModel(Guid EstateId, string? EstateName, string? Reference) { public Int32 MerchantCount { get; init; } public Int32 OperatorCount { get; init; } public Int32 ContractCount { get; init; } public Int32 UserCount { get; init; } - public List RecentMerchants { get; init; } - public List RecentContracts { get; init; } + public List RecentMerchants { get; init; } + public List RecentContracts { get; init; } public List AssignedOperators { get; init; } public List AllOperators { get; init; } } - +[ExcludeFromCodeCoverage] // File Processing Models public class FileImportLogModel { @@ -21,6 +24,7 @@ public class FileImportLogModel public DateTime FileUploadedDateTime { get; set; } } +[ExcludeFromCodeCoverage] public class FileDetailsModel { public Guid FileId { get; set; } @@ -37,89 +41,15 @@ public class FileDetailsModel } // Dashboard Models +[ExcludeFromCodeCoverage] public class ComparisonDateModel { public DateTime Date { get; set; } public string? Description { get; set; } } -public class TodaysSalesModel -{ - public int ComparisonSalesCount { get; set; } - public decimal ComparisonSalesValue { get; set; } - public decimal ComparisonAverageValue { get; set; } - public int TodaysSalesCount { get; set; } - public decimal TodaysSalesValue { get; set; } - public decimal TodaysAverageValue { get; set; } -} - -public class TodaysSettlementModel -{ - public int ComparisonSettlementCount { get; set; } - public decimal ComparisonSettlementValue { get; set; } - public int TodaysSettlementCount { get; set; } - public decimal TodaysSettlementValue { get; set; } - public int ComparisonPendingSettlementCount { get; set; } - public decimal ComparisonPendingSettlementValue { get; set; } - public int TodaysPendingSettlementCount { get; set; } - public decimal TodaysPendingSettlementValue { get; set; } -} - -public class TodaysSalesByHourModel -{ - public int Hour { get; set; } - public int TodaysSalesCount { get; set; } - public int ComparisonSalesCount { get; set; } - public decimal TodaysSalesValue { get; set; } - public decimal ComparisonSalesValue { get; set; } -} - -public class MerchantKpiModel -{ - public int MerchantsWithNoSaleInLast7Days { get; set; } - public int MerchantsWithNoSaleToday { get; set; } - public int MerchantsWithSaleInLastHour { get; set; } -} - -public class TopBottomProductDataModel -{ - public string? ProductName { get; set; } - public decimal SalesValue { get; set; } -} - -// Transaction Summary Models -public class MerchantTransactionSummaryModel -{ - public Guid MerchantId { get; set; } - public string? MerchantName { get; set; } - public int TotalTransactionCount { get; set; } - public decimal TotalTransactionValue { get; set; } - public decimal AverageTransactionValue { get; set; } - public int SuccessfulTransactionCount { get; set; } - public int FailedTransactionCount { get; set; } -} - -public class OperatorTransactionSummaryModel -{ - public Guid OperatorId { get; set; } - public string? OperatorName { get; set; } - public int TotalTransactionCount { get; set; } - public decimal TotalTransactionValue { get; set; } - public decimal AverageTransactionValue { get; set; } - public int SuccessfulTransactionCount { get; set; } - public int FailedTransactionCount { get; set; } - public decimal TotalFeesEarned { get; set; } -} - -public class ProductPerformanceModel -{ - public string? ProductName { get; set; } - public int TransactionCount { get; set; } - public decimal TransactionValue { get; set; } - public decimal PercentageContribution { get; set; } -} - // Settlement History Models +[ExcludeFromCodeCoverage] public class MerchantSettlementHistoryModel { public DateTime SettlementDate { get; set; } @@ -129,6 +59,7 @@ public class MerchantSettlementHistoryModel } // Settlement Summary Models +[ExcludeFromCodeCoverage] public class SettlementSummaryModel { public DateTime SettlementPeriodStart { get; set; } @@ -141,39 +72,5 @@ public class SettlementSummaryModel public string? SettlementStatus { get; set; } } -// Transaction Detail Models -public class TransactionDetailModel -{ - public Guid TransactionId { get; set; } - public DateTime TransactionDateTime { get; set; } - public string? MerchantName { get; set; } - public Guid MerchantId { get; set; } - public string? OperatorName { get; set; } - public Guid OperatorId { get; set; } - public string? ProductName { get; set; } - public Guid ProductId { get; set; } - public string? TransactionType { get; set; } // sale, refund, reversal - public string? TransactionStatus { get; set; } // successful, failed, reversed - public decimal GrossAmount { get; set; } - public decimal FeesCommission { get; set; } - public decimal NetAmount { get; set; } - public string? SettlementReference { get; set; } - public string? ResponseCode { get; set; } - public DateTime? SettlementDateTime { get; set; } -} - -public class RecentMerchantsModel -{ - public DateTime CreatedDateTime { get; set; } - public Guid MerchantId { get; set; } - public String Name { get; set; } - public String Reference { get; set; } -} -public class RecentContractModel -{ - public Guid ContractId { get; set; } - public string? Description { get; set; } - public string? OperatorName { get; set; } -} diff --git a/EstateManagementUI.BlazorServer/Models/OperatorModels.cs b/EstateManagementUI.BlazorServer/Models/OperatorModels.cs index 7123932d..59eba8b9 100644 --- a/EstateManagementUI.BlazorServer/Models/OperatorModels.cs +++ b/EstateManagementUI.BlazorServer/Models/OperatorModels.cs @@ -1,7 +1,9 @@ using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; namespace EstateManagementUI.BlazorServer.Models; +[ExcludeFromCodeCoverage] public record OperatorModels { public record EditOperatorModel { [Required(ErrorMessage = "Operator name is required")] diff --git a/EstateManagementUI.BlazorServer/Models/TransactionModels.cs b/EstateManagementUI.BlazorServer/Models/TransactionModels.cs index 43baf400..c7e96cfe 100644 --- a/EstateManagementUI.BlazorServer/Models/TransactionModels.cs +++ b/EstateManagementUI.BlazorServer/Models/TransactionModels.cs @@ -1,7 +1,9 @@ using EstateManagementUI.BusinessLogic.BackendAPI.DataTransferObjects; +using System.Diagnostics.CodeAnalysis; namespace EstateManagementUI.BlazorServer.Models; +[ExcludeFromCodeCoverage] public record TransactionModels { public class TransactionDetailReportResponse @@ -117,4 +119,62 @@ public class ProductPerformanceSummary public Decimal TotalValue { get; set; } public Decimal AveragePerProduct { get; set; } } + + public class TodaysSalesModel + { + public int ComparisonSalesCount { get; set; } + public decimal ComparisonSalesValue { get; set; } + public decimal ComparisonAverageValue { get; set; } + public int TodaysSalesCount { get; set; } + public decimal TodaysSalesValue { get; set; } + public decimal TodaysAverageValue { get; set; } + } + + public class TodaysSettlementModel + { + public int ComparisonSettlementCount { get; set; } + public decimal ComparisonSettlementValue { get; set; } + public int TodaysSettlementCount { get; set; } + public decimal TodaysSettlementValue { get; set; } + public int ComparisonPendingSettlementCount { get; set; } + public decimal ComparisonPendingSettlementValue { get; set; } + public int TodaysPendingSettlementCount { get; set; } + public decimal TodaysPendingSettlementValue { get; set; } + } + + public class TodaysSalesByHourModel + { + public int Hour { get; set; } + public int TodaysSalesCount { get; set; } + public int ComparisonSalesCount { get; set; } + public decimal TodaysSalesValue { get; set; } + public decimal ComparisonSalesValue { get; set; } + } + + public class MerchantKpiModel + { + public int MerchantsWithNoSaleInLast7Days { get; set; } + public int MerchantsWithNoSaleToday { get; set; } + public int MerchantsWithSaleInLastHour { get; set; } + } + + public class TransactionDetailModel + { + public Guid TransactionId { get; set; } + public DateTime TransactionDateTime { get; set; } + public string? MerchantName { get; set; } + public Guid MerchantId { get; set; } + public string? OperatorName { get; set; } + public Guid OperatorId { get; set; } + public string? ProductName { get; set; } + public Guid ProductId { get; set; } + public string? TransactionType { get; set; } // sale, refund, reversal + public string? TransactionStatus { get; set; } // successful, failed, reversed + public decimal GrossAmount { get; set; } + public decimal FeesCommission { get; set; } + public decimal NetAmount { get; set; } + public string? SettlementReference { get; set; } + public string? ResponseCode { get; set; } + public DateTime? SettlementDateTime { get; set; } + } } \ No newline at end of file diff --git a/EstateManagementUI.BlazorServer/UIServices/ICalendarUIService.cs b/EstateManagementUI.BlazorServer/UIServices/ICalendarUIService.cs new file mode 100644 index 00000000..233cc3b4 --- /dev/null +++ b/EstateManagementUI.BlazorServer/UIServices/ICalendarUIService.cs @@ -0,0 +1,29 @@ +using EstateManagementUI.BlazorServer.Factories; +using EstateManagementUI.BlazorServer.Models; +using EstateManagementUI.BusinessLogic.Requests; +using MediatR; +using Shared.Results; +using SimpleResults; + +namespace EstateManagementUI.BlazorServer.UIServices; + +public interface ICalendarUIService { + Task>> GetComparisonDates(CorrelationId correlationId, Guid estateId); +} + +public class CalendarUIService : ICalendarUIService { + private readonly IMediator Mediator; + + public CalendarUIService(IMediator mediator) { + this.Mediator = mediator; + } + + public async Task>> GetComparisonDates(CorrelationId correlationId, Guid estateId) { + var query = new Queries.GetComparisonDatesQuery(correlationId, estateId); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var comparisonDates = ModelFactory.ConvertFrom(result.Data); + return Result.Success(comparisonDates); + } +} \ No newline at end of file diff --git a/EstateManagementUI.BlazorServer/UIServices/MerchantUIService.cs b/EstateManagementUI.BlazorServer/UIServices/MerchantUIService.cs index 349ebe7b..739c5546 100644 --- a/EstateManagementUI.BlazorServer/UIServices/MerchantUIService.cs +++ b/EstateManagementUI.BlazorServer/UIServices/MerchantUIService.cs @@ -1,5 +1,6 @@ using EstateManagementUI.BlazorServer.Factories; using EstateManagementUI.BlazorServer.Models; +using EstateManagementUI.BusinessLogic.BackendAPI.DataTransferObjects; using EstateManagementUI.BusinessLogic.Requests; using MediatR; using Shared.Results; @@ -84,10 +85,11 @@ Task MakeMerchantDeposit(CorrelationId correlationId, Guid merchantId, MerchantModels.DepositModel depositModel); - //Contract UI Service - // GetContractsForDropDownQuery + Task>> GetRecentMerchants(CorrelationId correlationId, Guid estateId); + Task> GetMerchantKpis(CorrelationId correlationId, Guid estateId); + Task>> GetMerchantsForDropDown(CorrelationId correlationId, Guid estateId); } public class MerchantUIService : IMerchantUIService { @@ -265,4 +267,34 @@ public async Task MakeMerchantDeposit(CorrelationId correlationId, return ResultHelpers.CreateFailure(result); return Result.Success(); } + + public async Task>> GetRecentMerchants(CorrelationId correlationId, + Guid estateId) { + MerchantQueries.GetRecentMerchantsQuery command = new(correlationId, estateId); + var result = await this.Mediator.Send(command); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var recentMerchantsModels = ModelFactory.ConvertFrom(result.Data); + return Result.Success(recentMerchantsModels); + } + + public async Task> GetMerchantKpis(CorrelationId correlationId, + Guid estateId) { + MerchantQueries.GetMerchantKpiQuery command = new(correlationId, estateId); + var result = await this.Mediator.Send(command); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var merchantKpiModel = ModelFactory.ConvertFrom(result.Data); + return Result.Success(merchantKpiModel); + } + + public async Task>> GetMerchantsForDropDown(CorrelationId correlationId, + Guid estateId) { + MerchantQueries.GetMerchantsForDropDownQuery query = new(correlationId, estateId); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var merchantList = ModelFactory.ConvertFrom(result.Data); + return Result.Success(merchantList); + } } \ No newline at end of file diff --git a/EstateManagementUI.BlazorServer/UIServices/OperatorUIService.cs b/EstateManagementUI.BlazorServer/UIServices/OperatorUIService.cs index 7833c0d7..3ba4b9ae 100644 --- a/EstateManagementUI.BlazorServer/UIServices/OperatorUIService.cs +++ b/EstateManagementUI.BlazorServer/UIServices/OperatorUIService.cs @@ -13,19 +13,19 @@ public interface IOperatorUIService Task>> GetOperators(CorrelationId correlationId, Guid estateId); Task>> GetOperatorsForDropDown(CorrelationId correlationId, Guid estateId); Task> GetOperator(CorrelationId correlationId, Guid estateId, Guid operatorId); - Task UpdateOperator(CorrelationId correlationId, Guid estateId, Guid operatorId, OperatorModels.EditOperatorModel editOperatorModel); Task CreateOperator(CorrelationId correlationId, Guid estateId, OperatorModels.CreateOperatorModel createOperatorModel); } -public class OperatorUIService : IOperatorUIService -{ + +public class OperatorUIService : IOperatorUIService { private readonly IMediator Mediator; - public OperatorUIService(IMediator mediator) - { + + public OperatorUIService(IMediator mediator) { this.Mediator = mediator; } - public async Task>> GetOperators(CorrelationId correlationId, Guid estateId) - { + + public async Task>> GetOperators(CorrelationId correlationId, + Guid estateId) { var result = await this.Mediator.Send(new OperatorQueries.GetOperatorsQuery(correlationId, estateId)); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -66,14 +66,11 @@ public async Task CreateOperator(CorrelationId correlationId, Guid estateId, OperatorModels.CreateOperatorModel createOperatorModel) { // Create operator - var command = new OperatorCommands.CreateOperatorCommand(correlationId, - estateId, - createOperatorModel.OperatorName!, - createOperatorModel.RequireCustomMerchantNumber, - createOperatorModel.RequireCustomTerminalNumber - ); + var command = new OperatorCommands.CreateOperatorCommand(correlationId, estateId, createOperatorModel.OperatorName!, createOperatorModel.RequireCustomMerchantNumber, createOperatorModel.RequireCustomTerminalNumber); var result = await Mediator.Send(command); return result; } -} \ No newline at end of file +} + + diff --git a/EstateManagementUI.BlazorServer/UIServices/TransactionUIService.cs b/EstateManagementUI.BlazorServer/UIServices/TransactionUIService.cs new file mode 100644 index 00000000..2637fddf --- /dev/null +++ b/EstateManagementUI.BlazorServer/UIServices/TransactionUIService.cs @@ -0,0 +1,138 @@ +using EstateManagementUI.BlazorServer.Factories; +using EstateManagementUI.BlazorServer.Models; +using EstateManagementUI.BusinessLogic.BackendAPI.DataTransferObjects; +using EstateManagementUI.BusinessLogic.Requests; +using MediatR; +using Shared.Results; +using SimpleResults; + +namespace EstateManagementUI.BlazorServer.UIServices; + +public interface ITransactionUIService { + Task> GetTodaysSales(CorrelationId correlationId, Guid estateId, DateTime comparisonDate); + Task> GetTodaysFailedSales(CorrelationId correlationId, Guid estateId, String responseCode, DateTime comparisonDate); + Task>> GetTodaysSalesByHour(CorrelationId correlationId, Guid estateId, DateTime comparisonDate); + Task> GetTodaysSettlement(CorrelationId correlationId, Guid estateId, DateTime comparisonDate); + Task> GetProductPerformance(CorrelationId correlationId, Guid estateId, DateTime startDate, DateTime endDate); + Task> GetTransactionDetail(CorrelationId correlationId, Guid estateId, DateTime startDate, DateTime endDate,List? merchantIds, + List? operatorIds, List? productIds); + + Task> GetMerchantTransactionSummary(CorrelationId correlationId, + Guid estateId, + DateTime startDate, + DateTime endDate, + Int32? merchant, + Int32? @operator); + + Task> GetOperatorTransactionSummary(CorrelationId correlationId, + Guid estateId, + DateTime startDate, + DateTime endDate, + Int32? merchant, + Int32? @operator); +} + +public class TransactionUIService : ITransactionUIService +{ + private readonly IMediator Mediator; + + public TransactionUIService(IMediator mediator) { + this.Mediator = mediator; + } + + public async Task> GetTodaysSales(CorrelationId correlationId, Guid estateId, DateTime comparisonDate) { + var query = new TransactionQueries.GetTodaysSalesQuery(correlationId, estateId, comparisonDate); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var todaysSales = ModelFactory.ConvertFrom(result.Data); + return Result.Success(todaysSales); + } + + public async Task> GetTodaysFailedSales(CorrelationId correlationId, Guid estateId, String responseCode, DateTime comparisonDate) { + var query = new TransactionQueries.GetTodaysFailedSalesQuery(correlationId, estateId, responseCode, comparisonDate); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var todaysFailedSales = ModelFactory.ConvertFrom(result.Data); + return Result.Success(todaysFailedSales); + } + + public async Task>> GetTodaysSalesByHour(CorrelationId correlationId, + Guid estateId, + DateTime comparisonDate) { + var query = new TransactionQueries.GetTodaysSalesByHourQuery(correlationId, estateId, comparisonDate); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var todaysSalesByHour = ModelFactory.ConvertFrom(result.Data); + return Result.Success(todaysSalesByHour); + } + + public async Task> GetTodaysSettlement(CorrelationId correlationId, + Guid estateId, + DateTime comparisonDate) { + var query = new SettlementQueries.GetTodaysSettlementQuery(correlationId, estateId, comparisonDate); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var todaysSettlement = ModelFactory.ConvertFrom(result.Data); + return Result.Success(todaysSettlement); + + } + + public async Task> GetProductPerformance(CorrelationId correlationId, + Guid estateId, + DateTime startDate, + DateTime endDate) { + var query = new TransactionQueries.GetProductPerformanceQuery(correlationId, estateId, startDate, endDate); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var productPerformance = ModelFactory.ConvertFrom(result.Data); + return Result.Success(productPerformance); + } + + public async Task> GetTransactionDetail(CorrelationId correlationId, + Guid estateId, + DateTime startDate, + DateTime endDate, + List? merchantIds, + List? operatorIds, + List? productIds) { + var query = new TransactionQueries.GetTransactionDetailQuery(correlationId, estateId, startDate, endDate, merchantIds, operatorIds, productIds); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var transactionDetail = ModelFactory.ConvertFrom(result.Data); + return Result.Success(transactionDetail); + } + + public async Task> GetMerchantTransactionSummary(CorrelationId correlationId, + Guid estateId, + DateTime startDate, + DateTime endDate, + Int32? merchant, + Int32? @operator) { + var query = new TransactionQueries.GetMerchantTransactionSummaryQuery(correlationId, estateId, startDate, endDate, merchant, @operator); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var merchantTransactionSummary = ModelFactory.ConvertFrom(result.Data); + return Result.Success(merchantTransactionSummary); + } + + public async Task> GetOperatorTransactionSummary(CorrelationId correlationId, + Guid estateId, + DateTime startDate, + DateTime endDate, + Int32? merchant, + Int32? @operator) { + var query = new TransactionQueries.GetOperatorTransactionSummaryQuery(correlationId, estateId, startDate, endDate, merchant, @operator); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var operatorTransactionSummary = ModelFactory.ConvertFrom(result.Data); + return Result.Success(operatorTransactionSummary); + } +} \ No newline at end of file