Enhance EnvelopeViewer UI and integrate receiver data

Updated the `EnvelopeViewer` component to improve the user interface and functionality:
- Added `EnvelopeReceiverService` injection to fetch receiver data.
- Redesigned `envelope-action-bar` for better alignment and responsiveness.
- Displayed dynamic document title and compact badges for receiver info, sender name, signature count, and security features (e.g., access code, 2FA).
- Refactored logout button styling and `envelope-content` layout.
- Introduced `_envelopeReceiver` field and updated `SignatureService.GetAsync` to fetch receiver data.
- Added debugging logs for loaded signatures.
- Added fields for managing signature navigation state.
This commit is contained in:
2026-06-08 08:48:59 +02:00
parent 0b15496adb
commit dbe1ad3b53

View File

@@ -13,6 +13,7 @@
@inject IJSRuntime JSRuntime
@inject SignatureService SignatureService
@inject EnvelopeGenerator.ReceiverUI.Services.AuthService AuthService
@inject EnvelopeGenerator.ReceiverUI.Services.EnvelopeReceiverService EnvelopeReceiverService
@implements IAsyncDisposable
<link href="_content/DevExpress.Blazor.Themes/blazing-berry.bs5.min.css" rel="stylesheet" />
@@ -23,21 +24,71 @@
<script src="js/receiver-signature.js"></script>
<div class="envelope-viewer-layout">
<div class="envelope-action-bar">
<div class="envelope-action-bar__inner">
<div class="d-flex align-items-center gap-3">
<div class="envelope-logo">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 16 16">
<path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/>
</svg>
<div class="envelope-action-bar">
<div class="envelope-action-bar__inner" style="flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5rem 1.5rem; min-height: auto;">
@* Left: Document Title *@
<div style="flex: 0 1 auto; min-width: 0;">
@if (_envelopeReceiver is not null) {
<div style="font-size: 0.95rem; font-weight: 600; color: #1f2937; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
@(_envelopeReceiver.Envelope?.Title ?? "Dokument")
</div>
<div>
<div class="envelope-title">Dokumentenansicht</div>
<div class="envelope-key">ID: @EnvelopeKey</div>
} else {
<div style="font-size: 0.95rem; font-weight: 600; color: #1f2937;">Dokumentenansicht</div>
}
</div>
@* Right: Compact Info + Logout *@
<div class="d-flex align-items-center" style="gap: 1rem; flex: 0 0 auto;">
@if (_envelopeReceiver is not null) {
@* Compact badges row *@
<div class="d-flex flex-wrap align-items-center" style="gap: 0.375rem; font-size: 0.75rem;">
@if (!string.IsNullOrWhiteSpace(_envelopeReceiver.Name)) {
<span style="display: inline-flex; align-items: center; padding: 0.15rem 0.5rem; background: #f3f4f6; border-radius: 0.25rem; color: #374151; white-space: nowrap;">
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" fill="currentColor" class="me-1" viewBox="0 0 16 16">
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6Zm2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4Z"/>
</svg>
@_envelopeReceiver.Name
</span>
}
@if (!string.IsNullOrWhiteSpace(_envelopeReceiver.Envelope?.User?.FullName)) {
<span style="display: inline-flex; align-items-center; padding: 0.15rem 0.5rem; background: #f3f4f6; border-radius: 0.25rem; color: #6b7280; white-space: nowrap;">
Von @_envelopeReceiver.Envelope.User.FullName
</span>
}
@{
int sigCount = _signatures.Count;
}
@if (sigCount > 0) {
<span style="display: inline-flex; align-items: center; padding: 0.15rem 0.5rem; background: #ede9fe; border-radius: 0.25rem; color: #6d28d9; font-weight: 500; white-space: nowrap;">
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" fill="currentColor" class="me-1" viewBox="0 0 16 16">
<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
</svg>
@sigCount
</span>
}
@if (_envelopeReceiver.Envelope?.UseAccessCode == true) {
<span style="display: inline-flex; align-items: center; padding: 0.15rem 0.5rem; background: #fef3c7; border-radius: 0.25rem; color: #92400e; font-weight: 500; white-space: nowrap;">
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" fill="currentColor" class="me-1" viewBox="0 0 16 16">
<path d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z"/>
</svg>
Code
</span>
}
@if (_envelopeReceiver.Envelope?.TFAEnabled == true) {
<span style="display: inline-flex; align-items: center; padding: 0.15rem 0.5rem; background: #dbeafe; border-radius: 0.25rem; color: #1e40af; font-weight: 500; white-space: nowrap;">
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" fill="currentColor" class="me-1" viewBox="0 0 16 16">
<path d="M5.338 1.59a61.44 61.44 0 0 0-2.837.856.481.481 0 0 0-.328.39c-.554 4.157.726 7.19 2.253 9.188a10.725 10.725 0 0 0 2.287 2.233c.346.244.652.42.893.533.12.057.218.095.293.118a.55.55 0 0 0 .101.025.615.615 0 0 0 .1-.025c.076-.023.174-.061.294-.118.24-.113.547-.29.893-.533a10.726 10.726 0 0 0 2.287-2.233c1.527-1.997 2.807-5.031 2.253-9.188a.48.48 0 0 0-.328-.39c-.651-.213-1.75-.56-2.837-.855C9.552 1.29 8.531 1.067 8 1.067c-.53 0-1.552.223-2.662.524zM5.072.56C6.157.265 7.31 0 8 0s1.843.265 2.928.56c1.11.3 2.229.655 2.887.87a1.54 1.54 0 0 1 1.044 1.262c.596 4.477-.787 7.795-2.465 9.99a11.775 11.775 0 0 1-2.517 2.453 7.159 7.159 0 0 1-1.048.625c-.28.132-.581.24-.829.24s-.548-.108-.829-.24a7.158 7.158 0 0 1-1.048-.625 11.777 11.777 0 0 1-2.517-2.453C1.928 10.487.545 7.169 1.141 2.692A1.54 1.54 0 0 1 2.185 1.43 62.456 62.456 0 0 1 5.072.56z"/>
<path d="M10.854 5.146a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7.5 7.793l2.646-2.647a.5.5 0 0 1 .708 0z"/>
</svg>
2FA
</span>
}
</div>
</div>
}
@* Logout button *@
@if (!string.IsNullOrWhiteSpace(EnvelopeKey)) {
<button class="pdf-toolbar__btn" @onclick="LogoutAsync" disabled="@_isLoggingOut" title="Abmelden" style="margin-left: auto;">
<button class="pdf-toolbar__btn" @onclick="LogoutAsync" disabled="@_isLoggingOut" title="Abmelden" style="flex-shrink: 0;">
@if (_isLoggingOut) {
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="width: 14px; height: 14px;"></span>
} else {
@@ -50,8 +101,9 @@
}
</div>
</div>
</div>
<div class="envelope-content">
<div class="envelope-content">
@if (_isLoading) {
<div class="d-flex justify-content-center align-items-center h-100">
<div class="text-center">
@@ -393,6 +445,7 @@ bool _showThumbnails = true;
bool _isLoggingOut = false;
DotNetObjectReference<EnvelopeViewer>? _dotNetRef;
IReadOnlyList<SignatureDto> _signatures = [];
EnvelopeReceiverDto? _envelopeReceiver;
// Signature navigation state
int _totalSignatures = 0;
@@ -455,6 +508,8 @@ const int MaxThumbnailWidth = 400;
var signatures = await SignatureService.GetAsync(EnvelopeKey);
_signatures = signatures.Convert(UnitOfLength.Point);
_envelopeReceiver = await EnvelopeReceiverService.GetAsync(EnvelopeKey);
await JSRuntime.InvokeVoidAsync("console.log", "Loaded signatures:", _signatures);
// Open signature popup on page load