Application creating but without past visas and visits

This commit is contained in:
2024-09-06 23:31:07 +03:00
parent f90a0e7e82
commit c197a45f83
24 changed files with 516 additions and 86 deletions

View File

@@ -13,7 +13,7 @@
Expiration date:<br/>
<InputDate DisplayName="Expiration date of permission to destination Country" class="rounded"
@bind-Value="PermissionToDestCountry.ExpirationDate"
max="@formattedDate"/>
min="@formattedDate"/>
</label><br/>
<ValidationMessage For="() => PermissionToDestCountry.ExpirationDate"></ValidationMessage>
</div>

View File

@@ -0,0 +1,34 @@
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider
@using VisaApiClient
<div>
<label>
Number:<br/>
<InputText DisplayName="Number of re-entry permit" class="rounded"
@bind-Value="ReentryPermit.Number"/>
</label><br/>
<ValidationMessage For="() => ReentryPermit.Number"></ValidationMessage><br/>
<label>
Expiration date:<br/>
<InputDate DisplayName="Expiration date of re-entry permit" class="rounded"
@bind-Value="ReentryPermit.ExpirationDate"
min="@formattedDate"/>
</label><br/>
<ValidationMessage For="() => ReentryPermit.ExpirationDate"></ValidationMessage>
</div>
@code {
private string formattedDate = null!;
[Parameter, EditorRequired] public ReentryPermitModel ReentryPermit { get; set; } = null!;
[Inject] IDateTimeProvider DateTimeProvider { get; set; } = null!;
protected override void OnInitialized()
{
formattedDate = DateTimeProvider.FormattedNow();
ReentryPermit.ExpirationDate = DateTimeProvider.Now();
}
}

View File

@@ -1,4 +1,6 @@
<CascadingValue Value="this">
@using System.Net
@using VisaApiClient
<CascadingValue Value="this">
@ChildContent
</CascadingValue>
@@ -7,8 +9,13 @@
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Inject] private NavigationManager Nav { get; set; } = null!;
public void Handle(Exception ex)
{
if (ex is ApiException<ProblemDetails> { StatusCode: (int)HttpStatusCode.Unauthorized or (int)HttpStatusCode.Forbidden })
{
Nav.NavigateTo("/");
}
}
}

View File

@@ -0,0 +1,14 @@
using AutoMapper;
using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models;
using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.AutoMapper.Profiles
{
public class VisaApplicationCreateRequestProfile : Profile
{
public VisaApplicationCreateRequestProfile()
{
CreateMap<VisaApplicationCreateRequestModel, VisaApplicationCreateRequest>(MemberList.Destination);
}
}
}

View File

@@ -0,0 +1,22 @@
using System.Text;
using FluentValidation.Results;
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
{
public static class ValidationResultExtensions
{
public static string ToErrorsString(this ValidationResult validationResult)
=> ErrorsToString(validationResult.Errors.Select(e => e.ErrorMessage));
private static string ErrorsToString(IEnumerable<string> errors)
{
var stringBuilder = new StringBuilder();
foreach (var error in errors)
{
stringBuilder.Append($"{error}<br/>");
}
return stringBuilder.ToString();
}
}
}

View File

@@ -5,7 +5,7 @@ namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvide
public interface IUserDataProvider
{
public ApplicantModel? GetApplicant();
public Task<ApplicantModel> GetApplicant();
public string? GetCurrentRole();
}

View File

@@ -8,10 +8,9 @@ namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvide
{
private static readonly JwtSecurityTokenHandler tokenHandler = new ();
public ApplicantModel? GetApplicant()
public async Task<ApplicantModel> GetApplicant()
{
//todo api action
return null;
return await client.GetApplicantAsync();
}
public string? GetCurrentRole()

View File

@@ -22,6 +22,13 @@
</NavLink>
</div>
</nav>
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="applications/new" Match="NavLinkMatch.All">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> New application
</NavLink>
</div>
</nav>
</div>
@code {

View File

@@ -1,5 +1,4 @@
@page "/applications"
@using System.Net
@using System.Text
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@@ -61,14 +60,7 @@
}
catch (Exception e)
{
if (e is ApiException<ProblemDetails> { Result.Status: (int)HttpStatusCode.Unauthorized } problemDetailsException)
{
htmlBody = $"<p>{problemDetailsException.Result.Detail!}</p>";
}
else
{
ErrorHandler.Handle(e);
}
}
}

View File

