Add rejection handling and dedicated page for envelopes
Introduced a new rejection flow in `ReceiverPage.razor` to detect rejected envelopes and redirect users to a dedicated rejection page (`ReceiverRejectedPage.razor`). - Added `IsRejected` helper method to check envelope rejection status based on current status and history. - Created `ReceiverRejectedPage.razor` to display rejection messages, sender details, and navigation options. - Implemented localized strings for dynamic rejection messages. - Designed a visually distinct layout for the rejection page. - Improved user experience with clear messaging and proper handling of invalid or unauthorized access.
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
@using EnvelopeGenerator.Server.Client.Models.Constants
|
||||
@using EnvelopeGenerator.Server.Client.Services
|
||||
@using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver
|
||||
@using EnvelopeGenerator.Domain.Constants
|
||||
@using Microsoft.JSInterop
|
||||
@using DevExpress.Blazor
|
||||
@using System.Drawing
|
||||
@@ -510,6 +511,11 @@
|
||||
_envelopeReceiver = await PageDataService.GetEnvelopeReceiverAsync(EnvelopeKey);
|
||||
if (_envelopeReceiver is null)
|
||||
Logger.LogWarning("Envelope receiver data is null for {EnvelopeKey}", EnvelopeKey);
|
||||
else if (IsRejected(_envelopeReceiver))
|
||||
{
|
||||
Navigation.NavigateTo($"/envelope/{Uri.EscapeDataString(EnvelopeKey)}/rejected", forceLoad: true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build initial report (no signature image yet)
|
||||
_report = BuildReport(_pdfBytes, _signatures, capturedSignature: null);
|
||||
@@ -555,6 +561,16 @@
|
||||
}
|
||||
|
||||
// ----- Report builder -----
|
||||
static bool IsRejected(EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver.EnvelopeReceiverDto envelopeReceiver)
|
||||
{
|
||||
var status = envelopeReceiver.Envelope?.Status;
|
||||
if (status is EnvelopeStatus.DocumentRejected or EnvelopeStatus.EnvelopeRejected)
|
||||
return true;
|
||||
|
||||
return envelopeReceiver.Envelope?.Histories?.Any(h =>
|
||||
h.Status is EnvelopeStatus.DocumentRejected or EnvelopeStatus.EnvelopeRejected) == true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds an XtraReport wrapping the PDF bytes.
|
||||
/// If a signature is captured and there are signature fields, the signature image is
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
@page "/envelope/{EnvelopeKey}/rejected"
|
||||
@rendermode InteractiveServer
|
||||
@using EnvelopeGenerator.Application.Resources
|
||||
@using EnvelopeGenerator.Domain.Constants
|
||||
@using EnvelopeGenerator.Domain.Interfaces
|
||||
@using Microsoft.Extensions.Localization
|
||||
@inject NavigationManager Navigation
|
||||
@inject EnvelopeGenerator.Server.Services.EnvelopeReceiverAuthorizationService ReceiverAuthorizationService
|
||||
@inject EnvelopeGenerator.Server.Services.EnvelopeReceiverPageDataService PageDataService
|
||||
@inject IStringLocalizer<Resource> UiLocalizer
|
||||
|
||||
<div class="envelope-viewer-layout" style="justify-content: center; align-items: center; padding: 1.5rem;">
|
||||
<div style="width: min(920px, 100%); background: rgba(255,255,255,0.96); border-radius: 20px; box-shadow: 0 18px 48px rgba(15,23,42,0.22); overflow: hidden;">
|
||||
<div style="padding: 1.2rem 1.4rem; border-bottom: 1px solid rgba(15,23,42,0.08); background: linear-gradient(135deg, rgba(239,68,68,0.12) 0%, rgba(220,38,38,0.08) 100%);">
|
||||
<div style="font-size: 1rem; font-weight: 700; color: #991b1b;">@L("DocRejected", "Dokument abgelehnt")</div>
|
||||
</div>
|
||||
|
||||
<div style="padding: 2rem 1.5rem;">
|
||||
<div style="display: flex; justify-content: center; margin-bottom: 1rem;">
|
||||
<div style="width: 90px; height: 90px; border-radius: 999px; background: #fee2e2; display: inline-flex; align-items: center; justify-content: center; color: #b91c1c;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 style="text-align: center; font-size: 1.4rem; color: #7f1d1d; margin-bottom: 0.75rem;">
|
||||
@(_isReadAndConfirm
|
||||
? L("RejectionInfo1Confirmation", "Dieses Dokument wurde als nicht bestaetigt markiert.")
|
||||
: L("RejectionInfo1", "Dieses Dokument wurde abgelehnt."))
|
||||
</h2>
|
||||
|
||||
<p style="text-align: center; margin: 0 auto; max-width: 760px; color: #374151; line-height: 1.6;">
|
||||
@((MarkupString)_messageLine2)
|
||||
</p>
|
||||
|
||||
<div style="margin-top: 1.4rem; text-align: center;">
|
||||
<a href="/" class="btn btn-outline-secondary" style="font-weight: 600;">Portal Startseite</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string? EnvelopeKey { get; set; }
|
||||
|
||||
bool _isReadAndConfirm;
|
||||
string _messageLine2 = string.Empty;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(EnvelopeKey))
|
||||
{
|
||||
Navigation.NavigateTo("/");
|
||||
return;
|
||||
}
|
||||
|
||||
var user = await ReceiverAuthorizationService.AuthorizeAsync(EnvelopeKey);
|
||||
if (user is null)
|
||||
{
|
||||
Navigation.NavigateTo($"/envelope/login/{Uri.EscapeDataString(EnvelopeKey)}");
|
||||
return;
|
||||
}
|
||||
|
||||
var envelopeReceiver = await PageDataService.GetEnvelopeReceiverAsync(EnvelopeKey);
|
||||
if (envelopeReceiver is null)
|
||||
{
|
||||
Navigation.NavigateTo($"/envelope/{Uri.EscapeDataString(EnvelopeKey)}", forceLoad: true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsRejected(envelopeReceiver))
|
||||
{
|
||||
Navigation.NavigateTo($"/envelope/{Uri.EscapeDataString(EnvelopeKey)}", forceLoad: true);
|
||||
return;
|
||||
}
|
||||
|
||||
_isReadAndConfirm = envelopeReceiver.Envelope?.IsReadAndConfirm() ?? false;
|
||||
|
||||
var senderName = envelopeReceiver.Envelope?.User is null
|
||||
? string.Empty
|
||||
: $"{envelopeReceiver.Envelope.User.Prename} {envelopeReceiver.Envelope.User.Name}".Trim();
|
||||
var senderEmail = envelopeReceiver.Envelope?.User?.Email ?? string.Empty;
|
||||
var envelopeTitle = envelopeReceiver.Envelope?.Title ?? string.Empty;
|
||||
|
||||
var template = L("RejectionInfo2", "Das Dokument \"{2}\" von {0} ({1}) wurde abgelehnt.");
|
||||
_messageLine2 = string.Format(template, senderName, senderEmail, envelopeTitle);
|
||||
}
|
||||
|
||||
static bool IsRejected(EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver.EnvelopeReceiverDto envelopeReceiver)
|
||||
{
|
||||
var status = envelopeReceiver.Envelope?.Status;
|
||||
if (status is EnvelopeStatus.DocumentRejected or EnvelopeStatus.EnvelopeRejected)
|
||||
return true;
|
||||
|
||||
return envelopeReceiver.Envelope?.Histories?.Any(h =>
|
||||
h.Status is EnvelopeStatus.DocumentRejected or EnvelopeStatus.EnvelopeRejected) == true;
|
||||
}
|
||||
|
||||
string L(string key, string fallback)
|
||||
{
|
||||
var value = UiLocalizer[key];
|
||||
return value.ResourceNotFound ? fallback : value.Value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user