Add localization support for multi-language UI
This commit introduces comprehensive localization support for the application, enabling dynamic language switching and localized UI elements. Key changes include: - Added `.resx` files for German (de-DE), English (en-US), and French (fr-FR). - Updated Razor components to use `IStringLocalizer` for dynamic string localization. - Introduced the `L(string key, string fallback)` helper method for consistent localized string retrieval with fallback support. - Implemented dynamic culture initialization in `Program.cs` via an API call to `/api/Localization/lang`. - Localized error pages, grid columns, tabs, popups, validation messages, and tooltips. - Dynamically generated privacy policy links based on the current culture. - Improved user experience with fully localized loading states, error handling, and UI labels. These changes enhance accessibility and usability, making the application more user-friendly for a global audience.
This commit is contained in:
@@ -5,11 +5,13 @@
|
||||
@using DevExpress.Blazor
|
||||
@using EnvelopeGenerator.Application.Common.Dto
|
||||
@using EnvelopeGenerator.Application.Envelopes.Commands
|
||||
@using EnvelopeGenerator.Application.Resources
|
||||
@using EnvelopeGenerator.Server.Client.Services
|
||||
@using EnvelopeGenerator.Server.Services
|
||||
@using Microsoft.AspNetCore.WebUtilities
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.Extensions.Caching.Memory
|
||||
@using Microsoft.Extensions.Localization
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject AppVersionService AppVersion
|
||||
@@ -18,6 +20,7 @@
|
||||
@inject EnvelopeService EnvelopeService
|
||||
@inject IEnvelopeAuthService EnvelopeAuthService
|
||||
@inject IMemoryCache MemoryCache
|
||||
@inject IStringLocalizer<Resource> UiLocalizer
|
||||
|
||||
<link href="_content/DevExpress.Blazor.Themes/blazing-berry.bs5.min.css" rel="stylesheet" />
|
||||
<link href="_content/DevExpress.Blazor.Viewer/css/dx-blazor-viewer-components.bs5.css" rel="stylesheet" />
|
||||
@@ -41,12 +44,12 @@
|
||||
<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>
|
||||
@L("Title", "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"
|
||||
placeholder='@L("SenderEditor_Title_Placeholder", "z.B. Arbeitsvertrag 2025")'
|
||||
maxlength="120"
|
||||
style="font-size: 0.78rem; font-weight: 500; color: #1f2937;
|
||||
border: 1px solid #d1d5db; border-radius: 5px;
|
||||
@@ -55,7 +58,7 @@
|
||||
@(_titleTouched && string.IsNullOrWhiteSpace(_envelopeTitle) ? "border-color:#dc2626;" : "")" />
|
||||
@if (_titleTouched && string.IsNullOrWhiteSpace(_envelopeTitle))
|
||||
{
|
||||
<span style="font-size:0.6rem;color:#dc2626;">Pflichtfeld</span>
|
||||
<span style="font-size:0.6rem;color:#dc2626;">@L("RequiredField", "Pflichtfeld")</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -63,12 +66,12 @@
|
||||
<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>
|
||||
@L("Message", "Nachricht") <span style="color:#6b7280;font-weight:400;">(@L("Optional", "optional"))</span>
|
||||
</label>
|
||||
<input type="text"
|
||||
value="@_envelopeMessage"
|
||||
@oninput="e => _envelopeMessage = e.Value?.ToString() ?? string.Empty"
|
||||
placeholder="Bitte unterzeichnen Sie das beigefügte Dokument."
|
||||
placeholder='@L("SenderEditor_Message_Placeholder", "Bitte unterzeichnen Sie das beigefügte Dokument.")'
|
||||
maxlength="500"
|
||||
style="font-size: 0.78rem; color: #1f2937;
|
||||
border: 1px solid #d1d5db; border-radius: 5px;
|
||||
@@ -86,7 +89,7 @@
|
||||
<span style="display: inline-flex; align-items: center; padding: 0.125rem 0.4rem;
|
||||
background: #ede9fe; border-radius: 0.25rem; color: #6d28d9;
|
||||
font-weight: 500; font-size: 0.7rem; white-space: nowrap;">
|
||||
@_signatureFields.Count Signaturfeld@(_signatureFields.Count != 1 ? "er" : "")
|
||||
@_signatureFields.Count @L("SenderEditor_SignatureField", "Signaturfeld")@(_signatureFields.Count != 1 ? "er" : "")
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
@@ -96,24 +99,24 @@
|
||||
<div class="sender-receivers-panel">
|
||||
<div class="sender-receivers-panel__header">
|
||||
<div class="sender-receivers-panel__title-wrap">
|
||||
<span class="sender-receivers-panel__title">Empfänger</span>
|
||||
<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"
|
||||
@onclick="OpenAddReceiverPopup"
|
||||
title="Empfänger hinzufügen">
|
||||
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">Empfänger hinzufügen</span>
|
||||
<span class="pdf-toolbar__btn-text">@L("AddReceiver", "Empfänger hinzufügen")</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (_receivers.Count == 0)
|
||||
{
|
||||
<div class="sender-receivers-panel__empty">
|
||||
Noch keine Empfänger hinzugefügt.
|
||||
@L("SenderEditor_NoReceiversAdded", "Noch keine Empfänger hinzugefügt.")
|
||||
</div>
|
||||
}
|
||||
else
|
||||
@@ -136,11 +139,11 @@
|
||||
@(_pendingReceiverForPlacement?.Id == receiver.Id ? "pdf-toolbar__btn--signature-change-active" : "")"
|
||||
@onclick="() => ActivatePlacementForReceiver(receiver)"
|
||||
style="@(_pendingReceiverForPlacement?.Id == receiver.Id ? $"background:{receiver.Color};color:#fff;border-color:{receiver.Color};" : $"color:{receiver.Color};border-color:{receiver.Color};")"
|
||||
title="Signaturfeld platzieren">
|
||||
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">Signatur hinzufügen</span>
|
||||
<span class="pdf-toolbar__btn-text">@L("SenderEditor_AddSignature", "Signatur hinzufügen")</span>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
@@ -154,19 +157,19 @@
|
||||
|
||||
<button class="pdf-toolbar__btn pdf-toolbar__btn--reset"
|
||||
@onclick="Cancel"
|
||||
title="Abbrechen">
|
||||
Abbrechen
|
||||
title='@L("Cancel", "Abbrechen")'>
|
||||
@L("Cancel", "Abbrechen")
|
||||
</button>
|
||||
|
||||
@* PDF Upload *@
|
||||
<label class="pdf-toolbar__btn"
|
||||
title="PDF hochladen"
|
||||
title='@L("UploadPdf", "PDF hochladen")'
|
||||
style="cursor: pointer; display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.3rem 0.75rem; font-size: 0.75rem; font-weight: 500;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" />
|
||||
<path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z" />
|
||||
</svg>
|
||||
PDF hochladen
|
||||
@L("UploadPdf", "PDF hochladen")
|
||||
<InputFile OnChange="OnPdfFileSelectedAsync"
|
||||
accept=".pdf"
|
||||
style="display: none;" />
|
||||
@@ -176,7 +179,7 @@
|
||||
{
|
||||
<button class="pdf-toolbar__btn pdf-toolbar__btn--reset"
|
||||
@onclick="ClearAllFieldsAsync"
|
||||
title="Alle Signaturfelder entfernen">
|
||||
title='@L("SenderEditor_RemoveAllSignatureFields", "Alle Signaturfelder entfernen")'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z" />
|
||||
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1 0-2h3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1h3a1 1 0 0 1 1 1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3h11V2h-11v1z" />
|
||||
@@ -187,7 +190,7 @@
|
||||
<button class="pdf-toolbar__btn pdf-toolbar__btn--draft"
|
||||
@onclick="SaveDraftAsync"
|
||||
disabled="@_isSaving"
|
||||
title="Speichern">
|
||||
title='@L("Save", "Speichern")'>
|
||||
@if (_isSaving)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-1" role="status"
|
||||
@@ -200,13 +203,13 @@
|
||||
<path d="M5.5 1.5v4a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-4h-4zM4 9a1 1 0 0 0-1 1v3h10v-3a1 1 0 0 0-1-1H4z"/>
|
||||
</svg>
|
||||
}
|
||||
<span class="pdf-toolbar__btn-text">Speichern</span>
|
||||
<span class="pdf-toolbar__btn-text">@L("Save", "Speichern")</span>
|
||||
</button>
|
||||
|
||||
<button class="pdf-toolbar__btn pdf-toolbar__btn--success"
|
||||
@onclick="SendAsync"
|
||||
disabled="@_isSaving"
|
||||
title="Umschlag senden"
|
||||
title='@L("SendEnvelope", "Umschlag senden")'
|
||||
style="background: linear-gradient(135deg,#059669,#047857); color:#fff; border-radius:6px; padding:0.3rem 0.75rem; font-size:0.75rem; font-weight:600; border:none;">
|
||||
@if (_isSaving)
|
||||
{
|
||||
@@ -219,7 +222,7 @@
|
||||
<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>
|
||||
}
|
||||
Senden
|
||||
@L("Send", "Senden")
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -229,10 +232,10 @@
|
||||
{
|
||||
<div style="background: @_pendingReceiverForPlacement.Color; color: white; font-size: 0.75rem; font-weight: 500;
|
||||
padding: 0.3rem 1.5rem; text-align: center; letter-spacing: 0.01em;">
|
||||
📌 Klicken Sie auf die Stelle im Dokument für <strong>@_pendingReceiverForPlacement.FullName</strong>.
|
||||
📌 @L("SenderEditor_ClickDocumentFor", "Klicken Sie auf die Stelle im Dokument für") <strong>@_pendingReceiverForPlacement.FullName</strong>.
|
||||
<button @onclick="CancelPlacement"
|
||||
style="background:none;border:none;color:white;cursor:pointer;font-size:0.75rem;text-decoration:underline;padding:0;">
|
||||
Abbrechen
|
||||
@L("Cancel", "Abbrechen")
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
@@ -252,16 +255,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>
|
||||
<h5 class="text-white mb-2">Kein Dokument geladen</h5>
|
||||
<h5 class="text-white mb-2">@L("SenderEditor_NoDocumentLoaded", "Kein Dokument geladen")</h5>
|
||||
<p class="text-white mb-4" style="opacity: 0.75; font-size: 0.85rem;">
|
||||
Laden Sie eine PDF-Datei hoch, um Signaturfelder zu platzieren.
|
||||
@L("SenderEditor_UploadPdfToPlaceFields", "Laden Sie eine PDF-Datei hoch, um Signaturfelder zu platzieren.")
|
||||
</p>
|
||||
<label class="btn btn-light btn-sm" style="cursor: pointer; font-weight: 500;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="me-1" viewBox="0 0 16 16">
|
||||
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" />
|
||||
<path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z" />
|
||||
</svg>
|
||||
PDF hochladen
|
||||
@L("UploadPdf", "PDF hochladen")
|
||||
<InputFile OnChange="OnPdfFileSelectedAsync" accept=".pdf" style="display: none;" />
|
||||
</label>
|
||||
</div>
|
||||
@@ -271,7 +274,7 @@
|
||||
{
|
||||
<div class="error-container">
|
||||
<div class="alert alert-danger shadow-lg m-4">
|
||||
<strong>Fehler:</strong> @_errorMessage
|
||||
<strong>@L("Error", "Fehler"):</strong> @_errorMessage
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -290,7 +293,7 @@
|
||||
</div>
|
||||
|
||||
<DxPopup @bind-Visible="_receiverPopupVisible"
|
||||
HeaderText="Empfänger hinzufügen"
|
||||
HeaderText='@L("AddReceiver", "Empfänger hinzufügen")'
|
||||
ShowFooter="true"
|
||||
CloseOnEscape="true"
|
||||
Width="720px"
|
||||
@@ -299,7 +302,7 @@
|
||||
<div class="sender-receiver-popup__body">
|
||||
<div class="sender-receiver-popup__form-grid">
|
||||
<div class="sender-receiver-popup__field">
|
||||
<div class="sender-receiver-popup__label">Vor- und Nachname</div>
|
||||
<div class="sender-receiver-popup__label">@L("FullName", "Vor- und Nachname")</div>
|
||||
<DxTextBox Text="@_receiverDraftName"
|
||||
TextChanged="OnReceiverNameChanged"
|
||||
NullText="Max Mustermann"
|
||||
@@ -309,7 +312,7 @@
|
||||
</div>
|
||||
|
||||
<div class="sender-receiver-popup__field">
|
||||
<div class="sender-receiver-popup__label">Telefonnummer (optional)</div>
|
||||
<div class="sender-receiver-popup__label">@L("PhoneNumber", "Telefonnummer") (@L("Optional", "optional"))</div>
|
||||
<DxTextBox Text="@_receiverDraftPhoneNumber"
|
||||
TextChanged="OnReceiverPhoneNumberChanged"
|
||||
NullText="+49 170 1234567"
|
||||
@@ -319,7 +322,7 @@
|
||||
</div>
|
||||
|
||||
<div class="sender-receiver-popup__field">
|
||||
<div class="sender-receiver-popup__label">E-Mail-Adresse</div>
|
||||
<div class="sender-receiver-popup__label">@L("EmailAddress", "E-Mail-Adresse")</div>
|
||||
<div @onkeydown="OnReceiverEmailKeyDownAsync">
|
||||
<DxTextBox Text="@_receiverDraftEmail"
|
||||
TextChanged="OnReceiverEmailTextChangedAsync"
|
||||
@@ -347,12 +350,12 @@
|
||||
</div>
|
||||
|
||||
<div class="sender-receiver-popup__hint">
|
||||
Bereits verwendete E-Mail-Adressen werden bei der Eingabe vorgeschlagen.
|
||||
@L("SenderEditor_EmailSuggestionsHint", "Bereits verwendete E-Mail-Adressen werden bei der Eingabe vorgeschlagen.")
|
||||
</div>
|
||||
|
||||
@if (_isReceiverEmailSearchRunning)
|
||||
{
|
||||
<div class="sender-receiver-popup__loading">E-Mail-Vorschläge werden geladen…</div>
|
||||
<div class="sender-receiver-popup__loading">@L("SenderEditor_EmailSuggestionsLoading", "E-Mail-Vorschläge werden geladen…")</div>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(_receiverPopupValidationMessage))
|
||||
@@ -363,10 +366,10 @@
|
||||
</BodyContentTemplate>
|
||||
<FooterContentTemplate>
|
||||
<div class="sender-receiver-popup__footer">
|
||||
<DxButton Text="Abbrechen"
|
||||
<DxButton Text='@L("Cancel", "Abbrechen")'
|
||||
RenderStyle="ButtonRenderStyle.Secondary"
|
||||
Click="CloseAddReceiverPopup" />
|
||||
<DxButton Text="Empfänger hinzufügen"
|
||||
<DxButton Text='@L("AddReceiver", "Empfänger hinzufügen")'
|
||||
RenderStyle="ButtonRenderStyle.Primary"
|
||||
Click="SaveReceiverAsync" />
|
||||
</div>
|
||||
@@ -375,7 +378,7 @@
|
||||
|
||||
@* ── Save result popup (success + error) ── *@
|
||||
<DxPopup @bind-Visible="_savePopupVisible"
|
||||
HeaderText="@(_saveErrorMessage is null ? _saveSuccessHeader : "Fehler")"
|
||||
HeaderText='@(_saveErrorMessage is null ? _saveSuccessHeader : L("Error", "Fehler"))'
|
||||
Width="460px"
|
||||
MaxWidth="95vw"
|
||||
ShowFooter="true"
|
||||
@@ -414,7 +417,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<p style="margin:0 0 0.4rem;font-weight:600;color:#1f2937;font-size:0.95rem;">
|
||||
Fehler beim Erstellen des Umschlags
|
||||
@L("SenderEditor_ErrorCreatingEnvelope", "Fehler beim Erstellen des Umschlags")
|
||||
</p>
|
||||
<p style="margin:0;color:#6b7280;font-size:0.85rem;line-height:1.5;word-break:break-word;">
|
||||
@_saveErrorMessage
|
||||
@@ -430,7 +433,7 @@
|
||||
<button class="btn btn-outline-secondary"
|
||||
@onclick="() => _savePopupVisible = false"
|
||||
style="border-radius:6px;padding:0.5rem 1.25rem;font-weight:500;">
|
||||
Schließen
|
||||
@L("Close", "Schließen")
|
||||
</button>
|
||||
|
||||
@if (_saveNavigateToDashboard)
|
||||
@@ -439,7 +442,7 @@
|
||||
@onclick="GoToDashboard"
|
||||
style="background:linear-gradient(135deg,#059669,#047857);border:none;border-radius:6px;
|
||||
padding:0.5rem 1.5rem;font-weight:600;color:#fff;">
|
||||
Zur Übersicht
|
||||
@L("SenderEditor_ToOverview", "Zur Übersicht")
|
||||
</button>
|
||||
}
|
||||
}
|
||||
@@ -448,7 +451,7 @@
|
||||
<button class="btn btn-outline-secondary"
|
||||
@onclick="() => _savePopupVisible = false"
|
||||
style="border-radius:6px;padding:0.5rem 1.25rem;font-weight:500;">
|
||||
Schließen
|
||||
@L("Close", "Schließen")
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
@@ -653,7 +656,10 @@
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Failed to load envelope {EnvelopeId} for edit mode", envelopeId);
|
||||
_errorMessage = $"Fehler beim Laden des Umschlags #{envelopeId}: {ex.Message}";
|
||||
_errorMessage = string.Format(
|
||||
L("SenderEditor_LoadEnvelopeError", "Fehler beim Laden des Umschlags #{0}: {1}"),
|
||||
envelopeId,
|
||||
ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -670,7 +676,7 @@
|
||||
|
||||
if (!file.Name.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_errorMessage = "Bitte wählen Sie eine PDF-Datei aus.";
|
||||
_errorMessage = L("SenderEditor_SelectPdf", "Bitte wählen Sie eine PDF-Datei aus.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -695,7 +701,9 @@
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_errorMessage = $"Fehler beim Laden der Datei: {ex.Message}";
|
||||
_errorMessage = string.Format(
|
||||
L("SenderEditor_LoadFileError", "Fehler beim Laden der Datei: {0}"),
|
||||
ex.Message);
|
||||
Logger.LogError(ex, "Failed to load PDF file");
|
||||
}
|
||||
}
|
||||
@@ -853,25 +861,25 @@
|
||||
_titleTouched = true;
|
||||
if (!_pdfLoaded || _originalPdfBytes is null)
|
||||
{
|
||||
_saveErrorMessage = "Bitte laden Sie zuerst ein PDF-Dokument hoch.";
|
||||
_saveErrorMessage = L("SenderEditor_Validation_UploadPdf", "Bitte laden Sie zuerst ein PDF-Dokument hoch.");
|
||||
_savePopupVisible = true;
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(_envelopeTitle))
|
||||
{
|
||||
_saveErrorMessage = "Bitte geben Sie einen Titel ein.";
|
||||
_saveErrorMessage = L("SenderEditor_Validation_TitleRequired", "Bitte geben Sie einen Titel ein.");
|
||||
_savePopupVisible = true;
|
||||
return;
|
||||
}
|
||||
if (_receivers.Count == 0)
|
||||
{
|
||||
_saveErrorMessage = "Bitte fügen Sie mindestens einen Empfänger hinzu.";
|
||||
_saveErrorMessage = L("SenderEditor_Validation_ReceiverRequired", "Bitte fügen Sie mindestens einen Empfänger hinzu.");
|
||||
_savePopupVisible = true;
|
||||
return;
|
||||
}
|
||||
if (_signatureFields.Count == 0)
|
||||
{
|
||||
_saveErrorMessage = "Bitte platzieren Sie mindestens ein Signaturfeld.";
|
||||
_saveErrorMessage = L("SenderEditor_Validation_SignatureFieldRequired", "Bitte platzieren Sie mindestens ein Signaturfeld.");
|
||||
_savePopupVisible = true;
|
||||
return;
|
||||
}
|
||||
@@ -916,9 +924,9 @@
|
||||
|
||||
// Success — show popup; GoToDashboard clears cache and navigates
|
||||
_saveErrorMessage = null;
|
||||
_saveSuccessHeader = "Umschlag gesendet";
|
||||
_saveSuccessTitle = "Umschlag wurde erfolgreich gesendet.";
|
||||
_saveSuccessBody = "Der bestehende Entwurf wurde versendet.";
|
||||
_saveSuccessHeader = L("SenderEditor_Sent_Header", "Umschlag gesendet");
|
||||
_saveSuccessTitle = L("SenderEditor_Sent_Title", "Umschlag wurde erfolgreich gesendet.");
|
||||
_saveSuccessBody = L("SenderEditor_Sent_Body", "Der bestehende Entwurf wurde versendet.");
|
||||
_saveNavigateToDashboard = true;
|
||||
_savePopupVisible = true;
|
||||
}
|
||||
@@ -1222,22 +1230,22 @@
|
||||
|
||||
if (string.IsNullOrWhiteSpace(fullName))
|
||||
{
|
||||
_receiverPopupValidationMessage = "Bitte geben Sie einen Vor- und Nachnamen ein.";
|
||||
_receiverPopupValidationMessage = L("SenderEditor_Validation_FullNameRequired", "Bitte geben Sie einen Vor- und Nachnamen ein.");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(email))
|
||||
{
|
||||
_receiverPopupValidationMessage = "Bitte geben Sie eine E-Mail-Adresse ein.";
|
||||
_receiverPopupValidationMessage = L("SenderEditor_Validation_EmailRequired", "Bitte geben Sie eine E-Mail-Adresse ein.");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
if (!ReceiverEmailValidator.IsValid(email))
|
||||
{
|
||||
_receiverPopupValidationMessage = "Bitte geben Sie eine gültige E-Mail-Adresse ein.";
|
||||
_receiverPopupValidationMessage = L("SenderEditor_Validation_EmailInvalid", "Bitte geben Sie eine gültige E-Mail-Adresse ein.");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
if (_receivers.Any(r => string.Equals(r.Email, email, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
_receiverPopupValidationMessage = "Diese E-Mail-Adresse wurde bereits hinzugefügt.";
|
||||
_receiverPopupValidationMessage = L("SenderEditor_Validation_EmailDuplicate", "Diese E-Mail-Adresse wurde bereits hinzugefügt.");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -1275,4 +1283,10 @@
|
||||
"#BE185D", // pink
|
||||
"#65A30D", // lime
|
||||
];
|
||||
|
||||
string L(string key, string fallback)
|
||||
{
|
||||
var value = UiLocalizer[key];
|
||||
return value.ResourceNotFound ? fallback : value.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
@using EnvelopeGenerator.Server.Client.Services
|
||||
@using EnvelopeGenerator.Application.Common.Dto
|
||||
@using EnvelopeGenerator.Application.Common.Dto.History
|
||||
@using EnvelopeGenerator.Application.Resources
|
||||
@using EnvelopeGenerator.Domain.Resources
|
||||
@using EnvelopeGenerator.Server.Services
|
||||
@using Microsoft.Extensions.Localization
|
||||
@@ -23,6 +24,7 @@
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject AppVersionService AppVersion
|
||||
@inject IStringLocalizer<Model> ModelLocalizer
|
||||
@inject IStringLocalizer<Resource> UiLocalizer
|
||||
|
||||
<link href="@AppVersion.GetVersionedUrl("css/envelope-viewer.css")" rel="stylesheet" />
|
||||
<link href="@AppVersion.GetVersionedUrl("css/sender-page.css")" rel="stylesheet" />
|
||||
@@ -70,29 +72,29 @@
|
||||
|
||||
@* ── Toolbar (rechts) ─────────────────────────────────────────── *@
|
||||
<div class="sender-toolbar">
|
||||
<button class="sender-btn sender-btn--primary" @onclick="CreateEnvelope" title="Neuen Umschlag erstellen">
|
||||
<button class="sender-btn sender-btn--primary" @onclick="CreateEnvelope" title="@L("Sender_CreateEnvelope_Title", "Neuen Umschlag erstellen")">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z" />
|
||||
</svg>
|
||||
Neuer Umschlag
|
||||
@L("Sender_CreateEnvelope", "Neuer Umschlag")
|
||||
</button>
|
||||
|
||||
<button class="sender-btn" @onclick="EditEnvelope" disabled="@(_selectedEnvelope == null || !IsEnvelopeEditable(_selectedEnvelope))" title="Ausgewählten Umschlag bearbeiten">
|
||||
<button class="sender-btn" @onclick="EditEnvelope" disabled="@(_selectedEnvelope == null || !IsEnvelopeEditable(_selectedEnvelope))" title="@L("Sender_EditEnvelope_Title", "Ausgewählten Umschlag bearbeiten")">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" 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-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>
|
||||
Bearbeiten
|
||||
@L("Sender_Edit", "Bearbeiten")
|
||||
</button>
|
||||
|
||||
<button class="sender-btn sender-btn--danger" @onclick="DeleteEnvelope" disabled="@(_selectedEnvelope == null)" title="Ausgewählten Umschlag löschen">
|
||||
<button class="sender-btn sender-btn--danger" @onclick="DeleteEnvelope" disabled="@(_selectedEnvelope == null)" title="@L("Sender_DeleteEnvelope_Title", "Ausgewählten Umschlag löschen")">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z" />
|
||||
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z" />
|
||||
</svg>
|
||||
Löschen
|
||||
@L("Delete", "Löschen")
|
||||
</button>
|
||||
|
||||
<button class="sender-btn" @onclick="RefreshEnvelopes" disabled="@_isLoading" title="Aktualisieren">
|
||||
<button class="sender-btn" @onclick="RefreshEnvelopes" disabled="@_isLoading" title="@L("Refresh", "Aktualisieren")">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z" />
|
||||
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" />
|
||||
@@ -103,16 +105,16 @@
|
||||
}
|
||||
</button>
|
||||
|
||||
<button class="sender-btn" @onclick="ShowPreview" disabled="@(_selectedEnvelope == null)" title="Ausgewählten Umschlag als PDF anzeigen">
|
||||
<button class="sender-btn" @onclick="ShowPreview" disabled="@(_selectedEnvelope == null)" title="@L("Sender_Preview_Title", "Ausgewählten Umschlag als PDF anzeigen")">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M10.5 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0z"/>
|
||||
<path d="M0 8s3-5.5 8-5.5S16 8 16 8s-3 5.5-8 5.5S0 8 0 8zm8 3.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z"/>
|
||||
</svg>
|
||||
Vorschau
|
||||
@L("Preview", "Vorschau")
|
||||
</button>
|
||||
|
||||
<div class="sender-export-group" title="Export der aktuellen Tabellenansicht">
|
||||
<select id="sender-export-format" class="sender-export-format" @bind="_selectedExportFormat" aria-label="Exportformat auswählen">
|
||||
<div class="sender-export-group" title="@L("Sender_ExportView_Title", "Export der aktuellen Tabellenansicht")">
|
||||
<select id="sender-export-format" class="sender-export-format" @bind="_selectedExportFormat" aria-label="@L("Sender_ExportFormat_Aria", "Exportformat auswählen")">
|
||||
<option value="xlsx">XLSX</option>
|
||||
<option value="csv">CSV</option>
|
||||
<option value="pdf">PDF</option>
|
||||
@@ -121,15 +123,15 @@
|
||||
<label class="sender-export-selected">
|
||||
<input type="checkbox" @bind="_exportSelectedRowsOnly" />
|
||||
<span class="sender-export-selected-text">
|
||||
<span>Nur</span>
|
||||
<span>ausgewählte</span>
|
||||
<span>@L("Only", "Nur")</span>
|
||||
<span>@L("Selected", "ausgewählte")</span>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<button class="sender-btn sender-btn--export"
|
||||
@onclick="ExportSelectedFormatAsync"
|
||||
disabled="@IsExportDisabled()"
|
||||
title="Aktuelle Ansicht exportieren">
|
||||
title="@L("Sender_ExportCurrentView_Title", "Aktuelle Ansicht exportieren")">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M.5 9.9a.5.5 0 0 1 .5.5V13a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.6a.5.5 0 0 1 1 0V13a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.6a.5.5 0 0 1 .5-.5z" />
|
||||
<path d="M5.354 6.146a.5.5 0 0 1 .707 0L7.5 7.586V.5a.5.5 0 0 1 1 0v7.086l1.439-1.44a.5.5 0 0 1 .707.708l-2.293 2.293a.5.5 0 0 1-.707 0L5.354 6.854a.5.5 0 0 1 0-.708z" />
|
||||
@@ -140,12 +142,12 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Exportieren</span>
|
||||
<span>@L("Export", "Exportieren")</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button class="sender-btn sender-btn--logout" @onclick="LogoutAsync" disabled="@_isLoggingOut" title="Abmelden">
|
||||
<button class="sender-btn sender-btn--logout" @onclick="LogoutAsync" disabled="@_isLoggingOut" title="@L("Logout", "Abmelden")">
|
||||
@if (_isLoggingOut)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm" style="width: 14px; height: 14px;"></span>
|
||||
@@ -168,9 +170,9 @@
|
||||
<div class="d-flex justify-content-center align-items-center h-100">
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-white mb-3" style="width: 3.5rem; height: 3.5rem;" role="status">
|
||||
<span class="visually-hidden">Lädt...</span>
|
||||
<span class="visually-hidden">@L("Loading", "Lädt...")</span>
|
||||
</div>
|
||||
<p class="text-white fw-semibold">Umschläge werden geladen...</p>
|
||||
<p class="text-white fw-semibold">@L("Sender_LoadingEnvelopes", "Umschläge werden geladen...")</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -184,7 +186,7 @@
|
||||
<path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z" />
|
||||
</svg>
|
||||
<div>
|
||||
<h5 class="mb-2">Fehler beim Laden der Umschläge</h5>
|
||||
<h5 class="mb-2">@L("Sender_LoadError_Title", "Fehler beim Laden der Umschläge")</h5>
|
||||
<p class="mb-0">@_errorMessage</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -196,14 +198,14 @@
|
||||
<div class="sender-grid-container">
|
||||
<div class="sender-tabs">
|
||||
<button class="sender-tab @(_activeTab == "active" ? "sender-tab--active" : "")" @onclick='() => _activeTab = "active"'>
|
||||
<span>Aktive Umschläge</span>
|
||||
<span>@L("Sender_ActiveEnvelopes", "Aktive Umschläge")</span>
|
||||
@if (_activeEnvelopes != null)
|
||||
{
|
||||
<span style="opacity: 0.6; margin-left: 0.5rem;">(@_activeEnvelopes.Count())</span>
|
||||
}
|
||||
</button>
|
||||
<button class="sender-tab @(_activeTab == "completed" ? "sender-tab--active" : "")" @onclick='() => _activeTab = "completed"'>
|
||||
<span>Abgeschlossene Umschläge</span>
|
||||
<span>@L("Sender_CompletedEnvelopes", "Abgeschlossene Umschläge")</span>
|
||||
@if (_completedEnvelopes != null)
|
||||
{
|
||||
<span style="opacity: 0.6; margin-left: 0.5rem;">(@_completedEnvelopes.Count())</span>
|
||||
@@ -241,12 +243,12 @@
|
||||
@((cellContext.DataItem as EnvelopeDto)?.Id)
|
||||
</CellDisplayTemplate>
|
||||
</DxGridDataColumn>
|
||||
<DxGridDataColumn FieldName="Title" Caption="Titel">
|
||||
<DxGridDataColumn FieldName="Title" Caption="@L("Title", "Titel")">
|
||||
<CellDisplayTemplate Context="cellContext">
|
||||
<strong>@((cellContext.DataItem as EnvelopeDto)?.Title)</strong>
|
||||
</CellDisplayTemplate>
|
||||
</DxGridDataColumn>
|
||||
<DxGridDataColumn FieldName="Status" Caption="Status" ExportEnabled="false">
|
||||
<DxGridDataColumn FieldName="Status" Caption="@L("Status", "Status")" ExportEnabled="false">
|
||||
<CellDisplayTemplate Context="cellContext">
|
||||
@{
|
||||
var envelope = cellContext.DataItem as EnvelopeDto;
|
||||
@@ -261,7 +263,7 @@
|
||||
}
|
||||
</CellDisplayTemplate>
|
||||
</DxGridDataColumn>
|
||||
<DxGridDataColumn FieldName="EnvelopeReceivers" Caption="Empfänger" ExportEnabled="false">
|
||||
<DxGridDataColumn FieldName="EnvelopeReceivers" Caption="@L("Receiver", "Empfänger")" ExportEnabled="false">
|
||||
<CellDisplayTemplate Context="cellContext">
|
||||
@{
|
||||
var envelope = cellContext.DataItem as EnvelopeDto;
|
||||
@@ -272,7 +274,7 @@
|
||||
var total = receivers.Count;
|
||||
<div style="display: flex; align-items: center; gap: 0.5rem;">
|
||||
<span style="font-size: 0.875rem; color: #6b7280;">
|
||||
@signed / @total unterschrieben
|
||||
@signed / @total @L("Signed", "unterschrieben")
|
||||
</span>
|
||||
@if (total > 0)
|
||||
{
|
||||
@@ -285,16 +287,16 @@
|
||||
}
|
||||
</CellDisplayTemplate>
|
||||
</DxGridDataColumn>
|
||||
<DxGridDataColumn FieldName="EnvelopeTypeTitle" Caption="Typ"
|
||||
<DxGridDataColumn FieldName="EnvelopeTypeTitle" Caption="@L("Type", "Typ")"
|
||||
Width="120px" AllowSort="true" />
|
||||
<DxGridDataColumn FieldName="AddedWhen" Caption="Erstellt"
|
||||
<DxGridDataColumn FieldName="AddedWhen" Caption="@L("Created", "Erstellt")"
|
||||
DisplayFormat="{0:dd.MM.yyyy}" Width="110px" AllowSort="true" />
|
||||
<DxGridDataColumn FieldName="ChangedWhen" Caption="Geändert am"
|
||||
<DxGridDataColumn FieldName="ChangedWhen" Caption="@L("ChangedAt", "Geändert am")"
|
||||
DisplayFormat="{0:dd.MM.yyyy}" Width="110px" AllowSort="true" />
|
||||
</Columns>
|
||||
<DetailRowTemplate Context="detailContext">
|
||||
<div style="padding: 1rem; background: #f9fafb;">
|
||||
<h6 style="font-weight: 600; color: #374151; margin-bottom: 0.75rem;">Empfänger</h6>
|
||||
<h6 style="font-weight: 600; color: #374151; margin-bottom: 0.75rem;">@L("Receiver", "Empfänger")</h6>
|
||||
@{
|
||||
var envelope = detailContext.DataItem as EnvelopeDto;
|
||||
if (envelope?.EnvelopeReceivers?.Any() == true)
|
||||
@@ -309,7 +311,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" 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>Unterschrieben</span>
|
||||
<span>@L("Signed", "Unterschrieben")</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -317,7 +319,7 @@
|
||||
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
||||
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z" />
|
||||
</svg>
|
||||
<span>Ausstehend</span>
|
||||
<span>@L("Pending", "Ausstehend")</span>
|
||||
}
|
||||
</span>
|
||||
<div style="flex: 1; font-size: 0.875rem;">
|
||||
@@ -330,11 +332,11 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<p style="color: #9ca3af; font-size: 0.875rem; margin: 0;">Keine Empfänger</p>
|
||||
<p style="color: #9ca3af; font-size: 0.875rem; margin: 0;">@L("Sender_NoReceivers", "Keine Empfänger")</p>
|
||||
}
|
||||
}
|
||||
|
||||
<h6 style="font-weight: 600; color: #374151; margin: 1rem 0 0.75rem;">Historie</h6>
|
||||
<h6 style="font-weight: 600; color: #374151; margin: 1rem 0 0.75rem;">@L("History", "Historie")</h6>
|
||||
@{
|
||||
var historyEntries = envelope is null
|
||||
? Enumerable.Empty<HistoryDto>()
|
||||
@@ -362,7 +364,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<p style="color: #9ca3af; font-size: 0.875rem; margin: 0;">Keine Historie vorhanden</p>
|
||||
<p style="color: #9ca3af; font-size: 0.875rem; margin: 0;">@L("Sender_NoHistory", "Keine Historie vorhanden")</p>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@@ -395,12 +397,12 @@
|
||||
@((cellContext.DataItem as EnvelopeDto)?.Id)
|
||||
</CellDisplayTemplate>
|
||||
</DxGridDataColumn>
|
||||
<DxGridDataColumn FieldName="Title" Caption="Titel">
|
||||
<DxGridDataColumn FieldName="Title" Caption="@L("Title", "Titel")">
|
||||
<CellDisplayTemplate Context="cellContext">
|
||||
<strong>@((cellContext.DataItem as EnvelopeDto)?.Title)</strong>
|
||||
</CellDisplayTemplate>
|
||||
</DxGridDataColumn>
|
||||
<DxGridDataColumn FieldName="Status" Caption="Status" ExportEnabled="false">
|
||||
<DxGridDataColumn FieldName="Status" Caption="@L("Status", "Status")" ExportEnabled="false">
|
||||
<CellDisplayTemplate Context="cellContext">
|
||||
@{
|
||||
var envelope = cellContext.DataItem as EnvelopeDto;
|
||||
@@ -415,7 +417,7 @@
|
||||
}
|
||||
</CellDisplayTemplate>
|
||||
</DxGridDataColumn>
|
||||
<DxGridDataColumn FieldName="EnvelopeReceivers" Caption="Empfänger" ExportEnabled="false">
|
||||
<DxGridDataColumn FieldName="EnvelopeReceivers" Caption="@L("Receiver", "Empfänger")" ExportEnabled="false">
|
||||
<CellDisplayTemplate Context="cellContext">
|
||||
@{
|
||||
var envelope = cellContext.DataItem as EnvelopeDto;
|
||||
@@ -426,7 +428,7 @@
|
||||
var total = receivers.Count;
|
||||
<div style="display: flex; align-items: center; gap: 0.5rem;">
|
||||
<span style="font-size: 0.875rem; color: #6b7280;">
|
||||
@signed / @total unterschrieben
|
||||
@signed / @total @L("Signed", "unterschrieben")
|
||||
</span>
|
||||
@if (total > 0)
|
||||
{
|
||||
@@ -439,16 +441,16 @@
|
||||
}
|
||||
</CellDisplayTemplate>
|
||||
</DxGridDataColumn>
|
||||
<DxGridDataColumn FieldName="EnvelopeTypeTitle" Caption="Typ"
|
||||
<DxGridDataColumn FieldName="EnvelopeTypeTitle" Caption="@L("Type", "Typ")"
|
||||
Width="120px" AllowSort="true" />
|
||||
<DxGridDataColumn FieldName="AddedWhen" Caption="Erstellt"
|
||||
<DxGridDataColumn FieldName="AddedWhen" Caption="@L("Created", "Erstellt")"
|
||||
DisplayFormat="{0:dd.MM.yyyy}" Width="110px" AllowSort="true" />
|
||||
<DxGridDataColumn FieldName="ChangedWhen" Caption="Geändert am"
|
||||
<DxGridDataColumn FieldName="ChangedWhen" Caption="@L("ChangedAt", "Geändert am")"
|
||||
DisplayFormat="{0:dd.MM.yyyy}" Width="110px" AllowSort="true" />
|
||||
</Columns>
|
||||
<DetailRowTemplate Context="detailContext">
|
||||
<div style="padding: 1rem; background: #f9fafb;">
|
||||
<h6 style="font-weight: 600; color: #374151; margin-bottom: 0.75rem;">Empfänger</h6>
|
||||
<h6 style="font-weight: 600; color: #374151; margin-bottom: 0.75rem;">@L("Receiver", "Empfänger")</h6>
|
||||
@{
|
||||
var envelope = detailContext.DataItem as EnvelopeDto;
|
||||
if (envelope?.EnvelopeReceivers?.Any() == true)
|
||||
@@ -463,7 +465,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" 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>Unterschrieben</span>
|
||||
<span>@L("Signed", "Unterschrieben")</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -471,7 +473,7 @@
|
||||
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
||||
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z" />
|
||||
</svg>
|
||||
<span>Ausstehend</span>
|
||||
<span>@L("Pending", "Ausstehend")</span>
|
||||
}
|
||||
</span>
|
||||
<div style="flex: 1; font-size: 0.875rem;">
|
||||
@@ -484,11 +486,11 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<p style="color: #9ca3af; font-size: 0.875rem; margin: 0;">Keine Empfänger</p>
|
||||
<p style="color: #9ca3af; font-size: 0.875rem; margin: 0;">@L("Sender_NoReceivers", "Keine Empfänger")</p>
|
||||
}
|
||||
}
|
||||
|
||||
<h6 style="font-weight: 600; color: #374151; margin: 1rem 0 0.75rem;">Historie</h6>
|
||||
<h6 style="font-weight: 600; color: #374151; margin: 1rem 0 0.75rem;">@L("History", "Historie")</h6>
|
||||
@{
|
||||
var historyEntries = envelope is null
|
||||
? Enumerable.Empty<HistoryDto>()
|
||||
@@ -516,7 +518,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<p style="color: #9ca3af; font-size: 0.875rem; margin: 0;">Keine Historie vorhanden</p>
|
||||
<p style="color: #9ca3af; font-size: 0.875rem; margin: 0;">@L("Sender_NoHistory", "Keine Historie vorhanden")</p>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@@ -532,7 +534,7 @@
|
||||
|
||||
@* ── PDF Vorschau Popup ────────────────────────────────────────────────── *@
|
||||
<DxPopup @bind-Visible="@_previewVisible"
|
||||
HeaderText="@($"Vorschau — {_selectedEnvelope?.Title} (#{_selectedEnvelope?.Id})")"
|
||||
HeaderText="@($"{L("Preview", "Vorschau")} — {_selectedEnvelope?.Title} (#{_selectedEnvelope?.Id})")"
|
||||
Width="80vw"
|
||||
MaxWidth="80vw"
|
||||
Height="90vh"
|
||||
@@ -555,9 +557,9 @@
|
||||
<div class="sender-preview-state-wrap">
|
||||
<div class="text-center">
|
||||
<div class="spinner-border mb-3" style="width:3rem;height:3rem;" role="status">
|
||||
<span class="visually-hidden">Lädt...</span>
|
||||
<span class="visually-hidden">@L("Loading", "Lädt...")</span>
|
||||
</div>
|
||||
<p style="color:#6b7280;font-size:0.875rem;">Dokument wird geladen…</p>
|
||||
<p style="color:#6b7280;font-size:0.875rem;">@L("DocumentLoading", "Dokument wird geladen…")</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -572,7 +574,7 @@
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p class="sender-preview-error-title">Dokument konnte nicht geladen werden</p>
|
||||
<p class="sender-preview-error-title">@L("DocumentCouldNotBeLoaded", "Dokument konnte nicht geladen werden")</p>
|
||||
<p class="sender-preview-error-msg">@_previewErrorMessage</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -591,7 +593,7 @@
|
||||
</DxPopup>
|
||||
|
||||
<DxPopup @bind-Visible="@_deletePopupVisible"
|
||||
HeaderText="Umschlag löschen"
|
||||
HeaderText="@L("Sender_DeleteEnvelope_Header", "Umschlag löschen")"
|
||||
Width="560px"
|
||||
MaxWidth="95vw"
|
||||
ShowFooter="true"
|
||||
@@ -603,16 +605,16 @@
|
||||
<BodyContentTemplate>
|
||||
<div style="display:flex;flex-direction:column;gap:0.75rem;">
|
||||
<p style="margin:0;color:#374151;font-size:0.9rem;line-height:1.45;">
|
||||
Bitte geben Sie einen Grund an, warum der Umschlag gelöscht/zurückgezogen werden soll.
|
||||
@L("Sender_DeleteEnvelope_ReasonPrompt", "Bitte geben Sie einen Grund an, warum der Umschlag gelöscht/zurückgezogen werden soll.")
|
||||
</p>
|
||||
@if (_selectedEnvelope is not null)
|
||||
{
|
||||
<p style="margin:0;color:#6b7280;font-size:0.825rem;">
|
||||
Umschlag: <strong>#@_selectedEnvelope.Id - @_selectedEnvelope.Title</strong>
|
||||
@L("Envelope", "Umschlag"): <strong>#@_selectedEnvelope.Id - @_selectedEnvelope.Title</strong>
|
||||
</p>
|
||||
}
|
||||
<DxMemo @bind-Text="_deleteReason"
|
||||
NullText="Begründung eingeben..."
|
||||
NullText="@L("Sender_DeleteEnvelope_ReasonPlaceholder", "Begründung eingeben...")"
|
||||
Rows="5"
|
||||
MaxLength="2000"
|
||||
CssClass="w-100" />
|
||||
@@ -626,11 +628,11 @@
|
||||
</BodyContentTemplate>
|
||||
<FooterContentTemplate>
|
||||
<div style="display:flex;justify-content:flex-end;gap:0.5rem;width:100%;">
|
||||
<DxButton Text="Abbrechen"
|
||||
<DxButton Text="@L("Cancel", "Abbrechen")"
|
||||
RenderStyle="ButtonRenderStyle.Secondary"
|
||||
Click="CloseDeletePopup"
|
||||
Enabled="@(!_isDeletingEnvelope)" />
|
||||
<DxButton Text="Löschen"
|
||||
<DxButton Text="@L("Delete", "Löschen")"
|
||||
RenderStyle="ButtonRenderStyle.Danger"
|
||||
Click="ConfirmDeleteEnvelopeAsync"
|
||||
Enabled="@(!_isDeletingEnvelope)" />
|
||||
@@ -826,7 +828,7 @@
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_deleteReason))
|
||||
{
|
||||
_deleteErrorMessage = "Bitte geben Sie einen Löschgrund an.";
|
||||
_deleteErrorMessage = L("Sender_DeleteEnvelope_ReasonRequired", "Bitte geben Sie einen Löschgrund an.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -925,7 +927,13 @@
|
||||
if (!string.IsNullOrWhiteSpace(history.UserReference))
|
||||
return history.UserReference;
|
||||
|
||||
return "System";
|
||||
return L("System", "System");
|
||||
}
|
||||
|
||||
string L(string key, string fallback)
|
||||
{
|
||||
var value = UiLocalizer[key];
|
||||
return value.ResourceNotFound ? fallback : value.Value;
|
||||
}
|
||||
|
||||
void OnCustomizeElement(GridCustomizeElementEventArgs e)
|
||||
@@ -979,7 +987,7 @@
|
||||
var user = EnvelopeAuthService.GetCurrentUser();
|
||||
if (user?.Identity is null || !user.Identity.IsAuthenticated)
|
||||
{
|
||||
_previewErrorMessage = "Nicht authentifiziert. Bitte melden Sie sich erneut an.";
|
||||
_previewErrorMessage = L("NotAuthenticatedPleaseLoginAgain", "Nicht authentifiziert. Bitte melden Sie sich erneut an.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -990,12 +998,16 @@
|
||||
}
|
||||
catch (DigitalData.Core.Exceptions.NotFoundException)
|
||||
{
|
||||
_previewErrorMessage = $"Das Dokument für Umschlag #{envelope.Id} wurde nicht gefunden oder Sie haben keinen Zugriff darauf.";
|
||||
_previewErrorMessage = string.Format(
|
||||
L("Sender_PreviewDocumentNotFound", "Das Dokument für Umschlag #{0} wurde nicht gefunden oder Sie haben keinen Zugriff darauf."),
|
||||
envelope.Id);
|
||||
Logger.LogWarning("Preview: document not found for envelope {EnvelopeId}", envelope.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_previewErrorMessage = $"Fehler beim Laden des Dokuments: {ex.Message}";
|
||||
_previewErrorMessage = string.Format(
|
||||
L("ErrorLoadingDocument_WithReason", "Fehler beim Laden des Dokuments: {0}"),
|
||||
ex.Message);
|
||||
Logger.LogError(ex, "Preview: failed to load document for envelope {EnvelopeId}", envelope.Id);
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
@page "/Error"
|
||||
@using System.Diagnostics
|
||||
@using EnvelopeGenerator.Application.Resources
|
||||
@using Microsoft.Extensions.Localization
|
||||
@inject IStringLocalizer<Resource> UiLocalizer
|
||||
|
||||
<PageTitle>Error</PageTitle>
|
||||
<PageTitle>@L("Error", "Error")</PageTitle>
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
<h1 class="text-danger">@L("Error", "Error").</h1>
|
||||
<h2 class="text-danger">@L("ErrorPage_ProcessingRequest", "An error occurred while processing your request.")</h2>
|
||||
|
||||
@if (ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@RequestId</code>
|
||||
<strong>@L("ErrorPage_RequestId", "Request ID"):</strong> <code>@RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<h3>@L("ErrorPage_DevelopmentMode", "Development Mode")</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
@L("ErrorPage_DevelopmentMode_Info", "Swapping to Development environment will display more detailed information about the error that occurred.")
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
<strong>@L("ErrorPage_DevelopmentMode_Warning", "The Development environment shouldn't be enabled for deployed applications.")</strong>
|
||||
@L("ErrorPage_DevelopmentMode_Detail", "It can result in displaying sensitive information from exceptions to end users. For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development and restarting the app.")
|
||||
</p>
|
||||
|
||||
@code{
|
||||
@@ -33,4 +34,10 @@
|
||||
|
||||
protected override void OnInitialized() =>
|
||||
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
|
||||
string L(string key, string fallback)
|
||||
{
|
||||
var value = UiLocalizer[key];
|
||||
return value.ResourceNotFound ? fallback : value.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
@using EnvelopeGenerator.Server.Client.Models
|
||||
@using EnvelopeGenerator.Server.Client.Services
|
||||
@using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver
|
||||
@using EnvelopeGenerator.Application.Resources
|
||||
@using Microsoft.Extensions.Caching.Memory
|
||||
@using Microsoft.Extensions.Localization
|
||||
@using System.Security.Claims
|
||||
@inject NavigationManager Navigation
|
||||
@inject IJSRuntime JSRuntime
|
||||
@@ -15,6 +17,7 @@
|
||||
@inject AppVersionService AppVersion
|
||||
@inject IMemoryCache MemoryCache
|
||||
@inject ILogger<ReceiverSignedPage> Logger
|
||||
@inject IStringLocalizer<Resource> UiLocalizer
|
||||
@implements IDisposable
|
||||
|
||||
<link href="_content/DevExpress.Blazor.Themes/blazing-berry.bs5.min.css" rel="stylesheet" />
|
||||
@@ -29,18 +32,18 @@
|
||||
@if (_envelopeReceiver is not null)
|
||||
{
|
||||
<div style="font-size: 0.9rem; font-weight: 600; color: #1f2937; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
@(_envelopeReceiver.Envelope?.Title ?? "Dokument")
|
||||
@(_envelopeReceiver.Envelope?.Title ?? L("Document", "Dokument"))
|
||||
</div>
|
||||
@if (!string.IsNullOrWhiteSpace(_envelopeReceiver.Envelope?.User?.FullName))
|
||||
{
|
||||
<span style="font-size: 0.7rem; color: #6b7280; white-space: nowrap;">
|
||||
Von <span style="font-weight: 500; color: #374151;">@_envelopeReceiver.Envelope.User.FullName</span>
|
||||
@L("From", "Von") <span style="font-weight: 500; color: #374151;">@_envelopeReceiver.Envelope.User.FullName</span>
|
||||
</span>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<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;">@L("SignedDocument", "Signiertes Dokument")</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -49,12 +52,12 @@
|
||||
<button class="pdf-toolbar__btn pdf-toolbar__btn--signature-change pdf-toolbar__btn--signature-change-active"
|
||||
@onclick="OpenSubmitConfirmPopup"
|
||||
disabled="@_isLoggingOut"
|
||||
title="Abschließen"
|
||||
title="@L("Complete", "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>
|
||||
<span class="pdf-toolbar__btn-text">@L("Complete", "Abschließen")</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -67,9 +70,9 @@
|
||||
<div class="d-flex justify-content-center align-items-center h-100">
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-white mb-3" style="width: 3.5rem; height: 3.5rem;" role="status">
|
||||
<span class="visually-hidden">Lädt...</span>
|
||||
<span class="visually-hidden">@L("Loading", "Lädt...")</span>
|
||||
</div>
|
||||
<p class="text-white fw-semibold">Dokument wird geladen...</p>
|
||||
<p class="text-white fw-semibold">@L("DocumentLoading", "Dokument wird geladen...")</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -83,7 +86,7 @@
|
||||
<path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z" />
|
||||
</svg>
|
||||
<div>
|
||||
<h5 class="mb-2">Fehler</h5>
|
||||
<h5 class="mb-2">@L("Error", "Fehler")</h5>
|
||||
<p class="mb-0">@_errorMessage</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -99,7 +102,7 @@
|
||||
|
||||
@* Submit confirmation popup *@
|
||||
<DxPopup @bind-Visible="_submitConfirmVisible"
|
||||
HeaderText="Unterschrift bestätigen"
|
||||
HeaderText="@L("ConfirmSignature", "Unterschrift bestätigen")"
|
||||
Width="440px"
|
||||
MaxWidth="95vw"
|
||||
ShowFooter="true"
|
||||
@@ -115,10 +118,10 @@
|
||||
</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?
|
||||
@L("ReceiverSigned_ConfirmQuestion", "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.
|
||||
@L("ReceiverSigned_ConfirmBody", "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>
|
||||
@@ -128,7 +131,7 @@
|
||||
<button class="btn btn-outline-secondary"
|
||||
@onclick="() => _submitConfirmVisible = false"
|
||||
style="border-radius: 6px; padding: 0.5rem 1.25rem; font-weight: 500;">
|
||||
Abbrechen
|
||||
@L("Cancel", "Abbrechen")
|
||||
</button>
|
||||
<button class="btn btn-primary"
|
||||
@onclick="SubmitAndLogoutAsync"
|
||||
@@ -138,7 +141,7 @@
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-1" role="status"></span>
|
||||
}
|
||||
Abschließen
|
||||
@L("Complete", "Abschließen")
|
||||
</button>
|
||||
</div>
|
||||
</FooterContentTemplate>
|
||||
@@ -178,7 +181,7 @@
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(EnvelopeKey))
|
||||
{
|
||||
_errorMessage = "Envelope-Schlüssel fehlt.";
|
||||
_errorMessage = L("ReceiverSigned_EnvelopeKeyMissing", "Envelope-Schlüssel fehlt.");
|
||||
_isLoading = false;
|
||||
return;
|
||||
}
|
||||
@@ -215,7 +218,7 @@
|
||||
var pdfBytes = await PageDataService.GetDocumentOfReceiverAsync(_receiverUser);
|
||||
if (pdfBytes is not { Length: > 0 })
|
||||
{
|
||||
_errorMessage = "Dokument konnte nicht geladen werden.";
|
||||
_errorMessage = L("DocumentCouldNotBeLoaded", "Dokument konnte nicht geladen werden.");
|
||||
_isLoading = false;
|
||||
return;
|
||||
}
|
||||
@@ -244,7 +247,7 @@
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_errorMessage = $"Fehler: {ex.Message}";
|
||||
_errorMessage = string.Format(L("ErrorWithReason", "Fehler: {0}"), ex.Message);
|
||||
Logger.LogError(ex, "Error loading signed page for {EnvelopeKey}", EnvelopeKey);
|
||||
}
|
||||
|
||||
@@ -393,4 +396,10 @@
|
||||
if (comma < 0) return null;
|
||||
return Convert.FromBase64String(dataUrl[(comma + 1)..]);
|
||||
}
|
||||
|
||||
string L(string key, string fallback)
|
||||
{
|
||||
var value = UiLocalizer[key];
|
||||
return value.ResourceNotFound ? fallback : value.Value;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user