From 959f353f94e70d1e4e5c10b372aa91305e095f20 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 16:01:11 +0000 Subject: [PATCH 1/6] Initial plan From 55684f3e9b86a1b12462334bd1bb3790e9ce29c9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 16:08:58 +0000 Subject: [PATCH 2/6] Make Deposit Screen: shorter text boxes and verify 2 cancel buttons Co-authored-by: StuartFerguson <16325469+StuartFerguson@users.noreply.github.com> --- .../Merchants/MerchantsDepositPageTests.cs | 17 +++++++++++++++++ .../Components/Pages/Merchants/Deposit.razor | 6 +++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsDepositPageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsDepositPageTests.cs index 5df319bc..fa4b0f39 100644 --- a/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsDepositPageTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsDepositPageTests.cs @@ -170,6 +170,23 @@ public void Deposit_MakeDepositButton_ShowsErrorOnFailure() timeout: TimeSpan.FromSeconds(5)); } + [Fact] + public void Deposit_HasTwoCancelButtons() + { + // Arrange + SetupSuccessfulMerchantLoad(); + + // Act + IRenderedComponent cut = RenderComponent(parameters => + parameters.Add(p => p.MerchantId, _testMerchantId)); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + // Assert - there should be exactly 2 Cancel buttons + IRefreshableElementCollection allButtons = cut.FindAll("button"); + List cancelButtonList = allButtons.Where(b => b.TextContent.Trim() == "Cancel").ToList(); + cancelButtonList.Count.ShouldBe(2); + } + [Fact] public void Deposit_CancelButton_NavigatesToMerchantsIndex() { diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Deposit.razor b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Deposit.razor index 24fc0f0a..b13f5026 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Deposit.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Deposit.razor @@ -51,7 +51,7 @@ - +

Please enter whole pounds only (no pence)

@@ -60,7 +60,7 @@ - +

Can be in the past for deposit catch-ups, but cannot be in the future

@@ -69,7 +69,7 @@ - + From f1c299094c509dc0232203665589ab3cfaeeef17 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 06:03:11 +0000 Subject: [PATCH 3/6] Convert deposit screen to modal dialog on Merchants Index page Co-authored-by: StuartFerguson <16325469+StuartFerguson@users.noreply.github.com> --- .../Merchants/MerchantsIndexPageTests.cs | 193 +++++++++++++++++- .../Components/Pages/Merchants/Index.razor | 86 ++++++++ .../Components/Pages/Merchants/Index.razor.cs | 58 +++++- 3 files changed, 333 insertions(+), 4 deletions(-) diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsIndexPageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsIndexPageTests.cs index 895ac88b..c900fb9c 100644 --- a/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsIndexPageTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsIndexPageTests.cs @@ -413,7 +413,7 @@ public void MerchantsIndex_EditButton_NavigatesToEditMerchantPage() } [Fact] - public void MerchantsIndex_MakeDepositButton_NavigatesToDepositPage() + public void MerchantsIndex_MakeDepositButton_OpensDepositModal() { // Arrange var merchantId = Guid.NewGuid(); @@ -434,6 +434,13 @@ public void MerchantsIndex_MakeDepositButton_NavigatesToDepositPage() It.IsAny())) .ReturnsAsync(Result.Success(merchants)); + this.MerchantUIService.Setup(m => m.GetMerchant(It.IsAny(), It.IsAny(), merchantId)) + .ReturnsAsync(Result.Success(new MerchantModels.MerchantModel + { + MerchantId = merchantId, + MerchantName = "Test Merchant" + })); + var cut = RenderComponent(); cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); @@ -441,8 +448,188 @@ public void MerchantsIndex_MakeDepositButton_NavigatesToDepositPage() var makeDepositButton = cut.Find("#makeDepositLink"); makeDepositButton.Click(); - // Assert - _fakeNavigationManager.Uri.ShouldContain($"/merchants/{merchantId}/deposit"); + // Assert - modal is shown with the deposit form + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Make Merchant Deposit"), TimeSpan.FromSeconds(5)); + cut.Markup.ShouldContain("depositAmount"); + cut.Markup.ShouldContain("depositDate"); + cut.Markup.ShouldContain("depositReference"); + } + + [Fact] + public void MerchantsIndex_DepositModal_HasTwoCancelButtons() + { + // Arrange + var merchantId = Guid.NewGuid(); + var merchants = new List + { + new() + { + MerchantId = merchantId, + MerchantName = "Test Merchant", + MerchantReference = "REF001" + } + }; + + this.MerchantUIService.Setup(m => m.GetMerchants(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Success(merchants)); + + this.MerchantUIService.Setup(m => m.GetMerchant(It.IsAny(), It.IsAny(), merchantId)) + .ReturnsAsync(Result.Success(new MerchantModels.MerchantModel + { + MerchantId = merchantId, + MerchantName = "Test Merchant" + })); + + var cut = RenderComponent(); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + // Open modal + cut.Find("#makeDepositLink").Click(); + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Make Merchant Deposit"), TimeSpan.FromSeconds(5)); + + // Assert - exactly 2 Cancel buttons in the modal + var allButtons = cut.FindAll("button"); + var cancelButtons = allButtons.Where(b => b.TextContent.Trim() == "Cancel").ToList(); + cancelButtons.Count.ShouldBe(2); + } + + [Fact] + public void MerchantsIndex_DepositModal_CancelButton_ClosesModal() + { + // Arrange + var merchantId = Guid.NewGuid(); + var merchants = new List + { + new() + { + MerchantId = merchantId, + MerchantName = "Test Merchant", + MerchantReference = "REF001" + } + }; + + this.MerchantUIService.Setup(m => m.GetMerchants(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Success(merchants)); + + this.MerchantUIService.Setup(m => m.GetMerchant(It.IsAny(), It.IsAny(), merchantId)) + .ReturnsAsync(Result.Success(new MerchantModels.MerchantModel + { + MerchantId = merchantId, + MerchantName = "Test Merchant" + })); + + var cut = RenderComponent(); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + cut.Find("#makeDepositLink").Click(); + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Make Merchant Deposit"), TimeSpan.FromSeconds(5)); + + // Act - click Cancel + var cancelButton = cut.FindAll("button").FirstOrDefault(b => b.TextContent.Trim() == "Cancel"); + cancelButton?.Click(); + + // Assert - modal is closed + cut.WaitForAssertion(() => cut.Markup.ShouldNotContain("depositAmount"), TimeSpan.FromSeconds(5)); + _fakeNavigationManager.Uri.ShouldNotContain("/deposit"); + } + + [Fact] + public void MerchantsIndex_DepositModal_Submit_ShowsSuccess() + { + // Arrange + var merchantId = Guid.NewGuid(); + var merchants = new List + { + new() + { + MerchantId = merchantId, + MerchantName = "Test Merchant", + MerchantReference = "REF001" + } + }; + + this.MerchantUIService.Setup(m => m.GetMerchants(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Success(merchants)); + + this.MerchantUIService.Setup(m => m.GetMerchant(It.IsAny(), It.IsAny(), merchantId)) + .ReturnsAsync(Result.Success(new MerchantModels.MerchantModel + { + MerchantId = merchantId, + MerchantName = "Test Merchant" + })); + + this.MerchantUIService.Setup(m => m.MakeMerchantDeposit( + It.IsAny(), + It.IsAny(), + merchantId, + It.IsAny())) + .ReturnsAsync(Result.Success); + + var cut = RenderComponent(); + cut.Instance.SetDelayOverride(500); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + cut.Find("#makeDepositLink").Click(); + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Make Merchant Deposit"), TimeSpan.FromSeconds(5)); + + // Act - fill and submit form + cut.Find("#depositAmount").Change(100); + cut.Find("#depositDate").Change(DateTime.Today.ToString("yyyy-MM-dd")); + cut.Find("#depositReference").Change("TEST-REF-001"); + cut.Find("#makeDepositButton").Click(); + + // Assert - success message is shown before modal closes + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Deposit recorded successfully"), TimeSpan.FromSeconds(5)); + cut.WaitForAssertion(() => cut.Markup.ShouldNotContain("depositAmount"), TimeSpan.FromSeconds(5)); + } + + [Fact] + public void MerchantsIndex_DepositModal_OpensWithoutMerchantName_WhenGetMerchantFails() + { + // Arrange + var merchantId = Guid.NewGuid(); + var merchants = new List + { + new() + { + MerchantId = merchantId, + MerchantName = "Test Merchant", + MerchantReference = "REF001" + } + }; + + this.MerchantUIService.Setup(m => m.GetMerchants(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Success(merchants)); + + this.MerchantUIService.Setup(m => m.GetMerchant(It.IsAny(), It.IsAny(), merchantId)) + .ReturnsAsync(Result.Failure()); + + var cut = RenderComponent(); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + // Act - click Make Deposit when GetMerchant fails + cut.Find("#makeDepositLink").Click(); + + // Assert - modal opens but without merchant name + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Make Merchant Deposit"), TimeSpan.FromSeconds(5)); + cut.Markup.ShouldContain("depositAmount"); + cut.Markup.ShouldNotContain("For merchant:"); } [Fact] diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor index f2a8a874..58ba3e4b 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor @@ -233,3 +233,89 @@ } + +@if (showDepositModal) +{ +
+
+
+
+

Make Merchant Deposit

+ @if (!string.IsNullOrEmpty(depositModalMerchantName)) + { +

For merchant: @depositModalMerchantName

+ } +
+ +
+ + @if (!string.IsNullOrWhiteSpace(depositErrorMessage)) + { +
+

Error

+

@depositErrorMessage

+
+ } + + @if (!string.IsNullOrWhiteSpace(depositSuccessMessage)) + { +
+

Success

+

@depositSuccessMessage

+
+ } + + + + +
+
+ + + +

Please enter whole pounds only (no pence)

+
+ +
+ + + +

Can be in the past for deposit catch-ups, but cannot be in the future

+
+ +
+ + + +
+
+ +
+ + +
+
+
+
+} + diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor.cs index 5f6c849d..cc2b846d 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor.cs @@ -76,6 +76,15 @@ private int pageSize private int _totalPages = 1; private int totalPages => _totalPages; + // Deposit modal fields + private bool showDepositModal = false; + private Guid depositMerchantId; + private string? depositModalMerchantName; + private MerchantModels.DepositModel depositModel = new(); + private bool isDepositSaving = false; + private string? depositErrorMessage; + private string? depositSuccessMessage; + protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) @@ -163,7 +172,54 @@ private void LastPage() private void EditMerchant(Guid merchantId) => NavigationManager.NavigateToEditMerchant(merchantId); - private void MakeDeposit(Guid merchantId) => this.NavigationManager.NavigateToMakeMerchantDeposit(merchantId); + private async Task MakeDeposit(Guid merchantId) + { + depositMerchantId = merchantId; + depositModel = new MerchantModels.DepositModel(); + depositErrorMessage = null; + depositSuccessMessage = null; + + CorrelationId correlationId = new(Guid.NewGuid()); + Guid estateId = await this.GetEstateId(); + Result getMerchantResult = await this.MerchantUiService.GetMerchant(correlationId, estateId, merchantId); + depositModalMerchantName = getMerchantResult.IsSuccess ? getMerchantResult.Data.MerchantName : null; + + showDepositModal = true; + } + + private void CloseDepositModal() + { + showDepositModal = false; + depositModel = new MerchantModels.DepositModel(); + depositErrorMessage = null; + depositSuccessMessage = null; + } + + private async Task HandleDepositSubmit() + { + isDepositSaving = true; + depositErrorMessage = null; + depositSuccessMessage = null; + + CorrelationId correlationId = new(Guid.NewGuid()); + Guid estateId = await this.GetEstateId(); + + Result result = await this.MerchantUiService.MakeMerchantDeposit(correlationId, estateId, depositMerchantId, depositModel); + + if (result.IsSuccess) + { + depositSuccessMessage = "Deposit recorded successfully"; + StateHasChanged(); + await this.WaitOnUIRefresh(); + CloseDepositModal(); + } + else + { + depositErrorMessage = "Failed to make deposit"; + } + + isDepositSaving = false; + } private void NavigateToNewMerchant() => NavigationManager.NavigateToNewMerchant(); } From 29ba8d57909c9c19eaa1f2d40128d35a1d570a59 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 06:08:12 +0000 Subject: [PATCH 4/6] Remove top cancel button from deposit modal header Co-authored-by: StuartFerguson <16325469+StuartFerguson@users.noreply.github.com> --- .../Pages/Merchants/MerchantsIndexPageTests.cs | 6 +++--- .../Components/Pages/Merchants/Index.razor | 17 ++++++----------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsIndexPageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsIndexPageTests.cs index c900fb9c..2bc69cc4 100644 --- a/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsIndexPageTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsIndexPageTests.cs @@ -456,7 +456,7 @@ public void MerchantsIndex_MakeDepositButton_OpensDepositModal() } [Fact] - public void MerchantsIndex_DepositModal_HasTwoCancelButtons() + public void MerchantsIndex_DepositModal_HasOneCancelButton() { // Arrange var merchantId = Guid.NewGuid(); @@ -491,10 +491,10 @@ public void MerchantsIndex_DepositModal_HasTwoCancelButtons() cut.Find("#makeDepositLink").Click(); cut.WaitForAssertion(() => cut.Markup.ShouldContain("Make Merchant Deposit"), TimeSpan.FromSeconds(5)); - // Assert - exactly 2 Cancel buttons in the modal + // Assert - exactly 1 Cancel button in the modal footer var allButtons = cut.FindAll("button"); var cancelButtons = allButtons.Where(b => b.TextContent.Trim() == "Cancel").ToList(); - cancelButtons.Count.ShouldBe(2); + cancelButtons.Count.ShouldBe(1); } [Fact] diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor index 58ba3e4b..434ceaf1 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor @@ -238,17 +238,12 @@ {
-
-
-