@@ -1,10 +1,17 @@
@page "/visaApplications/new"
@page "/applications/new"
@using System.Net
@using AutoMapper
@using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents.Applicants
@using VisaApiClient
@using BlazorWebAssemblyVisaApiClient.Components
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents.VisaApplications
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@using FluentValidation
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<PageTitle>New Application</PageTitle>
@@ -23,9 +30,10 @@
<ValidationMessage For="() => requestModel.DestinationCountry"></ValidationMessage><br/>
<label>
Category: <EnumInputList Model="requestModel"
EnumProperty="r => r.VisaCategory"
OnChanged="StateHasChanged"/>
Category:
<EnumInputList Model="requestModel"
EnumProperty="r => r.VisaCategory"
OnChanged="StateHasChanged"/>
</label><br/>
<ValidationMessage For="() => requestModel.VisaCategory"></ValidationMessage><br/>
@@ -46,33 +54,98 @@
<ValidationMessage For="() => requestModel.ValidDaysRequested"></ValidationMessage><br/>
</div>
<div class="form-block">
<fieldset disabled="@(requestModel.VisaCategory is not VisaCategory.Transit)">
@if (requestModel.VisaCategory is VisaCategory.Transit)
{
<div class="form-block">
<h5>Permission to destination Country</h5>
<PermissionToDestCountryInput PermissionToDestCountry="requestModel.PermissionToDestCountry" />
</fieldset>
</div><br/>
<PermissionToDestCountryInput PermissionToDestCountry="requestModel.PermissionToDestCountry"/>
</div>
}
@if (isNonResident)
{
<div class="form-block">
<h5>Re-entry permission</h5>
<ReentryPermitInput ReentryPermit="requestModel.ReentryPermit" />
</div>
<br/>
}
<input type="submit" class="btn-outline-primary" value="Register"/>
<ValidationSummary/>
<Status @ref="status"/>
</EditForm>
</div>
//todo past visas and visits
@code {
private VisaApplicationCreateRequestModel requestModel = new();
private Status status = null!;
private bool isNonResident;
[Inject] IDateTimeProvider DateTimeProvider { get; set; } = null!;
protected override void OnInitialized()
[Inject] IUserDataProvider UserDataProvider { get; set; } = null!;
[Inject] IValidator<VisaApplicationCreateRequestModel> VisaApplicationCreateRequestValidator { get; set; } = null!;
[Inject] IMapper Mapper { get; set; } = null!;
protected override async Task OnInitializedAsync()
{
try
{
isNonResident = (await UserDataProvider.GetApplicant()).IsNonResident;
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
requestModel.PermissionToDestCountry!.ExpirationDate = DateTimeProvider.Now();
}
private static void TryCreate()
private async Task TryCreate()
{
var validationResult = await VisaApplicationCreateRequestValidator.ValidateAsync(requestModel);
if (!validationResult.IsValid)
{
var errorsString = validationResult.ToErrorsString();
status.SetError(errorsString);
}
status.SetMessage("Wait...");
var request = Mapper.Map<VisaApplicationCreateRequest>(requestModel);
try
{
await Client.CreateApplicationAsync(request);
status.SetSucces("Application created successfully.");
}
catch (ApiException<ProblemDetails> e)
{
if (e.StatusCode == (int)HttpStatusCode.BadRequest
&& e.Result.AdditionalProperties.TryGetValue("errors", out var errors))
{
var errorsList = ((JArray)errors).ToObject<List<string>>();
if (errorsList is null)
{
ErrorHandler.Handle(new JsonException("Can't convert validation errors to list"));
return;
}
status.SetError(string.Join("<br/>", errorsList));
}
else
{
ErrorHandler.Handle(e);
}
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
}

View File

@@ -1,6 +1,5 @@
@page "/register"
@using System.Net
@using System.Text
@using AutoMapper
@using VisaApiClient
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents.Applicants
@@ -8,6 +7,7 @@
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq
@using BlazorWebAssemblyVisaApiClient.Components
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider
@using BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
@@ -144,23 +144,12 @@
requestModel.BirthDate = DateTime.Now;
}
private string ErrorsToString(IEnumerable<string> errors)
{
var stringBuilder = new StringBuilder();
foreach (var error in errors)
{
stringBuilder.Append($"{error}<br/>");
}
return stringBuilder.ToString();
}
private async void TryRegisterApplicant()
{
var validationResult = await RegisterApplicantRequestValidator.ValidateAsync(requestModel);
if (!validationResult.IsValid)
{
var errorsString = ErrorsToString(validationResult.Errors.Select(e => e.ErrorMessage));
var errorsString = validationResult.ToErrorsString();
status.SetError(errorsString);
return;
@@ -187,7 +176,7 @@
return;
}
status.SetError(ErrorsToString(errorsList));
status.SetError(string.Join("<br/>", errorsList));
}
else
{
@@ -196,6 +185,7 @@
}
catch (Exception e)
{
status.SetError("Error occured");
ErrorHandler.Handle(e);
}
}

View File

@@ -26,14 +26,12 @@ namespace BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models
[Range(0, ConfigurationConstraints.MaxValidDays)]
public int ValidDaysRequested { get; set; }
[Required]
[ValidateComplexType]
public PastVisaModel[] PastVisas { get; set; } = default!;
[ValidateComplexType]
public PermissionToDestCountryModel? PermissionToDestCountry { get; set; } = new();
[Required]
[ValidateComplexType]
public PastVisitModel[] PastVisits { get; set; } = default!;
}

