Skip to content

Commit fc647d2

Browse files
Merge pull request #686 from TransactionProcessing/task/#624_made_merchant_deposit
Refactor merchant deposit logic and improve UX
2 parents e5eceab + 681534e commit fc647d2

6 files changed

Lines changed: 164 additions & 140 deletions

File tree

EstateManagementUI.BlazorServer/Components/Pages/Merchants/Deposit.razor

Lines changed: 9 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,6 @@
1010

1111
<PageTitle>Make Merchant Deposit</PageTitle>
1212

13-
@if (!hasPermission)
14-
{
15-
<div class="space-y-6">
16-
<div class="bg-red-50 border border-red-200 text-red-700 px-6 py-8 rounded-lg text-center">
17-
<svg class="w-16 h-16 mx-auto mb-4 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
18-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
19-
</svg>
20-
<h2 class="text-2xl font-bold mb-2">Access Denied</h2>
21-
<p class="text-lg mb-4">You don't have permission to make deposits.</p>
22-
<p class="mb-6">This operation requires the MakeDeposit permission for the Merchant section.</p>
23-
<button class="btn btn-primary" @onclick="@(() => NavigationManager.NavigateTo("/merchants"))">
24-
Back to Merchants
25-
</button>
26-
</div>
27-
</div>
28-
}
29-
else
30-
{
3113
<div class="space-y-6">
3214
<!-- Page Header -->
3315
<div class="flex items-center justify-between">
@@ -51,6 +33,14 @@ else
5133
</div>
5234
}
5335

