Add Title and Message fields to Envelope editor

Introduced input fields for "Title" and "Message" in the
EnvelopeSenderEditorPage, allowing users to specify metadata
for envelopes. The "Title" field is required and validated,
while the "Message" field is optional with a default value.

Updated the save logic to validate the title and display
appropriate error messages. Persisted the "Title" and
"Message" fields in the session cache via the
`EditorSessionData` record.

Modified the `CreateEnvelopeReceiverCommand` to use the
user-provided "Title" and "Message" values. Adjusted the UI
layout and styling to accommodate the new fields, ensuring
a seamless user experience.
This commit is contained in:
2026-07-02 02:00:14 +02:00
parent 1b2731b4b2
commit 40c95100ff

View File

@@ -28,14 +28,55 @@
<div class="envelope-action-bar__inner" <div class="envelope-action-bar__inner"
style="flex-direction: row; align-items: center; padding: 0.35rem 1.5rem; gap: 0.75rem;"> style="flex-direction: row; align-items: center; padding: 0.35rem 1.5rem; gap: 0.75rem;">
@* Left: Title *@ @* Left: Title + meta inputs + receivers *@
<div style="flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: stretch; gap: 0.75rem;"> <div style="flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: stretch; gap: 0.5rem;">
<div style="display: flex; align-items: center; gap: 0.75rem; min-width: 0; flex-wrap: wrap;">
<div style="font-size: 0.9rem; font-weight: 600; color: #1f2937; white-space: nowrap;"> @* ── Title & Message inputs ── *@
Neues Dokument <div style="display: flex; align-items: flex-start; gap: 0.5rem; flex-wrap: wrap;">
@* Titel *@
<div style="display: flex; flex-direction: column; gap: 0.2rem; flex: 1 1 180px; min-width: 140px;">
<label style="font-size: 0.65rem; font-weight: 600; color: #6b7280;
text-transform: uppercase; letter-spacing: 0.05em;">
Titel <span style="color:#dc2626;">*</span>
</label>
<input type="text"
value="@_envelopeTitle"
@oninput="e => _envelopeTitle = e.Value?.ToString() ?? string.Empty"
placeholder="z.B. Arbeitsvertrag 2025"
maxlength="120"
style="font-size: 0.78rem; font-weight: 500; color: #1f2937;
border: 1px solid #d1d5db; border-radius: 5px;
padding: 0.3rem 0.55rem; outline: none; width: 100%;
background: #fff;
@(_titleTouched && string.IsNullOrWhiteSpace(_envelopeTitle) ? "border-color:#dc2626;" : "")" />
@if (_titleTouched && string.IsNullOrWhiteSpace(_envelopeTitle))
{
<span style="font-size:0.6rem;color:#dc2626;">Pflichtfeld</span>
}
</div> </div>
@* Nachricht *@
<div style="display: flex; flex-direction: column; gap: 0.2rem; flex: 2 1 260px; min-width: 180px;">
<label style="font-size: 0.65rem; font-weight: 600; color: #6b7280;
text-transform: uppercase; letter-spacing: 0.05em;">
Nachricht <span style="color:#6b7280;font-weight:400;">(optional)</span>
</label>
<input type="text"
value="@_envelopeMessage"
@oninput="e => _envelopeMessage = e.Value?.ToString() ?? string.Empty"
placeholder="Bitte unterzeichnen Sie das beigefügte Dokument."
maxlength="500"
style="font-size: 0.78rem; color: #1f2937;
border: 1px solid #d1d5db; border-radius: 5px;
padding: 0.3rem 0.55rem; outline: none; width: 100%;
background: #fff;" />
</div>
@* PDF filename badge *@
@if (_pdfLoaded) @if (_pdfLoaded)
{ {
<div style="display:flex;align-items:flex-end;gap:0.4rem;padding-bottom:0.05rem;flex-shrink:0;">
<span style="font-size: 0.7rem; color: #6b7280;">@_fileName</span> <span style="font-size: 0.7rem; color: #6b7280;">@_fileName</span>
@if (_signatureFields.Count > 0) @if (_signatureFields.Count > 0)
{ {
@@ -45,6 +86,7 @@
@_signatureFields.Count Signaturfeld@(_signatureFields.Count != 1 ? "er" : "") @_signatureFields.Count Signaturfeld@(_signatureFields.Count != 1 ? "er" : "")
</span> </span>
} }
</div>
} }
</div> </div>
@@ -419,6 +461,11 @@
bool _savePopupVisible = false; bool _savePopupVisible = false;
string? _saveErrorMessage = null; string? _saveErrorMessage = null;
// ── Envelope metadata ──
string _envelopeTitle = string.Empty;
string _envelopeMessage = string.Empty;
bool _titleTouched = false;
List<ReceiverDraft> _receivers = []; List<ReceiverDraft> _receivers = [];
bool _receiverPopupVisible; bool _receiverPopupVisible;
string _receiverDraftName = string.Empty; string _receiverDraftName = string.Empty;
@@ -469,6 +516,8 @@
_fileName = cached.FileName; _fileName = cached.FileName;
_pdfLoaded = _originalPdfBytes is { Length: > 0 }; _pdfLoaded = _originalPdfBytes is { Length: > 0 };
_receivers = cached.Receivers; _receivers = cached.Receivers;
_envelopeTitle = cached.Title;
_envelopeMessage = cached.Message;
// Redraw placeholders onto the original PDF // Redraw placeholders onto the original PDF
if (_pdfLoaded) if (_pdfLoaded)
@@ -625,12 +674,19 @@
async Task SaveAsync() async Task SaveAsync()
{ {
// ── Validation ── // ── Validation ──
_titleTouched = true;
if (!_pdfLoaded || _originalPdfBytes is null) if (!_pdfLoaded || _originalPdfBytes is null)
{ {
_saveErrorMessage = "Bitte laden Sie zuerst ein PDF-Dokument hoch."; _saveErrorMessage = "Bitte laden Sie zuerst ein PDF-Dokument hoch.";
_savePopupVisible = true; _savePopupVisible = true;
return; return;
} }
if (string.IsNullOrWhiteSpace(_envelopeTitle))
{
_saveErrorMessage = "Bitte geben Sie einen Titel ein.";
_savePopupVisible = true;
return;
}
if (_receivers.Count == 0) if (_receivers.Count == 0)
{ {
_saveErrorMessage = "Bitte fügen Sie mindestens einen Empfänger hinzu."; _saveErrorMessage = "Bitte fügen Sie mindestens einen Empfänger hinzu.";
@@ -690,8 +746,10 @@
var command = new CreateEnvelopeReceiverCommand var command = new CreateEnvelopeReceiverCommand
{ {
Title = "Neuer Umschlag", // placeholder — dedicated field will be added later Title = _envelopeTitle.Trim(),
Message = "Bitte unterzeichnen Sie das beigefügte Dokument.", Message = string.IsNullOrWhiteSpace(_envelopeMessage)
? "Bitte unterzeichnen Sie das beigefügte Dokument."
: _envelopeMessage.Trim(),
TFAEnabled = false, TFAEnabled = false,
Document = new DocumentCreateCommand { DataAsBase64 = docBase64 }, Document = new DocumentCreateCommand { DataAsBase64 = docBase64 },
Receivers = receiversCmd, Receivers = receiversCmd,
@@ -728,7 +786,9 @@
OriginalPdfBytes: _originalPdfBytes ?? [], OriginalPdfBytes: _originalPdfBytes ?? [],
Fields: [.. _signatureFields], Fields: [.. _signatureFields],
FileName: _fileName, FileName: _fileName,
Receivers: [.. _receivers]); Receivers: [.. _receivers],
Title: _envelopeTitle,
Message: _envelopeMessage);
MemoryCache.Set(SessionKey, data, SessionTtl); MemoryCache.Set(SessionKey, data, SessionTtl);
} }
@@ -977,7 +1037,9 @@
byte[] OriginalPdfBytes, byte[] OriginalPdfBytes,
List<SignatureFieldDraft> Fields, List<SignatureFieldDraft> Fields,
string FileName, string FileName,
List<ReceiverDraft> Receivers); List<ReceiverDraft> Receivers,
string Title,
string Message);
// ── Receiver colour palette (cycles when > 8 receivers) ── // ── Receiver colour palette (cycles when > 8 receivers) ──
static readonly string[] ReceiverPalette = static readonly string[] ReceiverPalette =