Page for seeing full application
This commit is contained in:
		| @@ -1,4 +1,4 @@ | ||||
| namespace BlazorWebAssemblyVisaApiClient.Common | ||||
| namespace BlazorWebAssemblyVisaApiClient.Common.Exceptions | ||||
| { | ||||
|     public class BlazorClientException(string message) : Exception(message); | ||||
| } | ||||
| @@ -0,0 +1,4 @@ | ||||
| namespace BlazorWebAssemblyVisaApiClient.Common.Exceptions | ||||
| { | ||||
|     public class NotLoggedInException() : BlazorClientException("User is not logged in."); | ||||
| } | ||||
| @@ -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"; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| using BlazorWebAssemblyVisaApiClient.Common; | ||||
| using BlazorWebAssemblyVisaApiClient.Common.Exceptions; | ||||
|  | ||||
| namespace BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider.Exceptions | ||||
| { | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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}"; | ||||
|  | ||||
| } | ||||
| @@ -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); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -1,6 +0,0 @@ | ||||
| using BlazorWebAssemblyVisaApiClient.Common; | ||||
|  | ||||
| namespace BlazorWebAssemblyVisaApiClient.PagesExceptions.Applications | ||||
| { | ||||
|     public class UnknownApplicationTypeException() : BlazorClientException("Application type is unknown"); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user