Refactor and optimize envelope handling
Refactored `ReadDocumentDetailsQueryHandler` to remove AutoMapper, improving performance by manually selecting necessary data. Simplified `SenderEditorPage.razor` by removing bootstrap logic and updating the UI for a more compact design. Streamlined envelope creation in `SenderPage.razor` by eliminating the bootstrap token. Updated styles in `envelope-viewer.css` for better alignment and readability.
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using EnvelopeGenerator.Application.Common.Dto;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
@@ -21,12 +19,10 @@ public record ReadDocumentDetailsQuery(int EnvelopeId, int? UserId = null) : IRe
|
||||
public class ReadDocumentDetailsQueryHandler : IRequestHandler<ReadDocumentDetailsQuery, DocumentDetailsDto?>
|
||||
{
|
||||
private readonly IRepository<Document> _repo;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public ReadDocumentDetailsQueryHandler(IRepository<Document> repo, IMapper mapper)
|
||||
public ReadDocumentDetailsQueryHandler(IRepository<Document> repo)
|
||||
{
|
||||
_repo = repo;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<DocumentDetailsDto?> Handle(ReadDocumentDetailsQuery query, CancellationToken cancel)
|
||||
@@ -36,9 +32,71 @@ public class ReadDocumentDetailsQueryHandler : IRequestHandler<ReadDocumentDetai
|
||||
if (query.UserId is int userId)
|
||||
docQuery = docQuery.Where(doc => doc.Envelope.UserId == userId);
|
||||
|
||||
return await docQuery
|
||||
// shortcut: manual select avoids AutoMapper ProjectTo pulling BYTE_DATA and the full navigation
|
||||
// graph (Envelope → History → User etc.) which caused an extremely slow query (~87s).
|
||||
// Annotations navigation is IEnumerable<T> on the entity (not ICollection<T>), so EF Core
|
||||
// cannot translate it in a Select projection — we skip it entirely here; sender editor only
|
||||
// needs element coordinates, not annotation payloads.
|
||||
var raw = await docQuery
|
||||
.Where(d => d.EnvelopeId == query.EnvelopeId)
|
||||
.ProjectTo<DocumentDetailsDto>(_mapper.ConfigurationProvider)
|
||||
.Select(d => new
|
||||
{
|
||||
d.Id,
|
||||
d.EnvelopeId,
|
||||
d.AddedWhen,
|
||||
Elements = d.Elements == null ? null : d.Elements.Select(e => new
|
||||
{
|
||||
e.Id,
|
||||
e.DocumentId,
|
||||
e.ReceiverId,
|
||||
e.ElementType,
|
||||
e.X,
|
||||
e.Y,
|
||||
e.Width,
|
||||
e.Height,
|
||||
e.Page,
|
||||
e.Required,
|
||||
e.Tooltip,
|
||||
e.ReadOnly,
|
||||
e.AnnotationIndex,
|
||||
e.AddedWhen,
|
||||
e.ChangedWhen,
|
||||
e.FullName,
|
||||
e.Position,
|
||||
e.Place,
|
||||
}).ToList()
|
||||
})
|
||||
.FirstOrDefaultAsync(cancel);
|
||||
|
||||
if (raw is null)
|
||||
return null;
|
||||
|
||||
return new DocumentDetailsDto
|
||||
{
|
||||
Id = raw.Id,
|
||||
EnvelopeId = raw.EnvelopeId,
|
||||
AddedWhen = raw.AddedWhen,
|
||||
Elements = raw.Elements?.Select(e => new DocReceiverElementDto
|
||||
{
|
||||
Id = e.Id,
|
||||
DocumentId = e.DocumentId,
|
||||
ReceiverId = e.ReceiverId,
|
||||
ElementType = e.ElementType,
|
||||
X = e.X,
|
||||
Y = e.Y,
|
||||
Width = e.Width,
|
||||
Height = e.Height,
|
||||
Page = e.Page,
|
||||
Required = e.Required,
|
||||
Tooltip = e.Tooltip,
|
||||
ReadOnly = e.ReadOnly,
|
||||
AnnotationIndex = e.AnnotationIndex,
|
||||
AddedWhen = e.AddedWhen,
|
||||
ChangedWhen = e.ChangedWhen,
|
||||
FullName = e.FullName,
|
||||
Position = e.Position,
|
||||
Place = e.Place,
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
@using EnvelopeGenerator.Application.Common.Configurations
|
||||
@using EnvelopeGenerator.Server.Client.Components.PdfEditor
|
||||
@using EnvelopeGenerator.Server.Client.Services
|
||||
@using Microsoft.AspNetCore.WebUtilities
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.Extensions.Localization
|
||||
@using Microsoft.Extensions.Options
|
||||
@@ -24,7 +23,6 @@
|
||||
@inject EnvelopeReceiverService EnvelopeReceiverService
|
||||
@inject EnvelopeService EnvelopeService
|
||||
@inject EnvelopeTypeService EnvelopeTypeService
|
||||
@inject CreateEnvelopeBootstrapStorageService CreateEnvelopeBootstrapStorageService
|
||||
@inject AuthService AuthService
|
||||
@inject IStringLocalizer<Resource> UiLocalizer
|
||||
@inject IStringLocalizer<Model> ModelLocalizer
|
||||
@@ -35,11 +33,6 @@
|
||||
<link href="@AppVersion.GetVersionedUrl("css/envelope-viewer.css")" rel="stylesheet" />
|
||||
<script src="@AppVersion.GetVersionedUrl("js/envelope-editor.js")"></script>
|
||||
|
||||
<DxLoadingPanel Visible="@IsBootstrapLoading"
|
||||
IsContentBlocked="true"
|
||||
ApplyBackgroundShading="true"
|
||||
IndicatorAreaVisible="true"
|
||||
Text='@L("SenderEditor_BootstrapLoadingHint", "Der Umschlag wird vorbereitet und die Daten werden uebernommen.")'>
|
||||
<div class="envelope-viewer-layout">
|
||||
|
||||
@* ── Action Bar ── *@
|
||||
@@ -199,20 +192,43 @@
|
||||
</div>
|
||||
|
||||
<div class="sender-receivers-panel">
|
||||
<div class="sender-receivers-panel__header">
|
||||
<div class="sender-receivers-panel__title-wrap">
|
||||
<span class="sender-receivers-panel__title">@L("Receiver", "Empfänger")</span>
|
||||
<span class="sender-receivers-panel__count">@_receivers.Count</span>
|
||||
</div>
|
||||
|
||||
<button class="pdf-toolbar__btn pdf-toolbar__btn--signature-change sender-toolbar-action-btn"
|
||||
<div class="sender-receivers-list">
|
||||
<button class="pdf-toolbar__btn pdf-toolbar__btn--signature-change sender-toolbar-action-btn sender-receivers-list__add-btn"
|
||||
@onclick="OpenAddReceiverPopup"
|
||||
title='@L("AddReceiver", "Empfänger hinzufügen")'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M8 1a.5.5 0 0 1 .5.5v6h6a.5.5 0 0 1 0 1h-6v6a.5.5 0 0 1-1 0v-6h-6a.5.5 0 0 1 0-1h6v-6A.5.5 0 0 1 8 1z" />
|
||||
</svg>
|
||||
<span class="pdf-toolbar__btn-text">@L("AddReceiver", "Empfänger hinzufügen")</span>
|
||||
<span class="sender-receivers-list__add-text">@L("AddReceiver", "Empfänger hinzufügen")</span>
|
||||
<span class="badge rounded-pill text-bg-primary sender-receivers-list__count">@_receivers.Count</span>
|
||||
</button>
|
||||
|
||||
@foreach (var receiver in _receivers)
|
||||
{
|
||||
<div class="sender-receiver-chip" style="--receiver-color:@receiver.Color;">
|
||||
<div class="sender-receiver-chip__body">
|
||||
<div class="sender-receiver-chip__email" title="@receiver.FullName">@receiver.Email</div>
|
||||
</div>
|
||||
|
||||
<button class="pdf-toolbar__btn pdf-toolbar__btn--reset sender-toolbar-action-btn sender-toolbar-action-btn--compact sender-receiver-chip__action sender-receiver-chip__remove-btn"
|
||||
@onclick="() => RemoveReceiverAsync(receiver)"
|
||||
title='@L("Delete", "Loeschen")'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M4 4L12 12M12 4L4 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button class="pdf-toolbar__btn pdf-toolbar__btn--signature-change sender-toolbar-action-btn sender-toolbar-action-btn--compact sender-receiver-chip__action sender-receiver-chip__place-btn
|
||||
@(_pendingReceiverForPlacement?.Id == receiver.Id ? "sender-receiver-chip__place-btn--active" : "sender-receiver-chip__place-btn--idle")"
|
||||
@onclick="() => ActivatePlacementForReceiver(receiver)"
|
||||
style="--receiver-color:@receiver.Color;"
|
||||
title='@L("SenderEditor_PlaceSignatureField", "Signaturfeld platzieren")'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" 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-10z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (_receivers.Count == 0)
|
||||
@@ -221,43 +237,6 @@
|
||||
@L("SenderEditor_NoReceiversAdded", "Noch keine Empfänger hinzugefügt.")
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="sender-receivers-list">
|
||||
@foreach (var receiver in _receivers)
|
||||
{
|
||||
<div class="sender-receiver-chip" style="--receiver-color:@receiver.Color;">
|
||||
<div class="sender-receiver-chip__body">
|
||||
<div class="sender-receiver-chip__name">@receiver.FullName</div>
|
||||
<div class="sender-receiver-chip__email">@receiver.Email</div>
|
||||
@if (!string.IsNullOrWhiteSpace(receiver.PhoneNumber))
|
||||
{
|
||||
<div class="sender-receiver-chip__phone">@receiver.PhoneNumber</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<button class="pdf-toolbar__btn pdf-toolbar__btn--reset sender-toolbar-action-btn sender-toolbar-action-btn--compact sender-receiver-chip__action sender-receiver-chip__remove-btn"
|
||||
@onclick="() => RemoveReceiverAsync(receiver)"
|
||||
title='@L("Delete", "Loeschen")'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M4 4L12 12M12 4L4 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button class="pdf-toolbar__btn pdf-toolbar__btn--signature-change sender-toolbar-action-btn sender-toolbar-action-btn--compact sender-receiver-chip__place-btn
|
||||
@(_pendingReceiverForPlacement?.Id == receiver.Id ? "sender-receiver-chip__place-btn--active" : "sender-receiver-chip__place-btn--idle")"
|
||||
@onclick="() => ActivatePlacementForReceiver(receiver)"
|
||||
style="--receiver-color:@receiver.Color;"
|
||||
title='@L("SenderEditor_PlaceSignatureField", "Signaturfeld platzieren")'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" 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-10z" />
|
||||
</svg>
|
||||
<span class="pdf-toolbar__btn-text">@L("SenderEditor_AddSignature", "Signatur hinzufügen")</span>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -442,7 +421,6 @@
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</DxLoadingPanel>
|
||||
|
||||
<DxPopup @bind-Visible="_receiverPopupVisible"
|
||||
HeaderText='@L("AddReceiver", "Empfänger hinzufügen")'
|
||||
@@ -607,9 +585,6 @@
|
||||
[SupplyParameterFromQuery(Name = "envelopeId")]
|
||||
public int? EnvelopeId { get; set; }
|
||||
|
||||
[SupplyParameterFromQuery(Name = "bootstrapToken")]
|
||||
public string? BootstrapToken { get; set; }
|
||||
|
||||
// ── Signature field size (configurable) ──
|
||||
double SigWidthInches => SignatureFieldOptions.CurrentValue.Width;
|
||||
double SigHeightInches => SignatureFieldOptions.CurrentValue.Height;
|
||||
@@ -629,10 +604,7 @@
|
||||
int _pageCount;
|
||||
List<PdfPageImageDto> _pagePreviews = [];
|
||||
int? _loadedEnvelopeId;
|
||||
int? _editingEnvelopeId;
|
||||
bool _isLoadingEnvelopeForEdit;
|
||||
bool _bootstrapApplied;
|
||||
string? _pendingBootstrapToken;
|
||||
|
||||
List<SignatureFieldDraft> _signatureFields = [];
|
||||
ReceiverDraft? _pendingReceiverForPlacement; // Set when user clicks "Signatur hinzufügen"
|
||||
@@ -653,7 +625,6 @@
|
||||
string _saveSuccessTitle = "Vorgang erfolgreich abgeschlossen.";
|
||||
string _saveSuccessBody = string.Empty;
|
||||
bool _saveNavigateToDashboard = false;
|
||||
bool _saveDraftAfterBootstrap;
|
||||
|
||||
// ── Envelope metadata ──
|
||||
string _envelopeTitle = string.Empty;
|
||||
@@ -687,9 +658,6 @@
|
||||
|
||||
static readonly System.ComponentModel.DataAnnotations.EmailAddressAttribute ReceiverEmailValidator = new();
|
||||
|
||||
bool IsEditMode => EnvelopeId.HasValue && EnvelopeId.Value > 0;
|
||||
bool IsBootstrapLoading => !IsEditMode && !_bootstrapApplied;
|
||||
|
||||
// ── Lifecycle ──
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@@ -704,12 +672,6 @@
|
||||
try
|
||||
{
|
||||
_envelopeTypes = (await EnvelopeTypeService.GetAsync()).ToList();
|
||||
|
||||
if (!IsEditMode && _selectedEnvelopeTypeId is null && _envelopeTypes.Count > 0)
|
||||
{
|
||||
_selectedEnvelopeTypeId = _envelopeTypes[0].Id;
|
||||
ApplyEnvelopeTypeDefaults(_envelopeTypes[0]);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -720,96 +682,14 @@
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (EnvelopeId.HasValue && EnvelopeId.Value > 0)
|
||||
_editingEnvelopeId = EnvelopeId.Value;
|
||||
|
||||
if (IsEditMode && _loadedEnvelopeId != EnvelopeId)
|
||||
{
|
||||
await LoadEnvelopeForEditAsync(EnvelopeId!.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsEditMode || _bootstrapApplied)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(BootstrapToken))
|
||||
if (!EnvelopeId.HasValue || EnvelopeId.Value <= 0)
|
||||
{
|
||||
NavigationManager.NavigateTo("/sender", replace: true);
|
||||
return;
|
||||
}
|
||||
|
||||
_pendingBootstrapToken = BootstrapToken;
|
||||
}
|
||||
|
||||
async Task TryApplyBootstrapAsync(string token)
|
||||
{
|
||||
var payload = await CreateEnvelopeBootstrapStorageService.ConsumeAsync(token);
|
||||
_bootstrapApplied = true;
|
||||
|
||||
if (payload is null)
|
||||
{
|
||||
NavigationManager.NavigateTo("/sender", replace: true);
|
||||
return;
|
||||
}
|
||||
|
||||
_envelopeTitle = payload.Title ?? string.Empty;
|
||||
_envelopeMessage = payload.Message ?? string.Empty;
|
||||
_selectedEnvelopeTypeId = payload.EnvelopeTypeId;
|
||||
_language = string.IsNullOrWhiteSpace(payload.Language) ? "de-DE" : payload.Language;
|
||||
_useAccessCode = payload.UseAccessCode;
|
||||
_tfaEnabled = payload.TFAEnabled;
|
||||
_certificationType = payload.CertificationType;
|
||||
_finalEmailToCreator = payload.FinalEmailToCreator;
|
||||
_finalEmailToReceivers = payload.FinalEmailToReceivers;
|
||||
_sendReminderEmails = payload.SendReminderEmails;
|
||||
_firstReminderDays = payload.FirstReminderDays;
|
||||
_reminderIntervalDays = payload.ReminderIntervalDays;
|
||||
_expiresWhenDays = payload.ExpiresWhenDays;
|
||||
_expiresWarningWhenDays = payload.ExpiresWarningWhenDays;
|
||||
NormalizeSettings();
|
||||
|
||||
_receivers = payload.Receivers
|
||||
.Select((receiver, index) => new ReceiverDraft(
|
||||
Guid.NewGuid(),
|
||||
null,
|
||||
receiver.FullName,
|
||||
receiver.EmailAddress,
|
||||
string.Empty,
|
||||
ReceiverPalette[index % ReceiverPalette.Length]))
|
||||
.ToList();
|
||||
|
||||
_originalPdfBytes = payload.DocumentBytes;
|
||||
_fileName = string.IsNullOrWhiteSpace(payload.DocumentFileName) ? "document.pdf" : payload.DocumentFileName;
|
||||
_pdfLoaded = _originalPdfBytes is { Length: > 0 };
|
||||
|
||||
_signatureFields.Clear();
|
||||
_pendingReceiverForPlacement = null;
|
||||
|
||||
_saveDraftAfterBootstrap = true;
|
||||
|
||||
if (_pdfLoaded && _originalPdfBytes is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_pagePreviews = (await PdfRenderService.RenderPagesAsync(_originalPdfBytes))
|
||||
.OrderBy(p => p.PageNumber)
|
||||
.ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Failed to render bootstrap PDF previews.");
|
||||
_pagePreviews = [];
|
||||
}
|
||||
|
||||
_pageCount = _pagePreviews.Count > 0
|
||||
? _pagePreviews.Count
|
||||
: EstimatePdfPageCount(_originalPdfBytes) ?? 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_pagePreviews = [];
|
||||
_pageCount = 0;
|
||||
}
|
||||
if (_loadedEnvelopeId != EnvelopeId && !_isLoadingEnvelopeForEdit)
|
||||
await LoadEnvelopeForEditAsync(EnvelopeId.Value);
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
@@ -819,26 +699,6 @@
|
||||
_isPageReady = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
if (!OperatingSystem.IsBrowser())
|
||||
return;
|
||||
|
||||
if (!IsEditMode && !_bootstrapApplied && !string.IsNullOrWhiteSpace(_pendingBootstrapToken))
|
||||
{
|
||||
var token = _pendingBootstrapToken;
|
||||
_pendingBootstrapToken = null;
|
||||
await TryApplyBootstrapAsync(token);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_saveDraftAfterBootstrap && !_isSaving)
|
||||
{
|
||||
_saveDraftAfterBootstrap = false;
|
||||
await SaveDraftAndStayOnEditorAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async Task LoadEnvelopeForEditAsync(int envelopeId)
|
||||
@@ -960,7 +820,6 @@
|
||||
_fileName = $"Envelope-{envelopeId}.pdf";
|
||||
_pendingReceiverForPlacement = null;
|
||||
_loadedEnvelopeId = envelopeId;
|
||||
_editingEnvelopeId = envelopeId;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1263,9 +1122,8 @@
|
||||
|
||||
try
|
||||
{
|
||||
var previousEnvelopeId = EnvelopeId;
|
||||
var command = BuildCreateEnvelopeCommand(send: false);
|
||||
Logger.LogInformation("[SenderEditor] Saving draft. EnvelopeId={EnvelopeId} IsEditMode={IsEditMode}", command.EnvelopeId, IsEditMode);
|
||||
Logger.LogInformation("[SenderEditor] Saving draft. EnvelopeId={EnvelopeId}", command.EnvelopeId);
|
||||
var result = await EnvelopeService.UpsertAsync(command);
|
||||
|
||||
if (result is null)
|
||||
@@ -1273,16 +1131,9 @@
|
||||
|
||||
EnvelopeId = result.Id;
|
||||
_loadedEnvelopeId = result.Id;
|
||||
_editingEnvelopeId = result.Id;
|
||||
|
||||
if (navigateToDashboard)
|
||||
{
|
||||
NavigationManager.NavigateTo("/sender", forceLoad: false);
|
||||
}
|
||||
else if (previousEnvelopeId != result.Id)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/sender/editor?envelopeId={result.Id}", forceLoad: false);
|
||||
}
|
||||
|
||||
return result.Id;
|
||||
}
|
||||
@@ -1300,12 +1151,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
async Task SaveDraftAndStayOnEditorAsync()
|
||||
{
|
||||
Logger.LogInformation("[SenderEditor] Trigger bootstrap draft save.");
|
||||
await SaveDraftCoreAsync(navigateToDashboard: false);
|
||||
}
|
||||
|
||||
// ── Send envelope ──
|
||||
async Task SendAsync()
|
||||
{
|
||||
@@ -1360,21 +1205,10 @@
|
||||
|
||||
try
|
||||
{
|
||||
var envelopeId = _editingEnvelopeId
|
||||
?? EnvelopeId
|
||||
?? _loadedEnvelopeId
|
||||
?? GetEnvelopeIdFromCurrentUri();
|
||||
var envelopeId = EnvelopeId ?? _loadedEnvelopeId;
|
||||
|
||||
if (!envelopeId.HasValue || envelopeId.Value <= 0)
|
||||
{
|
||||
var saveResult = await EnvelopeService.UpsertAsync(BuildCreateEnvelopeCommand(send: false))
|
||||
?? throw new InvalidOperationException("Draft save did not return a result.");
|
||||
|
||||
envelopeId = saveResult.Id;
|
||||
EnvelopeId = saveResult.Id;
|
||||
_loadedEnvelopeId = saveResult.Id;
|
||||
_editingEnvelopeId = saveResult.Id;
|
||||
}
|
||||
throw new InvalidOperationException("Envelope id is missing for send.");
|
||||
|
||||
var sendResult = await EnvelopeService.UpsertAsync(BuildCreateEnvelopeCommand(send: true) with { EnvelopeId = envelopeId })
|
||||
?? throw new InvalidOperationException("Send did not return a result.");
|
||||
@@ -1405,13 +1239,10 @@
|
||||
{
|
||||
NormalizeSettings();
|
||||
|
||||
var envelopeId = _editingEnvelopeId
|
||||
?? EnvelopeId
|
||||
?? _loadedEnvelopeId
|
||||
?? GetEnvelopeIdFromCurrentUri();
|
||||
var envelopeId = EnvelopeId ?? _loadedEnvelopeId;
|
||||
|
||||
if (IsEditMode && envelopeId is null)
|
||||
throw new InvalidOperationException("Edit mode envelope id is missing.");
|
||||
if (envelopeId is null)
|
||||
throw new InvalidOperationException("Envelope id is missing.");
|
||||
|
||||
var docBase64 = _originalPdfBytes is { Length: > 0 }
|
||||
? Convert.ToBase64String(_originalPdfBytes)
|
||||
@@ -1499,28 +1330,6 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
int? GetEnvelopeIdFromCurrentUri()
|
||||
{
|
||||
try
|
||||
{
|
||||
var uri = NavigationManager.ToAbsoluteUri(NavigationManager.Uri);
|
||||
if (string.IsNullOrWhiteSpace(uri.Query))
|
||||
return null;
|
||||
|
||||
var query = QueryHelpers.ParseQuery(uri.Query);
|
||||
if (!query.TryGetValue("envelopeId", out var raw))
|
||||
return null;
|
||||
|
||||
return int.TryParse(raw.ToString(), out var id) && id > 0
|
||||
? id
|
||||
: null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Receiver popup ──
|
||||
void OpenAddReceiverPopup()
|
||||
{
|
||||
@@ -1693,10 +1502,7 @@
|
||||
if (_isSaving)
|
||||
return;
|
||||
|
||||
var envelopeId = _editingEnvelopeId
|
||||
?? EnvelopeId
|
||||
?? _loadedEnvelopeId
|
||||
?? GetEnvelopeIdFromCurrentUri();
|
||||
var envelopeId = EnvelopeId ?? _loadedEnvelopeId;
|
||||
|
||||
if (envelopeId is int persistedEnvelopeId && persistedEnvelopeId > 0 && receiver.ReceiverId is int persistedReceiverId && persistedReceiverId > 0)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
@using EnvelopeGenerator.Server.Client.Services
|
||||
@using EnvelopeGenerator.Application.Common.Dto
|
||||
@using EnvelopeGenerator.Application.Common.Dto.History
|
||||
@using EnvelopeGenerator.Application.Envelopes.Commands
|
||||
@using EnvelopeGenerator.Application.Resources
|
||||
@using EnvelopeGenerator.Domain.Resources
|
||||
@using EnvelopeGenerator.Server.Client.Components.Sender
|
||||
@@ -21,7 +22,6 @@
|
||||
@inject EnvelopeGenerator.Server.Client.Services.CustomImagesService CustomImagesService
|
||||
@inject EnvelopeGenerator.Server.Client.Services.PdfSignatureService PdfSignatureService
|
||||
@inject EnvelopeGenerator.Server.Client.Services.EnvelopeReceiverService EnvelopeReceiverService
|
||||
@inject EnvelopeGenerator.Server.Client.Services.CreateEnvelopeBootstrapStorageService CreateEnvelopeBootstrapStorageService
|
||||
@inject ILogger<SenderPage> Logger
|
||||
@inject NavigationManager Navigation
|
||||
@inject IJSRuntime JSRuntime
|
||||
@@ -744,29 +744,44 @@
|
||||
|
||||
async Task StartEditorFromWizardAsync(CreateEnvelopeWizardResult result)
|
||||
{
|
||||
var payload = new CreateEnvelopeBootstrapPayload(
|
||||
Title: result.Title,
|
||||
Message: result.Message,
|
||||
EnvelopeTypeId: result.EnvelopeTypeId,
|
||||
Language: result.Language,
|
||||
UseAccessCode: result.UseAccessCode,
|
||||
TFAEnabled: result.TFAEnabled,
|
||||
CertificationType: result.CertificationType,
|
||||
FinalEmailToCreator: result.FinalEmailToCreator,
|
||||
FinalEmailToReceivers: result.FinalEmailToReceivers,
|
||||
SendReminderEmails: result.SendReminderEmails,
|
||||
FirstReminderDays: result.FirstReminderDays,
|
||||
ReminderIntervalDays: result.ReminderIntervalDays,
|
||||
ExpiresWhenDays: result.ExpiresWhenDays,
|
||||
ExpiresWarningWhenDays: result.ExpiresWarningWhenDays,
|
||||
Receivers: result.Receivers
|
||||
.Select(x => new CreateEnvelopeBootstrapReceiver(x.FullName, x.EmailAddress))
|
||||
.ToList(),
|
||||
DocumentBytes: result.DocumentBytes,
|
||||
DocumentFileName: result.DocumentFileName);
|
||||
var docBase64 = result.DocumentBytes is { Length: > 0 }
|
||||
? Convert.ToBase64String(result.DocumentBytes)
|
||||
: null;
|
||||
|
||||
var token = await CreateEnvelopeBootstrapStorageService.StoreAsync(payload);
|
||||
Navigation.NavigateTo($"/sender/editor?bootstrapToken={token}");
|
||||
var command = new CreateEnvelopeCommand
|
||||
{
|
||||
EnvelopeTypeId = result.EnvelopeTypeId,
|
||||
Title = result.Title,
|
||||
Message = result.Message,
|
||||
Language = string.IsNullOrWhiteSpace(result.Language) ? "de-DE" : result.Language,
|
||||
UseAccessCode = result.UseAccessCode,
|
||||
TFAEnabled = result.TFAEnabled,
|
||||
CertificationType = result.CertificationType,
|
||||
FinalEmailToCreator = result.FinalEmailToCreator,
|
||||
FinalEmailToReceivers = result.FinalEmailToReceivers,
|
||||
SendReminderEmails = result.SendReminderEmails,
|
||||
FirstReminderDays = result.FirstReminderDays,
|
||||
ReminderIntervalDays = result.ReminderIntervalDays,
|
||||
ExpiresWhenDays = result.ExpiresWhenDays,
|
||||
ExpiresWarningWhenDays = result.ExpiresWarningWhenDays,
|
||||
Send = false,
|
||||
Document = string.IsNullOrWhiteSpace(docBase64) ? null : new CreateEnvelopeDocumentDto(docBase64),
|
||||
Receivers = result.Receivers
|
||||
.Select(x => new CreateEnvelopeReceiverDto(x.EmailAddress, x.FullName, null, []))
|
||||
.ToList()
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var envelope = await EnvelopeService.UpsertAsync(command)
|
||||
?? throw new InvalidOperationException("Envelope creation returned no result.");
|
||||
Navigation.NavigateTo($"/sender/editor?envelopeId={envelope.Id}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Failed to create envelope from wizard.");
|
||||
_errorMessage = ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
void EditEnvelope()
|
||||
|
||||
@@ -989,7 +989,7 @@ body.resizing {
|
||||
}
|
||||
|
||||
.sender-toolbar-action-btn--compact {
|
||||
padding: 0.45rem 0.7rem;
|
||||
padding: 0.28rem 0.42rem;
|
||||
}
|
||||
|
||||
.sender-receivers-panel {
|
||||
@@ -1208,23 +1208,68 @@ body.resizing {
|
||||
.sender-receivers-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.625rem;
|
||||
gap: 0.4rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sender-receivers-list__add-btn {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.38rem;
|
||||
min-height: 30px;
|
||||
padding: 0.35rem 0.6rem;
|
||||
margin-right: 0.28rem;
|
||||
}
|
||||
|
||||
.sender-receivers-list__add-text {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.sender-receivers-list__count {
|
||||
position: absolute;
|
||||
top: -0.45rem;
|
||||
right: -0.45rem;
|
||||
min-width: 1.15rem;
|
||||
height: 1.15rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.65rem;
|
||||
font-weight: 700;
|
||||
padding: 0 0.3rem;
|
||||
}
|
||||
|
||||
.sender-receiver-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
min-width: 220px;
|
||||
gap: 0.35rem;
|
||||
min-width: 190px;
|
||||
max-width: 100%;
|
||||
padding: 0.625rem 0.75rem;
|
||||
border-radius: 12px;
|
||||
padding: 0.28rem 0.4rem;
|
||||
border-radius: 9px;
|
||||
background: rgba(255, 255, 255, 0.88);
|
||||
border: 1px solid rgba(126, 34, 206, 0.12);
|
||||
border-left: 3px solid var(--receiver-color, #7c3aed);
|
||||
box-shadow: 0 6px 16px rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
|
||||
.sender-receiver-chip__action {
|
||||
min-width: auto;
|
||||
min-height: 26px;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
padding: 0;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.sender-receiver-chip__action svg {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
}
|
||||
|
||||
.sender-receiver-chip__place-btn--idle {
|
||||
color: var(--receiver-color, #7c3aed);
|
||||
border-color: var(--receiver-color, #7c3aed);
|
||||
@@ -1516,8 +1561,9 @@ body.resizing {
|
||||
}
|
||||
|
||||
.sender-receiver-chip__email {
|
||||
margin-top: 0.15rem;
|
||||
font-size: 0.72rem;
|
||||
margin-top: 0;
|
||||
font-size: 0.7rem;
|
||||
line-height: 1.1;
|
||||
color: #64748b;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
Reference in New Issue
Block a user