Make Merchant Deposit

- @if (!string.IsNullOrEmpty(depositModalMerchantName)) - { -

For merchant: @depositModalMerchantName

- } -
- +
+

Make Merchant Deposit

+ @if (!string.IsNullOrEmpty(depositModalMerchantName)) + { +

For merchant: @depositModalMerchantName

+ }
@if (!string.IsNullOrWhiteSpace(depositErrorMessage)) From 4bf8e9c9d32fe6696fab68e629c2d4b2585e8285 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Tue, 10 Mar 2026 09:18:59 +0000 Subject: [PATCH 5/6] Add using alias for MerchantModels namespace in Index.razor.cs Introduced a using alias directive for EstateManagementUI.BlazorServer.Models.MerchantModels as MerchantModels. This simplifies references to types within the MerchantModels namespace throughout the file. --- .../Components/Pages/Merchants/Index.razor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor.cs index cc2b846d..6bee878a 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor.cs @@ -7,6 +7,7 @@ using Shared.Results; using SimpleResults; using MerchantListModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantListModel; +using MerchantModels = EstateManagementUI.BlazorServer.Models.MerchantModels; namespace EstateManagementUI.BlazorServer.Components.Pages.Merchants { From 0f5e48c0faf1627d8c82cd93b2615e151bf72e49 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Tue, 10 Mar 2026 09:25:51 +0000 Subject: [PATCH 6/6] Remove prlinked.yml workflow and related automation The prlinked.yml GitHub Actions workflow has been deleted. This removes all automation for moving linked issues, formatting PR dates, and handling project columns on pull request events. The repository will no longer perform these actions automatically. --- .github/workflows/prlinked.yml | 47 ---------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 .github/workflows/prlinked.yml diff --git a/.github/workflows/prlinked.yml b/.github/workflows/prlinked.yml deleted file mode 100644 index 84037c86..00000000 --- a/.github/workflows/prlinked.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Move Linked Issues - -on: - pull_request: - types: - - opened - - synchronize - - reopened - -jobs: - get-date: - runs-on: ubuntu-latest - outputs: - project_name_prefix: ${{ steps.format_date.outputs.formatted_date }} - steps: - - name: Get PR creation date - id: format_date - run: | - # Extract the month and year from the PR creation date - PR_DATE="${{ github.event.pull_request.created_at }}" - FORMATTED_DATE=$(date -d "$PR_DATE" "+%B %Y") # Format to Month Year - - # Debugging: print out the formatted date - echo "Formatted Date: ${FORMATTED_DATE} Sprint" - - # Set output using the Environment File method - echo "formatted_date=${FORMATTED_DATE} Sprint" >> $GITHUB_OUTPUT # Set the output for later jobs - - debug-date: - needs: get-date - runs-on: ubuntu-latest - steps: - - name: Debug the outputs - run: | - echo "PR Number: ${{ github.event.pull_request.number }}" - echo "Project Column Name: Review" - echo "Project Name Prefix (from get-date job output): ${{ needs.get-date.outputs.project_name_prefix }}" # Access the output correctly - - move-issues: - needs: get-date - uses: TransactionProcessing/org-ci-workflows/.github/workflows/move-linked-issue.yml@main - with: - pr_number: ${{ github.event.pull_request.number }} - project_column_name: "Review" - project_name_prefix: ${{ needs.get-date.outputs.project_name_prefix }} # Access the output from get-date job - secrets: - gh_token: ${{ secrets.GH_TOKEN }}