36+
@if (!string.IsNullOrWhiteSpace(successMessage))
37+
{
38+
<div class="bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-lg">
39+
<p class="font-medium">Success</p>
40+
<p class="text-sm">@successMessage</p>
41+
</div>
42+
}
43+
5444
<!-- Form -->
5545
<div class="bg-white rounded-lg shadow-md p-6">
5646
<EditForm Model="@model" OnValidSubmit="@HandleSubmit">
@@ -103,119 +93,4 @@ else
10393
</div>
10494
</EditForm>
10595
</div>
106-
</div>
107-
}
108-
109-
@code {
110-
[Parameter]
111-
public Guid MerchantId { get; set; }
112-
113-
private DepositModel model = new();
114-
private bool isSaving = false;
115-
private string? errorMessage;
116-
private string? merchantName;
117-
private bool hasPermission = false;
118-
119-
protected override async Task OnInitializedAsync()
120-
{
121-
await RequirePermission(PermissionSection.Merchant, PermissionFunction.MakeDeposit);
122-
123-
// Set default date to today
124-
model.Date = DateTime.Today;
125-
126-
// Load merchant name
127-
try
128-
{
129-
var correlationId = new CorrelationId(Guid.NewGuid());
130-
var estateId = Guid.Parse("11111111-1111-1111-1111-111111111111");
131-
var accessToken = "stubbed-token";
132-
133-
var result = await Mediator.Send(new MerchantQueries.GetMerchantQuery(correlationId, estateId, MerchantId));
134-
if (result.IsSuccess && result.Data != null)
135-
{
136-
merchantName = result.Data.MerchantName;
137-
}
138-
}
139-
catch (Exception ex)
140-
{
141-
errorMessage = $"Failed to load merchant details: {ex.Message}";
142-
}
143-
}
144-
145-
private async Task HandleSubmit()
146-
{
147-
isSaving = true;
148-
errorMessage = null;
149-
150-
try
151-
{
152-
var correlationId = new CorrelationId(Guid.NewGuid());
153-
var estateId = Guid.Parse("11111111-1111-1111-1111-111111111111");
154-
var accessToken = "stubbed-token";
155-
156-
var command = new Commands.MakeMerchantDepositCommand(
157-
correlationId,
158-
accessToken,
159-
estateId,
160-
MerchantId,
161-
model.Amount,
162-
model.Date,
163-
model.Reference!
164-
);
165-
166-
var result = await Mediator.Send(command);
167-
168-
if (!result.IsSuccess)
169-
{
170-
errorMessage = result.Message ?? "Failed to make deposit";
171-
return;
172-
}
173-
174-
// Navigate back to merchant list on success
175-
NavigationManager.NavigateTo("/merchants");
176-
}
177-
catch (Exception ex)
178-
{
179-
errorMessage = $"An error occurred: {ex.Message}";
180-
}
181-
finally
182-
{
183-
isSaving = false;
184-
}
185-
}
186-
187-
private void Cancel()
188-
{
189-
NavigationManager.NavigateTo("/merchants");
190-
}
191-
192-
public class DepositModel
193-
{
194-
[Required(ErrorMessage = "Deposit amount is required")]
195-
[Range(1, int.MaxValue, ErrorMessage = "Deposit amount must be greater than 0")]
196-
public int Amount { get; set; }
197-
198-
[Required(ErrorMessage = "Date of deposit is required")]
199-
[DateNotInFuture(ErrorMessage = "Date cannot be in the future")]
200-
public DateTime Date { get; set; } = DateTime.Today;
201-
202-
[Required(ErrorMessage = "Reference is required")]
203-
public string? Reference { get; set; }
204-
}
205-
206-
// Custom validation attribute for date not in future
207-
private class DateNotInFutureAttribute : ValidationAttribute
208-
{
209-
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
210-
{
211-
if (value is DateTime date)
212-
{
213-
if (date.Date > DateTime.Today)
214-
{
215-
return new ValidationResult(ErrorMessage ?? "Date cannot be in the future");
216-
}
217-
}
218-
return ValidationResult.Success;
219-
}
220-
}
221-
}
96+
</div>
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
using EstateManagementUI.BlazorServer.Permissions;
2+
using EstateManagementUI.BusinessLogic.Requests;
3+
using Microsoft.AspNetCore.Components;
4+
using System.ComponentModel.DataAnnotations;
5+
6+
namespace EstateManagementUI.BlazorServer.Components.Pages.Merchants
7+
{
8+
public partial class Deposit
9+
{
10+
[Parameter]
11+
public Guid MerchantId { get; set; }
12+
13+
private DepositModel model = new();
14+
private bool isSaving = false;
15+
private string? errorMessage;
16+
private string? successMessage;
17+
private string? merchantName;
18+
private bool hasPermission = false;
19+
20+
protected override async Task OnAfterRenderAsync(bool firstRender) {
21+
if (!firstRender) {
22+
await base.OnAfterRenderAsync(firstRender);
23+
return;
24+
}
25+
26+
await RequirePermission(PermissionSection.Merchant, PermissionFunction.MakeDeposit);
27+
28+
// Set default date to today
29+
model.Date = DateTime.Today;
30+
31+
// Load merchant name
32+
try
33+
{
34+
var correlationId = new CorrelationId(Guid.NewGuid());
35+
var estateId = await this.GetEstateId();
36+
37+
var result = await Mediator.Send(new MerchantQueries.GetMerchantQuery(correlationId, estateId, MerchantId));
38+
if (result.IsSuccess && result.Data != null)
39+
{
40+
merchantName = result.Data.MerchantName;
41+
}
42+
}
43+
catch (Exception ex)
44+
{
45+
errorMessage = $"Failed to load merchant details: {ex.Message}";
46+
}
47+
}
48+
49+
private async Task HandleSubmit()
50+
{
51+
isSaving = true;
52+
errorMessage = null;
53+
successMessage = null;
54+
55+
try
56+
{
57+
var correlationId = new CorrelationId(Guid.NewGuid());
58+
var estateId = await this.GetEstateId();
59+
60+
var command = new MerchantCommands.MakeMerchantDepositCommand(
61+
correlationId,
62+
estateId,
63+
MerchantId,
64+
model.Amount,
65+
model.Date,
66+
model.Reference!
67+
);
68+
69+
var result = await Mediator.Send(command);
70+
71+
if (!result.IsSuccess)
72+
{
73+
errorMessage = result.Message ?? "Failed to make deposit";
74+
return;
75+
}
76+
77+
// Show success message briefly before navigating away
78+
successMessage = "Deposit recorded successfully.";
79+
StateHasChanged();
80+
81+
// Small delay so user sees confirmation (adjust duration as needed)
82+
await Task.Delay(2500);
83+
84+
// Navigate back to merchant list on success
85+
NavigationManager.NavigateTo("/merchants");
86+
}
87+
catch (Exception ex)
88+
{
89+
errorMessage = $"An error occurred: {ex.Message}";
90+
}
91+
finally
92+
{
93+
isSaving = false;
94+
}
95+
}
96+
97+
private void Cancel()
98+
{
99+
NavigationManager.NavigateTo("/merchants");
100+
}
101+
102+
public class DepositModel
103+
{
104+
[Required(ErrorMessage = "Deposit amount is required")]
105+
[Range(1, int.MaxValue, ErrorMessage = "Deposit amount must be greater than 0")]
106+
public int Amount { get; set; }
107+
108+
[Required(ErrorMessage = "Date of deposit is required")]
109+
[DateNotInFuture(ErrorMessage = "Date cannot be in the future")]
110+
public DateTime Date { get; set; } = DateTime.Today;
111+
112+
[Required(ErrorMessage = "Reference is required")]
113+
public string? Reference { get; set; }
114+
}
115+
116+
// Custom validation attribute for date not in future
117+
private class DateNotInFutureAttribute : ValidationAttribute
118+
{
119+
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
120+
{
121+
if (value is DateTime date)
122+
{
123+
if (date.Date > DateTime.Today)
124+
{
125+
return new ValidationResult(ErrorMessage ?? "Date cannot be in the future");
126+
}
127+
}
128+
return ValidationResult.Success;
129+
}
130+
}
131+
}
132+
}

