Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 9 additions & 137 deletions EstateManagementUI.BlazorServer/Components/Pages/Merchants/New.razor
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@
<p class="text-sm">@errorMessage</p>
</div>
}

@if (!string.IsNullOrWhiteSpace(successMessage))
{
<div class="bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-lg">
<p class="font-medium">Success</p>
<p class="text-sm">@successMessage</p>
</div>
}

<!-- Form -->
<div class="bg-white rounded-lg shadow-md p-6">
Expand Down Expand Up @@ -168,140 +176,4 @@
</div>
</EditForm>
</div>
</div>

@code {
private readonly New.CreateMerchantModel model = new();
private bool isSaving = false;
private string? errorMessage;

protected override async Task OnInitializedAsync()
{
await RequirePermission(PermissionSection.Merchant, PermissionFunction.Create);
}

private async Task HandleSubmit()
{
isSaving = true;
errorMessage = null;

try
{
var correlationId = new CorrelationId(Guid.NewGuid());
var estateId = Guid.Parse("11111111-1111-1111-1111-111111111111");
var accessToken = "stubbed-token";

// Create merchant
var createCommand = new Commands.CreateMerchantCommand(
correlationId,
accessToken,
estateId,
model.MerchantName!,
model.ContactName!,
model.EmailAddress!
);

var createResult = await Mediator.Send(createCommand);

if (!createResult.IsSuccess)
{
errorMessage = createResult.Message ?? "Failed to create merchant";
return;
}

// Get the newly created merchant ID (in real implementation, this would be returned from the create command)
var merchantId = Guid.NewGuid();

// // Update address
// var addressCommand = new Commands.UpdateMerchantAddressCommand(
// correlationId,
// accessToken,
// estateId,
// merchantId,
// model.AddressLine1!,
// model.Town!,
// model.Region!,
// model.PostCode!,
// model.Country!
// );

// await Mediator.Send(addressCommand);

// // Update contact (including phone)
// var contactCommand = new Commands.UpdateMerchantContactCommand(
// correlationId,
// accessToken,
// estateId,
// merchantId,
// model.ContactName!,
// model.EmailAddress!,
// model.PhoneNumber!
// );

// await Mediator.Send(contactCommand);

// // Set settlement schedule
// var scheduleCommand = new Commands.SetMerchantSettlementScheduleCommand(
// correlationId,
// accessToken,
// estateId,
// merchantId,
// model.SettlementSchedule!
// );

// await Mediator.Send(scheduleCommand);

// Navigate to merchant list
NavigationManager.NavigateTo($"/merchants");
}
catch (Exception ex)
{
errorMessage = $"An error occurred: {ex.Message}";
}
finally
{
isSaving = false;
}
}

private void Cancel()
{
NavigationManager.NavigateTo("/merchants");
}

public class CreateMerchantModel
{
[Required(ErrorMessage = "Merchant name is required")]
public string? MerchantName { get; set; }

[Required(ErrorMessage = "Settlement schedule is required")]
public string? SettlementSchedule { get; set; }

[Required(ErrorMessage = "Address line 1 is required")]
public string? AddressLine1 { get; set; }

public string? AddressLine2 { get; set; }

[Required(ErrorMessage = "Town is required")]
public string? Town { get; set; }

[Required(ErrorMessage = "Region is required")]
public string? Region { get; set; }

[Required(ErrorMessage = "PostCode is required")]
public string? PostCode { get; set; }

[Required(ErrorMessage = "Country is required")]
public string? Country { get; set; }

[Required(ErrorMessage = "Contact name is required")]
public string? ContactName { get; set; }

[Required(ErrorMessage = "Email address is required")]
[EmailAddress(ErrorMessage = "Invalid email address")]
public string? EmailAddress { get; set; }

[Required(ErrorMessage = "Phone number is required")]
public string? PhoneNumber { get; set; }
}
}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using EstateManagementUI.BlazorServer.Permissions;
using EstateManagementUI.BusinessLogic.Requests;
using System.ComponentModel.DataAnnotations;

