Вытащил солюшен на уровень выше, чтобы прощё было дотнетить
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2025-10-05 14:32:06 +03:00
parent fa87a56ad1
commit aae4b28089
242 changed files with 159 additions and 159 deletions

View File

@@ -0,0 +1,60 @@
@page "/authorities/add"
@using AutoMapper
@using BlazorWebAssemblyVisaApiClient.Components
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models
@using FluentValidation
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<EditForm Model="requestModel" class="with-centered-content">
<ObjectGraphDataAnnotationsValidator/>
<div >
<label>
Email:<br/>
<InputText class="rounded" @bind-Value="requestModel.AuthData.Email"/>
<ValidationMessage For="() => requestModel.AuthData.Email"/>
</label><br/>
<p/>
<label>
Password:<br/>
<InputText type="password" class="rounded" @bind-Value="requestModel.AuthData.Password"/>
<ValidationMessage For="() => requestModel.AuthData.Password"/>
</label><br/>
<p/>
<button class="btn-primary rounded" @onclick="Add">Add</button><br/>
<Status @ref="status"/>
</div>
</EditForm>
@code
{
private RegisterRequestModel requestModel = new();
private Status status = new();
[Inject] private IValidator<RegisterRequestModel> RegisterRequestModelValidator { get; set; } = null!;
[Inject] private IMapper Mapper { get; set; } = null!;
private async Task Add()
{
var validationResult = await RegisterRequestModelValidator.ValidateAsync(requestModel);
if (!validationResult.IsValid)
{
status.SetError(validationResult.ToErrorsString());
return;
}
try
{
status.SetMessage("Wait...");
await Client.RegisterAuthorityAsync(Mapper.Map<RegisterRequest>(requestModel));
status.SetSuccess("Success");
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
}

View File

@@ -0,0 +1,261 @@
@page "/applications/{ApplicationId}"
@using System.Net
@using BlazorWebAssemblyVisaApiClient.Common.Exceptions
@using BlazorWebAssemblyVisaApiClient.Components
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@using BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models
@using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<PageTitle>Application</PageTitle>
<table class="table table-bordered table-hover table-sm">
<tbody>
<tr>
<td >
Applicant's fullname:<br/>
<em>@NameToString(application.Applicant.Name)</em>
</td>
<td colspan="2">
Date of birth:<br/>
<em>@application.Applicant.BirthDate.ToString("d")</em>
</td>
</tr>
<tr>
<td colspan="3">
Country and city of birth:<br/>
<em>@application.Applicant.CountryOfBirth, @application.Applicant.CityOfBirth</em>
</td>
</tr>
<tr>
<td colspan="2">
Citizenship:<br/>
<em>@application.Applicant.Citizenship</em>
</td>
<td>
Citizenship by birth:<br/>
<em>@application.Applicant.CitizenshipByBirth</em>
</td>
</tr>
<tr>
<td >
Gender:<br/>
<em>@application.Applicant.Gender.GetDisplayName()</em>
</td>
<td colspan="2">
Marital status:<br/>
<em>@(((MaritalStatusModel)application.Applicant.MaritalStatus).GetDisplayName())</em>
</td>
</tr>
<tr>
<td >
Father's fullname:<br/>
<em>@NameToString(application.Applicant.FatherName)</em>
</td>
<td colspan="2">
Mother's fullname:<br/>
<em>@NameToString(application.Applicant.MotherName)</em>
</td>
</tr>
<tr>
<td >
Passport number:<br/>
<em>@application.Applicant.Passport.Number</em>
</td>
<td >
Issue date:<br/>
<em>@application.Applicant.Passport.IssueDate.ToString("d")</em>
</td>
<td >
Expiration date:<br/>
<em>@application.Applicant.Passport.ExpirationDate.ToString("d")</em>
</td>
</tr>
<tr>
<td colspan="3">
Passport issuer:<br/>
<em>@application.Applicant.Passport.Issuer</em>
</td>
</tr>
<tr>
<td colspan="3">
Re-entry permission (for non-residents):<br/>
@if (application.Applicant.IsNonResident)
{
<em>@(application.ReentryPermit is null ? "None" : $"{application.ReentryPermit.Number}, expires at {application.ReentryPermit.ExpirationDate:d}")</em>
}
else
{
<em>Not non-resident</em>
}
</td>
</tr>
<tr>
<td colspan="3">
Job title:<br/>
<em>@application.Applicant.JobTitle</em>
</td>
</tr>
<tr>
<td colspan="3">
Place of work, address, hirer's phone number:<br/>
<em>
@((MarkupString)$"{application.Applicant.PlaceOfWork.Name}<br>Address: {AddressToString(application.Applicant.PlaceOfWork.Address)}<br>Phone num: {application.Applicant.PlaceOfWork.PhoneNum}")
</em>
</td>
</tr>
<tr>
<td >
Destination Country:<br/>
<em>@application.DestinationCountry</em>
</td>
<td >
Visa category:<br/>
<em>@(((VisaCategoryModel)application.VisaCategory).GetDisplayName())</em>
</td>
<td >
Visa:<br/>
<em>@(application.ForGroup ? "For group" : "Individual")</em>
</td>
</tr>
<tr>
<td >
Requested number of entries:<br/>
<em>@application.RequestedNumberOfEntries.GetDisplayName()</em>
</td>
<td colspan="2">
Valid for:<br/>
<em>@($"{application.ValidDaysRequested} days")</em>
</td>
</tr>
<tr>
<td colspan="3">
Past visas:<br/>
@if (application.PastVisas.Any())
{
foreach (var visa in application.PastVisas)
{
<em>@($"{visa.Name} issued at {visa.IssueDate:d} and was valid until {visa.ExpirationDate:d}")</em>
<br/>
}
}
else
{
<em>None</em>
}
</td>
</tr>
<tr>
<td colspan="3">
Permission to destination Country, if transit:<br/>
@if (application.VisaCategory is VisaCategory.Transit)
{
<em>@(application.PermissionToDestCountry is null ? "None" : $"Expires at {application.PermissionToDestCountry.ExpirationDate}, issued by: {application.PermissionToDestCountry.Issuer}")</em>
}
else
{
<em>Non-transit</em>
}
</td>
</tr>
<tr>
<td colspan="3">
Past visits:<br/>
@if (application.PastVisas.Any())
{
foreach (var visit in application.PastVisits)
{
<em>@($"Visit to {visit.DestinationCountry}, entered at {visit.StartDate:d} and lasts until {visit.EndDate:d}")</em>
<br/>
}
}
else
{
<em>None</em>
}
</td>
</tr>
</tbody>
</table>
@if (currentRole == Constants.ApprovingAuthorityRole)
{
<button class="btn-outline-primary" @onclick="Approve">Approve</button>
<button class="btn-outline-danger" @onclick="Reject">Reject</button>
<Status @ref="status"/>
}
@code {
private VisaApplicationModel application = new();
private string currentRole = null!;
private Status status = null!;
[Parameter] public string ApplicationId { get; set; } = null!;
[Inject] private IUserDataProvider UserDataProvider { get; set; } = null!;
[Inject] private NavigationManager Nav { get; set; } = null!;
protected override async Task OnInitializedAsync()
{
try
{
var applicationId = Guid.Parse(ApplicationId);
currentRole = UserDataProvider.CurrentRole ?? throw new NotLoggedInException();
application = currentRole switch
{
Constants.ApplicantRole => await Client.GetApplicationForApplicantAsync(applicationId),
Constants.ApprovingAuthorityRole => await Client.GetApplicationForAuthorityAsync(applicationId),
_ => throw new NotLoggedInException()
};
}
catch (Exception e)
{
if (e is ApiException<ProblemDetails> { Result.Status: (int)HttpStatusCode.Conflict })
{
Nav.NavigateTo("/applications");
}
ErrorHandler.Handle(e);
}
}
private static string NameToString(NameModel name)
=> $"{name.FirstName} {name.Surname} {name.Patronymic}".TrimEnd();
private static string AddressToString(AddressModel address)
=> $"{address.Country}, {address.City}, {address.Street} {address.Building}";
private async void Approve()
{
try
{
status.SetMessage("Wait...");
await Client.SetStatusFromAuthorityAsync(application.Id, AuthorityRequestStatuses.Approved);
Nav.NavigateTo("/applications");
}
catch (Exception e)
{
status.SetError("Error occured.");
ErrorHandler.Handle(e);
}
}
private async void Reject()
{
try
{
status.SetMessage("Wait...");
await Client.SetStatusFromAuthorityAsync(application.Id, AuthorityRequestStatuses.Rejected);
Nav.NavigateTo("/applications");
}
catch (Exception e)
{
status.SetError("Error occured.");
ErrorHandler.Handle(e);
}
}
}

View File

@@ -0,0 +1,143 @@
@page "/applications"
@using BlazorWebAssemblyVisaApiClient.Common.Exceptions
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<PageTitle>Applications</PageTitle>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Destination Country</th>
<th>Visa Category</th>
<th>Request date</th>
<th>Days requested</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var application in applications)
{
var rowClass = application.Status switch
{
ApplicationStatus.Pending => "",
ApplicationStatus.Approved => "table-success",
ApplicationStatus.Rejected => "table-danger",
ApplicationStatus.Closed => "table-danger",
_ => throw new ArgumentOutOfRangeException()
};
<tr class="@rowClass">
<td>@application.DestinationCountry</td>
<td>@(((VisaCategoryModel)application.VisaCategory).GetDisplayName())</td>
<td>@application.RequestDate.ToString("d")</td>
<td>@application.ValidDaysRequested</td>
<td>@application.Status.GetDisplayName()</td>
<td>
<NavLink href="@($"/applications/{application.Id}")">
<button class="btn-primary">See</button>
</NavLink>
@if (currentRole == Constants.ApplicantRole)
{
<span> | </span>
<input type="button" class="btn-outline-primary" @onclick="() => DownloadApplication(application)" value="Download"/>
if (application.Status is ApplicationStatus.Pending)
{
<span> | </span>
<input type="button" class="border-danger" @onclick="() => CloseApplication(application)" value="Close"/>
}
}
</td>
</tr>
}
</tbody>
</table >
<script>
window.downloadFileFromStream = async (contentStreamReference) => {
const arrayBuffer = await contentStreamReference.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
const anchorElement = document.createElement('a');
anchorElement.href = url;
anchorElement.download = 'Application.xlsx';
anchorElement.click();
anchorElement.remove();
URL.revokeObjectURL(url);
}
</script>
@code {
private string currentRole = null!;
private List<VisaApplicationPreview> applications = [];
[Inject] private IUserDataProvider UserDataProvider { get; set; } = null!;
[Inject] private IJSRuntime JavaScriptInterop { get; set; } = null!;
protected override async Task OnInitializedAsync()
{
try
{
currentRole = UserDataProvider.CurrentRole ?? throw new NotLoggedInException();
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
await Fetch();
}
private async Task Fetch()
{
try
{
applications = currentRole switch
{
Constants.ApplicantRole => (await Client.GetApplicationsForApplicantAsync()).OrderByDescending(a => a.RequestDate).ToList(),
Constants.ApprovingAuthorityRole => (await Client.GetPendingAsync()).OrderByDescending(a => a.RequestDate).ToList(),
_ => throw new NotLoggedInException()
};
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
private async Task CloseApplication(VisaApplicationPreview application)
{
try
{
await Client.CloseApplicationAsync(application.Id);
application.Status = ApplicationStatus.Closed;
StateHasChanged();
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
private async Task DownloadApplication(VisaApplicationPreview application)
{
try
{
var response = await Client.DownloadApplicationForApplicantAsync(application.Id);
using var streamRef = new DotNetStreamReference(stream: response.Stream);
await JavaScriptInterop.InvokeVoidAsync("downloadFileFromStream", streamRef);
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
}

View File

@@ -0,0 +1,32 @@
@page "/"
@using BlazorWebAssemblyVisaApiClient.Components
@using BlazorWebAssemblyVisaApiClient.Components.Auth
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents.Applicants
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<PageTitle>Authentication</PageTitle>
<div class="with-centered-content">
<EditForm class="form" Model="loginData" OnValidSubmit="TryLogin">
<DataAnnotationsValidator/>
<AuthDataInput AuthData="loginData"/><br/>
<input class="btn-outline-primary rounded" type="submit" value="Login"/>
or
<NavLink href="register">Register</NavLink >
<Status><AuthComponent @ref="auth"/></Status>
</EditForm>
</div>
@code
{
private AuthData loginData = new();
private AuthComponent auth = null!;
private async Task TryLogin()
{
await auth.TryAuthorize(loginData);
}
}

View File

@@ -0,0 +1,58 @@
@page "/authorities"
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Email</th><th></th>
</tr>
</thead>
<tbody>
@foreach (var authority in authorities)
{
var path = $"authorities/{authority.Id}/{authority.Email}";
<tr>
<td>@authority.Email</td>
<td>
<NavLink href="@path">
<button class="btn-outline-primary">Change</button>
</NavLink>
|
<button class="btn-outline-danger" @onclick="() => Delete(authority)">Delete</button>
</td>
</tr>
}
</tbody>
</table>
@code {
private List<UserModel> authorities = [];
protected override async Task OnInitializedAsync()
{
try
{
authorities = (await Client.GetAuthorityAccountsAsync()).ToList();
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
private async Task Delete(UserModel authority)
{
try
{
await Client.RemoveAuthorityAccountAsync(authority.Id);
authorities.Remove(authority);
StateHasChanged();
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
}

View File

@@ -0,0 +1,83 @@
@page "/authorities/{authorityId}/{oldEmail}"
@using BlazorWebAssemblyVisaApiClient.Common.Exceptions
@using BlazorWebAssemblyVisaApiClient.Components
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@using FluentValidation
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<EditForm Model="model" class="with-centered-content">
<div >
<label>
New email:<br/>
<InputText class="rounded" @bind-Value="model.Email"/>
</label><br/><p/>
<label>
New password (leave blank if shouldn't be changed):<br/>
<InputText type="password" class="rounded" @bind-Value="model.Password"/>
</label><br/><p/>
<button class="btn-primary rounded" @onclick="Save">Save</button><br/>
<Status @ref="status"/>
</div>
</EditForm>
@code
{
private Status status = null!;
private ChangeAuthData model = new();
[Parameter] public string AuthorityId { get; set; } = null!;
[Parameter] public string OldEmail { get; set; } = null!;
[Inject] private IUserDataProvider UserDataProvider { get; set; } = null!;
[Inject] private IValidator<ChangeUserAuthDataRequest> ChangeUserAuthDataRequestValidator { get; set; } = null!;
protected override void OnInitialized()
{
try
{
if (UserDataProvider.CurrentRole is not Constants.AdminRole)
{
throw new NotLoggedInException();
}
model.Email = OldEmail;
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
private async Task Save()
{
var request = new ChangeUserAuthDataRequest
{
UserId = Guid.Parse(AuthorityId),
NewAuthData = model
};
var validationResult = await ChangeUserAuthDataRequestValidator.ValidateAsync(request);
if (!validationResult.IsValid)
{
status.SetError(validationResult.ToErrorsString());
return;
}
try
{
status.SetMessage("Wait...");
await Client.ChangeAuthorityAuthDataAsync(request);
status.SetSuccess("Success");
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
}

View File

@@ -0,0 +1,360 @@
@page "/applications/new"
@using System.Net
@using AutoMapper
@using BlazorWebAssemblyVisaApiClient.Components
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents.VisaApplications
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@using BlazorWebAssemblyVisaApiClient.Validation
@using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models
@using FluentValidation
@using Newtonsoft.Json.Linq
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<PageTitle>New Application</PageTitle>
<div class="horizontal-centered-content">
<h3>New application</h3>
<EditForm class="form" Model="requestModel" OnValidSubmit="TryCreate">
<ObjectGraphDataAnnotationsValidator/>
<div class="form-block">
<h5>Visa@(Constants.RequiredFieldMarkup)</h5>
<label>
Destination Country:<br/>
<InputText DisplayName="Destination Country" class="rounded" @bind-Value="requestModel.DestinationCountry"/>
</label><br/>
<ValidationMessage For="() => requestModel.DestinationCountry"></ValidationMessage><br/>
<label>
Category:
<EnumInputList Model="requestModel"
EnumProperty="r => r.VisaCategory"
OnChanged="StateHasChanged"/>
</label><br/>
<ValidationMessage For="() => requestModel.VisaCategory"></ValidationMessage><br/>
<label>
Number of entries: <EnumInputList Model="requestModel" EnumProperty="r => r.RequestedNumberOfEntries"/>
</label><br/>
<ValidationMessage For="() => requestModel.RequestedNumberOfEntries"></ValidationMessage><br/>
<label>
For group: <InputCheckbox @bind-Value="requestModel.IsForGroup"/>
</label><br/>
<ValidationMessage For="() => requestModel.IsForGroup"></ValidationMessage><br/>
<label>
Valid for days:<br/>
<InputNumber DisplayName="Valid days" class="rounded" @bind-Value="requestModel.ValidDaysRequested"/>
</label>
<ValidationMessage For="() => requestModel.ValidDaysRequested"></ValidationMessage><br/>
</div>
<div class="form-block">
<h5>Past visas</h5>
@if (requestModel.PastVisas.Count > 0)
{
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Name</th><th>Issue date</th><th>Expiration date</th><th></th>
</tr>
</thead>
<tbody>
@foreach (var visa in requestModel.PastVisas)
{
<tr>
<td>@visa.Name</td>
<td>@visa.IssueDate.ToString("d.MM.yyyy")</td>
<td>@visa.ExpirationDate.ToString("d.MM.yyyy")</td>
<td>
<input type="button" class="border-danger" @onclick="() => RemovePastVisa(visa)" value="X"/>
</td>
</tr>
}
</tbody>
</table>
}
<label>
Name:<br/>
<InputText DisplayName="Past visa name" class="rounded" @bind-Value="editableVisa.Name"/>
</label><br/>
<ValidationMessage For="() => editableVisa.Name"></ValidationMessage><br/>
<label>
Issue date:<br/>
<InputDate DisplayName="Past visa issue date"
class="rounded"
@bind-Value="editableVisa.IssueDate"
max="@formattedNow"/>
</label><br/>
<ValidationMessage For="() => editableVisa.IssueDate"></ValidationMessage><br/>
<label>
Expiration date:<br/>
<InputDate DisplayName="Past visa expiration date"
class="rounded"
@bind-Value="editableVisa.ExpirationDate"/>
</label><br/>
<ValidationMessage For="() => editableVisa.ExpirationDate"></ValidationMessage><br/>
<input type="button" class="btn-outline-primary rounded"
disabled="@(requestModel.PastVisas.Count == ConfigurationConstraints.MaxPastVisas)"
@onclick="AddPastVisa" value="Add"/>
<Status @ref="pastVisaStatus"/>
</div>
<div class="form-block">
<h5>Past visits</h5>
@if (requestModel.PastVisits.Count > 0)
{
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Destination Country</th><th>Start date</th><th>End date</th><th></th>
</tr>
</thead>
<tbody>
@foreach (var visit in requestModel.PastVisits)
{
<tr>
<td>@visit.DestinationCountry</td>
<td>@visit.StartDate.ToString("d.MM.yyyy")</td>
<td>@visit.EndDate.ToString("d.MM.yyyy")</td>
<td>
<input type="button" class="border-danger" @onclick="() => RemovePastVisit(visit)" value="X"/>
</td>
</tr>
}
</tbody>
</table>
}
<label>
Destination Country:<br/>
<InputText DisplayName="Past visit destination Country" class="rounded" @bind-Value="editableVisit.DestinationCountry"/>
</label><br/>
<ValidationMessage For="() => editableVisit.DestinationCountry"></ValidationMessage><br/>
<label>
Start date:<br/>
<InputDate DisplayName="Past visit start date"
class="rounded"
@bind-Value="editableVisit.StartDate"
max="@formattedNow"/>
</label><br/>
<ValidationMessage For="() => editableVisit.StartDate"></ValidationMessage><br/>
<label>
End date:<br/>
<InputDate DisplayName="Past visit end date"
class="rounded"
@bind-Value="editableVisit.EndDate"
max="@formattedNow"/>
</label><br/>
<ValidationMessage For="() => editableVisit.EndDate"></ValidationMessage><br/>
<input type="button" class="btn-outline-primary rounded"
disabled="@(requestModel.PastVisits.Count == ConfigurationConstraints.MaxPastVisits)"
@onclick="AddPastVisit" value="Add"/>
<Status @ref="pastVisitStatus"/>
</div>
@if (requestModel.VisaCategory is VisaCategoryModel.Transit)
{
requestModel.PermissionToDestCountry ??= NewPermissionToDestCountry();
<div class="form-block">
<h5>Permission to destination Country@(Constants.RequiredFieldMarkup)</h5>
<PermissionToDestCountryInput PermissionToDestCountry="requestModel.PermissionToDestCountry"/>
</div>
}
else
{
requestModel.PermissionToDestCountry = null;
}
@if (isNonResident)
{
requestModel.ReentryPermit ??= NewReentryPermit();
<div class="form-block">
<h5>Re-entry permission@(Constants.RequiredFieldMarkup)</h5>
<ReentryPermitInput ReentryPermit="requestModel.ReentryPermit"/>
</div>
}
<br/><input type="submit" class="btn-outline-primary rounded" value="Register"/>
<ValidationSummary/>
<Status @ref="status"/>
</EditForm>
</div>
@code {
private VisaApplicationCreateRequestModel requestModel = new();
private Status status = null!;
private Status pastVisaStatus = null!;
private Status pastVisitStatus = null!;
private bool isNonResident;
private string formattedNow = null!;
private PastVisaModel editableVisa = null!;
private PastVisitModel editableVisit = null!;
[Inject] IDateTimeProvider DateTimeProvider { get; set; } = null!;
[Inject] IUserDataProvider UserDataProvider { get; set; } = null!;
[Inject] IValidator<VisaApplicationCreateRequestModel> VisaApplicationCreateRequestValidator { get; set; } = null!;
[Inject] IValidator<PastVisaModel> PastVisaModelValidator { get; set; } = null!;
[Inject] IValidator<PastVisitModel> PastVisitModelValidator { get; set; } = null!;
[Inject] IMapper Mapper { get; set; } = null!;
protected override async Task OnInitializedAsync()
{
editableVisa = NewPastVisa();
editableVisit = NewPastVisit();
requestModel.PermissionToDestCountry = NewPermissionToDestCountry();
formattedNow = DateTimeProvider.FormattedNow();
try
{
isNonResident = (await UserDataProvider.GetApplicant()).IsNonResident;
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
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.SetSuccess("Application created successfully.");
}
catch (ApiException<ProblemDetails> e)
{
if (e.StatusCode == (int)HttpStatusCode.BadRequest
&& e.Result.AdditionalProperties.TryGetValue("errors", out var errors))
{
try
{
var errorsList = ((JArray)errors).ToObject<List<string>>();
status.SetError(string.Join("<br/>", errorsList!));
}
catch (Exception inner)
{
ErrorHandler.Handle(inner);
status.SetError("Error occured");
}
}
else
{
throw;
}
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
private PastVisaModel NewPastVisa()
{
return new()
{
ExpirationDate = DateTimeProvider.Now(),
IssueDate = DateTimeProvider.Now()
};
}
private ReentryPermitModel NewReentryPermit()
{
return new()
{
ExpirationDate = DateTimeProvider.Now()
};
}
private PermissionToDestCountryModel NewPermissionToDestCountry()
{
return new()
{
ExpirationDate = DateTimeProvider.Now()
};
}
private PastVisitModel NewPastVisit()
{
return new()
{
StartDate = DateTimeProvider.Now(),
EndDate = DateTimeProvider.Now()
};
}
private void AddPastVisa()
{
if (requestModel.PastVisas.Count >= ConfigurationConstraints.MaxPastVisas)
{
pastVisaStatus.SetError($"{ConfigurationConstraints.MaxPastVisas} past visas is maximum");
return;
}
var validationResult = PastVisaModelValidator.Validate(editableVisa);
if (!validationResult.IsValid)
{
pastVisaStatus.SetError(validationResult.ToErrorsString());
return;
}
requestModel.PastVisas.Add(editableVisa);
editableVisa = NewPastVisa();
pastVisaStatus.SetSuccess("Added successfully");
}
private void RemovePastVisa(PastVisaModel visa)
{
requestModel.PastVisas.Remove(visa);
}
private void AddPastVisit()
{
if (requestModel.PastVisits.Count >= ConfigurationConstraints.MaxPastVisits)
{
pastVisitStatus.SetError($"{ConfigurationConstraints.MaxPastVisits} past visits is maximum");
return;
}
var validationResult = PastVisitModelValidator.Validate(editableVisit);
if (!validationResult.IsValid)
{
pastVisitStatus.SetError(validationResult.ToErrorsString());
return;
}
requestModel.PastVisits.Add(editableVisit);
editableVisit = NewPastVisit();
pastVisitStatus.SetSuccess("Added successfully");
}
private void RemovePastVisit(PastVisitModel visit)
{
requestModel.PastVisits.Remove(visit);
}
}

View File

@@ -0,0 +1,192 @@
@page "/register"
@using System.Net
@using AutoMapper
@using BlazorWebAssemblyVisaApiClient.Components
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents.Applicants
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Validation
@using BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models
@using FluentValidation
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<PageTitle>Registration</PageTitle>
<div class="horizontal-centered-content">
<h3>Registration data</h3>
<EditForm class="form" Model="requestModel" OnValidSubmit="TryRegisterApplicant">
<ObjectGraphDataAnnotationsValidator/>
<div class="form-block">
<h5>Authentication data@(Constants.RequiredFieldMarkup)</h5>
<AuthDataInput AuthData="requestModel.RegisterRequest.AuthData"/>
</div>
<div class="form-block">
<h5>Your Fullname</h5>
<NameInput Name="requestModel.ApplicantName"/>
</div>
<div class="form-block">
<h5>Fullname of your mother</h5>
<NameInput Name="requestModel.MotherName"/>
</div>
<div class="form-block">
<h5>Fullname of your father</h5>
<NameInput Name="requestModel.FatherName"/>
</div>
<div class="form-block">
<h5>Your passport@(Constants.RequiredFieldMarkup)</h5>
<PassportInput Passport="requestModel.Passport"/>
</div>
<div class="form-block">
<h5>Birth data@(Constants.RequiredFieldMarkup)</h5>
<div >
<label>
Country of birth:<br/>
<InputText DisplayName="Country of birth" class="rounded" @bind-Value="requestModel.CountryOfBirth"/>
</label><br/>
<ValidationMessage For="() => requestModel.CountryOfBirth"></ValidationMessage><br/>
<label>
City of birth:<br/>
<InputText DisplayName="City of birth" class="rounded" @bind-Value="requestModel.CityOfBirth"/>
</label><br/>
<ValidationMessage For="() => requestModel.CityOfBirth"></ValidationMessage><br/>
<label>
Birth date:<br/>
<InputDate DisplayName="Birth date" class="rounded" @bind-Value="requestModel.BirthDate" max="@formattedMaxBirthdayDate"/>
</label><br/>
<ValidationMessage For="() => requestModel.BirthDate"></ValidationMessage>
</div>
</div>
<div class="form-block">
<h5>Citizenship@(Constants.RequiredFieldMarkup)</h5>
<div >
<label>
Citizenship:<br/>
<InputText class="rounded" @bind-Value="requestModel.Citizenship"/>
</label><br/>
<ValidationMessage For="() => requestModel.Citizenship"></ValidationMessage><br/>
<label>
Citizenship by birth:<br/>
<InputText DisplayName="Citizenship by birth" class="rounded" @bind-Value="requestModel.CitizenshipByBirth"/>
</label><br/>
<ValidationMessage For="() => requestModel.CitizenshipByBirth"></ValidationMessage>
</div>
</div>
<div class="form-block">
<h5>Address of your place of work@(Constants.RequiredFieldMarkup)</h5>
<div >
<AddressInput Address="requestModel.PlaceOfWork.Address"/>
</div>
</div>
<div class="form-block">
<h5>Place of work data@(Constants.RequiredFieldMarkup)</h5>
<div >
<PlaceOfWorkInput PlaceOfWork="requestModel.PlaceOfWork"/><br/>
<label>
Job title:<br/>
<InputText DisplayName="Job title" class="rounded" @bind-Value="requestModel.JobTitle"/>
</label><br/>
<ValidationMessage For="() => requestModel.JobTitle"></ValidationMessage>
</div>
</div>
<div class="form-block">
<h5>Other</h5>
<div >
<label>
Gender: <EnumInputList Model="requestModel" EnumProperty="r => r.Gender"/>
</label>
</div><br/>
<div >
<label>
Marital status: <EnumInputList Model="requestModel" EnumProperty="r => r.MaritalStatus"/>
</label>
</div><br/>
<div >
<label>
Non-resident: <InputCheckbox @bind-Value="requestModel.IsNonResident"/>
</label>
</div>
</div><br/>
<input type="submit" class="btn-outline-primary" value="Register"/>
<Status @ref="status"/>
</EditForm>
</div>
@code
{
private RegisterApplicantRequestModel requestModel = new();
private Status status = null!;
private string formattedMaxBirthdayDate = null!;
[Inject] IValidator<RegisterApplicantRequestModel> RegisterApplicantRequestValidator { get; set; } = null!;
[Inject] IMapper Mapper { get; set; } = null!;
protected override void OnInitialized()
{
requestModel.BirthDate = DateTime.Now.AddYears(-ConfigurationConstraints.ApplicantMinAge);
formattedMaxBirthdayDate = requestModel.BirthDate.ToString("yyyy-MM-dd");
}
private async void TryRegisterApplicant()
{
var validationResult = await RegisterApplicantRequestValidator.ValidateAsync(requestModel);
if (!validationResult.IsValid)
{
var errorsString = validationResult.ToErrorsString();
status.SetError(errorsString);
return;
}
status.SetMessage("Wait...");
var request = Mapper.Map<RegisterApplicantRequest>(requestModel);
try
{
await Client.RegisterAsync(request);
status.SetSuccess("Register successful. Now log in.");
}
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
{
throw;
}
}
catch (Exception e)
{
status.SetError("Error occured");
ErrorHandler.Handle(e);
}
}
}