EstateManagmentUI.BusinessLogic/Client/MerchantMethods.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public partial interface IApiClient
2828
Task<Result> AddContractToMerchant(MerchantCommands.AssignContractToMerchantCommand request, CancellationToken cancellationToken);
2929
Task<Result> AddDeviceToMerchant(MerchantCommands.AddMerchantDeviceCommand request, CancellationToken cancellationToken);
3030
Task<Result> SwapMerchantDevice(MerchantCommands.SwapMerchantDeviceCommand request, CancellationToken cancellationToken);
31+
Task<Result> MakeMerchantDeposit(MerchantCommands.MakeMerchantDepositCommand request, CancellationToken cancellationToken);
3132
}
3233

3334
public partial class ApiClient : IApiClient {
@@ -106,6 +107,21 @@ public async Task<Result> SwapMerchantDevice(MerchantCommands.SwapMerchantDevice
106107
return Result.Success();
107108
}
108109

110+
public async Task<Result> MakeMerchantDeposit(MerchantCommands.MakeMerchantDepositCommand request,
111+
CancellationToken cancellationToken) {
112+
var token = await this.GetToken(cancellationToken);
113+
if (token.IsFailed)
114+
return ResultHelpers.CreateFailure(token);
115+
116+
MakeMerchantDepositRequest apiRequest = new() { Amount = request.Amount, DepositDateTime = request.Date, Reference = request.Reference};
117+
118+
var apiResult = await this.TransactionProcessorClient.MakeMerchantDeposit(token.Data, request.EstateId, request.MerchantId,apiRequest, cancellationToken);
119+
if (apiResult.IsFailed)
120+
return ResultHelpers.CreateFailure(apiResult);
121+
122+
return Result.Success();
123+
}
124+
109125
public async Task<Result> RemoveOperatorFromMerchant(MerchantCommands.RemoveOperatorFromMerchantCommand request,
110126
CancellationToken cancellationToken) {
111127
var token = await this.GetToken(cancellationToken);

EstateManagmentUI.BusinessLogic/RequestHandlers/DateRequestHandler.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public class MerchantRequestHandler : IRequestHandler<MerchantQueries.GetMerchan
5757
IRequestHandler<MerchantCommands.AddMerchantDeviceCommand, Result>,
5858
IRequestHandler<MerchantCommands.AddOperatorToMerchantCommand, Result>,
5959
IRequestHandler<Commands.CreateMerchantCommand, Result>,
60-
IRequestHandler<Commands.MakeMerchantDepositCommand, Result>,
60+
IRequestHandler<MerchantCommands.MakeMerchantDepositCommand, Result>,
6161
IRequestHandler<MerchantCommands.RemoveContractFromMerchantCommand, Result>,
6262
IRequestHandler<MerchantCommands.RemoveOperatorFromMerchantCommand, Result>,
6363
IRequestHandler<MerchantCommands.SwapMerchantDeviceCommand, Result>,
@@ -97,9 +97,9 @@ public async Task<Result> Handle(Commands.CreateMerchantCommand request,
9797
return Result.Success();
9898
}
9999

100-
public async Task<Result> Handle(Commands.MakeMerchantDepositCommand request,
100+
public async Task<Result> Handle(MerchantCommands.MakeMerchantDepositCommand request,
101101
CancellationToken cancellationToken) {
102-
return Result.Success();
102+
return await this.ApiClient.MakeMerchantDeposit(request, cancellationToken);
103103
}
104104

105105
public async Task<Result> Handle(MerchantCommands.RemoveContractFromMerchantCommand request,

EstateManagmentUI.BusinessLogic/Requests/Requests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public record RemoveContractFromMerchantCommand(CorrelationId CorrelationId, Gui
8484
public record AssignContractToMerchantCommand(CorrelationId CorrelationId, Guid EstateId, Guid MerchantId, Guid ContractId) : IRequest<Result>;
8585
public record AddMerchantDeviceCommand(CorrelationId CorrelationId, Guid EstateId, Guid MerchantId, string DeviceIdentifier) : IRequest<Result>;
8686
public record SwapMerchantDeviceCommand(CorrelationId CorrelationId, Guid EstateId, Guid MerchantId, string OldDevice, string NewDevice) : IRequest<Result>;
87+
public record MakeMerchantDepositCommand(CorrelationId CorrelationId, Guid EstateId, Guid MerchantId, decimal Amount, DateTime Date, string Reference) : IRequest<Result>;
8788
}
8889

8990
public static class Commands
@@ -93,7 +94,7 @@ public record CreateContractCommand(CorrelationId CorrelationId, string AccessTo
9394
public record CreateMerchantCommand(CorrelationId CorrelationId, string AccessToken, Guid EstateId, string Name, string ContactName, string ContactEmail) : IRequest<Result>;
9495
public record CreateMerchantUserCommand(CorrelationId CorrelationId, string AccessToken, Guid EstateId, Guid MerchantId, string EmailAddress, string Password) : IRequest<Result>;
9596
public record CreateOperatorCommand(CorrelationId CorrelationId, string AccessToken, Guid EstateId, string Name, bool RequireCustomMerchantNumber, bool RequireCustomTerminalNumber) : IRequest<Result>;
96-
public record MakeMerchantDepositCommand(CorrelationId CorrelationId, string AccessToken, Guid EstateId, Guid MerchantId, decimal Amount, DateTime Date, string Reference) : IRequest<Result>;
97+
9798

9899

99100
public record UpdateOperatorCommand(CorrelationId CorrelationId, string AccessToken, Guid EstateId, Guid OperatorId, string Name, bool RequireCustomMerchantNumber, bool RequireCustomTerminalNumber) : IRequest<Result>;

EstateManagmentUI.BusinessLogic/Services/TestMediatorService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, Cancellation
8383
MerchantCommands.AddMerchantDeviceCommand => Task.FromResult((TResponse)(object)Result.Success()),
8484
MerchantCommands.SwapMerchantDeviceCommand => Task.FromResult((TResponse)(object)Result.Success()),
8585
Commands.CreateMerchantUserCommand => Task.FromResult((TResponse)(object)Result.Success()),
86-
Commands.MakeMerchantDepositCommand cmd => Task.FromResult((TResponse)(object)this.ExecuteMakeMerchantDeposit(cmd)),
86+
MerchantCommands.MakeMerchantDepositCommand cmd => Task.FromResult((TResponse)(object)this.ExecuteMakeMerchantDeposit(cmd)),
8787

8888
_ => throw new NotImplementedException($"Request type {request.GetType().Name} is not implemented in test mediator")
8989
};
@@ -299,7 +299,7 @@ private Result ExecuteRemoveOperatorFromMerchant(MerchantCommands.RemoveOperator
299299
return Result.Success();
300300
}
301301

302-
private Result ExecuteMakeMerchantDeposit(Commands.MakeMerchantDepositCommand cmd)
302+
private Result ExecuteMakeMerchantDeposit(MerchantCommands.MakeMerchantDepositCommand cmd)
303303
{
304304
var merchant = this._testDataStore.GetMerchant(cmd.EstateId, cmd.MerchantId);
305305
if (merchant == null)

0 commit comments

Comments
 (0)