Participant Registration Steps
This commit is contained in:
		
							parent
							
								
									7c54dd5a69
								
							
						
					
					
						commit
						5f9787902f
					
				| @ -5,6 +5,7 @@ | ||||
|         New = 0, | ||||
|         EmailSent = 1, | ||||
|         Registered = 2, | ||||
|         FaceMatch = 3 | ||||
|         FaceMatch = 3, | ||||
|         Completed = 10 | ||||
|     } | ||||
| } | ||||
| @ -8,6 +8,8 @@ | ||||
| 
 | ||||
|         public static string DefaultIfEmpty(this string s, string defaultValue) => !string.IsNullOrWhiteSpace(s) ? s : (defaultValue ?? string.Empty); | ||||
| 
 | ||||
|         public static bool IsInList(this string s, params string[] list) => list.Contains(s, StringComparer.OrdinalIgnoreCase); | ||||
| 
 | ||||
|         public static string NullIfWhiteSpace(this string s) => string.IsNullOrWhiteSpace(s) ? null : s; | ||||
| 
 | ||||
|         public static Guid ToGuidFromBase64(this string s) | ||||
|  | ||||
| @ -20,7 +20,7 @@ namespace EnotaryoPH.Web.Common.Models | ||||
| 
 | ||||
|         internal static IdentificationDocument ToEntity(this IdentificationDocumentModel model, IdentificationDocument entity) | ||||
|         { | ||||
|             ArgumentNullException.ThrowIfNull(model.File, nameof(model.File)); | ||||
|             ArgumentNullException.ThrowIfNull(model.File); | ||||
| 
 | ||||
|             entity.ExpirationDate = model.ExpirationDate.ToUTC(); | ||||
|             entity.DateIssued = model.DateIssued.ToUTC(); | ||||
|  | ||||
							
								
								
									
										137
									
								
								EnotaryoPH/EnotaryoPH.Web/Pages/BaseTakeSelfiePageModel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								EnotaryoPH/EnotaryoPH.Web/Pages/BaseTakeSelfiePageModel.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,137 @@ | ||||
| using EnotaryoPH.Data; | ||||
| using EnotaryoPH.Data.Entities; | ||||
| using Exadel.Compreface.Clients.CompreFaceClient; | ||||
| using Exadel.Compreface.DTOs.FaceVerificationDTOs.FaceVerification; | ||||
| using Exadel.Compreface.Services; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.AspNetCore.Mvc.RazorPages; | ||||
| 
 | ||||
| namespace EnotaryoPH.Web.Pages | ||||
| { | ||||
|     public class BaseTakeSelfiePageModel : PageModel | ||||
|     { | ||||
|         protected readonly NotaryoDBContext _notaryoDBContext; | ||||
|         private readonly ICurrentUserService _currentUserService; | ||||
|         private readonly ICompreFaceClient _compreFaceClient; | ||||
|         private readonly IConfiguration _configuration; | ||||
| 
 | ||||
|         public BaseTakeSelfiePageModel(NotaryoDBContext notaryoDBContext, ICurrentUserService currentUserService, ICompreFaceClient compreFaceClient, IConfiguration configuration) | ||||
|         { | ||||
|             _notaryoDBContext = notaryoDBContext; | ||||
|             _currentUserService = currentUserService; | ||||
|             _compreFaceClient = compreFaceClient; | ||||
|             _configuration = configuration; | ||||
|         } | ||||
| 
 | ||||
|         protected async Task<bool> PostAsync() | ||||
|         { | ||||
|             var selfieImage = Convert.FromBase64String(SelfieBase64Image.Split(",")[1]) ?? []; | ||||
|             var identificationDocument = _notaryoDBContext.IdentificationDocuments.First(e => e.UserID == UserID); | ||||
|             var faceMatches = await VerifySelfieAsync(selfieImage, identificationDocument); | ||||
| 
 | ||||
|             var isMatchSuccess = faceMatches.Any(); | ||||
|             if (isMatchSuccess) | ||||
|             { | ||||
|                 var signatory = _notaryoDBContext.TransactionSignatories.First(x => x.UserID == UserID); | ||||
|                 signatory.Status = nameof(SignatoryStatus.FaceMatch); | ||||
|                 _notaryoDBContext.Update(signatory); | ||||
| 
 | ||||
|                 var selfieEntity = CreateOrUpdateSelfie(selfieImage); | ||||
|                 if (_notaryoDBContext.Entry(selfieEntity).State == EntityState.Detached) | ||||
|                 { | ||||
|                     _notaryoDBContext.Add(selfieEntity); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     _notaryoDBContext.Update(selfieEntity); | ||||
|                 } | ||||
|                 _notaryoDBContext.SaveChanges(); | ||||
|             } | ||||
|             return isMatchSuccess; | ||||
|         } | ||||
| 
 | ||||
|         private Transaction _transactionEntity; | ||||
| 
 | ||||
|         private int TransactionID | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 if (_transactionEntity == null) | ||||
|                 { | ||||
|                     _transactionEntity = _notaryoDBContext.Transactions.AsNoTracking().First(x => x.Transaction_UID == Transaction_UID); | ||||
|                 } | ||||
|                 return _transactionEntity?.TransactionID ?? 0; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private User _user; | ||||
| 
 | ||||
|         private int UserID | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 if (_user == null) | ||||
|                 { | ||||
|                     _user = _notaryoDBContext.Users.AsNoTracking().First(x => x.User_UID == _currentUserService.GetUser_UID()); | ||||
|                 } | ||||
|                 return _user?.UserID ?? 0; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private async Task<IEnumerable<FaceMatches>> VerifySelfieAsync(byte[] selfieImage, IdentificationDocument identificationDocument) | ||||
|         { | ||||
|             var selfiePath = Path.Combine(Path.GetTempPath(), "Selfies"); | ||||
|             if (!Directory.Exists(selfiePath)) | ||||
|             { | ||||
|                 Directory.CreateDirectory(selfiePath); | ||||
|             } | ||||
|             var identificationDocumentPath = Path.Combine(selfiePath, $"{identificationDocument.IdentificationDocument_UID}.jpg"); | ||||
| 
 | ||||
|             var apiKey = _configuration.GetValue<string>("CompreFaceConfig:APIKey"); | ||||
|             var client = _compreFaceClient.GetCompreFaceService<VerificationService>(apiKey); | ||||
|             var faceVerificationRequest = new FaceVerificationRequestByBytes() | ||||
|             { | ||||
|                 SourceImageInBytes = selfieImage, | ||||
|                 TargetImageInBytes = identificationDocument.File, | ||||
|                 DetProbThreshold = 0.81m, | ||||
|                 Limit = 1, | ||||
|                 Status = false, | ||||
|                 FacePlugins = [] | ||||
|             }; | ||||
|             var result = await client.VerifyAsync(faceVerificationRequest); | ||||
|             var faceMatches = result.Result.SelectMany(x => x.FaceMatches); | ||||
|             return faceMatches; | ||||
|         } | ||||
| 
 | ||||
|         private TransactionSelfie CreateOrUpdateSelfie(byte[] selfieImage) | ||||
|         { | ||||
|             TransactionSelfie selfieEntity; | ||||
|             if (TransactionSelfie_UID == Guid.Empty) | ||||
|             { | ||||
|                 selfieEntity = new TransactionSelfie | ||||
|                 { | ||||
|                     CreatedOn = DateTime.UtcNow, | ||||
|                     TransactionSelfie_UID = Guid.CreateVersion7(DateTime.UtcNow), | ||||
|                     UserID = UserID, | ||||
|                     TransactionID = TransactionID | ||||
|                 }; | ||||
|                 TransactionSelfie_UID = selfieEntity.TransactionSelfie_UID.Value; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 selfieEntity = _notaryoDBContext.TransactionSelfies.FirstOrDefault(e => e.TransactionSelfie_UID == TransactionSelfie_UID); | ||||
|             } | ||||
|             selfieEntity.File = Convert.FromBase64String(SelfieBase64Image.Split(",")[1]); | ||||
|             return selfieEntity; | ||||
|         } | ||||
| 
 | ||||
|         [BindProperty(SupportsGet = true)] | ||||
|         public Guid Transaction_UID { get; set; } | ||||
| 
 | ||||
|         [BindProperty] | ||||
|         public string SelfieBase64Image { get; set; } | ||||
| 
 | ||||
|         [BindProperty(SupportsGet = true)] | ||||
|         public Guid TransactionSelfie_UID { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -1,37 +1,42 @@ | ||||
| @page "{Transaction_UID:guid}" | ||||
| @model EnotaryoPH.Web.Pages.Participant.Registration.Steps.TakeSelfieModel | ||||
| @{ | ||||
| @section Head { | ||||
|     <link href="\lib\fontawesome-free-6.7.1-web\css\all.min.css" rel="stylesheet" /> | ||||
|     <style> | ||||
|         #Photo { | ||||
|             height: 500px; | ||||
|         } | ||||
|     </style> | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| <section class="my-5"> | ||||
|     <div class="container"> | ||||
|         <h1>Registration Steps</h1> | ||||
|         <div class="mt-3"> | ||||
|             <ul id="transactionwizardnav" class="nav nav-pills nav-justified d-block d-sm-flex" role="tablist"> | ||||
|                 <li class="nav-item" role="presentation"> | ||||
|                     <a class="nav-link disabled" role="tab" data-bs-toggle="pill" href="#tab-1"> | ||||
|                         <div><span class="badge rounded-pill fs-6 me-1">STEP 1</span></div><span class="text-dark step-text">Choose Identification</span> | ||||
|                     </a> | ||||
|                 </li> | ||||
|                 <li class="nav-item" role="presentation"> | ||||
|                     <a class="nav-link disabled active" role="tab" data-bs-toggle="pill" href="#" style="border-bottom-width: 3px;border-bottom-color: var(--bs-body-color);"> | ||||
|                         <div class="text-dark"><span class="badge rounded-pill fs-6 me-1">STEP 2</span></div><span class="text-dark step-text">Take Selfie</span> | ||||
|                     </a> | ||||
|                 </li> | ||||
|             </ul> | ||||
|             <div class="tab-content mt-3"></div> | ||||
|         </div> | ||||
|         <div class="row"> | ||||
|             <div class="col"> | ||||
|                 <form class="mt-3"> | ||||
|                     <div class="ratio ratio-16x9"><iframe allowfullscreen frameborder="0" src="https://www.youtube.com/embed/d1CT5wr0I1c" width="560" height="315"></iframe></div> | ||||
|                 </form> | ||||
|                 <div class="d-flex mt-3"> | ||||
|                     <a class="btn btn-secondary btn-lg me-1" role="button" href="dashboard.html"><i class="fas fa-camera me-2"></i>TAKE PICTURE</a><a class="btn btn-secondary btn-lg" role="button" href="dashboard.html"><i class="fas fa-redo-alt me-2"></i>AGAIN</a> | ||||
|                     <div class="flex-grow-1"></div><button class="btn btn-primary btn-lg" type="button" data-bs-target="#modal-message" data-bs-toggle="modal">NEXT<i class="fas fa-chevron-right ms-2"></i></button> | ||||
|                 <h1>Notaryo Steps</h1> | ||||
|                 <div class="mt-3"> | ||||
|                     @await Component.InvokeAsync("NotaryoSteps", new | ||||
|                         { | ||||
|                             NotaryoSteps = new List<NotaryoStep>() { | ||||
|                                 new NotaryoStep { Name = "Upload Identification", Step = 1 }, | ||||
|                                 new NotaryoStep { Name = "Take Selfie", Step = 2, IsActive = true } | ||||
|                             } | ||||
|                         }) | ||||
|                     <div class="tab-content mt-3"></div> | ||||
|                 </div> | ||||
|                 <form class="mt-3" method="post"> | ||||
|                     @await Component.InvokeAsync("TakeSelfieImage", new TakeSelfieImageModel { SelfieBase64Image = Model.SelfieBase64Image }) | ||||
|                 </form> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </section> | ||||
| </section> | ||||
| 
 | ||||
| 
 | ||||
| @section Scripts { | ||||
|     <script src="~/lib/jquery/dist/jquery.min.js"></script> | ||||
|     <partial name="_ValidationScriptsPartial" /> | ||||
|     <script src="\dist\js\jfa.min.js"></script> | ||||
|     <script src="~/Pages/Principal/NotaryoSteps/TakeSelfie.cshtml.js" asp-append-version="true"></script> | ||||
| } | ||||
| @ -1,15 +1,38 @@ | ||||
| using EnotaryoPH.Data; | ||||
| using EnotaryoPH.Web.Pages.Shared.Components.TakeSelfieImage; | ||||
| using Exadel.Compreface.Clients.CompreFaceClient; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.AspNetCore.Mvc.RazorPages; | ||||
| 
 | ||||
| namespace EnotaryoPH.Web.Pages.Participant.Registration.Steps | ||||
| { | ||||
|     public class TakeSelfieModel : PageModel | ||||
|     public class TakeSelfieModel : BaseTakeSelfiePageModel | ||||
|     { | ||||
|         public TakeSelfieModel(NotaryoDBContext notaryoDBContext, ICurrentUserService currentUserService, ICompreFaceClient compreFaceClient, IConfiguration configuration) | ||||
|             : base(notaryoDBContext, currentUserService, compreFaceClient, configuration) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         public void OnGet() | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         [BindProperty(SupportsGet = true)] | ||||
|         public Guid Transaction_UID { get; set; } | ||||
|         public async Task<IActionResult> OnPostAsync() | ||||
|         { | ||||
|             if (!ModelState.IsValid) | ||||
|             { | ||||
|                 return Page(); | ||||
|             } | ||||
|             var isMatchSuccess = await PostAsync(); | ||||
|             if (isMatchSuccess) | ||||
|             { | ||||
|                 return Redirect($"/Participant/VideoCall/Waiting/{Transaction_UID}"); | ||||
|             } | ||||
|             ModelState.AddModelError("", "Face Verification Failed"); | ||||
| 
 | ||||
|             return Page(); | ||||
|         } | ||||
| 
 | ||||
|         [BindProperty] | ||||
|         public TakeSelfieImageModel TakeSelfieImage { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -1,6 +1,4 @@ | ||||
| @page "{Transaction_UID:guid}" | ||||
| @using EnotaryoPH.Web.Pages.Shared.Components.NotaryoSteps | ||||
| @using EnotaryoPH.Web.Pages.Shared.Components.UploadOrChooseIdentificationDocument | ||||
| @model EnotaryoPH.Web.Pages.Participant.Registration.Steps.UploadIdentificationModel | ||||
| 
 | ||||
| @section Head { | ||||
|  | ||||
| @ -18,7 +18,6 @@ namespace EnotaryoPH.Web.Pages.Participant.Registration.Steps | ||||
|         { | ||||
|             if (UploadNewIdentification) | ||||
|             { | ||||
| 
 | ||||
|                 if (!ModelState.IsValid) | ||||
|                 { | ||||
|                     return Page(); | ||||
|  | ||||
| @ -0,0 +1,28 @@ | ||||
| @page "{Transaction_UID:guid}" | ||||
| @model EnotaryoPH.Web.Pages.Participant.VideoCall.WaitingModel | ||||
| @{ | ||||
| } | ||||
| 
 | ||||
| @section Head { | ||||
|     <link href="\lib\fontawesome-free-6.7.1-web\css\all.min.css" rel="stylesheet" /> | ||||
| } | ||||
| 
 | ||||
| <section class="d-flex align-items-start align-items-lg-start mt-5"> | ||||
|     <div class="container"> | ||||
|         <h1>Waiting for participants</h1> | ||||
|         <ul id="ParticipantsWaitStatus" class="list-unstyled"> | ||||
|             <li class="my-2"> | ||||
|                 <div><i class="far fa-check-circle text-success me-1 fa-fw" data-bs-toggle="tooltip" data-bss-tooltip title="Ready"></i><span>notary1@example.com </span><span class="ms-1">- host</span></div> | ||||
|             </li> | ||||
|             <li class="my-2"> | ||||
|                 <div><i class="far fa-check-circle text-success me-1 fa-fw" data-bs-toggle="tooltip" data-bss-tooltip title="Ready"></i><span>principal1@example.com </span><span class="ms-1">- principal</span></div> | ||||
|             </li> | ||||
|             <li class="my-2"> | ||||
|                 <div><i class="far fa-check-circle text-success me-1 fa-fw" data-bs-toggle="tooltip" data-bss-tooltip title="Ready"></i><span>principal2@example.com </span><span class="ms-1">- principal</span></div> | ||||
|             </li> | ||||
|             <li class="my-2"> | ||||
|                 <div><i class="far fa-hourglass text-muted me-1 fa-fw" data-bs-toggle="tooltip" data-bss-tooltip title="Waiting..."></i><span>witness1@example.com </span><span class="ms-1">- witness</span></div> | ||||
|             </li> | ||||
|         </ul> | ||||
|     </div> | ||||
| </section> | ||||
| @ -0,0 +1,15 @@ | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.AspNetCore.Mvc.RazorPages; | ||||
| 
 | ||||
| namespace EnotaryoPH.Web.Pages.Participant.VideoCall | ||||
| { | ||||
|     public class WaitingModel : PageModel | ||||
|     { | ||||
|         public void OnGet() | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         [BindProperty(SupportsGet = true)] | ||||
|         public Guid Transaction_UID { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -1,5 +1,4 @@ | ||||
| @page "{Transaction_UID}" | ||||
| @using EnotaryoPH.Web.Pages.Shared.Components.NotaryoSteps | ||||
| @model EnotaryoPH.Web.Pages.Principal.NotaryoSteps.ChooseNotaryModel | ||||
| @{ | ||||
| } | ||||
|  | ||||
| @ -1,29 +1,15 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using EnotaryoPH.Data; | ||||
| using EnotaryoPH.Data.Entities; | ||||
| using Exadel.Compreface.Clients.CompreFaceClient; | ||||
| using Exadel.Compreface.DTOs.FaceVerificationDTOs.FaceVerification; | ||||
| using Exadel.Compreface.Services; | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.AspNetCore.Mvc.RazorPages; | ||||
| 
 | ||||
| namespace EnotaryoPH.Web.Pages.Principal.NotaryoSteps | ||||
| { | ||||
|     public class TakeSelfieModel : PageModel | ||||
|     public class TakeSelfieModel : BaseTakeSelfiePageModel | ||||
|     { | ||||
|         private readonly NotaryoDBContext _notaryoDBContext; | ||||
|         private readonly ICurrentUserService _currentUserService; | ||||
|         private readonly ICompreFaceClient _compreFaceClient; | ||||
|         private readonly IConfiguration _configuration; | ||||
| 
 | ||||
|         public TakeSelfieModel(NotaryoDBContext notaryoDBContext, ICurrentUserService currentUserService, ICompreFaceClient compreFaceClient, IConfiguration configuration) | ||||
|         { | ||||
|             _notaryoDBContext = notaryoDBContext; | ||||
|             _currentUserService = currentUserService; | ||||
|             _compreFaceClient = compreFaceClient; | ||||
|             _configuration = configuration; | ||||
|         } | ||||
|             : base(notaryoDBContext, currentUserService, compreFaceClient, configuration) | ||||
|         { } | ||||
| 
 | ||||
|         public void OnGet() | ||||
|         { | ||||
| @ -35,86 +21,13 @@ namespace EnotaryoPH.Web.Pages.Principal.NotaryoSteps | ||||
|             { | ||||
|                 return Page(); | ||||
|             } | ||||
| 
 | ||||
|             var user = _notaryoDBContext.Users.FirstOrDefault(u => u.User_UID == _currentUserService.GetUser_UID()); | ||||
| 
 | ||||
|             TransactionSelfie selfieEntity; | ||||
|             if (TransactionSelfie_UID == Guid.Empty) | ||||
|             var isMatchSuccess = await PostAsync(); | ||||
|             if (isMatchSuccess) | ||||
|             { | ||||
|                 selfieEntity = new TransactionSelfie | ||||
|                 { | ||||
|                     CreatedOn = DateTime.UtcNow, | ||||
|                     TransactionSelfie_UID = Guid.CreateVersion7(DateTime.UtcNow), | ||||
|                     UserID = user.UserID | ||||
|                 }; | ||||
|                 TransactionSelfie_UID = selfieEntity.TransactionSelfie_UID.Value; | ||||
|                 return Redirect($"/Principal/NotaryoSteps/UploadDocument/{Transaction_UID}"); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 selfieEntity = _notaryoDBContext.TransactionSelfies.FirstOrDefault(e => e.TransactionSelfie_UID == TransactionSelfie_UID); | ||||
|             } | ||||
|             selfieEntity.File = Convert.FromBase64String(SelfieBase64Image.Split(",")[1]); | ||||
|             ModelState.AddModelError("", "Face Verification Failed"); | ||||
| 
 | ||||
|             if (selfieEntity.TransactionID > 0) | ||||
|             { | ||||
|                 _notaryoDBContext.TransactionSelfies.Update(selfieEntity); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 _notaryoDBContext.TransactionSelfies.Add(selfieEntity); | ||||
|             } | ||||
|             _notaryoDBContext.SaveChanges(); | ||||
| 
 | ||||
|             var identificationDocuments = _notaryoDBContext.IdentificationDocuments.Where(e => e.UserID == user.UserID); | ||||
|             var identificationDocument = identificationDocuments.First(); | ||||
| 
 | ||||
|             var selfiePath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "Selfies"); | ||||
|             if (!System.IO.Directory.Exists(selfiePath)) | ||||
|             { | ||||
|                 System.IO.Directory.CreateDirectory(selfiePath); | ||||
|             } | ||||
|             var identificationDocumentPath = System.IO.Path.Combine(selfiePath, $"{identificationDocument.IdentificationDocument_UID}.jpg"); | ||||
|             var selfieImagePath = System.IO.Path.Combine(selfiePath, $"{TransactionSelfie_UID}.png"); | ||||
| 
 | ||||
|             var apiKey = _configuration.GetValue<string>("CompreFaceConfig:APIKey"); | ||||
|             var client = _compreFaceClient.GetCompreFaceService<VerificationService>(apiKey); | ||||
|             var faceVerificationRequest = new FaceVerificationRequestByBytes() | ||||
|             { | ||||
|                 SourceImageInBytes = selfieEntity.File, | ||||
|                 TargetImageInBytes = identificationDocument.File, | ||||
|                 DetProbThreshold = 0.81m, | ||||
|                 Limit = 1, | ||||
|                 Status = false, | ||||
|                 FacePlugins = [] | ||||
|             }; | ||||
|             try | ||||
|             { | ||||
|                 var result = await client.VerifyAsync(faceVerificationRequest); | ||||
|                 var faceMatches = result.Result.SelectMany(x => x.FaceMatches); | ||||
| 
 | ||||
|                 if (faceMatches.Any()) | ||||
|                 { | ||||
|                     var newTransaction = new Transaction | ||||
|                     { | ||||
|                         TransactionDate = DateTime.UtcNow, | ||||
|                         CreatedOn = DateTime.UtcNow, | ||||
|                         PrincipalID = user.UserID, | ||||
|                         Status = nameof(TransactionState.New), | ||||
|                         Transaction_UID = Guid.CreateVersion7(DateTime.UtcNow) | ||||
|                     }; | ||||
|                     _notaryoDBContext.Transactions.Add(newTransaction); | ||||
|                     selfieEntity.Transaction = newTransaction; | ||||
|                     _notaryoDBContext.SaveChanges(); | ||||
| 
 | ||||
|                     return Redirect($"/Principal/NotaryoSteps/UploadDocument/{newTransaction.Transaction_UID}"); | ||||
|                 } | ||||
|                 ModelState.AddModelError("", "Face Verification Failed"); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 // capture error here | ||||
|                 ModelState.AddModelError("", ex.Message); | ||||
|             } | ||||
|             return Page(); | ||||
|         } | ||||
| 
 | ||||
| @ -156,11 +69,5 @@ namespace EnotaryoPH.Web.Pages.Principal.NotaryoSteps | ||||
| 
 | ||||
|             return File(selfie.File, "image/png"); | ||||
|         } | ||||
| 
 | ||||
|         [BindProperty, Required] | ||||
|         public string SelfieBase64Image { get; set; } | ||||
| 
 | ||||
|         [BindProperty(SupportsGet = true)] | ||||
|         public Guid TransactionSelfie_UID { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -86,7 +86,7 @@ | ||||
| 
 | ||||
|     const data = control_canvas.toDataURL('image/jpg'); | ||||
|     control_photo.setAttribute('src', data); | ||||
| 
 | ||||
|     debugger; | ||||
|     control_selfieBase64Image.value = data; | ||||
| 
 | ||||
|     _showPhoto(); | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| @page "{Transaction_UID}" | ||||
| @using EnotaryoPH.Web.Pages.Shared.Components.NotaryoSteps | ||||
| @model EnotaryoPH.Web.Pages.Principal.NotaryoSteps.UploadDocumentModel | ||||
| @model UploadDocumentModel | ||||
| @{ | ||||
| } | ||||
| 
 | ||||
| @ -25,79 +24,12 @@ | ||||
|                         }) | ||||
|                     <div class="tab-content mt-3"></div> | ||||
|                 </div> | ||||
|                 <form class="mt-5" enctype="multipart/form-data" method="post" id="UploadDocumentForm"> | ||||
|                     <div class="row mt-3"> | ||||
|                         <div class="col-12 col-lg-6"> | ||||
|                             <div class="mb-3"> | ||||
|                                 <label class="form-label">Document</label> | ||||
|                                 <input class="form-control" type="file" asp-for="DocumentFile" required /> | ||||
|                                 @Html.ValidationMessageFor(x => x.DocumentFile) | ||||
|                             </div> | ||||
|                 <form class="mt-4" id="UploadIdentificationForm" method="post" enctype="multipart/form-data"> | ||||
| 
 | ||||
|                         </div> | ||||
| 
 | ||||
|                         <div class="col"> | ||||
|                             <div class="mb-3"><label class="form-label">Document Type</label> | ||||
|                                 <select class="form-select" asp-for="DocumentType" asp-items="Model.DocumentTypes" required> | ||||
|                                     <option value="">Please choose an option</option> | ||||
|                                 </select> | ||||
|                             </div> | ||||
|                             @Html.ValidationMessageFor(x => x.DocumentType) | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     <div class="row mt-3"> | ||||
|                         <div class="col-12 col-lg-6 col-xxl-6"><label class="form-label">More than one principal?</label> | ||||
|                             <div class="input-group"> | ||||
|                                 <span class="input-group-text">Email</span> | ||||
|                                 <input class="form-control" type="email" id="NewPrincipalEmail" /> | ||||
|                                 <button class="btn btn-secondary" type="button" id="AddAdditionalPrincipalButton">ADD</button> | ||||
|                             </div> | ||||
|                             <div class="field-validation-error"> | ||||
|                                 <span id="AdditionalPrincipalsValidation"></span> | ||||
|                             </div> | ||||
|                             <ul class="mt-2" id="AdditionalPrincipalsList"> | ||||
|                             </ul> | ||||
|                         </div> | ||||
|                         <div class="col-12 col-lg-6 col-xxl-6"><label class="form-label">More than one witness?</label> | ||||
|                             <div class="input-group"> | ||||
|                                 <span class="input-group-text">Email</span> | ||||
|                                 <input class="form-control" type="email" id="NewWitnessEmail" /> | ||||
|                                 <button class="btn btn-secondary" type="button" id="AddWitnessButton">ADD</button> | ||||
|                             </div> | ||||
|                             <div class="field-validation-error"> | ||||
|                                 <span id="WitnessesValidation"></span> | ||||
|                             </div> | ||||
|                             <ul class="mt-2" id="WitnessesList"> | ||||
|                             </ul> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     <div class="row"> | ||||
|                         <div class="col"> | ||||
|                             <fieldset><label class="form-label">Would you like to record the video conference?</label> | ||||
|                                 <div class="d-flex"> | ||||
|                                     <div class="form-check me-3"><input id="formCheck-2" class="form-check-input" type="radio" name="IsRecorded" checked /><label class="form-check-label" for="formCheck-2">Yes, record the session</label></div> | ||||
|                                     <div class="form-check"><input id="formCheck-3" class="form-check-input" type="radio" name="IsRecorded" /><label class="form-check-label" for="formCheck-3">No, do not record the session</label></div> | ||||
|                                 </div> | ||||
|                             </fieldset> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     <div class="row mt-4"> | ||||
|                         <div class="col"> | ||||
|                             <div class="form-check"> | ||||
|                                 <input class="form-check-input" type="checkbox" asp-for="IsConfirmed" /> | ||||
|                                 <label class="form-check-label" asp-for="IsConfirmed">I confirm and attest under oath that I freely and voluntarily executed the document; that I read and understood the same; and that the contents of the document are true and correct.</label> | ||||
|                             </div> | ||||
|                             @Html.ValidationMessageFor(x => x.IsConfirmed) | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     <input type="hidden" asp-for="CurrentUserEmail" /> | ||||
|                     <input type="hidden" asp-for="ParticipantsJson" /> | ||||
|                     <div class="d-flex"> | ||||
|                     @await Component.InvokeAsync("UploadOrChooseIdentificationDocument", Model.Transaction_UID) | ||||
|                     <div class="d-flex mt-3"> | ||||
|                         <div class="flex-grow-1"></div> | ||||
|                         <button type="submit" class="btn btn-primary btn-lg" id="NextButton"> | ||||
|                             <span>NEXT</span> | ||||
|                             <i class="fas fa-chevron-right ms-2"></i> | ||||
|                         </button> | ||||
|                         <button class="btn btn-primary btn-lg wizard__nextbutton" type="submit">NEXT<i class="fas fa-chevron-right ms-2"></i></button> | ||||
|                     </div> | ||||
|                 </form> | ||||
|             </div> | ||||
|  | ||||
| @ -1,124 +1,67 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using System.Text.Json; | ||||
| using Coravel.Queuing.Interfaces; | ||||
| using EnotaryoPH.Data; | ||||
| using EnotaryoPH.Data.Entities; | ||||
| using EnotaryoPH.Web.Common.Jobs; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.AspNetCore.Mvc.RazorPages; | ||||
| using Microsoft.AspNetCore.Mvc.Rendering; | ||||
| 
 | ||||
| namespace EnotaryoPH.Web.Pages.Principal.NotaryoSteps | ||||
| { | ||||
|     public class UploadDocumentModel : PageModel | ||||
|     public class UploadDocumentModel : BaseIdentificationDocumentPageModel | ||||
|     { | ||||
|         private readonly ICurrentUserService _currentUserService; | ||||
|         private readonly NotaryoDBContext _notaryoDBContext; | ||||
|         private readonly IQueue _queue; | ||||
| 
 | ||||
|         public UploadDocumentModel(NotaryoDBContext notaryoDBContext, ICurrentUserService currentUserService, IQueue queue) | ||||
|             : base(notaryoDBContext) | ||||
|         { | ||||
|             _notaryoDBContext = notaryoDBContext; | ||||
|             _currentUserService = currentUserService; | ||||
|             _queue = queue; | ||||
|         } | ||||
| 
 | ||||
|         public IActionResult OnGet(Guid transaction_UID) | ||||
|         public void OnGet() | ||||
|         { | ||||
|             var _transaction = _notaryoDBContext.Transactions | ||||
|                 .Include(t => t.TransactionDocument) | ||||
|                 .Include(t => t.TransactionSignatories) | ||||
|                 .AsNoTracking().FirstOrDefault(e => e.Transaction_UID == transaction_UID); | ||||
|             DocumentTypes = GetDocumentTypes(); | ||||
|             CurrentUserEmail = _currentUserService.GetEmail(); | ||||
|             var signatories = _transaction.TransactionSignatories.Select(ts => new SignatoryViewModel | ||||
|             { | ||||
|                 Email = ts.Email, | ||||
|                 Type = ts.Type, | ||||
|                 UID = ts.TransactionSignatory_UID.GetValueOrDefault() | ||||
|             }).ToList(); | ||||
|             ParticipantsJson = JsonSerializer.Serialize(signatories); | ||||
| 
 | ||||
|             return Page(); | ||||
|         } | ||||
| 
 | ||||
|         public async Task<IActionResult> OnPostAsync() | ||||
|         { | ||||
|             if (!ModelState.IsValid) | ||||
|             if (UploadNewIdentification) | ||||
|             { | ||||
|                 DocumentTypes = GetDocumentTypes(); | ||||
|                 return Page(); | ||||
|             } | ||||
| 
 | ||||
|             if (!IsConfirmed) | ||||
|             { | ||||
|                 ModelState.AddModelError(nameof(IsConfirmed), "You must tick this box to continue."); | ||||
|                 DocumentTypes = GetDocumentTypes(); | ||||
|                 return Page(); | ||||
|             } | ||||
| 
 | ||||
|             var transaction = _notaryoDBContext.Transactions | ||||
|                 .Include(t => t.TransactionSignatories) | ||||
|                 .Include(t => t.TransactionDocument) | ||||
|                 .FirstOrDefault(t => t.Transaction_UID == Transaction_UID); | ||||
|             if (transaction == null) | ||||
|             { | ||||
|                 return NotFound(); | ||||
|             } | ||||
| 
 | ||||
|             transaction.Status = nameof(TransactionState.DocumentUploaded); | ||||
|             transaction.IsRecorded = IsVideoConferenceRecorded; | ||||
| 
 | ||||
|             if (transaction.TransactionDocument == null) | ||||
|             { | ||||
|                 transaction.TransactionDocument = new TransactionDocument | ||||
|                 if (!ModelState.IsValid) | ||||
|                 { | ||||
|                     CreatedOn = DateTime.UtcNow, | ||||
|                     Transaction = transaction, | ||||
|                     TransactionDocument_UID = Guid.CreateVersion7(DateTime.UtcNow), | ||||
|                 }; | ||||
|                     return Page(); | ||||
|                 } | ||||
| 
 | ||||
|                 CreateIdentificationDocument(_currentUserService.GetUser_UID()); | ||||
|             } | ||||
| 
 | ||||
|             var stream = new MemoryStream((int)DocumentFile.Length); | ||||
|             DocumentFile.CopyTo(stream); | ||||
|             transaction.TransactionDocument.File = stream.ToArray(); | ||||
|             transaction.TransactionDocument.Filename = DocumentFile.FileName; | ||||
|             transaction.TransactionDocument.DocumentType = DocumentType; | ||||
|             transaction.TransactionDocument.UploadedOn = DateTime.UtcNow; | ||||
| 
 | ||||
|             var participants = JsonSerializer.Deserialize<List<SignatoryViewModel>>(ParticipantsJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }) ?? []; | ||||
|             transaction.TransactionSignatories = participants.Select(p => new TransactionSignatory | ||||
|             { | ||||
|                 CreatedOn = DateTime.UtcNow, | ||||
|                 Email = p.Email, | ||||
|                 Status = nameof(SignatoryStatus.New), | ||||
|                 TransactionSignatory_UID = Guid.CreateVersion7(DateTime.UtcNow), | ||||
|                 Type = p.Type, | ||||
|                 InvitationCode = Guid.CreateVersion7(DateTime.UtcNow).ToString() | ||||
|             }).ToList(); | ||||
| 
 | ||||
|             _notaryoDBContext.Update(transaction); | ||||
|             _notaryoDBContext.SaveChanges(); | ||||
| 
 | ||||
|             foreach (var signatory in transaction.TransactionSignatories) | ||||
|             { | ||||
|                 _queue.QueueInvocableWithPayload<SignatoryInvitationInvocable, Guid>(signatory.TransactionSignatory_UID.GetValueOrDefault()); | ||||
|             } | ||||
|             SendSignatoryInvitations(); | ||||
| 
 | ||||
|             return Redirect($"/Principal/NotaryoSteps/ChooseNotary/{Transaction_UID}"); | ||||
|         } | ||||
| 
 | ||||
|         private List<SelectListItem> GetDocumentTypes() | ||||
|         private void SendSignatoryInvitations() | ||||
|         { | ||||
|             var lookupIdentificationTypes = _notaryoDBContext.LookupData.AsNoTracking().Include(e => e.LookupDataValues).FirstOrDefault(e => e.Name == "Document Types"); | ||||
|             return lookupIdentificationTypes.LookupDataValues | ||||
|                 .ConvertAll(m => new SelectListItem | ||||
|                 { | ||||
|                     Text = m.Title.DefaultIfEmpty(m.Value), | ||||
|                     Value = m.Value | ||||
|                 }); | ||||
|             var transaction = _notaryoDBContext.Transactions | ||||
|                 .Include(e => e.TransactionSignatories) | ||||
|                 .FirstOrDefault(e => e.Transaction_UID == Transaction_UID); | ||||
|             foreach (var signatory in transaction.TransactionSignatories) | ||||
|             { | ||||
|                 _queue.QueueInvocableWithPayload<SignatoryInvitationInvocable, Guid>(signatory.TransactionSignatory_UID.GetValueOrDefault()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         //private List<SelectListItem> GetDocumentTypes() | ||||
|         //{ | ||||
|         //    var lookupIdentificationTypes = _notaryoDBContext.LookupData.AsNoTracking().Include(e => e.LookupDataValues).FirstOrDefault(e => e.Name == "Document Types"); | ||||
|         //    return lookupIdentificationTypes.LookupDataValues | ||||
|         //        .ConvertAll(m => new SelectListItem | ||||
|         //        { | ||||
|         //            Text = m.Title.DefaultIfEmpty(m.Value), | ||||
|         //            Value = m.Value | ||||
|         //        }); | ||||
|         //} | ||||
| 
 | ||||
|         public string CurrentUserEmail { get; private set; } | ||||
| 
 | ||||
|         [BindProperty, Required] | ||||
|  | ||||
| @ -1,9 +1,5 @@ | ||||
| @page | ||||
| @using EnotaryoPH.Web.Pages.Shared.Components.NotaryoSteps | ||||
| 
 | ||||
| @model UploadIdentificationModel | ||||
| @{ | ||||
| } | ||||
| 
 | ||||
| @section Head { | ||||
|     <link href="\lib\fontawesome-free-6.7.1-web\css\all.min.css" rel="stylesheet" /> | ||||
| @ -24,61 +20,7 @@ | ||||
|                     <div class="tab-content mt-3"></div> | ||||
|                 </div> | ||||
|                 <form class="mt-4" id="UploadIdentificationForm" method="post" enctype="multipart/form-data"> | ||||
| 
 | ||||
|                     @if (Model.ExistingIdentificationDocuments.Count > 0) | ||||
|                     { | ||||
|                         <div class="row mt-4 mb-3"> | ||||
|                             <div class="col"> | ||||
|                                 <div class="d-flex"> | ||||
|                                     <div class="form-check me-2"><input asp-for="UploadNewIdentification" id="UploadNewIdentificationYes" class="form-check-input" type="radio" value="true" /> <label class="form-check-label me-3" for="UploadNewIdentificationYes">Upload a new Identification Document</label></div> | ||||
|                                     <div class="form-check me-2"><input asp-for="UploadNewIdentification" id="UploadNewIdentificationNah" class="form-check-input" type="radio" value="false" /><label class="form-check-label me-3" for="UploadNewIdentificationNah">Use any of the existing Identification Documents</label></div> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     } | ||||
|                     else { | ||||
|                         <input type="hidden" asp-for="UploadNewIdentification" /> | ||||
|                     } | ||||
| 
 | ||||
|                     <div id="NewIdentificationDocumentContainer"> | ||||
|                         @Html.EditorFor(m => m.NewIdentificationDocument) | ||||
|                     </div> | ||||
|                     @if (Model.ExistingIdentificationDocuments.Count > 0) | ||||
|                     { | ||||
|                         <div id="ExistingDocumentsContainer"> | ||||
|                             <div class="row mt-4"> | ||||
|                                 <div class="col"> | ||||
|                                     <h4>Existing Identification Documents</h4> | ||||
|                                     <div class="table-responsive"> | ||||
|                                         <table class="table"> | ||||
|                                             <thead> | ||||
|                                                 <tr> | ||||
|                                                     <th>Identification Document Type</th> | ||||
|                                                     <th>Expiration</th> | ||||
|                                                     <th></th> | ||||
|                                                 </tr> | ||||
|                                             </thead> | ||||
|                                             <tbody> | ||||
|                                                 @foreach(var id in Model.ExistingIdentificationDocuments) | ||||
|                                                 { | ||||
|                                                     <tr class="existingdocument__row"> | ||||
|                                                         <td>@id.IdentificationType</td> | ||||
|                                                         <td>@id.ExpirationDate.Value.ToShortDateString()</td> | ||||
|                                                         <td> | ||||
|                                                             <button class="btn btn-danger btn-sm identificationdocument__delete__button" type="button" data-uid="@id.IdentificationDocument_UID"> | ||||
|                                                                 <i class="fas fa-times me-1"></i> | ||||
|                                                                 <span class="d-none d-md-inline-block">Delete</span> | ||||
|                                                             </button> | ||||
|                                                         </td> | ||||
|                                                     </tr> | ||||
|                                                 } | ||||
|                                             </tbody> | ||||
|                                         </table> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     } | ||||
|                     @await Component.InvokeAsync("UploadOrChooseIdentificationDocument", Model.Transaction_UID) | ||||
|                     <div class="d-flex mt-3"> | ||||
|                         <div class="flex-grow-1"></div> | ||||
|                         <button class="btn btn-primary btn-lg wizard__nextbutton" type="submit">NEXT<i class="fas fa-chevron-right ms-2"></i></button> | ||||
|  | ||||
| @ -81,5 +81,7 @@ namespace EnotaryoPH.Web.Pages.Principal.NotaryoSteps | ||||
| 
 | ||||
|         [BindProperty] | ||||
|         public bool UploadNewIdentification { get; set; } | ||||
| 
 | ||||
|         public Guid Transaction_UID { get; private set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| @model TakeSelfieImageModel | ||||
| 
 | ||||
| @Html.ValidationSummary() | ||||
| 
 | ||||
| <div id="VideoContainer" class="bg-light"> | ||||
|     <video id="Video" height="500" class="d-block mx-auto">Video stream not available.</video> | ||||
| </div> | ||||
| 
 | ||||
| <div class="d-none"> | ||||
|     <canvas id="Canvas"></canvas> | ||||
| </div> | ||||
| 
 | ||||
| <div id="PhotoContainer" class="photo__container"> | ||||
|     <div class="bg-light"> | ||||
|         <img id="Photo" class="d-block mx-auto img img-fluid" alt="The screen capture will appear in this box."> | ||||
|     </div> | ||||
| </div> | ||||
| 
 | ||||
| <div class="d-flex mt-3"> | ||||
|     <button type="button" class="btn btn-secondary btn-lg me-1" id="TakePictureButton"><i class="fas fa-camera me-2"></i>TAKE PICTURE</button> | ||||
|     <button type="button" class="btn btn-secondary btn-lg" id="AgainButton"><i class="fas fa-redo-alt me-2"></i>AGAIN</button> | ||||
|     <div class="flex-grow-1"></div> | ||||
|     <button type="submit" class="btn btn-primary btn-lg" id="NextButton">NEXT<i class="fas fa-chevron-right ms-2"></i></button> | ||||
| </div> | ||||
| 
 | ||||
| <input type="hidden" asp-for="@Model.SelfieBase64Image" /> | ||||
| @ -0,0 +1,9 @@ | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| 
 | ||||
| namespace EnotaryoPH.Web.Pages.Shared.Components.TakeSelfieImage | ||||
| { | ||||
|     public class TakeSelfieImageViewComponent : ViewComponent | ||||
|     { | ||||
|         public async Task<IViewComponentResult> InvokeAsync(TakeSelfieImageModel model) => View(model); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,10 @@ | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| 
 | ||||
| namespace EnotaryoPH.Web.Pages.Shared.Components.TakeSelfieImage | ||||
| { | ||||
|     public class TakeSelfieImageModel | ||||
|     { | ||||
|         [BindProperty] | ||||
|         public string SelfieBase64Image { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -1,5 +1,8 @@ | ||||
| @using EnotaryoPH.Web | ||||
| @using Microsoft.AspNetCore.Http.Extensions | ||||
| @using EnotaryoPH.Web.Pages.Shared.Components.NotaryoSteps | ||||
| @using EnotaryoPH.Web.Pages.Shared.Components.UploadOrChooseIdentificationDocument | ||||
| @using EnotaryoPH.Web.Pages.Shared.Components.TakeSelfieImage | ||||
| @namespace EnotaryoPH.Web.Pages | ||||
| @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers | ||||
| @addTagHelper *, EnotaryoPH | ||||
| @ -24,6 +24,7 @@ namespace EnotaryoPH.Web | ||||
|             { | ||||
|                 options.Conventions.AuthorizeFolder("/Principal", "PrincipalPolicy"); | ||||
|                 options.Conventions.AuthorizeFolder("/Participant/Registration/Steps", "ParticipantPolicy"); | ||||
|                 options.Conventions.AuthorizeFolder("/Participant/VideoCall"); | ||||
|             }); | ||||
| #if DEBUG | ||||
|             razorBuilder.AddRazorRuntimeCompilation(); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user