namespace EstateManagementUI.BlazorServer.Components.Pages.Merchants
{
public partial class New
{
private readonly New.CreateMerchantModel model = new();
private bool isSaving = false;
private string? successMessage;
private string? errorMessage;

protected override async Task OnAfterRenderAsync(bool firstRender) {
if (!firstRender) {
await base.OnAfterRenderAsync(firstRender);
return;
}

await RequirePermission(PermissionSection.Merchant, PermissionFunction.Create);
}

private async Task HandleSubmit()
{
isSaving = true;
errorMessage = null;

try
{
var correlationId = new CorrelationId(Guid.NewGuid());
var estateId = await this.GetEstateId();

var address = new MerchantCommands.MerchantAddress(Guid.Empty, model.AddressLine1, model.Town, model.Region, model.PostCode, model.Country);
var contact = new MerchantCommands.MerchantContact(Guid.Empty, model.ContactName, model.EmailAddress, model.PhoneNumber);

// Get the newly created merchant ID
var merchantId = Guid.NewGuid();

// Create merchant
var createCommand = new MerchantCommands.CreateMerchantCommand(
correlationId,
estateId, merchantId,
this.model.MerchantName,
this.model.SettlementSchedule,address, contact);

var createResult = await Mediator.Send(createCommand);

if (!createResult.IsSuccess)
{
errorMessage = createResult.Message ?? "Failed to create merchant";
return;
}

// Show success message briefly before navigating away
successMessage = "Merchant created successfully.";
StateHasChanged();

// Small delay so user sees confirmation (adjust duration as needed)
await Task.Delay(2500);

// Navigate to merchant list
NavigationManager.NavigateTo($"/merchants");
}
catch (Exception ex)
{
errorMessage = $"An error occurred: {ex.Message}";
}
finally
{
isSaving = false;
}
}

private void Cancel()
{
NavigationManager.NavigateTo("/merchants");
}

public class CreateMerchantModel
{
[Required(ErrorMessage = "Merchant name is required")]
public string? MerchantName { get; set; }

[Required(ErrorMessage = "Settlement schedule is required")]
public string? SettlementSchedule { get; set; }

[Required(ErrorMessage = "Address line 1 is required")]
public string? AddressLine1 { get; set; }

public string? AddressLine2 { get; set; }

[Required(ErrorMessage = "Town is required")]
public string? Town { get; set; }

[Required(ErrorMessage = "Region is required")]
public string? Region { get; set; }

[Required(ErrorMessage = "PostCode is required")]
public string? PostCode { get; set; }

[Required(ErrorMessage = "Country is required")]
public string? Country { get; set; }

[Required(ErrorMessage = "Contact name is required")]
public string? ContactName { get; set; }

[Required(ErrorMessage = "Email address is required")]
[EmailAddress(ErrorMessage = "Invalid email address")]
public string? EmailAddress { get; set; }

[Required(ErrorMessage = "Phone number is required")]
public string? PhoneNumber { get; set; }
}
}
}
29 changes: 29 additions & 0 deletions EstateManagmentUI.BusinessLogic/Client/MerchantMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public partial interface IApiClient
Task<Result> AddDeviceToMerchant(MerchantCommands.AddMerchantDeviceCommand request, CancellationToken cancellationToken);
Task<Result> SwapMerchantDevice(MerchantCommands.SwapMerchantDeviceCommand request, CancellationToken cancellationToken);
Task<Result> MakeMerchantDeposit(MerchantCommands.MakeMerchantDepositCommand request, CancellationToken cancellationToken);
Task<Result> CreateMerchant(MerchantCommands.CreateMerchantCommand request, CancellationToken cancellationToken);
}

