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">
|
<section class="my-5">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -22,7 +25,99 @@
|
|||||||
})
|
})
|
||||||
<div class="tab-content mt-3"></div>
|
<div class="tab-content mt-3"></div>
|
||||||
</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>
|
</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,14 +1,143 @@
|
|||||||
|
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;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
|
||||||
namespace EnotaryoPH.Web.Pages.Principal.NotaryoSteps
|
namespace EnotaryoPH.Web.Pages.Principal.NotaryoSteps
|
||||||
{
|
{
|
||||||
public class UploadDocumentModel : PageModel
|
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)]
|
[BindProperty(SupportsGet = true)]
|
||||||
public Guid Transaction_UID { get; set; }
|
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 System.Security.Principal;
|
||||||
|
using Coravel;
|
||||||
using EnotaryoPH.Data;
|
using EnotaryoPH.Data;
|
||||||
|
using EnotaryoPH.Web.Common.Jobs;
|
||||||
|
using EnotaryoPH.Web.Common.Models;
|
||||||
using Exadel.Compreface.Clients.CompreFaceClient;
|
using Exadel.Compreface.Clients.CompreFaceClient;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
|
||||||
@ -21,20 +24,28 @@ namespace EnotaryoPH.Web
|
|||||||
razorBuilder.AddRazorRuntimeCompilation();
|
razorBuilder.AddRazorRuntimeCompilation();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
var config = builder.Configuration;
|
||||||
|
|
||||||
builder.Services.AddDbContext<NotaryoDBContext>();
|
builder.Services.AddDbContext<NotaryoDBContext>();
|
||||||
builder.Services.AddHttpContextAccessor();
|
builder.Services.AddHttpContextAccessor();
|
||||||
builder.Services.AddSession(options => options.IdleTimeout = TimeSpan.FromMinutes(120));
|
builder.Services.AddSession(options => options.IdleTimeout = TimeSpan.FromMinutes(120));
|
||||||
builder.Services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>()?.HttpContext?.User);
|
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<IPasswordService, PasswordService>();
|
||||||
builder.Services.AddTransient<ICurrentUserService, CurrentUserService>();
|
builder.Services.AddTransient<ICurrentUserService, CurrentUserService>();
|
||||||
builder.Services.AddTransient<ICompreFaceClient>(provider =>
|
builder.Services.AddTransient<ICompreFaceClient>(provider =>
|
||||||
{
|
{
|
||||||
var config = provider.GetRequiredService<IConfiguration>();
|
|
||||||
var host = config.GetValue<string>("CompreFaceConfig:Host");
|
var host = config.GetValue<string>("CompreFaceConfig:Host");
|
||||||
var port = config.GetValue<string>("CompreFaceConfig:Port");
|
var port = config.GetValue<string>("CompreFaceConfig:Port");
|
||||||
return new CompreFaceClient(host, port);
|
return new CompreFaceClient(host, port);
|
||||||
});
|
});
|
||||||
|
builder.Services.AddQueue();
|
||||||
|
builder.Services.AddMailer(config);
|
||||||
|
builder.Services.AddTransient<SignatoryInvitationInvocable>();
|
||||||
|
|
||||||
var app = builder.Build();
|
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",
|
"APIKey": "secret",
|
||||||
"Host": "http://localhost",
|
"Host": "http://localhost",
|
||||||
"Port": "8000"
|
"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