feat(sender): add create-envelope wizard with bootstrap handoff

Introduce a 4-step modal wizard on SenderPage for creating envelopes.

Validate required fields, support optional skip, receiver management, PDF upload, and keyboard shortcuts.

Persist wizard payload in sessionStorage via token-based bootstrap service and navigate to editor with bootstrapToken.

Register bootstrap storage service in both client and server DI containers and add dedicated sender wizard styling.
This commit is contained in:
2026-10-05 14:53:48 +02:00
parent 621e22701a
commit 715add7f51
7 changed files with 1079 additions and 1 deletions

View File

@@ -9,14 +9,17 @@
@using EnvelopeGenerator.Application.Common.Dto.History
@using EnvelopeGenerator.Application.Resources
@using EnvelopeGenerator.Domain.Resources
@using EnvelopeGenerator.Server.Client.Components.Sender
@using Microsoft.Extensions.Localization
@inject EnvelopeGenerator.Server.Client.Services.EnvelopeService EnvelopeService
@inject EnvelopeGenerator.Server.Client.Services.DocumentService DocumentService
@inject EnvelopeGenerator.Server.Client.Services.PdfRenderService PdfRenderService
@inject EnvelopeGenerator.Server.Client.Services.EnvelopeTypeService EnvelopeTypeService
@inject EnvelopeGenerator.Server.Client.Services.AuthService AuthService
@inject EnvelopeGenerator.Server.Client.Services.ThemeService ThemeService
@inject EnvelopeGenerator.Server.Client.Services.UserPreferencesService UserPreferencesService
@inject EnvelopeGenerator.Server.Client.Services.CustomImagesService CustomImagesService
@inject EnvelopeGenerator.Server.Client.Services.CreateEnvelopeBootstrapStorageService CreateEnvelopeBootstrapStorageService
@inject ILogger<SenderPage> Logger
@inject NavigationManager Navigation
@inject IJSRuntime JSRuntime
@@ -414,6 +417,11 @@
</div>
<CreateEnvelopeWizardPopup Visible="@_createWizardVisible"
VisibleChanged="OnCreateWizardVisibleChangedAsync"
EnvelopeTypes="@_envelopeTypes"
StartEditorRequested="StartEditorFromWizardAsync" />
@* ── PDF Vorschau Popup ────────────────────────────────────────────────── *@
<DxPopup @bind-Visible="@_previewVisible"
HeaderText="@($"{L("Preview", "Vorschau")} — {_selectedEnvelope?.Title} (#{_selectedEnvelope?.Id})")"
@@ -567,6 +575,9 @@
private string _deleteReason = string.Empty;
private string? _deleteErrorMessage;
private bool _createWizardVisible = false;
private IReadOnlyList<EnvelopeTypeDto> _envelopeTypes = [];
// Stable keys used to store/restore each tab's layout in UserPreferences.GridLayouts
private const string GridKeyActive = "sender.active-envelopes";
private const string GridKeyCompleted = "sender.completed-envelopes";
@@ -596,6 +607,16 @@
_appLogo = logos.App;
_companyLogo = logos.Company;
try
{
_envelopeTypes = (await EnvelopeTypeService.GetAsync()).ToList();
}
catch (Exception ex)
{
Logger.LogWarning(ex, "Failed to load envelope types for create wizard.");
_envelopeTypes = [];
}
await LoadEnvelopesAsync();
}
@@ -693,7 +714,30 @@
void CreateEnvelope()
{
Navigation.NavigateTo("/sender/editor");
_createWizardVisible = true;
}
Task OnCreateWizardVisibleChangedAsync(bool visible)
{
_createWizardVisible = visible;
return Task.CompletedTask;
}
async Task StartEditorFromWizardAsync(CreateEnvelopeWizardResult result)
{
var payload = new CreateEnvelopeBootstrapPayload(
Title: result.Title,
Message: result.Message,
EnvelopeTypeId: result.EnvelopeTypeId,
Language: result.Language,
Receivers: result.Receivers
.Select(x => new CreateEnvelopeBootstrapReceiver(x.FullName, x.EmailAddress))
.ToList(),
DocumentBytes: result.DocumentBytes,
DocumentFileName: result.DocumentFileName);
var token = await CreateEnvelopeBootstrapStorageService.StoreAsync(payload);
Navigation.NavigateTo($"/sender/editor?bootstrapToken={token}");
}
void EditEnvelope()