Page for seeing full application

This commit is contained in:
2024-09-08 22:19:14 +03:00
parent 11b28359dd
commit 3ef09cb478
17 changed files with 613 additions and 179 deletions

View File

@@ -1,4 +1,4 @@
namespace BlazorWebAssemblyVisaApiClient.Common
namespace BlazorWebAssemblyVisaApiClient.Common.Exceptions
{
public class BlazorClientException(string message) : Exception(message);
}

View File

@@ -0,0 +1,4 @@
namespace BlazorWebAssemblyVisaApiClient.Common.Exceptions
{
public class NotLoggedInException() : BlazorClientException("User is not logged in.");
}

View File

@@ -12,5 +12,9 @@ namespace BlazorWebAssemblyVisaApiClient
public readonly static Regex PhoneNumRegex = new(@"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$");
public readonly static MarkupString RequiredFieldMarkup = (MarkupString)"<span style=\"color: red;\">*</span>";
public const string ApplicantRole = "Applicant";
public const string ApprovingAuthorityRole = "ApprovingAuthority";
public const string AdminRole = "Admin";
}
}

View File

@@ -1,4 +1,4 @@
using BlazorWebAssemblyVisaApiClient.Common;
using BlazorWebAssemblyVisaApiClient.Common.Exceptions;
namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider.Exceptions
{

View File

@@ -7,8 +7,7 @@ namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvide
{
public class UserDataProvider(Client client) : IUserDataProvider
{
private readonly static JwtSecurityTokenHandler tokenHandler = new ();
private readonly static string[] knownRoles = ["Applicant", "ApprovingAuthority", "Admin"];
private readonly static JwtSecurityTokenHandler tokenHandler = new();
public async Task<ApplicantModel> GetApplicant()
{
@@ -25,9 +24,12 @@ namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvide
var token = tokenHandler.ReadJwtToken(client.AuthToken.Token);
var role = token.Claims.FirstOrDefault(claim => claim.Type == ClaimTypes.Role)?.Value;
if (!knownRoles.Contains(role))
switch (role)
{
throw new UnknownRoleException();
case Constants.ApplicantRole: break;
case Constants.ApprovingAuthorityRole: break;
case Constants.AdminRole: break;
default: throw new UnknownRoleException();
}
return role;

View File

@@ -0,0 +1,213 @@
@page "/applications/{ApplicationId}"
@using BlazorWebAssemblyVisaApiClient.Common.Exceptions
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@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.Passport.Number</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 >
Marital status:<br/>
<em>@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>@application.VisaCategory</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>
@code {
private VisaApplicationModel application = new();
private string currentRole = null!;
[Parameter] public string ApplicationId { get; set; } = null!;
[Inject] private IUserDataProvider UserDataProvider { get; set; } = null!;
protected override async Task OnInitializedAsync()
{
try
{
var applicationId = Guid.Parse(ApplicationId);
currentRole = UserDataProvider.GetCurrentRole() ?? throw new NotLoggedInException();
application = currentRole switch
{
Constants.ApplicantRole => await Client.GetApplicationForApplicantAsync(applicationId),
Constants.ApprovingAuthorityRole => await Client.GetApplicationForAuthorityAsync(applicationId),
_ => throw new NotLoggedInException()
};
}
catch (Exception e)
{
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}";
}

View File

@@ -1,68 +1,62 @@
@page "/applications"
@using System.Reflection
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider
@using BlazorWebAssemblyVisaApiClient.PagesExceptions.Applications
@using VisaApiClient
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<PageTitle>Applications</PageTitle>
<table class="table table-bordered">
@switch (currentRole)
<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)
{
case "Applicant":
<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>
var rowClass = application.Status switch
{
ApplicationStatus.Pending => "",
ApplicationStatus.Approved => "table-success",
ApplicationStatus.Rejected => "table-danger",
ApplicationStatus.Closed => "table-danger",
_ => throw new ArgumentOutOfRangeException()
};
<tbody>
@foreach (var application in applications.Cast<VisaApplicationModelForApplicant>())
{
var rowClass = application.Status switch
<tr class="@rowClass">
<td>@application.DestinationCountry</td>
<td>@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-outline-primary">See</button>
</NavLink>
@if (currentRole == Constants.ApplicantRole && application.Status is not ApplicationStatus.Closed)
{
ApplicationStatus.Pending => "",
ApplicationStatus.Approved => "table-success",
ApplicationStatus.Rejected => "table-danger",
ApplicationStatus.Closed => "table-danger",
_ => throw new ArgumentOutOfRangeException()
};
<tr class="@rowClass">
<th>@application.DestinationCountry</th>
<th>@application.VisaCategory.GetDisplayName()</th>
<th>@application.RequestDate.ToString("d")</th>
<th>@application.ValidDaysRequested</th>
<th>@application.Status.GetDisplayName()</th>
<th>
<input type="button" class="border-danger" @onclick="() => CloseApplication(application)" value="Close"/>
</th>
</tr>
}
</tbody>
break;
default:
Nav.NavigateTo("/"); //todo feedback
break;
<span> | </span>
<input type="button" class="border-danger" @onclick="() => CloseApplication(application)" value="Close"/>
}
</td>
</tr>
}
</tbody>
</table >
@code {
private string currentRole = null!;
private IEnumerable<object> applications = [];
private List<VisaApplicationPreview> applications = [];
[Inject] private IUserDataProvider UserDataProvider { get; set; } = null!;
[Inject] private NavigationManager Nav { get; set; } = null!;
protected override async Task OnInitializedAsync()
{
currentRole = UserDataProvider.GetCurrentRole()!;
@@ -75,7 +69,8 @@
{
applications = currentRole switch
{
"Applicant" => (await Client.GetForApplicantAsync()).OrderByDescending(a => a.RequestDate),
Constants.ApplicantRole => (await Client.GetForApplicantAsync()).OrderByDescending(a => a.RequestDate).ToList(),
Constants.ApprovingAuthorityRole => (await Client.GetPendingAsync()).OrderByDescending(a => a.RequestDate).ToList(),
_ => throw new ArgumentOutOfRangeException()
};
}
@@ -85,39 +80,18 @@
}
}
private async Task CloseApplication(object application)
private async Task CloseApplication(VisaApplicationPreview application)
{
try
{
PropertyInfo idProp;
PropertyInfo statusProp;
try
{
var type = applications.First().GetType();
idProp = type.GetProperty("Id")!;
statusProp = type.GetProperty("Status")!;
if (idProp.PropertyType != typeof(Guid)
|| statusProp.PropertyType != typeof(ApplicationStatus))
{
throw new();
}
}
catch (Exception)
{
throw new UnknownApplicationTypeException();
}
var id = (Guid)idProp.GetValue(application)!;
await Client.CloseApplicationAsync(id);
statusProp.SetValue(application, ApplicationStatus.Closed);
await Client.CloseApplicationAsync(application.Id);
application.Status = ApplicationStatus.Closed;
StateHasChanged();
}
catch (Exception e)
{
ErrorHandler.Handle(e);
}
}
}

View File

@@ -58,7 +58,7 @@
<h5>Past visas</h5>
@if (requestModel.PastVisas.Count > 0)
{
<table class="table table-bordered">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Name</th><th>Issue date</th><th>Expiration date</th><th></th>
@@ -68,12 +68,12 @@
@foreach (var visa in requestModel.PastVisas)
{
<tr>
<th>@visa.Name</th>
<th>@visa.IssueDate.ToString("d.MM.yyyy")</th>
<th>@visa.ExpirationDate.ToString("d.MM.yyyy")</th>
<th>
<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"/>
</th>
</td>
</tr>
}
</tbody>
@@ -112,7 +112,7 @@
<h5>Past visits</h5>
@if (requestModel.PastVisits.Count > 0)
{
<table class="table table-bordered">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Destination Country</th><th>Start date</th><th>End date</th><th></th>
@@ -122,12 +122,12 @@
@foreach (var visit in requestModel.PastVisits)
{
<tr>
<th>@visit.DestinationCountry</th>
<th>@visit.StartDate.ToString("d.MM.yyyy")</th>
<th>@visit.EndDate.ToString("d.MM.yyyy")</th>
<th>
<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"/>
</th>
</td>
</tr>
}
</tbody>

View File

@@ -1,6 +0,0 @@
using BlazorWebAssemblyVisaApiClient.Common;
namespace BlazorWebAssemblyVisaApiClient.PagesExceptions.Applications
{
public class UnknownApplicationTypeException() : BlazorClientException("Application type is unknown");
}