one time password
This commit is contained in:
parent
1d29b6ef4d
commit
77c665f64e
@ -0,0 +1,9 @@
|
||||
namespace EnotaryoPH.Data.Constants
|
||||
{
|
||||
public enum DocumentSigningStatus
|
||||
{
|
||||
New = 0,
|
||||
ReadyForSigning = 10,
|
||||
Signed = 20
|
||||
}
|
||||
}
|
@ -22,6 +22,9 @@
|
||||
TransactionApproved = 41,
|
||||
TransactionRejected = 42,
|
||||
|
||||
OTPSent = 45,
|
||||
OTPVerified = 46,
|
||||
|
||||
VideoConferenceStarted = 50,
|
||||
VideoRecordingStarted = 51,
|
||||
VideoRecordingStopped = 52,
|
||||
|
@ -8,6 +8,15 @@ namespace EnotaryoPH.Data.Entities
|
||||
[Column("CreatedOn")]
|
||||
public DateTime? CreatedOn { get; set; }
|
||||
|
||||
[Column("Device")]
|
||||
public string? Device { get; set; }
|
||||
|
||||
[Column("IPAddress")]
|
||||
public string? IPAddress { get; set; }
|
||||
|
||||
[Column("Latitude")]
|
||||
public decimal? Latitude { get; set; }
|
||||
|
||||
[Column("LawyerVideoConferenceParticipant_UID")]
|
||||
public Guid? LawyerVideoConferenceParticipant_UID { get; set; }
|
||||
|
||||
@ -20,12 +29,21 @@ namespace EnotaryoPH.Data.Entities
|
||||
[Column("LawyerVideoConferenceScheduleID")]
|
||||
public int LawyerVideoConferenceScheduleID { get; set; }
|
||||
|
||||
[Column("Longitude")]
|
||||
public decimal? Longitude { get; set; }
|
||||
|
||||
[Column("MeetingRoomTokenID")]
|
||||
public string? MeetingRoomTokenID { get; set; }
|
||||
|
||||
[Column("MeetingRoomUserID")]
|
||||
public string? MeetingRoomUserID { get; set; }
|
||||
|
||||
[Column("OTPEntered")]
|
||||
public string? OTPEntered { get; set; }
|
||||
|
||||
[Column("OTPHash")]
|
||||
public string? OTPHash { get; set; }
|
||||
|
||||
[ForeignKey("ParticipantID")]
|
||||
public User Participant { get; set; }
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
namespace EnotaryoPH.Web.Common.Jobs.Models
|
||||
{
|
||||
public class OTPEmailModel
|
||||
{
|
||||
public Dictionary<Guid, string> ParticipantOTP { get; set; }
|
||||
public Guid Transaction_UID { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
using Coravel.Invocable;
|
||||
using Coravel.Mailer.Mail.Interfaces;
|
||||
using EnotaryoPH.Data;
|
||||
using EnotaryoPH.Web.Common.Jobs.Models;
|
||||
using EnotaryoPH.Web.Common.Models;
|
||||
using EnotaryoPH.Web.Mailables;
|
||||
|
||||
namespace EnotaryoPH.Web.Common.Jobs
|
||||
{
|
||||
public class OneTimePasswordInvocable : IInvocable, IInvocableWithPayload<OTPEmailModel>
|
||||
{
|
||||
private readonly IMailer _mailer;
|
||||
private readonly NotaryoDBContext _notaryoDBContext;
|
||||
private readonly Settings _settings;
|
||||
private readonly IPasswordService _passwordService;
|
||||
private readonly IEventService _eventService;
|
||||
|
||||
public OneTimePasswordInvocable(IMailer mailer, NotaryoDBContext notaryoDBContext, Settings settings, IPasswordService passwordService, IEventService eventService)
|
||||
{
|
||||
_mailer = mailer;
|
||||
_notaryoDBContext = notaryoDBContext;
|
||||
_settings = settings;
|
||||
_passwordService = passwordService;
|
||||
_eventService = eventService;
|
||||
}
|
||||
|
||||
public OTPEmailModel Payload { get; set; }
|
||||
|
||||
public async Task Invoke()
|
||||
{
|
||||
var schedule = _notaryoDBContext.LawyerVideoConferenceSchedules
|
||||
.AsNoTracking()
|
||||
.Include(sched => sched.Transaction)
|
||||
.ThenInclude(transaction => transaction.TransactionDocument)
|
||||
.Include(sched => sched.Transaction)
|
||||
.ThenInclude(transaction => transaction.Lawyer)
|
||||
.ThenInclude(lawyer => lawyer.User)
|
||||
.Include(sched => sched.LawyerVideoConferenceParticipants)
|
||||
.ThenInclude(participant => participant.Participant)
|
||||
.FirstOrDefault(sched => sched.Transaction.Transaction_UID == Payload.Transaction_UID);
|
||||
|
||||
if (schedule == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var participantDic = schedule.LawyerVideoConferenceParticipants.ToDictionary(p => p.LawyerVideoConferenceParticipant_UID.GetValueOrDefault(), p => p);
|
||||
foreach (var otp in Payload.ParticipantOTP)
|
||||
{
|
||||
if (!participantDic.ContainsKey(otp.Key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var participant = participantDic[otp.Key];
|
||||
|
||||
var emailViewModel = new OneTimePasswordViewModel
|
||||
{
|
||||
DocumentType = schedule.Transaction.TransactionDocument.DocumentType,
|
||||
Email = participant.Participant.Email,
|
||||
OTP = otp.Value,
|
||||
ParticipantName = participant.Participant.Fullname,
|
||||
LawyerName = schedule.Transaction.Lawyer.User.Fullname,
|
||||
MeetingRoomURL = $"{_settings.BaseUrl}/Participant/VideoCall/Room/{schedule.Transaction.Transaction_UID}",
|
||||
};
|
||||
await _mailer.SendAsync(new OneTimePasswordMailable(emailViewModel));
|
||||
await _eventService.LogAsync(NotaryoEvent.OTPSent, Payload.Transaction_UID, new
|
||||
{
|
||||
emailViewModel.Email,
|
||||
emailViewModel.ParticipantName,
|
||||
emailViewModel.MeetingRoomURL
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,14 @@
|
||||
using Azure;
|
||||
using System.Security.Cryptography;
|
||||
using Azure;
|
||||
using Azure.Communication;
|
||||
using Azure.Communication.CallAutomation;
|
||||
using Azure.Communication.Identity;
|
||||
using Azure.Communication.Rooms;
|
||||
using Coravel.Queuing.Interfaces;
|
||||
using EnotaryoPH.Data;
|
||||
using EnotaryoPH.Data.Entities;
|
||||
using EnotaryoPH.Web.Common.Jobs;
|
||||
using EnotaryoPH.Web.Common.Jobs.Models;
|
||||
|
||||
namespace EnotaryoPH.Web.Common.Services
|
||||
{
|
||||
@ -17,9 +21,11 @@ namespace EnotaryoPH.Web.Common.Services
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly NotaryoDBContext _dbContext;
|
||||
private readonly IEventService _eventService;
|
||||
private readonly IPasswordService _passwordService;
|
||||
private readonly IQueue _queue;
|
||||
private readonly RoomsClient _roomsClient;
|
||||
|
||||
public VideoConferenceService(NotaryoDBContext dbContext, CommunicationIdentityClient communicationIdentityClient, RoomsClient roomsClient, CallAutomationClient callAutomationClient, IConfiguration configuration, IEventService eventService)
|
||||
public VideoConferenceService(NotaryoDBContext dbContext, CommunicationIdentityClient communicationIdentityClient, RoomsClient roomsClient, CallAutomationClient callAutomationClient, IConfiguration configuration, IEventService eventService, IPasswordService passwordService, IQueue queue)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
_communicationIdentityClient = communicationIdentityClient;
|
||||
@ -27,6 +33,8 @@ namespace EnotaryoPH.Web.Common.Services
|
||||
_callAutomationClient = callAutomationClient;
|
||||
_configuration = configuration;
|
||||
_eventService = eventService;
|
||||
_passwordService = passwordService;
|
||||
_queue = queue;
|
||||
}
|
||||
|
||||
public async Task ApproveTransactionAsync(Guid transaction_UID)
|
||||
@ -128,6 +136,7 @@ namespace EnotaryoPH.Web.Common.Services
|
||||
if (schedule.Status == nameof(VideoConferenceStatus.New))
|
||||
{
|
||||
await CreateMeetingRoomAsync(schedule);
|
||||
await CreateAndEmailOTPAsync(schedule);
|
||||
_dbContext.UpdateOrCreate(schedule);
|
||||
_dbContext.SaveChanges();
|
||||
}
|
||||
@ -154,6 +163,23 @@ namespace EnotaryoPH.Web.Common.Services
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CreateAndEmailOTPAsync(LawyerVideoConferenceSchedule schedule)
|
||||
{
|
||||
var participantOTP = new Dictionary<Guid, string>();
|
||||
const string validChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
foreach (var participant in schedule.LawyerVideoConferenceParticipants)
|
||||
{
|
||||
var otp = RandomNumberGenerator.GetString(validChars, 5);
|
||||
participant.OTPHash = _passwordService.HashPassword(otp);
|
||||
participantOTP.Add(participant.LawyerVideoConferenceParticipant_UID.GetValueOrDefault(), otp);
|
||||
}
|
||||
_queue.QueueInvocableWithPayload<OneTimePasswordInvocable, OTPEmailModel>(new OTPEmailModel
|
||||
{
|
||||
ParticipantOTP = participantOTP,
|
||||
Transaction_UID = schedule.Transaction.Transaction_UID.GetValueOrDefault()
|
||||
});
|
||||
}
|
||||
|
||||
private async Task CreateMeetingRoomAsync(LawyerVideoConferenceSchedule schedule)
|
||||
{
|
||||
var roomParticipants = new List<RoomParticipant>();
|
||||
@ -181,7 +207,9 @@ namespace EnotaryoPH.Web.Common.Services
|
||||
.Include(t => t.TransactionSignatories)
|
||||
.Single(t => t.Transaction_UID == transaction_UID);
|
||||
|
||||
var schedule = _dbContext.LawyerVideoConferenceSchedules.FirstOrDefault(sched => sched.TransactionID == transactionEntity.TransactionID);
|
||||
var schedule = _dbContext.LawyerVideoConferenceSchedules
|
||||
.Include(sched => sched.Transaction)
|
||||
.FirstOrDefault(sched => sched.TransactionID == transactionEntity.TransactionID);
|
||||
if (schedule != null)
|
||||
{
|
||||
return schedule;
|
||||
@ -200,7 +228,7 @@ namespace EnotaryoPH.Web.Common.Services
|
||||
CreatedOn = DateTime.UtcNow,
|
||||
Status = nameof(VideoConferenceStatus.New),
|
||||
LawyerVideoConferenceParticipant_UID = Guid.CreateVersion7(DateTime.UtcNow),
|
||||
ParticipantID = signatory.UserID,
|
||||
ParticipantID = signatory.UserID
|
||||
});
|
||||
participants.Add(new LawyerVideoConferenceParticipant
|
||||
{
|
||||
@ -212,6 +240,7 @@ namespace EnotaryoPH.Web.Common.Services
|
||||
|
||||
schedule.MeetingDate = DateTime.UtcNow;
|
||||
schedule.LawyerVideoConferenceParticipants = participants.ToList();
|
||||
schedule.Transaction = transactionEntity;
|
||||
return schedule;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,16 @@
|
||||
using Coravel.Mailer.Mail;
|
||||
|
||||
namespace EnotaryoPH.Web.Mailables
|
||||
{
|
||||
public class OneTimePasswordMailable : Mailable<OneTimePasswordViewModel>
|
||||
{
|
||||
private readonly OneTimePasswordViewModel _model;
|
||||
|
||||
public OneTimePasswordMailable(OneTimePasswordViewModel model) => _model = model;
|
||||
|
||||
public override void Build() => this
|
||||
.To(_model.Email)
|
||||
.From("noreply@enotaryoph.com")
|
||||
.View("~/Views/Mail/OneTimePassword.cshtml", _model);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
namespace EnotaryoPH.Web.Mailables
|
||||
{
|
||||
public class OneTimePasswordViewModel
|
||||
{
|
||||
public string DocumentType { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string LawyerName { get; set; }
|
||||
public string MeetingRoomURL { get; set; }
|
||||
public string OTP { get; set; }
|
||||
public string ParticipantName { get; set; }
|
||||
}
|
||||
}
|
@ -14,7 +14,6 @@
|
||||
<div class="sidemenu">
|
||||
<div class="align-items-center sidemenu__menuitem"><a class="d-flex flex-grow-1 justify-content-center align-items-center justify-content-md-start p-1 text-decoration-none" href="#available-transactions"><i class="far fa-check-circle fs-4 text-success sidemenu__menuitem__icon" style="padding: 5px;"></i><span class="d-none d-md-inline-block ms-1 sidemenu__menuitem__text">Available</span></a></div>
|
||||
<div class="align-items-center sidemenu__menuitem"><a class="d-flex flex-grow-1 justify-content-center align-items-center justify-content-md-start p-1 text-decoration-none" href="#incomplete-docs"><i class="far fa-clock fs-4 text-warning sidemenu__menuitem__icon" style="padding: 5px;"></i><span class="d-none d-md-inline-block ms-1 sidemenu__menuitem__text">My Jobs</span></a></div>
|
||||
<div class="align-items-center sidemenu__menuitem"><a class="d-flex flex-grow-1 justify-content-center align-items-center justify-content-md-start p-1 text-decoration-none" href="#identification-docs"><i class="far fa-address-card fs-4 text-dark sidemenu__menuitem__icon" style="padding: 5px;"></i><span class="d-none d-md-inline-block ms-1 sidemenu__menuitem__text">Identification Docs</span></a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col g-0 mx-2">
|
||||
@ -27,7 +26,6 @@
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Date</th>
|
||||
<th>Status</th>
|
||||
<th>Link</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -37,8 +35,7 @@
|
||||
<tr>
|
||||
<td>@transaction.Type</td>
|
||||
<td>@transaction.Date.ToString("g")</td>
|
||||
<td>@transaction.Status</td>
|
||||
<td><a href="@transaction.Link">Link</a></td>
|
||||
<td><a href="@transaction.Link">View</a></td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
@ -67,7 +64,7 @@
|
||||
<td>@transaction.Type</td>
|
||||
<td>@transaction.Date.ToShortDateString()</td>
|
||||
<td>@transaction.Status</td>
|
||||
<td><a href="@transaction.Link">Link</a></td>
|
||||
<td><a href="@transaction.Link">View</a></td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
@ -78,4 +75,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
||||
@section Scripts {
|
||||
<script src="~/lib/fontawesome-free-6.7.1-web/js/all.min.js"></script>
|
||||
|
||||
}
|
@ -51,6 +51,8 @@ namespace EnotaryoPH.Web.Pages.Participant.VideoCall
|
||||
return Redirect("/");
|
||||
}
|
||||
|
||||
DocumentType = _Transaction.TransactionDocument.DocumentType;
|
||||
|
||||
var schedule_UID = await _videoConferenceService.StartAsync(Transaction_UID);
|
||||
|
||||
CommunicationUserToken = currentUser.Role == nameof(UserType.Notary)
|
||||
@ -70,7 +72,58 @@ namespace EnotaryoPH.Web.Pages.Participant.VideoCall
|
||||
return Page();
|
||||
}
|
||||
|
||||
private LawyerVideoConferenceParticipant GetParticipant(User currentUser) => _Transaction.Schedule.LawyerVideoConferenceParticipants.First(u => u.ParticipantID == currentUser.UserID);
|
||||
public IActionResult OnGetDocument()
|
||||
{
|
||||
var document = _dbContext.TransactionDocuments
|
||||
.Include(doc => doc.Transaction)
|
||||
.FirstOrDefault(doc => doc.Transaction.Transaction_UID == Transaction_UID);
|
||||
|
||||
return new FileContentResult(document.File, "application/pdf");
|
||||
}
|
||||
|
||||
public IActionResult OnGetIdentificationDocument(string meetingRoomUserID)
|
||||
{
|
||||
var participant = _dbContext.LawyerVideoConferenceParticipants
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(participant => participant.MeetingRoomUserID == meetingRoomUserID);
|
||||
if (participant == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
var identificationDocumentID = _dbContext.TransactionSelfies.AsNoTracking()
|
||||
.Where(selfie => selfie.UserID == participant.ParticipantID && selfie.Transaction.Transaction_UID == Transaction_UID)
|
||||
.Select(selfie => selfie.IdentificationDocumentID).FirstOrDefault();
|
||||
if (identificationDocumentID == 0)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var identificationDocument = _dbContext.IdentificationDocuments.FirstOrDefault(id => id.IdentificationDocumentID == identificationDocumentID);
|
||||
if (identificationDocument == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return new FileContentResult(identificationDocument.File, "image/jpeg");
|
||||
}
|
||||
|
||||
public IActionResult OnGetSelfieImage(string meetingRoomUserID)
|
||||
{
|
||||
var participant = _dbContext.LawyerVideoConferenceParticipants
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(participant => participant.MeetingRoomUserID == meetingRoomUserID);
|
||||
if (participant == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
var transactionSelfie = _dbContext.TransactionSelfies.FirstOrDefault(selfie => selfie.UserID == participant.ParticipantID && selfie.Transaction.Transaction_UID == Transaction_UID);
|
||||
if (transactionSelfie == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return new FileContentResult(transactionSelfie.File, "image/jpeg");
|
||||
}
|
||||
|
||||
public async Task<JsonResult> OnPostApproveAsync()
|
||||
{
|
||||
@ -85,6 +138,8 @@ namespace EnotaryoPH.Web.Pages.Participant.VideoCall
|
||||
return new JsonResult(true);
|
||||
}
|
||||
|
||||
private LawyerVideoConferenceParticipant GetParticipant(User currentUser) => _Transaction.Schedule.LawyerVideoConferenceParticipants.First(u => u.ParticipantID == currentUser.UserID);
|
||||
|
||||
private void LoadTransaction() => _Transaction = _dbContext.Transactions
|
||||
.Include(t => t.TransactionSignatories)
|
||||
.Include(t => t.Lawyer)
|
||||
@ -92,6 +147,7 @@ namespace EnotaryoPH.Web.Pages.Participant.VideoCall
|
||||
.Include(t => t.Principal)
|
||||
.Include(t => t.Schedule)
|
||||
.ThenInclude(sch => sch.LawyerVideoConferenceParticipants)
|
||||
.Include(t => t.TransactionDocument)
|
||||
.FirstOrDefault(t => t.Transaction_UID == Transaction_UID);
|
||||
|
||||
public string CommunicationRoomId { get; private set; }
|
||||
@ -102,7 +158,7 @@ namespace EnotaryoPH.Web.Pages.Participant.VideoCall
|
||||
|
||||
public string DisplayName { get; private set; }
|
||||
|
||||
public string ParticipantType { get; set; }
|
||||
public string DocumentType { get; set; }
|
||||
|
||||
public List<RoomParticipantViewModel> Participants
|
||||
{
|
||||
@ -134,52 +190,9 @@ namespace EnotaryoPH.Web.Pages.Participant.VideoCall
|
||||
}
|
||||
}
|
||||
|
||||
public string ParticipantType { get; set; }
|
||||
[BindProperty(SupportsGet = true)] public string ServerCallID { get; set; }
|
||||
|
||||
[BindProperty(SupportsGet = true)] public Guid Transaction_UID { get; set; }
|
||||
|
||||
public IActionResult OnGetSelfieImage(string meetingRoomUserID)
|
||||
{
|
||||
var participant = _dbContext.LawyerVideoConferenceParticipants
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(participant => participant.MeetingRoomUserID == meetingRoomUserID);
|
||||
if (participant == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
var transactionSelfie = _dbContext.TransactionSelfies.FirstOrDefault(selfie => selfie.UserID == participant.ParticipantID && selfie.Transaction.Transaction_UID == Transaction_UID);
|
||||
if (transactionSelfie == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return new FileContentResult(transactionSelfie.File, "image/jpeg");
|
||||
}
|
||||
|
||||
public IActionResult OnGetIdentificationDocument(string meetingRoomUserID)
|
||||
{
|
||||
var participant = _dbContext.LawyerVideoConferenceParticipants
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(participant => participant.MeetingRoomUserID == meetingRoomUserID);
|
||||
if (participant == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
var identificationDocumentID = _dbContext.TransactionSelfies.AsNoTracking()
|
||||
.Where(selfie => selfie.UserID == participant.ParticipantID && selfie.Transaction.Transaction_UID == Transaction_UID)
|
||||
.Select(selfie => selfie.IdentificationDocumentID).FirstOrDefault();
|
||||
if (identificationDocumentID == 0)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var identificationDocument = _dbContext.IdentificationDocuments.FirstOrDefault(id => id.IdentificationDocumentID == identificationDocumentID);
|
||||
if (identificationDocument == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return new FileContentResult(identificationDocument.File, "image/jpeg");
|
||||
}
|
||||
}
|
||||
}
|
@ -6,21 +6,22 @@
|
||||
control_communicationUserId = document.getElementById("CommunicationUserId"),
|
||||
control_communicationUserToken = document.getElementById("CommunicationUserToken"),
|
||||
control_displayName = document.getElementById("DisplayName"),
|
||||
control_participants = document.getElementById("Participants"),
|
||||
control_reject = document.getElementById("Reject"),
|
||||
control_templateSidePane = document.getElementById("TemplateSidePane"),
|
||||
control_templateParticipantItem = document.getElementById("TemplateParticipantItem"),
|
||||
control_draggableModal = document.getElementById("DraggableModal"),
|
||||
control_identificationImage = document.getElementById("IdentificationImage"),
|
||||
control_otpForm = document.getElementById("OtpForm"),
|
||||
control_participantListGroup = document.getElementById("ParticipantListGroup"),
|
||||
control_participants = document.getElementById("Participants"),
|
||||
control_participantType = document.getElementById("ParticipantType"),
|
||||
control_pdfViewer = document.getElementById("PdfViewer"),
|
||||
control_reject = document.getElementById("Reject"),
|
||||
control_rightSidebarModal = document.getElementById("RightSidebarModal"),
|
||||
/*control_videoGrid = document.getElementById("VideoGrid"),*/
|
||||
control_selfieImage = document.getElementById("SelfieImage"),
|
||||
control_serverCallIID = document.getElementById("ServerCallID"),
|
||||
control_signatoryName = document.getElementById("SignatoryName"),
|
||||
control_templateParticipantItem = document.getElementById("TemplateParticipantItem"),
|
||||
control_templateSidePane = document.getElementById("TemplateSidePane"),
|
||||
control_videoGridContainer = document.getElementById("videoGrid-container"),
|
||||
control_viewDocument = document.getElementById("ViewDocument"),
|
||||
control_serverCallIID = document.getElementById("ServerCallID"),
|
||||
control_participantType = document.getElementById("ParticipantType"),
|
||||
control_participantListGroup = document.getElementById("ParticipantListGroup"),
|
||||
control_selfieImage = document.getElementById("SelfieImage"),
|
||||
control_identificationImage = document.getElementById("IdentificationImage"),
|
||||
control_signatoryName = document.getElementById("SignatoryName"),
|
||||
x = 1;
|
||||
|
||||
let participants = JSON.parse(control_participants.value);
|
||||
@ -40,7 +41,21 @@
|
||||
tooltipContent: 'View the document.'
|
||||
},
|
||||
onItemClick: function () {
|
||||
alert('Document Modal goes here.');
|
||||
const modal = bootstrap.Modal.getOrCreateInstance(control_pdfViewer);
|
||||
modal.show();
|
||||
}
|
||||
};
|
||||
},
|
||||
function (args) {
|
||||
return {
|
||||
placement: 'primary',
|
||||
strings: {
|
||||
label: 'OTP',
|
||||
tooltipContent: 'Enter your OTP'
|
||||
},
|
||||
onItemClick: function () {
|
||||
const modal = bootstrap.Modal.getOrCreateInstance(control_otpForm);
|
||||
modal.show();
|
||||
}
|
||||
};
|
||||
},
|
||||
@ -180,56 +195,16 @@
|
||||
}
|
||||
|
||||
function approveTransaction(e) {
|
||||
jfa.communication.videocall.stopCall(true);
|
||||
let url = jfa.utilities.routing.getCurrentURLWithHandler("Approve");
|
||||
jfa.utilities.request.post(url, {})
|
||||
.then(resp => {
|
||||
if (resp.ok === true) {
|
||||
debugger;
|
||||
jfa.communication.videocall.stopCall(true);
|
||||
}
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
}
|
||||
|
||||
//function _updateGrid() {
|
||||
// control_videoGrid.innerHTML = '';
|
||||
|
||||
// participants.forEach((participant, index) => {
|
||||
// const col = document.createElement('div');
|
||||
// col.className = 'participant-col';
|
||||
// const tmpl = control_templateVideo.cloneNode(true).content;
|
||||
// const vidcontainer = tmpl.querySelector(".video-container")
|
||||
// if (vidcontainer) {
|
||||
// vidcontainer.id = participant.Id;
|
||||
// vidcontainer.classList.add(participant.Id);
|
||||
// vidcontainer.classList.add(participant.Type == 'Notary' ? 'local-video-container' : 'remote-video-container');
|
||||
// }
|
||||
// let participantName = tmpl.querySelector(".participant-name")
|
||||
// if (participantName) {
|
||||
// participantName.textContent = participant.DisplayName;
|
||||
// }
|
||||
// col.appendChild(tmpl);
|
||||
// control_videoGrid.appendChild(col);
|
||||
// });
|
||||
|
||||
// // Dynamically adjust grid columns based on participant count
|
||||
// const count = participants.length;
|
||||
// const cols = count <= 2 ? 'col-12 col-sm-12 offset-md-1 col-md-10 offset-lg-0 col-lg-6' :
|
||||
// count <= 4 ? 'col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6' :
|
||||
// count <= 8 ? 'col-6 col-md-6 col-lg-4 col-xl-4' :
|
||||
// count <= 9 ? 'col-4' :
|
||||
// 'col-6 col-sm-4 col-lg-3';
|
||||
|
||||
// document.querySelectorAll('.participant-col').forEach(el => {
|
||||
// el.className = `participant-col ${cols}`;
|
||||
// });
|
||||
|
||||
// const fluid = count <= 2 ? 'container-fluid' :
|
||||
// count <= 8 ? 'container-xxl' :
|
||||
// 'container-xl'
|
||||
// control_videoGridContainer.className = `${fluid} py-3`;
|
||||
//}
|
||||
|
||||
function _createParticipantListItems() {
|
||||
control_participantListGroup.innerHTML = '';
|
||||
participants.forEach(participant => {
|
||||
@ -250,7 +225,6 @@
|
||||
control_participantListGroup.addEventListener('click', function (event) {
|
||||
let target = event.target?.closest('.list-group-item');
|
||||
if (target) {
|
||||
|
||||
const participant = participants.find(p => p.UID == target.dataset.participantUid);
|
||||
if (!participant) {
|
||||
return;
|
||||
@ -260,7 +234,6 @@
|
||||
let selfieUrl = jfa.utilities.routing.getCurrentURLWithHandler("SelfieImage");
|
||||
selfieUrl.searchParams.append("meetingRoomUserID", participant.RoomUserID);
|
||||
|
||||
|
||||
let identificationUrl = jfa.utilities.routing.getCurrentURLWithHandler("IdentificationDocument");
|
||||
identificationUrl.searchParams.append("meetingRoomUserID", participant.RoomUserID);
|
||||
control_selfieImage.src = selfieUrl;
|
||||
@ -304,9 +277,7 @@
|
||||
|
||||
async function _init() {
|
||||
_bindEvents();
|
||||
//_updateGrid();
|
||||
await _initVideoCall();
|
||||
debugger;
|
||||
_createParticipantListItems();
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,6 @@ namespace EnotaryoPH.Web.Pages.Principal.TransactionStatus
|
||||
|
||||
Title = transaction.TransactionDocument.DocumentType;
|
||||
StartedOn = transaction.CreatedOn.GetValueOrDefault();
|
||||
StatusDescription = "Not all signatories have signed up to enotaryo and completed the onboarding process. This transaction cannot proceed until this has been resolved.";
|
||||
|
||||
Signatories = transaction.TransactionSignatories.ConvertAll(s => new SignatoryViewModel
|
||||
{
|
||||
@ -41,6 +40,10 @@ namespace EnotaryoPH.Web.Pages.Principal.TransactionStatus
|
||||
Status = s.Status
|
||||
});
|
||||
|
||||
StatusDescription = Signatories.Count > 1
|
||||
? "Not all signatories have signed up to enotaryo and completed the onboarding process. This transaction cannot proceed until this has been resolved."
|
||||
: "Please wait while our Notary Public team reviews your document.";
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
|
@ -64,6 +64,7 @@ namespace EnotaryoPH.Web
|
||||
builder.Services.AddScheduler();
|
||||
builder.Services.AddMailer(config);
|
||||
builder.Services.AddTransient<SignatoryInvitationInvocable>();
|
||||
builder.Services.AddTransient<OneTimePasswordInvocable>();
|
||||
builder.Services.AddTransient<CheckRecordingAvailabilityInvocable>();
|
||||
builder.Services.AddTransient<IVideoConferenceService, VideoConferenceService>();
|
||||
builder.Services.AddTransient<IEventService, EventService>();
|
||||
|
25
EnotaryoPH/EnotaryoPH.Web/Views/Mail/OneTimePassword.cshtml
Normal file
25
EnotaryoPH/EnotaryoPH.Web/Views/Mail/OneTimePassword.cshtml
Normal file
@ -0,0 +1,25 @@
|
||||
@using EnotaryoPH.Web.Mailables
|
||||
@model OneTimePasswordViewModel
|
||||
@{
|
||||
ViewBag.Heading = "One Time Password";
|
||||
ViewBag.Preview = "Your OTP";
|
||||
}
|
||||
|
||||
<p>
|
||||
Hello @Model.ParticipantName,
|
||||
|
||||
This is your one time password:
|
||||
|
||||
<blockquote>
|
||||
@Model.OTP
|
||||
</blockquote>
|
||||
|
||||
Please do not share this code with anyone other than the Notary Public, Atty. @Model.LawyerName during the Video Conference.
|
||||
|
||||
You can join the e-Notaryo video conference by clicking this button: @await Component.InvokeAsync("EmailLinkButton", new { text = "Video Conference", url = Model.MeetingRoomURL })
|
||||
</p>
|
||||
|
||||
@section links
|
||||
{
|
||||
<a href="@Model.MeetingRoomURL">Meeting Room Page</a>
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user