public partial class ApiClient : IApiClient {
Expand Down Expand Up @@ -122,6 +123,34 @@ public async Task<Result> MakeMerchantDeposit(MerchantCommands.MakeMerchantDepos
return Result.Success();
}

public async Task<Result> CreateMerchant(MerchantCommands.CreateMerchantCommand request,
CancellationToken cancellationToken) {
var token = await this.GetToken(cancellationToken);
if (token.IsFailed)
return ResultHelpers.CreateFailure(token);

SettlementSchedule settlementSchedule = Enum.Parse<SettlementSchedule>(request.SettlementSchedule);
CreateMerchantRequest apiRequest = new() { MerchantId = request.MerchantId, Name = request.Name,SettlementSchedule = settlementSchedule, Address = new Address {
AddressLine1 = request.MerchantAddress.AddressLine1,
Town = request.MerchantAddress.Town,
Country = request.MerchantAddress.Country,
PostalCode = request.MerchantAddress.PostalCode,
Region = request.MerchantAddress.Region
},
Contact = new Contact {
ContactName = request.MerchantContact.ContactName,
EmailAddress = request.MerchantContact.ContactEmail,
PhoneNumber = request.MerchantContact.ContactPhone
}
};

var apiResult = await this.TransactionProcessorClient.CreateMerchant(token.Data, request.EstateId, apiRequest, cancellationToken);
if (apiResult.IsFailed)
return ResultHelpers.CreateFailure(apiResult);

return Result.Success();
}

public async Task<Result> RemoveOperatorFromMerchant(MerchantCommands.RemoveOperatorFromMerchantCommand request,
CancellationToken cancellationToken) {
var token = await this.GetToken(cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public class MerchantRequestHandler : IRequestHandler<MerchantQueries.GetMerchan
IRequestHandler<MerchantQueries.GetMerchantQuery, Result<MerchantModel>>,
IRequestHandler<MerchantCommands.AddMerchantDeviceCommand, Result>,
IRequestHandler<MerchantCommands.AddOperatorToMerchantCommand, Result>,
IRequestHandler<Commands.CreateMerchantCommand, Result>,
IRequestHandler<MerchantCommands.CreateMerchantCommand, Result>,
IRequestHandler<MerchantCommands.MakeMerchantDepositCommand, Result>,
IRequestHandler<MerchantCommands.RemoveContractFromMerchantCommand, Result>,
IRequestHandler<MerchantCommands.RemoveOperatorFromMerchantCommand, Result>,
Expand Down Expand Up @@ -92,9 +92,9 @@ public async Task<Result> Handle(MerchantCommands.AddOperatorToMerchantCommand r
return await this.ApiClient.AddOperatorToMerchant(request, cancellationToken);
}

public async Task<Result> Handle(Commands.CreateMerchantCommand request,
public async Task<Result> Handle(MerchantCommands.CreateMerchantCommand request,
CancellationToken cancellationToken) {
return Result.Success();
return await this.ApiClient.CreateMerchant(request, cancellationToken);
}

public async Task<Result> Handle(MerchantCommands.MakeMerchantDepositCommand request,
Expand Down
2 changes: 1 addition & 1 deletion EstateManagmentUI.BusinessLogic/Requests/Requests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ public record AssignContractToMerchantCommand(CorrelationId CorrelationId, Guid
public record AddMerchantDeviceCommand(CorrelationId CorrelationId, Guid EstateId, Guid MerchantId, string DeviceIdentifier) : IRequest<Result>;
public record SwapMerchantDeviceCommand(CorrelationId CorrelationId, Guid EstateId, Guid MerchantId, string OldDevice, string NewDevice) : IRequest<Result>;
public record MakeMerchantDepositCommand(CorrelationId CorrelationId, Guid EstateId, Guid MerchantId, decimal Amount, DateTime Date, string Reference) : IRequest<Result>;
public record CreateMerchantCommand(CorrelationId CorrelationId, Guid EstateId, Guid MerchantId, string Name, String SettlementSchedule, MerchantAddress MerchantAddress, MerchantContact MerchantContact) : IRequest<Result>;
}

public static class Commands
{

public record CreateContractCommand(CorrelationId CorrelationId, string AccessToken, Guid EstateId, string Description, Guid OperatorId) : IRequest<Result>;
public record CreateMerchantCommand(CorrelationId CorrelationId, string AccessToken, Guid EstateId, string Name, string ContactName, string ContactEmail) : IRequest<Result>;
public record CreateMerchantUserCommand(CorrelationId CorrelationId, string AccessToken, Guid EstateId, Guid MerchantId, string EmailAddress, string Password) : IRequest<Result>;
public record CreateOperatorCommand(CorrelationId CorrelationId, string AccessToken, Guid EstateId, string Name, bool RequireCustomMerchantNumber, bool RequireCustomTerminalNumber) : IRequest<Result>;

Expand Down
10 changes: 5 additions & 5 deletions EstateManagmentUI.BusinessLogic/Services/TestMediatorService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, Cancellation
Queries.GetMerchantSettlementHistoryQuery query => Task.FromResult((TResponse)(object)Result.Success(this.GetMockMerchantSettlementHistory(query))),
Queries.GetSettlementSummaryQuery query => Task.FromResult((TResponse)(object)Result.Success(this.GetMockSettlementSummary(query))),
Queries.GetTransactionDetailQuery q => Task.FromResult((TResponse)(object)Result.Success(this.GetMockTransactionDetails(q))),

// Commands - execute against test data store
Commands.CreateMerchantCommand cmd => Task.FromResult((TResponse)(object)this.ExecuteCreateMerchant(cmd)),
MerchantCommands.CreateMerchantCommand cmd => Task.FromResult((TResponse)(object)this.ExecuteCreateMerchant(cmd)),
MerchantCommands.UpdateMerchantCommand cmd => Task.FromResult((TResponse)(object)this.ExecuteUpdateMerchant(cmd)),
Commands.CreateOperatorCommand cmd => Task.FromResult((TResponse)(object)this.ExecuteCreateOperator(cmd)),
Commands.UpdateOperatorCommand cmd => Task.FromResult((TResponse)(object)this.ExecuteUpdateOperator(cmd)),
Expand Down Expand Up @@ -145,14 +145,14 @@ private Result<ContractModel> GetContractResult(Guid estateId, Guid contractId)
}

// Command execution methods
private Result ExecuteCreateMerchant(Commands.CreateMerchantCommand cmd)
private Result ExecuteCreateMerchant(MerchantCommands.CreateMerchantCommand cmd)
{
var merchant = new MerchantModel
{
MerchantId = Guid.NewGuid(),
MerchantName = cmd.Name,
ContactName = cmd.ContactName,
ContactEmailAddress = cmd.ContactEmail,
ContactName = cmd.MerchantContact.ContactName,
ContactEmailAddress = cmd.MerchantContact.ContactEmail,
SettlementSchedule = "Immediate"
};
this._testDataStore.AddMerchant(cmd.EstateId, merchant);
Expand Down
Loading