View File

@@ -0,0 +1,27 @@
using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider;
using FluentValidation;
using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Validators;
public class PastVisaModelValidator : AbstractValidator<PastVisaModel>
{
public PastVisaModelValidator(IDateTimeProvider dateTimeProvider)
{
RuleFor(v => v.ExpirationDate)
.NotEmpty()
.WithMessage("Expiration date of past visa can not be empty")
.GreaterThan(v => v.IssueDate)
.WithMessage("Past visa expiration date can not be earlier than issue date");
RuleFor(v => v.IssueDate)
.NotEmpty()
.WithMessage("Issue date of past visa can not be empty")
.LessThan(dateTimeProvider.Now())
.WithMessage("Issue date of past visa must be in past");
RuleFor(v => v.Name)
.NotEmpty()
.WithMessage("Name of past visa can not be empty");
}
}

View File

@@ -0,0 +1,29 @@
using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider;
using FluentValidation;
using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Validators;
public class PastVisitModelValidator : AbstractValidator<PastVisitModel>
{
public PastVisitModelValidator(IDateTimeProvider dateTimeProvider)
{
RuleFor(v => v.StartDate)
.NotEmpty()
.WithMessage("Start date of past visit can not be empty")
.LessThan(v => v.EndDate)
.WithMessage("Start date of past visit must be earlier than end date")
.LessThan(dateTimeProvider.Now())
.WithMessage("Start date of past visit must be in past");
RuleFor(v => v.EndDate)
.NotEmpty()
.WithMessage("End date of past visit can not be empty");
RuleFor(v => v.DestinationCountry)
.NotEmpty()
.WithMessage("Destination Country of past visit can not be null")
.MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Destination Country of past visit length must be less than {ConfigurationConstraints.CountryNameLength}");
}
}

View File

@@ -0,0 +1,23 @@
using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider;
using FluentValidation;
using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Validators;
public class PermissionToDestCountryModelValidator : AbstractValidator<PermissionToDestCountryModel?>
{
public PermissionToDestCountryModelValidator(IDateTimeProvider dateTimeProvider)
{
RuleFor(p => p!.ExpirationDate)
.NotEmpty()
.WithMessage("Expiration date of permission to destination Country can not be empty")
.GreaterThan(dateTimeProvider.Now())
.WithMessage("Permission to destination Country must not be expired");
RuleFor(p => p!.Issuer)
.NotEmpty()
.WithMessage("Issuer of permission for destination Country can not be empty")
.MaximumLength(ConfigurationConstraints.IssuerNameLength)
.WithMessage($"Issuer of permission to destination Country length must be less than {ConfigurationConstraints.IssuerNameLength}");
}
}

View File

@@ -0,0 +1,42 @@
using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models;
using FluentValidation;
using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Validators;
public class VisaApplicationCreateRequestModelValidator : AbstractValidator<VisaApplicationCreateRequestModel>
{
public VisaApplicationCreateRequestModelValidator(
IValidator<PastVisaModel> pastVisaModelValidator,
IValidator<PermissionToDestCountryModel?> permissionToDestCountryModelValidator,
IValidator<PastVisitModel> pastVisitModelValidator)
{
RuleFor(r => r.PermissionToDestCountry)
.NotEmpty()
.WithMessage("For transit you must provide permission to destination country")
.SetValidator(permissionToDestCountryModelValidator)
.When(r => r.VisaCategory is VisaCategory.Transit);
RuleFor(r => r.DestinationCountry)
.NotEmpty()
.WithMessage("Destination country can not be empty");
RuleFor(r => r.VisaCategory)
.IsInEnum();
RuleFor(r => r.RequestedNumberOfEntries)
.IsInEnum();
RuleFor(r => r.ValidDaysRequested)
.GreaterThan(0)
.WithMessage($"Valid days requested should be positive number and less than {ConfigurationConstraints.MaxValidDays}")
.LessThanOrEqualTo(ConfigurationConstraints.MaxValidDays)
.WithMessage($"Valid days requested must be less than or equal to {ConfigurationConstraints.MaxValidDays}");
RuleForEach(r => r.PastVisas)
.SetValidator(pastVisaModelValidator);
RuleForEach(r => r.PastVisits)
.SetValidator(pastVisitModelValidator);
}
}