Merge branch 'feat/migr-DxReportViewer' of http://git.dd:3000/AppStd/EnvelopeGenerator into feat/migr-DxReportViewer
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
@page "/"
|
@page "/"
|
||||||
@inject IJSRuntime JS
|
@inject IJSRuntime JS
|
||||||
|
@rendermode InteractiveWebAssembly
|
||||||
|
|
||||||
<link href="_content/DevExpress.Blazor.Themes/blazing-berry.bs5.min.css" rel="stylesheet" />
|
<link href="_content/DevExpress.Blazor.Themes/blazing-berry.bs5.min.css" rel="stylesheet" />
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
@using System.Security.Claims
|
@using System.Security.Claims
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
|
@inject EnvelopeGenerator.Server.Client.Services.AuthService AuthService
|
||||||
@inject EnvelopeGenerator.Server.Services.EnvelopeReceiverAuthorizationService ReceiverAuthorizationService
|
@inject EnvelopeGenerator.Server.Services.EnvelopeReceiverAuthorizationService ReceiverAuthorizationService
|
||||||
@inject EnvelopeGenerator.Server.Services.EnvelopeReceiverPageDataService PageDataService
|
@inject EnvelopeGenerator.Server.Services.EnvelopeReceiverPageDataService PageDataService
|
||||||
@inject AppVersionService AppVersion
|
@inject AppVersionService AppVersion
|
||||||
@@ -42,6 +43,20 @@
|
|||||||
<div style="font-size: 0.9rem; font-weight: 600; color: #1f2937;">Signiertes Dokument</div>
|
<div style="font-size: 0.9rem; font-weight: 600; color: #1f2937;">Signiertes Dokument</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@* Right: Submit button *@
|
||||||
|
<div style="flex: 0 0 auto;">
|
||||||
|
<button class="pdf-toolbar__btn pdf-toolbar__btn--signature-change pdf-toolbar__btn--signature-change-active"
|
||||||
|
@onclick="OpenSubmitConfirmPopup"
|
||||||
|
disabled="@_isLoggingOut"
|
||||||
|
title="Abschließen"
|
||||||
|
style="flex-shrink: 0;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
|
||||||
|
<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="pdf-toolbar__btn-text">Abschließen</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,6 +97,53 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@* Submit confirmation popup *@
|
||||||
|
<DxPopup @bind-Visible="_submitConfirmVisible"
|
||||||
|
HeaderText="Unterschrift bestätigen"
|
||||||
|
Width="440px"
|
||||||
|
MaxWidth="95vw"
|
||||||
|
ShowFooter="true"
|
||||||
|
CloseOnOutsideClick="false"
|
||||||
|
ShowCloseButton="false"
|
||||||
|
CloseOnEscape="false">
|
||||||
|
<BodyContentTemplate>
|
||||||
|
<div style="display: flex; align-items: flex-start; gap: 1rem; padding: 0.5rem 0;">
|
||||||
|
<div style="flex-shrink: 0; width: 40px; height: 40px; background: #d1fae5; border-radius: 50%; display: flex; align-items: center; justify-content: center;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="#065f46" viewBox="0 0 16 16">
|
||||||
|
<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p style="margin: 0 0 0.4rem; font-weight: 600; color: #1f2937; font-size: 0.95rem;">
|
||||||
|
Möchten Sie das Dokument verbindlich unterschreiben?
|
||||||
|
</p>
|
||||||
|
<p style="margin: 0; color: #6b7280; font-size: 0.85rem; line-height: 1.5;">
|
||||||
|
Diese Aktion kann nicht rückgängig gemacht werden. Mit der Bestätigung erklären Sie, das oben angezeigte Dokument elektronisch unterzeichnet zu haben. Das unterzeichnete Dokument wird anschließend an alle beteiligten Parteien übermittelt.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BodyContentTemplate>
|
||||||
|
<FooterContentTemplate>
|
||||||
|
<div class="d-flex gap-2 justify-content-end w-100" style="padding: 0.5rem 0;">
|
||||||
|
<button class="btn btn-outline-secondary"
|
||||||
|
@onclick="() => _submitConfirmVisible = false"
|
||||||
|
style="border-radius: 6px; padding: 0.5rem 1.25rem; font-weight: 500;">
|
||||||
|
Abbrechen
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary"
|
||||||
|
@onclick="SubmitAndLogoutAsync"
|
||||||
|
disabled="@_isLoggingOut"
|
||||||
|
style="background: linear-gradient(135deg, #059669 0%, #047857 100%); border: none; border-radius: 6px; padding: 0.5rem 1.5rem; font-weight: 600; box-shadow: 0 2px 4px rgba(5, 150, 105, 0.3);">
|
||||||
|
@if (_isLoggingOut)
|
||||||
|
{
|
||||||
|
<span class="spinner-border spinner-border-sm me-1" role="status"></span>
|
||||||
|
}
|
||||||
|
Abschließen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</FooterContentTemplate>
|
||||||
|
</DxPopup>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public string? EnvelopeKey { get; set; }
|
[Parameter] public string? EnvelopeKey { get; set; }
|
||||||
|
|
||||||
@@ -96,6 +158,22 @@
|
|||||||
XtraReport? _report;
|
XtraReport? _report;
|
||||||
SignatureCaptureDto? _sig;
|
SignatureCaptureDto? _sig;
|
||||||
|
|
||||||
|
// ----- Submit / logout state -----
|
||||||
|
bool _isLoggingOut = false;
|
||||||
|
bool _submitConfirmVisible = false;
|
||||||
|
|
||||||
|
void OpenSubmitConfirmPopup() => _submitConfirmVisible = true;
|
||||||
|
|
||||||
|
async Task SubmitAndLogoutAsync()
|
||||||
|
{
|
||||||
|
if (_isLoggingOut) return;
|
||||||
|
_isLoggingOut = true;
|
||||||
|
_submitConfirmVisible = false;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
await AuthService.LogoutEnvelopeReceiverAsync(EnvelopeKey!);
|
||||||
|
Navigation.NavigateTo("/", forceLoad: true);
|
||||||
|
}
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(EnvelopeKey))
|
if (string.IsNullOrWhiteSpace(EnvelopeKey))
|
||||||
@@ -120,6 +198,18 @@
|
|||||||
_sig = cached;
|
_sig = cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cache miss or missing sid — redirect back to report page
|
||||||
|
if (_sig is null)
|
||||||
|
{
|
||||||
|
Logger.LogWarning(
|
||||||
|
"[SignedPage] Cache miss or no sid={Sid} for {EnvelopeKey}, redirecting to report page.",
|
||||||
|
Sid, EnvelopeKey);
|
||||||
|
Navigation.NavigateTo(
|
||||||
|
$"/envelope/{Uri.EscapeDataString(EnvelopeKey)}/report",
|
||||||
|
forceLoad: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var pdfBytes = await PageDataService.GetDocumentAsync(_receiverUser);
|
var pdfBytes = await PageDataService.GetDocumentAsync(_receiverUser);
|
||||||
@@ -206,15 +296,19 @@
|
|||||||
var document = PdfSharp.Pdf.IO.PdfReader.Open(
|
var document = PdfSharp.Pdf.IO.PdfReader.Open(
|
||||||
inputMs, PdfSharp.Pdf.IO.PdfDocumentOpenMode.Modify);
|
inputMs, PdfSharp.Pdf.IO.PdfDocumentOpenMode.Modify);
|
||||||
|
|
||||||
const double sigW = 1.77 * 72; // 127.44 pt
|
const double sigW = 1.77 * 72; // 127.44 pt
|
||||||
const double sigH = 1.96 * 72; // 141.12 pt
|
const double sigH = 1.96 * 72; // 141.12 pt
|
||||||
const double imgRatio = 0.60; // top 60% = image
|
const double imgRatio = 0.52; // top 52% = image
|
||||||
const double textRatio = 0.38; // bottom 38% = text (2% padding)
|
const double lineH = 11.5; // fixed row height matching font size (bold 7.5pt + normal 6.5pt)
|
||||||
|
const double bgPad = 3.0; // background box padding around content (pt)
|
||||||
|
|
||||||
var black = PdfSharp.Drawing.XColor.FromArgb(255, 20, 20, 20);
|
var black = PdfSharp.Drawing.XColor.FromArgb(255, 20, 20, 20);
|
||||||
var darkGray = PdfSharp.Drawing.XColor.FromArgb(255, 80, 80, 80);
|
var darkGray = PdfSharp.Drawing.XColor.FromArgb(255, 80, 80, 80);
|
||||||
var lineColor = PdfSharp.Drawing.XColor.FromArgb(180, 100, 100, 120);
|
var lineColor = PdfSharp.Drawing.XColor.FromArgb(180, 100, 100, 120);
|
||||||
|
|
||||||
|
var bgColor = PdfSharp.Drawing.XColor.FromArgb(255, 255, 253, 240);
|
||||||
|
var bgBrush = new PdfSharp.Drawing.XSolidBrush(bgColor);
|
||||||
|
|
||||||
var fontBold = new PdfSharp.Drawing.XFont("Arial", 7.5, PdfSharp.Drawing.XFontStyleEx.Bold);
|
var fontBold = new PdfSharp.Drawing.XFont("Arial", 7.5, PdfSharp.Drawing.XFontStyleEx.Bold);
|
||||||
var fontNormal = new PdfSharp.Drawing.XFont("Arial", 6.5, PdfSharp.Drawing.XFontStyleEx.Regular);
|
var fontNormal = new PdfSharp.Drawing.XFont("Arial", 6.5, PdfSharp.Drawing.XFontStyleEx.Regular);
|
||||||
var linePen = new PdfSharp.Drawing.XPen(lineColor, 0.5);
|
var linePen = new PdfSharp.Drawing.XPen(lineColor, 0.5);
|
||||||
@@ -239,40 +333,52 @@
|
|||||||
double x = field.X;
|
double x = field.X;
|
||||||
double y = field.Y;
|
double y = field.Y;
|
||||||
|
|
||||||
// --- Image area ---
|
// --- Calculate layout positions first (needed for bg rect) ---
|
||||||
double imgH = sigH * imgRatio;
|
double imgH = sigH * imgRatio;
|
||||||
var imgRect = new PdfSharp.Drawing.XRect(x, y, sigW, imgH);
|
double lineY = y + imgH + 1.0; // 1pt gap between image and separator
|
||||||
|
double textY = lineY + 1.5; // 1.5pt gap below separator line
|
||||||
|
double padding = 3;
|
||||||
|
|
||||||
|
// Row 1: FullName
|
||||||
|
double row1Y = textY;
|
||||||
|
// Row 2: Position (optional)
|
||||||
|
double row2Y = row1Y + lineH;
|
||||||
|
// Row 3: Place, Date — immediately after row2 regardless of position
|
||||||
|
double row3Y = !string.IsNullOrWhiteSpace(sig.Position) ? row2Y + lineH : row2Y;
|
||||||
|
double contentBottom = row3Y + lineH;
|
||||||
|
|
||||||
|
// --- Background rectangle sized to actual content (not full sigH) ---
|
||||||
|
var bgRect = new PdfSharp.Drawing.XRect(
|
||||||
|
x - bgPad,
|
||||||
|
y - bgPad,
|
||||||
|
sigW + bgPad * 2,
|
||||||
|
(contentBottom - y) + bgPad * 2);
|
||||||
|
gfx.DrawRectangle(bgBrush, bgRect);
|
||||||
|
|
||||||
|
// --- Image area ---
|
||||||
|
var imgRect = new PdfSharp.Drawing.XRect(x, y, sigW, imgH);
|
||||||
using var imgStream = new System.IO.MemoryStream(imgBytes);
|
using var imgStream = new System.IO.MemoryStream(imgBytes);
|
||||||
var xImg = PdfSharp.Drawing.XImage.FromStream(imgStream);
|
var xImg = PdfSharp.Drawing.XImage.FromStream(imgStream);
|
||||||
gfx.DrawImage(xImg, imgRect);
|
gfx.DrawImage(xImg, imgRect);
|
||||||
|
|
||||||
// --- Separator line ---
|
// --- Separator line ---
|
||||||
double lineY = y + imgH + sigH * 0.01;
|
|
||||||
gfx.DrawLine(linePen, x + 2, lineY, x + sigW - 2, lineY);
|
gfx.DrawLine(linePen, x + 2, lineY, x + sigW - 2, lineY);
|
||||||
|
|
||||||
// --- Text area ---
|
// --- Text rows ---
|
||||||
double textY = lineY + 2;
|
|
||||||
double textH = sigH * textRatio;
|
|
||||||
double lineH = textH / 3.5; // max 3 text rows
|
|
||||||
double padding = 3;
|
|
||||||
|
|
||||||
// Row 1: FullName (bold)
|
// Row 1: FullName (bold)
|
||||||
var nameRect = new PdfSharp.Drawing.XRect(x + padding, textY, sigW - padding * 2, lineH);
|
var nameRect = new PdfSharp.Drawing.XRect(x + padding, row1Y, sigW - padding * 2, lineH);
|
||||||
gfx.DrawString(sig.FullName, fontBold, new PdfSharp.Drawing.XSolidBrush(black), nameRect, fmtLeft);
|
gfx.DrawString(sig.FullName, fontBold, new PdfSharp.Drawing.XSolidBrush(black), nameRect, fmtLeft);
|
||||||
|
|
||||||
// Row 2: Position (optional)
|
// Row 2: Position (optional)
|
||||||
double row2Y = textY + lineH;
|
|
||||||
if (!string.IsNullOrWhiteSpace(sig.Position))
|
if (!string.IsNullOrWhiteSpace(sig.Position))
|
||||||
{
|
{
|
||||||
var posRect = new PdfSharp.Drawing.XRect(x + padding, row2Y, sigW - padding * 2, lineH);
|
var posRect = new PdfSharp.Drawing.XRect(x + padding, row2Y, sigW - padding * 2, lineH);
|
||||||
gfx.DrawString(sig.Position, fontNormal, new PdfSharp.Drawing.XSolidBrush(darkGray), posRect, fmtLeft);
|
gfx.DrawString(sig.Position, fontNormal, new PdfSharp.Drawing.XSolidBrush(darkGray), posRect, fmtLeft);
|
||||||
row2Y += lineH;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Row 3 (or 2 if no position): Place, Date
|
// Row 3: Place, Date
|
||||||
var placeDate = $"{sig.Place}, {date}";
|
var placeDate = $"{sig.Place}, {date}";
|
||||||
var dateRect = new PdfSharp.Drawing.XRect(x + padding, row2Y, sigW - padding * 2, lineH);
|
var dateRect = new PdfSharp.Drawing.XRect(x + padding, row3Y, sigW - padding * 2, lineH);
|
||||||
gfx.DrawString(placeDate, fontNormal, new PdfSharp.Drawing.XSolidBrush(darkGray), dateRect, fmtLeft);
|
gfx.DrawString(placeDate, fontNormal, new PdfSharp.Drawing.XSolidBrush(darkGray), dateRect, fmtLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user