Send email after Upload document
This commit is contained in:
		
							parent
							
								
									f29e5b3c17
								
							
						
					
					
						commit
						4d5a69ad2f
					
				| @ -0,0 +1,45 @@ | ||||
| using Coravel.Invocable; | ||||
| using Coravel.Mailer.Mail.Interfaces; | ||||
| using EnotaryoPH.Data; | ||||
| using EnotaryoPH.Web.Common.Models; | ||||
| using EnotaryoPH.Web.Mailables; | ||||
| 
 | ||||
| namespace EnotaryoPH.Web.Common.Jobs | ||||
| { | ||||
|     public class SignatoryInvitationInvocable : IInvocable, IInvocableWithPayload<Guid> | ||||
|     { | ||||
|         private readonly IMailer _mailer; | ||||
|         private readonly NotaryoDBContext _notaryoDBContext; | ||||
|         private readonly Settings _settings; | ||||
| 
 | ||||
|         public SignatoryInvitationInvocable(IMailer mailer, NotaryoDBContext notaryoDBContext, Settings settings) | ||||
|         { | ||||
|             _mailer = mailer; | ||||
|             _notaryoDBContext = notaryoDBContext; | ||||
|             _settings = settings; | ||||
|         } | ||||
| 
 | ||||
|         public Guid Payload { get; set; } | ||||
| 
 | ||||
|         public async Task Invoke() | ||||
|         { | ||||
|             var signatory = _notaryoDBContext | ||||
|                 .TransactionSignatories | ||||
|                 .Include(e => e.Transaction) | ||||
|                 .ThenInclude(t => t.Principal) | ||||
|                 .FirstOrDefault(e => e.TransactionSignatory_UID == Payload); | ||||
| 
 | ||||
|             var user = signatory.Transaction.Principal; | ||||
|             var name = $"{user.Firstname} {user.Lastname}".NullIfWhiteSpace() ?? user.Email; | ||||
|             var invitationCode = new Guid(signatory.InvitationCode).ToBase64String(); | ||||
| 
 | ||||
|             await _mailer.SendAsync(new SignatoryInvitationMailable(new SignatoryViewModel | ||||
|             { | ||||
|                 Email = signatory.Email, | ||||
|                 InvitationURL = $"{_settings.BaseUrl}/Principal/Registration/{invitationCode}", | ||||
|                 MainPrincipalName = name, | ||||
|                 SignatoryType = signatory.Type | ||||
|             })); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										7
									
								
								EnotaryoPH/EnotaryoPH.Web/Common/Models/Settings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								EnotaryoPH/EnotaryoPH.Web/Common/Models/Settings.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| namespace EnotaryoPH.Web.Common.Models | ||||
| { | ||||
|     public class Settings | ||||
|     { | ||||
|         public string BaseUrl { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| using Coravel.Mailer.Mail; | ||||
| 
 | ||||
| namespace EnotaryoPH.Web.Mailables | ||||
| { | ||||
|     public class SignatoryInvitationMailable : Mailable<SignatoryViewModel> | ||||
|     { | ||||
|         private readonly SignatoryViewModel _model; | ||||
| 
 | ||||
|         public SignatoryInvitationMailable(SignatoryViewModel model) => _model = model; | ||||
| 
 | ||||
|         public override void Build() => this | ||||
|             .To(_model.Email) | ||||
|             .From("noreply@enotaryoph.com") | ||||
|             .View("~/Views/Mail/SignatoryInvitation.cshtml", _model); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										10
									
								
								EnotaryoPH/EnotaryoPH.Web/Mailables/SignatoryViewModel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								EnotaryoPH/EnotaryoPH.Web/Mailables/SignatoryViewModel.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| namespace EnotaryoPH.Web.Mailables | ||||
| { | ||||
|     public class SignatoryViewModel | ||||
|     { | ||||
|         public string Email { get; set; } | ||||
|         public string InvitationURL { get; set; } | ||||
|         public string MainPrincipalName { get; set; } | ||||
|         public string SignatoryType { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,4 @@ | ||||
| @page "{InvitationCode?}" | ||||
| @model EnotaryoPH.Web.Pages.Participant.Registration.IndexModel | ||||
| @{ | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.AspNetCore.Mvc.RazorPages; | ||||
| 
 | ||||
| namespace EnotaryoPH.Web.Pages.Participant.Registration | ||||
| { | ||||
|     public class IndexModel : PageModel | ||||
|     { | ||||
|         public IActionResult OnGet() => Page(); | ||||
| 
 | ||||
|         [BindProperty(SupportsGet = true)] | ||||
|         public string InvitationCode { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,94 @@ | ||||
| @page "{Transaction_UID}" | ||||
| @using EnotaryoPH.Web.Pages.Shared.Components.NotaryoSteps | ||||
| @model EnotaryoPH.Web.Pages.Principal.NotaryoSteps.ChooseNotaryModel | ||||
| @{ | ||||
| } | ||||
| 
 | ||||
| @section Head { | ||||
|     <link href="\lib\fontawesome-free-6.7.1-web\css\all.min.css" rel="stylesheet" /> | ||||
| } | ||||
| 
 | ||||
| <section class="my-5"> | ||||
|     <div class="container"> | ||||
|         <div class="row"> | ||||
|             <div class="col"> | ||||
|                 <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 }, | ||||
|                             new NotaryoStep { Name = "Upload Document", Step = 3 }, | ||||
|                             new NotaryoStep { Name = "Choose Notary", Step = 4, IsActive = true }, | ||||
|                         } | ||||
|                     }) | ||||
| 
 | ||||
|                     <form class="mt-3"> | ||||
|                         <div class="row my-4"> | ||||
|                             <div class="col"> | ||||
|                                 <div class="d-flex"> | ||||
|                                     <div class="form-check me-3"><input id="formCheck-3" class="form-check-input" type="radio" name="NewOrOld" checked /><label class="form-check-label" for="formCheck-3">I will choose a Notary Public myself</label></div> | ||||
|                                     <div class="form-check"><input id="formCheck-4" class="form-check-input" type="radio" name="NewOrOld" /><label class="form-check-label" for="formCheck-4">Automatically choose an immediately available Notary Public</label></div> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <div class="row mt-3"> | ||||
|                             <div class="col-12 col-lg-8"> | ||||
|                                 <div class="input-group"><input class="form-control" type="text" placeholder="Find an Attorney" /><button class="btn btn-outline-secondary" type="button"><i class="fas fa-search"></i></button></div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <div class="row mt-3"> | ||||
|                             <div class="col"> | ||||
|                                 <div class="table-responsive"> | ||||
|                                     <table class="table"> | ||||
|                                         <thead> | ||||
|                                             <tr> | ||||
|                                                 <th>Name of Attorney</th> | ||||
|                                                 <th>Office Location</th> | ||||
|                                                 <th></th> | ||||
|                                             </tr> | ||||
|                                         </thead> | ||||
|                                         <tbody> | ||||
|                                             <tr> | ||||
|                                                 <td>Athony Co</td> | ||||
|                                                 <td>123 Fictional Road, Manila</td> | ||||
|                                                 <td class="text-end"><button class="btn btn-secondary btn-sm" type="button">Select</button></td> | ||||
|                                             </tr> | ||||
|                                             <tr> | ||||
|                                                 <td>Jude Romano</td> | ||||
|                                                 <td>342 Fake Road, Marikina</td> | ||||
|                                                 <td class="text-end"><button class="btn btn-secondary btn-sm" type="button">Select</button></td> | ||||
|                                             </tr> | ||||
|                                         </tbody> | ||||
|                                     </table> | ||||
|                                 </div> | ||||
|                                 <nav> | ||||
|                                     <ul class="pagination"> | ||||
|                                         <li class="page-item"><a class="page-link" aria-label="Previous" href="#"><span aria-hidden="true">«</span></a></li> | ||||
|                                         <li class="page-item"><a class="page-link" href="#">1</a></li> | ||||
|                                         <li class="page-item"><a class="page-link" href="#">2</a></li> | ||||
|                                         <li class="page-item"><a class="page-link" href="#">3</a></li> | ||||
|                                         <li class="page-item"><a class="page-link" aria-label="Next" href="#"><span aria-hidden="true">»</span></a></li> | ||||
|                                     </ul> | ||||
|                                 </nav> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <div class="d-flex"> | ||||
|                             <div class="flex-grow-1"></div> | ||||
|                             <button class="btn btn-primary btn-lg" type="submit"> | ||||
|                                 SUBMIT | ||||
|                                 <i class="fas fa-flag-checkered ms-2"></i> | ||||
|                             </button> | ||||
|                         </div> | ||||
|                     </form> | ||||
|                 </div> | ||||
| 
 | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </section> | ||||
| 
 | ||||
| 
 | ||||
| @section Scripts { | ||||
|     <script src="/dist/js/jfa.min.js"></script> | ||||
| } | ||||
| @ -0,0 +1,15 @@ | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.AspNetCore.Mvc.RazorPages; | ||||
| 
 | ||||
| namespace EnotaryoPH.Web.Pages.Principal.NotaryoSteps | ||||
| { | ||||
|     public class ChooseNotaryModel : PageModel | ||||
|     { | ||||
|         public void OnGet() | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         [BindProperty(SupportsGet = true)] | ||||
|         public Guid Transaction_UID { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| 
 | ||||
| namespace EnotaryoPH.Web.Pages.Principal.NotaryoSteps | ||||
| { | ||||
|     public class SignatoryViewModel | ||||
|     { | ||||
|         [EmailAddress, Required, BindProperty] | ||||
|         public string Email { get; set; } | ||||
| 
 | ||||
|         [Required, BindProperty] | ||||
|         public string Type { get; set; } | ||||
| 
 | ||||
|         public Guid UID { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -4,6 +4,9 @@ | ||||
| @{ | ||||
| } | ||||
| 
 | ||||
| @section Head { | ||||
|     <link href="\lib\fontawesome-free-6.7.1-web\css\all.min.css" rel="stylesheet" /> | ||||
| } | ||||
| 
 | ||||
| <section class="my-5"> | ||||
|     <div class="container"> | ||||
| @ -22,7 +25,99 @@ | ||||
|                         }) | ||||
|                     <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> | ||||
| 
 | ||||
|                         </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"> | ||||
|                         <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> | ||||
|                     </div> | ||||
|                 </form> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </section> | ||||
| </section> | ||||
| 
 | ||||
| <template id="ParticipantItemTemplate"> | ||||
|     <li class="participantitem__li"> | ||||
|         <div class="d-flex"> | ||||
|             <span class="participantitem__email">mail@example.com</span> | ||||
|             <div class="flex-grow-1"></div> | ||||
|             <a role="button" style="color: var(--bs-danger);" class="participantitem__delete"> | ||||
|                 <i class="fas fa-times" data-bs-toggle="tooltip" data-bss-tooltip data-bs-placement="left" title="delete signatory"></i> | ||||
|             </a> | ||||
|         </div> | ||||
|     </li> | ||||
| </template> | ||||
| 
 | ||||
| @section Scripts { | ||||
|     <script src="/dist/js/jfa.min.js"></script> | ||||
|     <script src="~/Pages/Principal/NotaryoSteps/UploadDocument.cshtml.js" asp-append-version="true"></script> | ||||
| } | ||||
| @ -1,15 +1,144 @@ | ||||
| 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 void OnGet() | ||||
|         private readonly ICurrentUserService _currentUserService; | ||||
|         private readonly NotaryoDBContext _notaryoDBContext; | ||||
|         private readonly IQueue _queue; | ||||
| 
 | ||||
|         public UploadDocumentModel(NotaryoDBContext notaryoDBContext, ICurrentUserService currentUserService, IQueue queue) | ||||
|         { | ||||
|             _notaryoDBContext = notaryoDBContext; | ||||
|             _currentUserService = currentUserService; | ||||
|             _queue = queue; | ||||
|         } | ||||
| 
 | ||||
|         public IActionResult OnGet(Guid transaction_UID) | ||||
|         { | ||||
|             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) | ||||
|             { | ||||
|                 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(TransactionStatus.DocumentUploaded); | ||||
|             transaction.IsRecorded = IsVideoConferenceRecorded; | ||||
| 
 | ||||
|             if (transaction.TransactionDocument == null) | ||||
|             { | ||||
|                 transaction.TransactionDocument = new TransactionDocument | ||||
|                 { | ||||
|                     CreatedOn = DateTime.UtcNow, | ||||
|                     Transaction = transaction, | ||||
|                     TransactionDocument_UID = Guid.CreateVersion7(DateTime.UtcNow), | ||||
|                 }; | ||||
|             } | ||||
| 
 | ||||
|             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()); | ||||
|             } | ||||
| 
 | ||||
|             return Redirect($"/Principal/NotaryoSteps/ChooseNotary/{Transaction_UID}"); | ||||
|         } | ||||
| 
 | ||||
|         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] | ||||
|         public IFormFile DocumentFile { get; set; } | ||||
| 
 | ||||
|         [BindProperty, Required] | ||||
|         public string DocumentType { get; set; } | ||||
| 
 | ||||
|         public List<SelectListItem> DocumentTypes { get; set; } | ||||
| 
 | ||||
|         [BindProperty] | ||||
|         public bool IsConfirmed { get; set; } | ||||
| 
 | ||||
|         [BindProperty] | ||||
|         public bool IsVideoConferenceRecorded { get; set; } | ||||
| 
 | ||||
|         [BindProperty] | ||||
|         public string ParticipantsJson { get; set; } = "[]"; | ||||
| 
 | ||||
|         [BindProperty(SupportsGet = true)] | ||||
|         public Guid Transaction_UID { get; set; } | ||||
|     } | ||||
| } | ||||
| } | ||||
| @ -0,0 +1,97 @@ | ||||
| "use strict"; | ||||
| (function () { | ||||
|   let | ||||
|     control_addAdditionalPrincipalButton = document.getElementById("AddAdditionalPrincipalButton"), | ||||
|     control_additionalPrincipalsList = document.getElementById("AdditionalPrincipalsList"), | ||||
|     control_additionalPrincipalsValidation = document.getElementById("AdditionalPrincipalsValidation"), | ||||
|     control_addWitnessButton = document.getElementById("AddWitnessButton"), | ||||
|     control_currentUserEmail = document.getElementById("CurrentUserEmail"), | ||||
|     control_documentFile = document.getElementById("DocumentFile"), | ||||
|     control_isConfirmed = document.getElementById("IsConfirmed"), | ||||
|     control_newPrincipalEmail = document.getElementById("NewPrincipalEmail"), | ||||
|     control_newWitnessEmail = document.getElementById("NewWitnessEmail"), | ||||
|     control_participantItemTemplate = document.getElementById("ParticipantItemTemplate"), | ||||
|     control_participantsJson = document.getElementById("ParticipantsJson"), | ||||
|     control_uploadDocumentForm = document.getElementById("UploadDocumentForm"), | ||||
|     control_witnessesList = document.getElementById("WitnessesList"), | ||||
|     control_witnessesValidation = document.getElementById("WitnessesValidation"); | ||||
| 
 | ||||
|   let signatories = []; | ||||
| 
 | ||||
|   function _addSignatory(control_email, signatoryType, control_signatoryList) { | ||||
|     let email = control_email.value; | ||||
| 
 | ||||
|     if (!email) { | ||||
|       return "Email is required."; | ||||
|     } | ||||
| 
 | ||||
|     if (control_currentUserEmail.value == email) { | ||||
|       return "Email must not be the same as the currently logged in user."; | ||||
|     } | ||||
| 
 | ||||
|     if (signatories.find(x => x.email === email)) { | ||||
|       return "Email already exists."; | ||||
|     } | ||||
| 
 | ||||
|     signatories.push({ | ||||
|       email: email, | ||||
|       type: signatoryType | ||||
|     }); | ||||
| 
 | ||||
|     let liTemplate = control_participantItemTemplate.cloneNode(true); | ||||
|     let li = liTemplate.content; | ||||
|     li.querySelector(".participantitem__li").setAttribute("data-uid", ""); | ||||
|     li.querySelector(".participantitem__email").textContent = email; | ||||
| 
 | ||||
|     let deleteButton = li.querySelector(".participantitem__delete"); | ||||
|     deleteButton.addEventListener("click", _deleteParticipant); | ||||
|     deleteButton.control_signatoryList = control_signatoryList; | ||||
|     control_signatoryList.appendChild(li); | ||||
| 
 | ||||
|     control_email.value = ""; | ||||
|   } | ||||
| 
 | ||||
|   function _deleteParticipant(sender) { | ||||
|     let li = sender.target.closest("li"); | ||||
|     let email = li.querySelector(".participantitem__email").textContent; | ||||
|     jfa.components.dialog.confirm({ | ||||
|       message: "Are you sure you want to remove this participant?", | ||||
|       callbackyes: function () { | ||||
|         signatories = signatories.filter(m => m.email != email); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   function _bindEvents() { | ||||
|     control_addAdditionalPrincipalButton.addEventListener("click", () => { | ||||
|       let err = _addSignatory(control_newPrincipalEmail, 'Principal', control_additionalPrincipalsList); | ||||
|       control_additionalPrincipalsValidation.textContent = err; | ||||
|     }); | ||||
|     control_addWitnessButton.addEventListener("click", () => { | ||||
|       let err = _addSignatory(control_newWitnessEmail, 'Witness', control_witnessesList); | ||||
|       control_witnessesValidation.textContent = err; | ||||
|     }); | ||||
|     control_uploadDocumentForm.addEventListener("submit", _onSubmitForm); | ||||
|   } | ||||
| 
 | ||||
|   function _init() { | ||||
|     signatories = JSON.parse(control_participantsJson.value)?.map(p => ({ | ||||
|       email: p.Email, | ||||
|       type: p.Type, | ||||
|       uid: p.UID | ||||
|     })); | ||||
| 
 | ||||
|     _bindEvents(); | ||||
|   } | ||||
| 
 | ||||
|   function _onSubmitForm(event) { | ||||
|     control_participantsJson.value = JSON.stringify(signatories); | ||||
|     if (control_documentFile.files.length === 0 || !control_isConfirmed.checked) { | ||||
|       event.preventDefault(); | ||||
|       return false; | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   _init(); | ||||
| })(); | ||||
| @ -1,5 +1,8 @@ | ||||
| using System.Security.Principal; | ||||
| using Coravel; | ||||
| using EnotaryoPH.Data; | ||||
| using EnotaryoPH.Web.Common.Jobs; | ||||
| using EnotaryoPH.Web.Common.Models; | ||||
| using Exadel.Compreface.Clients.CompreFaceClient; | ||||
| using Microsoft.AspNetCore.Authentication.Cookies; | ||||
| 
 | ||||
| @ -21,20 +24,28 @@ namespace EnotaryoPH.Web | ||||
|             razorBuilder.AddRazorRuntimeCompilation(); | ||||
| #endif | ||||
| 
 | ||||
|             var config = builder.Configuration; | ||||
| 
 | ||||
|             builder.Services.AddDbContext<NotaryoDBContext>(); | ||||
|             builder.Services.AddHttpContextAccessor(); | ||||
|             builder.Services.AddSession(options => options.IdleTimeout = TimeSpan.FromMinutes(120)); | ||||
|             builder.Services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>()?.HttpContext?.User); | ||||
|             builder.Services.AddTransient<Settings>(provider => new Settings | ||||
|             { | ||||
|                 BaseUrl = config.GetValue<string>("BaseUrl") ?? "" | ||||
|             }); | ||||
| 
 | ||||
|             builder.Services.AddTransient<IPasswordService, PasswordService>(); | ||||
|             builder.Services.AddTransient<ICurrentUserService, CurrentUserService>(); | ||||
|             builder.Services.AddTransient<ICompreFaceClient>(provider => | ||||
|             { | ||||
|                 var config = provider.GetRequiredService<IConfiguration>(); | ||||
|                 var host = config.GetValue<string>("CompreFaceConfig:Host"); | ||||
|                 var port = config.GetValue<string>("CompreFaceConfig:Port"); | ||||
|                 return new CompreFaceClient(host, port); | ||||
|             }); | ||||
|             builder.Services.AddQueue(); | ||||
|             builder.Services.AddMailer(config); | ||||
|             builder.Services.AddTransient<SignatoryInvitationInvocable>(); | ||||
| 
 | ||||
|             var app = builder.Build(); | ||||
| 
 | ||||
|  | ||||
| @ -0,0 +1,18 @@ | ||||
| @using EnotaryoPH.Web.Mailables | ||||
| @model SignatoryViewModel | ||||
| @{ | ||||
|     ViewBag.Heading = "Welcome!"; | ||||
|     ViewBag.Preview = "Example Email"; | ||||
| } | ||||
| 
 | ||||
| <p> | ||||
| 
 | ||||
|     Hello, you have been invited to enotaryo as a @Model.SignatoryType by @Model.MainPrincipalName. | ||||
| 
 | ||||
|     Please click here to get you started. @await Component.InvokeAsync("EmailLinkButton", new { text = "Registration Page", url = Model.InvitationURL }) | ||||
| </p> | ||||
| 
 | ||||
| @section links | ||||
| { | ||||
|     <a href="@Model.InvitationURL">Registration Page</a> | ||||
| } | ||||
							
								
								
									
										3
									
								
								EnotaryoPH/EnotaryoPH.Web/Views/Mail/_ViewImports.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								EnotaryoPH/EnotaryoPH.Web/Views/Mail/_ViewImports.cshtml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| @using EnotaryoPH.Web | ||||
| @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers | ||||
| @addTagHelper *, Coravel.Mailer.ViewComponents | ||||
							
								
								
									
										3
									
								
								EnotaryoPH/EnotaryoPH.Web/Views/Mail/_ViewStart.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								EnotaryoPH/EnotaryoPH.Web/Views/Mail/_ViewStart.cshtml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| @{ | ||||
|     Layout = "~/Areas/Coravel/Pages/Mail/Template.cshtml"; | ||||
| } | ||||
| @ -13,5 +13,15 @@ | ||||
|         "APIKey": "secret", | ||||
|         "Host": "http://localhost", | ||||
|         "Port": "8000" | ||||
|     } | ||||
|     }, | ||||
|     "Coravel": { | ||||
|         "Mail": { | ||||
|             "Driver": "SMTP", | ||||
|             "Host": "localhost", | ||||
|             "Port": 25, | ||||
|             "Username": "", | ||||
|             "Password": "" | ||||
|         } | ||||
|     }, | ||||
|     "BaseUrl": "https://localhost:7121" | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user