Compare commits
11 Commits
519a8bf3f9
...
e960c02cd4
| Author | SHA1 | Date | |
|---|---|---|---|
| e960c02cd4 | |||
| a0e2492757 | |||
| 28f8b829e3 | |||
| d00c459124 | |||
| 287c14e114 | |||
| 212ba9d569 | |||
| 735f072dd1 | |||
| 48cfd3f416 | |||
| 0d2e43b2d6 | |||
| 5aaa37b8ec | |||
| 051a8ce211 |
@@ -60,6 +60,11 @@ public record EnvelopeReceiverDto
|
||||
/// </summary>
|
||||
public string? PrivateMessage { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string? AccessCode { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@@ -83,4 +88,4 @@ public record EnvelopeReceiverDto
|
||||
h.Receiver?.Id == ReceiverId &&
|
||||
h.Status == EnvelopeStatus.DocumentSigned
|
||||
) ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
using AutoMapper;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
|
||||
using DigitalData.Core.Exceptions;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using EnvelopeGenerator.Application.Common.Dto;
|
||||
using EnvelopeGenerator.Application.Common.Configurations;
|
||||
using EnvelopeGenerator.Application.Common.Extensions;
|
||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
||||
using EnvelopeGenerator.Application.Histories.Commands;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
@@ -32,6 +37,16 @@ public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeComman
|
||||
|
||||
private IRepository<DocReceiverElement> DocReceiverElementRepository => _provider.GetRequiredService<IRepository<DocReceiverElement>>();
|
||||
|
||||
private IRepository<EmailTemplate> EmailTemplateRepository => _provider.GetRequiredService<IRepository<EmailTemplate>>();
|
||||
|
||||
private IRepository<EmailOut> EmailOutRepository => _provider.GetRequiredService<IRepository<EmailOut>>();
|
||||
|
||||
private MailParams MailParams => _provider.GetRequiredService<IOptions<MailParams>>().Value;
|
||||
|
||||
private DispatcherParams DispatcherParams => _provider.GetRequiredService<IOptions<DispatcherParams>>().Value;
|
||||
|
||||
private IConfigService ConfigService => _provider.GetRequiredService<IConfigService>();
|
||||
|
||||
private IMediator Mediator => _provider.GetRequiredService<IMediator>();
|
||||
|
||||
private readonly IMapper _mapper;
|
||||
@@ -370,6 +385,70 @@ public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeComman
|
||||
Status = EnvelopeStatus.EnvelopeQueued
|
||||
}, cancel);
|
||||
|
||||
await QueueInvitationEmailsAsync(envelope, links, userReference, cancel);
|
||||
|
||||
envelope.Status = EnvelopeStatus.EnvelopeQueued;
|
||||
}
|
||||
|
||||
private async Task QueueInvitationEmailsAsync(
|
||||
Envelope envelope,
|
||||
IReadOnlyCollection<EnvelopeReceiver> links,
|
||||
string senderReference,
|
||||
CancellationToken cancel)
|
||||
{
|
||||
var template = await EmailTemplateRepository.Query
|
||||
.SingleOrDefaultAsync(x => x.Name == EmailTemplateType.DocumentReceived.ToString(), cancel)
|
||||
?? throw new InvalidOperationException($"Email template not found: {EmailTemplateType.DocumentReceived}");
|
||||
|
||||
var signatureHost = await ConfigService.ReadDefaultSignatureHost();
|
||||
var senderName = senderReference;
|
||||
|
||||
foreach (var link in links)
|
||||
{
|
||||
var receiver = link.Receiver;
|
||||
if (receiver is null || string.IsNullOrWhiteSpace(receiver.EmailAddress) || string.IsNullOrWhiteSpace(receiver.Signature))
|
||||
continue;
|
||||
|
||||
var envelopeKey = (envelope.Uuid, receiver.Signature).ToEnvelopeKey();
|
||||
var documentLink = $"{signatureHost.TrimEnd('/')}/envelope/login/{Uri.EscapeDataString(envelopeKey)}";
|
||||
|
||||
var placeholders = new Dictionary<string, string>(MailParams.Placeholders)
|
||||
{
|
||||
["[NAME_RECEIVER]"] = link.Name ?? string.Empty,
|
||||
["[NAME_SENDER]"] = senderName ?? string.Empty,
|
||||
["[DOCUMENT_TITLE]"] = envelope.Title ?? string.Empty,
|
||||
["[MESSAGE]"] = envelope.Message ?? string.Empty,
|
||||
["[LINK_TO_DOCUMENT]"] = documentLink,
|
||||
["[LINK_TO_DOCUMENT_TEXT]"] = documentLink[..Math.Min(40, documentLink.Length)] + ".."
|
||||
};
|
||||
|
||||
var subject = ReplacePlaceHolders(template.Subject, placeholders);
|
||||
var body = ReplacePlaceHolders(template.Body, placeholders);
|
||||
|
||||
var emailOut = new EmailOut
|
||||
{
|
||||
EmailAddress = receiver.EmailAddress,
|
||||
EmailSubj = subject,
|
||||
EmailBody = body,
|
||||
AddedWhen = DateTime.Now,
|
||||
AddedWho = DispatcherParams.AddedWho,
|
||||
SendingProfile = DispatcherParams.SendingProfile,
|
||||
ReminderTypeId = DispatcherParams.ReminderTypeId,
|
||||
EmailAttmt1 = DispatcherParams.EmailAttmt1,
|
||||
WfId = (int)EnvelopeStatus.MessageInvitationSent,
|
||||
ReferenceString = receiver.EmailAddress,
|
||||
ReferenceId = link.ReceiverId
|
||||
};
|
||||
|
||||
await EmailOutRepository.CreateAsync(emailOut, cancel);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReplacePlaceHolders(string text, Dictionary<string, string> placeHolders)
|
||||
{
|
||||
foreach (var placeHolder in placeHolders)
|
||||
text = text.Replace(placeHolder.Key, placeHolder.Value);
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,9 +149,11 @@ public class ReadEnvelopeQueryHandler : IRequestHandler<ReadEnvelopeQuery, IEnum
|
||||
query = query.Where(e => Status.Completed.Contains(e.Status));
|
||||
|
||||
var envelopes = await query
|
||||
.Include(e => e.Type)
|
||||
.Include(e => e.EnvelopeReceivers).ThenInclude(er => er.Receiver)
|
||||
.Include(e => e.Histories)
|
||||
.ToListAsync(cancel);
|
||||
|
||||
return _mapper.Map<IEnumerable<EnvelopeDto>>(envelopes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,4 +651,123 @@
|
||||
<data name="Status" xml:space="preserve">
|
||||
<value>Status</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="ChangedAt" xml:space="preserve"><value>Geändert am</value></data>
|
||||
<data name="Close" xml:space="preserve"><value>Schließen</value></data>
|
||||
<data name="CloseLanguageMenu" xml:space="preserve"><value>Sprachmenü schließen</value></data>
|
||||
<data name="ConfirmSignature" xml:space="preserve"><value>Unterschrift bestätigen</value></data>
|
||||
<data name="Created" xml:space="preserve"><value>Erstellt</value></data>
|
||||
<data name="Delete" xml:space="preserve"><value>Löschen</value></data>
|
||||
<data name="Document" xml:space="preserve"><value>Dokument</value></data>
|
||||
<data name="DocumentCouldNotBeLoaded" xml:space="preserve"><value>Dokument konnte nicht geladen werden</value></data>
|
||||
<data name="DocumentLoading" xml:space="preserve"><value>Dokument wird geladen...</value></data>
|
||||
<data name="EmailAddress" xml:space="preserve"><value>E-Mail-Adresse</value></data>
|
||||
<data name="Envelope" xml:space="preserve"><value>Umschlag</value></data>
|
||||
<data name="Error" xml:space="preserve"><value>Fehler</value></data>
|
||||
<data name="ErrorLoadingDocument_WithReason" xml:space="preserve"><value>Fehler beim Laden des Dokuments: {0}</value></data>
|
||||
<data name="ErrorPage_DevelopmentMode" xml:space="preserve"><value>Entwicklungsmodus</value></data>
|
||||
<data name="ErrorPage_DevelopmentMode_Detail" xml:space="preserve"><value>Dies kann dazu führen, dass vertrauliche Informationen aus Ausnahmen Endbenutzern angezeigt werden. Für lokales Debugging setzen Sie ASPNETCORE_ENVIRONMENT auf Development und starten die App neu.</value></data>
|
||||
<data name="ErrorPage_DevelopmentMode_Info" xml:space="preserve"><value>Im Entwicklungsmodus werden detailliertere Fehlerinformationen angezeigt.</value></data>
|
||||
<data name="ErrorPage_DevelopmentMode_Warning" xml:space="preserve"><value>Der Entwicklungsmodus sollte in bereitgestellten Anwendungen nicht aktiviert sein.</value></data>
|
||||
<data name="ErrorPage_ProcessingRequest" xml:space="preserve"><value>Bei der Verarbeitung Ihrer Anfrage ist ein Fehler aufgetreten.</value></data>
|
||||
<data name="ErrorPage_RequestId" xml:space="preserve"><value>Anfrage-ID</value></data>
|
||||
<data name="ErrorWithReason" xml:space="preserve"><value>Fehler: {0}</value></data>
|
||||
<data name="From" xml:space="preserve"><value>Von</value></data>
|
||||
<data name="FullName" xml:space="preserve"><value>Vor- und Nachname</value></data>
|
||||
<data name="History" xml:space="preserve"><value>Historie</value></data>
|
||||
<data name="Index_DigitalSignature" xml:space="preserve"><value>Digitale Unterschrift</value></data>
|
||||
<data name="Index_PdfExport" xml:space="preserve"><value>PDF-Export</value></data>
|
||||
<data name="Index_SecureAccess" xml:space="preserve"><value>Sicherer Zugang</value></data>
|
||||
<data name="Index_Subtitle" xml:space="preserve"><value>Willkommen im eSign-Portal</value></data>
|
||||
<data name="Index_ToEnvelopeOverview" xml:space="preserve"><value>Zur Umschlag-Übersicht</value></data>
|
||||
<data name="Languages" xml:space="preserve"><value>Sprachen</value></data>
|
||||
<data name="Loading" xml:space="preserve"><value>Lädt...</value></data>
|
||||
<data name="Login" xml:space="preserve"><value>Anmelden</value></data>
|
||||
<data name="LoginInProgress" xml:space="preserve"><value>Anmelden ...</value></data>
|
||||
<data name="NotAuthenticatedPleaseLoginAgain" xml:space="preserve"><value>Nicht authentifiziert. Bitte melden Sie sich erneut an.</value></data>
|
||||
<data name="Only" xml:space="preserve"><value>Nur</value></data>
|
||||
<data name="OpenDocument" xml:space="preserve"><value>Dokument öffnen</value></data>
|
||||
<data name="Optional" xml:space="preserve"><value>optional</value></data>
|
||||
<data name="Pending" xml:space="preserve"><value>Ausstehend</value></data>
|
||||
<data name="Preview" xml:space="preserve"><value>Vorschau</value></data>
|
||||
<data name="PrivacyPolicy" xml:space="preserve"><value>Datenschutz</value></data>
|
||||
<data name="Receiver" xml:space="preserve"><value>Empfänger</value></data>
|
||||
<data name="ReceiverLogin_AccessCode_Placeholder" xml:space="preserve"><value>Zugangscode eingeben</value></data>
|
||||
<data name="ReceiverLogin_Description" xml:space="preserve"><value>Bitte geben Sie den Zugangscode ein, den Sie per E-Mail erhalten haben, um das Dokument sicher zu öffnen.</value></data>
|
||||
<data name="ReceiverLogin_FooterHelp" xml:space="preserve"><value>Bei Problemen wenden Sie sich bitte an den Absender des Dokuments.</value></data>
|
||||
<data name="ReceiverLogin_InvalidCode_Body" xml:space="preserve"><value>Der eingegebene Code ist falsch. Bitte versuchen Sie es erneut.</value></data>
|
||||
<data name="ReceiverLogin_InvalidCode_Title" xml:space="preserve"><value>Ungültiger Zugangscode.</value></data>
|
||||
<data name="ReceiverLogin_NotFound_Body" xml:space="preserve"><value>Der angegebene Zugangscode konnte keinem Dokument zugeordnet werden. Bitte prüfen Sie den Link in Ihrer E-Mail.</value></data>
|
||||
<data name="ReceiverLogin_NotFound_Title" xml:space="preserve"><value>Dokument nicht gefunden.</value></data>
|
||||
<data name="ReceiverLogin_Subtitle" xml:space="preserve"><value>Sicherer Zugang mit Zugangscode</value></data>
|
||||
<data name="ReceiverSigned_ConfirmBody" xml:space="preserve"><value>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.</value></data>
|
||||
<data name="ReceiverSigned_ConfirmQuestion" xml:space="preserve"><value>Möchten Sie das Dokument verbindlich unterschreiben?</value></data>
|
||||
<data name="ReceiverSigned_EnvelopeKeyMissing" xml:space="preserve"><value>Envelope-Schlüssel fehlt.</value></data>
|
||||
<data name="Refresh" xml:space="preserve"><value>Aktualisieren</value></data>
|
||||
<data name="RequiredField" xml:space="preserve"><value>Pflichtfeld</value></data>
|
||||
<data name="Selected" xml:space="preserve"><value>ausgewählte</value></data>
|
||||
<data name="Send" xml:space="preserve"><value>Senden</value></data>
|
||||
<data name="Sender_ActiveEnvelopes" xml:space="preserve"><value>Aktive Umschläge</value></data>
|
||||
<data name="Sender_CompletedEnvelopes" xml:space="preserve"><value>Abgeschlossene Umschläge</value></data>
|
||||
<data name="Sender_CreateEnvelope" xml:space="preserve"><value>Neuer Umschlag</value></data>
|
||||
<data name="Sender_CreateEnvelope_Title" xml:space="preserve"><value>Neuen Umschlag erstellen</value></data>
|
||||
<data name="Sender_DeleteEnvelope_Header" xml:space="preserve"><value>Umschlag löschen</value></data>
|
||||
<data name="Sender_DeleteEnvelope_ReasonPlaceholder" xml:space="preserve"><value>Begründung eingeben...</value></data>
|
||||
<data name="Sender_DeleteEnvelope_ReasonPrompt" xml:space="preserve"><value>Bitte geben Sie einen Grund an, warum der Umschlag gelöscht/zurückgezogen werden soll.</value></data>
|
||||
<data name="Sender_DeleteEnvelope_ReasonRequired" xml:space="preserve"><value>Bitte geben Sie einen Löschgrund an.</value></data>
|
||||
<data name="Sender_DeleteEnvelope_Title" xml:space="preserve"><value>Ausgewählten Umschlag löschen</value></data>
|
||||
<data name="Sender_Edit" xml:space="preserve"><value>Bearbeiten</value></data>
|
||||
<data name="Sender_EditEnvelope_Title" xml:space="preserve"><value>Ausgewählten Umschlag bearbeiten</value></data>
|
||||
<data name="Sender_ExportCurrentView_Title" xml:space="preserve"><value>Aktuelle Ansicht exportieren</value></data>
|
||||
<data name="Sender_ExportFormat_Aria" xml:space="preserve"><value>Exportformat auswählen</value></data>
|
||||
<data name="Sender_ExportView_Title" xml:space="preserve"><value>Export der aktuellen Tabellenansicht</value></data>
|
||||
<data name="Sender_LoadError_Title" xml:space="preserve"><value>Fehler beim Laden der Umschläge</value></data>
|
||||
<data name="Sender_LoadingEnvelopes" xml:space="preserve"><value>Umschläge werden geladen...</value></data>
|
||||
<data name="Sender_NoHistory" xml:space="preserve"><value>Keine Historie vorhanden</value></data>
|
||||
<data name="Sender_NoReceivers" xml:space="preserve"><value>Keine Empfänger</value></data>
|
||||
<data name="Sender_Preview_Title" xml:space="preserve"><value>Ausgewählten Umschlag als PDF anzeigen</value></data>
|
||||
<data name="Sender_PreviewDocumentNotFound" xml:space="preserve"><value>Das Dokument für Umschlag #{0} wurde nicht gefunden oder Sie haben keinen Zugriff darauf.</value></data>
|
||||
<data name="SenderEditor_AddSignature" xml:space="preserve"><value>Signatur hinzufügen</value></data>
|
||||
<data name="SenderEditor_ClickDocumentFor" xml:space="preserve"><value>Klicken Sie auf die Stelle im Dokument für</value></data>
|
||||
<data name="SenderEditor_EmailSuggestionsHint" xml:space="preserve"><value>Bereits verwendete E-Mail-Adressen werden bei der Eingabe vorgeschlagen.</value></data>
|
||||
<data name="SenderEditor_EmailSuggestionsLoading" xml:space="preserve"><value>E-Mail-Vorschläge werden geladen...</value></data>
|
||||
<data name="SenderEditor_ErrorCreatingEnvelope" xml:space="preserve"><value>Fehler beim Erstellen des Umschlags</value></data>
|
||||
<data name="SenderEditor_LoadEnvelopeError" xml:space="preserve"><value>Fehler beim Laden des Umschlags #{0}: {1}</value></data>
|
||||
<data name="SenderEditor_LoadFileError" xml:space="preserve"><value>Fehler beim Laden der Datei: {0}</value></data>
|
||||
<data name="SenderEditor_Message_Placeholder" xml:space="preserve"><value>Bitte unterzeichnen Sie das beigefügte Dokument.</value></data>
|
||||
<data name="SenderEditor_NoDocumentLoaded" xml:space="preserve"><value>Kein Dokument geladen</value></data>
|
||||
<data name="SenderEditor_NoReceiversAdded" xml:space="preserve"><value>Noch keine Empfänger hinzugefügt.</value></data>
|
||||
<data name="SenderEditor_PlaceSignatureField" xml:space="preserve"><value>Signaturfeld platzieren</value></data>
|
||||
<data name="SenderEditor_RemoveAllSignatureFields" xml:space="preserve"><value>Alle Signaturfelder entfernen</value></data>
|
||||
<data name="SenderEditor_SelectPdf" xml:space="preserve"><value>Bitte wählen Sie eine PDF-Datei aus.</value></data>
|
||||
<data name="SenderEditor_Sent_Body" xml:space="preserve"><value>Der bestehende Entwurf wurde versendet.</value></data>
|
||||
<data name="SenderEditor_Sent_Header" xml:space="preserve"><value>Umschlag gesendet</value></data>
|
||||
<data name="SenderEditor_Sent_Title" xml:space="preserve"><value>Umschlag wurde erfolgreich gesendet.</value></data>
|
||||
<data name="SenderEditor_SignatureField" xml:space="preserve"><value>Signaturfeld</value></data>
|
||||
<data name="SenderEditor_Title_Placeholder" xml:space="preserve"><value>z.B. Arbeitsvertrag 2025</value></data>
|
||||
<data name="SenderEditor_ToOverview" xml:space="preserve"><value>Zur Übersicht</value></data>
|
||||
<data name="SenderEditor_UploadPdfToPlaceFields" xml:space="preserve"><value>Laden Sie eine PDF-Datei hoch, um Signaturfelder zu platzieren.</value></data>
|
||||
<data name="SenderEditor_Validation_EmailDuplicate" xml:space="preserve"><value>Diese E-Mail-Adresse wurde bereits hinzugefügt.</value></data>
|
||||
<data name="SenderEditor_Validation_EmailInvalid" xml:space="preserve"><value>Bitte geben Sie eine gültige E-Mail-Adresse ein.</value></data>
|
||||
<data name="SenderEditor_Validation_EmailRequired" xml:space="preserve"><value>Bitte geben Sie eine E-Mail-Adresse ein.</value></data>
|
||||
<data name="SenderEditor_Validation_FullNameRequired" xml:space="preserve"><value>Bitte geben Sie einen Vor- und Nachnamen ein.</value></data>
|
||||
<data name="SenderEditor_Validation_ReceiverRequired" xml:space="preserve"><value>Bitte fügen Sie mindestens einen Empfänger hinzu.</value></data>
|
||||
<data name="SenderEditor_Validation_SignatureFieldRequired" xml:space="preserve"><value>Bitte platzieren Sie mindestens ein Signaturfeld.</value></data>
|
||||
<data name="SenderEditor_Validation_TitleRequired" xml:space="preserve"><value>Bitte geben Sie einen Titel ein.</value></data>
|
||||
<data name="SenderEditor_Validation_UploadPdf" xml:space="preserve"><value>Bitte laden Sie zuerst ein PDF-Dokument hoch.</value></data>
|
||||
<data name="SenderLogin_Description" xml:space="preserve"><value>Bitte melden Sie sich mit Ihren Zugangsdaten an, um auf das Sender-Dashboard zuzugreifen.</value></data>
|
||||
<data name="SenderLogin_FooterHelp" xml:space="preserve"><value>Bei Problemen wenden Sie sich bitte an den Administrator.</value></data>
|
||||
<data name="SenderLogin_InvalidCredentials_Body" xml:space="preserve"><value>Benutzername oder Passwort ist falsch. Bitte versuchen Sie es erneut.</value></data>
|
||||
<data name="SenderLogin_InvalidCredentials_Title" xml:space="preserve"><value>Ungültige Anmeldedaten.</value></data>
|
||||
<data name="SenderLogin_Password_Placeholder" xml:space="preserve"><value>Passwort eingeben</value></data>
|
||||
<data name="SenderLogin_Subtitle" xml:space="preserve"><value>Sicherer Zugang zum Sender-Dashboard</value></data>
|
||||
<data name="SenderLogin_Title" xml:space="preserve"><value>Sender Anmeldung</value></data>
|
||||
<data name="SenderLogin_Username_Placeholder" xml:space="preserve"><value>Benutzername eingeben</value></data>
|
||||
<data name="ServerError" xml:space="preserve"><value>Serverfehler.</value></data>
|
||||
<data name="Signed" xml:space="preserve"><value>Unterschrieben</value></data>
|
||||
<data name="SignedDocument" xml:space="preserve"><value>Signiertes Dokument</value></data>
|
||||
<data name="System" xml:space="preserve"><value>System</value></data>
|
||||
<data name="UnexpectedErrorTryAgainLater" xml:space="preserve"><value>Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.</value></data>
|
||||
<data name="UploadPdf" xml:space="preserve"><value>PDF hochladen</value></data>
|
||||
<data name="UserName" xml:space="preserve"><value>Benutzername</value></data>
|
||||
<data name="VerifyInProgress" xml:space="preserve"><value>Überprüfen ...</value></data>
|
||||
</root>
|
||||
|
||||
@@ -651,4 +651,123 @@
|
||||
<data name="Status" xml:space="preserve">
|
||||
<value>Status</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="ChangedAt" xml:space="preserve"><value>Changed at</value></data>
|
||||
<data name="Close" xml:space="preserve"><value>Close</value></data>
|
||||
<data name="CloseLanguageMenu" xml:space="preserve"><value>Close language menu</value></data>
|
||||
<data name="ConfirmSignature" xml:space="preserve"><value>Confirm signature</value></data>
|
||||
<data name="Created" xml:space="preserve"><value>Created</value></data>
|
||||
<data name="Delete" xml:space="preserve"><value>Delete</value></data>
|
||||
<data name="Document" xml:space="preserve"><value>Document</value></data>
|
||||
<data name="DocumentCouldNotBeLoaded" xml:space="preserve"><value>Document could not be loaded</value></data>
|
||||
<data name="DocumentLoading" xml:space="preserve"><value>Document is loading...</value></data>
|
||||
<data name="EmailAddress" xml:space="preserve"><value>Email address</value></data>
|
||||
<data name="Envelope" xml:space="preserve"><value>Envelope</value></data>
|
||||
<data name="Error" xml:space="preserve"><value>Error</value></data>
|
||||
<data name="ErrorLoadingDocument_WithReason" xml:space="preserve"><value>Error loading document: {0}</value></data>
|
||||
<data name="ErrorPage_DevelopmentMode" xml:space="preserve"><value>Development Mode</value></data>
|
||||
<data name="ErrorPage_DevelopmentMode_Detail" xml:space="preserve"><value>This can result in sensitive exception information being shown to end users. For local debugging, set ASPNETCORE_ENVIRONMENT to Development and restart the app.</value></data>
|
||||
<data name="ErrorPage_DevelopmentMode_Info" xml:space="preserve"><value>Switching to Development will display more detailed error information.</value></data>
|
||||
<data name="ErrorPage_DevelopmentMode_Warning" xml:space="preserve"><value>The Development environment should not be enabled in deployed applications.</value></data>
|
||||
<data name="ErrorPage_ProcessingRequest" xml:space="preserve"><value>An error occurred while processing your request.</value></data>
|
||||
<data name="ErrorPage_RequestId" xml:space="preserve"><value>Request ID</value></data>
|
||||
<data name="ErrorWithReason" xml:space="preserve"><value>Error: {0}</value></data>
|
||||
<data name="From" xml:space="preserve"><value>From</value></data>
|
||||
<data name="FullName" xml:space="preserve"><value>First and last name</value></data>
|
||||
<data name="History" xml:space="preserve"><value>History</value></data>
|
||||
<data name="Index_DigitalSignature" xml:space="preserve"><value>Digital signature</value></data>
|
||||
<data name="Index_PdfExport" xml:space="preserve"><value>PDF export</value></data>
|
||||
<data name="Index_SecureAccess" xml:space="preserve"><value>Secure access</value></data>
|
||||
<data name="Index_Subtitle" xml:space="preserve"><value>Welcome to the eSign portal</value></data>
|
||||
<data name="Index_ToEnvelopeOverview" xml:space="preserve"><value>Go to envelope overview</value></data>
|
||||
<data name="Languages" xml:space="preserve"><value>Languages</value></data>
|
||||
<data name="Loading" xml:space="preserve"><value>Loading...</value></data>
|
||||
<data name="Login" xml:space="preserve"><value>Sign in</value></data>
|
||||
<data name="LoginInProgress" xml:space="preserve"><value>Signing in ...</value></data>
|
||||
<data name="NotAuthenticatedPleaseLoginAgain" xml:space="preserve"><value>Not authenticated. Please sign in again.</value></data>
|
||||
<data name="Only" xml:space="preserve"><value>Only</value></data>
|
||||
<data name="OpenDocument" xml:space="preserve"><value>Open document</value></data>
|
||||
<data name="Optional" xml:space="preserve"><value>optional</value></data>
|
||||
<data name="Pending" xml:space="preserve"><value>Pending</value></data>
|
||||
<data name="Preview" xml:space="preserve"><value>Preview</value></data>
|
||||
<data name="PrivacyPolicy" xml:space="preserve"><value>Privacy policy</value></data>
|
||||
<data name="Receiver" xml:space="preserve"><value>Receiver</value></data>
|
||||
<data name="ReceiverLogin_AccessCode_Placeholder" xml:space="preserve"><value>Enter access code</value></data>
|
||||
<data name="ReceiverLogin_Description" xml:space="preserve"><value>Please enter the access code you received by email to securely open the document.</value></data>
|
||||
<data name="ReceiverLogin_FooterHelp" xml:space="preserve"><value>If you have issues, please contact the document sender.</value></data>
|
||||
<data name="ReceiverLogin_InvalidCode_Body" xml:space="preserve"><value>The entered code is incorrect. Please try again.</value></data>
|
||||
<data name="ReceiverLogin_InvalidCode_Title" xml:space="preserve"><value>Invalid access code.</value></data>
|
||||
<data name="ReceiverLogin_NotFound_Body" xml:space="preserve"><value>The provided access code could not be matched to a document. Please verify the link in your email.</value></data>
|
||||
<data name="ReceiverLogin_NotFound_Title" xml:space="preserve"><value>Document not found.</value></data>
|
||||
<data name="ReceiverLogin_Subtitle" xml:space="preserve"><value>Secure access with access code</value></data>
|
||||
<data name="ReceiverSigned_ConfirmBody" xml:space="preserve"><value>This action cannot be undone. By confirming, you declare that you have electronically signed the document shown above. The signed document will then be delivered to all involved parties.</value></data>
|
||||
<data name="ReceiverSigned_ConfirmQuestion" xml:space="preserve"><value>Do you want to sign this document as binding?</value></data>
|
||||
<data name="ReceiverSigned_EnvelopeKeyMissing" xml:space="preserve"><value>Envelope key is missing.</value></data>
|
||||
<data name="Refresh" xml:space="preserve"><value>Refresh</value></data>
|
||||
<data name="RequiredField" xml:space="preserve"><value>Required field</value></data>
|
||||
<data name="Selected" xml:space="preserve"><value>selected</value></data>
|
||||
<data name="Send" xml:space="preserve"><value>Send</value></data>
|
||||
<data name="Sender_ActiveEnvelopes" xml:space="preserve"><value>Active envelopes</value></data>
|
||||
<data name="Sender_CompletedEnvelopes" xml:space="preserve"><value>Completed envelopes</value></data>
|
||||
<data name="Sender_CreateEnvelope" xml:space="preserve"><value>New envelope</value></data>
|
||||
<data name="Sender_CreateEnvelope_Title" xml:space="preserve"><value>Create new envelope</value></data>
|
||||
<data name="Sender_DeleteEnvelope_Header" xml:space="preserve"><value>Delete envelope</value></data>
|
||||
<data name="Sender_DeleteEnvelope_ReasonPlaceholder" xml:space="preserve"><value>Enter reason...</value></data>
|
||||
<data name="Sender_DeleteEnvelope_ReasonPrompt" xml:space="preserve"><value>Please provide a reason why the envelope should be deleted/withdrawn.</value></data>
|
||||
<data name="Sender_DeleteEnvelope_ReasonRequired" xml:space="preserve"><value>Please provide a deletion reason.</value></data>
|
||||
<data name="Sender_DeleteEnvelope_Title" xml:space="preserve"><value>Delete selected envelope</value></data>
|
||||
<data name="Sender_Edit" xml:space="preserve"><value>Edit</value></data>
|
||||
<data name="Sender_EditEnvelope_Title" xml:space="preserve"><value>Edit selected envelope</value></data>
|
||||
<data name="Sender_ExportCurrentView_Title" xml:space="preserve"><value>Export current view</value></data>
|
||||
<data name="Sender_ExportFormat_Aria" xml:space="preserve"><value>Select export format</value></data>
|
||||
<data name="Sender_ExportView_Title" xml:space="preserve"><value>Export current table view</value></data>
|
||||
<data name="Sender_LoadError_Title" xml:space="preserve"><value>Error loading envelopes</value></data>
|
||||
<data name="Sender_LoadingEnvelopes" xml:space="preserve"><value>Loading envelopes...</value></data>
|
||||
<data name="Sender_NoHistory" xml:space="preserve"><value>No history available</value></data>
|
||||
<data name="Sender_NoReceivers" xml:space="preserve"><value>No receivers</value></data>
|
||||
<data name="Sender_Preview_Title" xml:space="preserve"><value>Show selected envelope as PDF</value></data>
|
||||
<data name="Sender_PreviewDocumentNotFound" xml:space="preserve"><value>The document for envelope #{0} was not found or you do not have access.</value></data>
|
||||
<data name="SenderEditor_AddSignature" xml:space="preserve"><value>Add signature</value></data>
|
||||
<data name="SenderEditor_ClickDocumentFor" xml:space="preserve"><value>Click on the location in the document for</value></data>
|
||||
<data name="SenderEditor_EmailSuggestionsHint" xml:space="preserve"><value>Previously used email addresses are suggested while typing.</value></data>
|
||||
<data name="SenderEditor_EmailSuggestionsLoading" xml:space="preserve"><value>Loading email suggestions...</value></data>
|
||||
<data name="SenderEditor_ErrorCreatingEnvelope" xml:space="preserve"><value>Error creating envelope</value></data>
|
||||
<data name="SenderEditor_LoadEnvelopeError" xml:space="preserve"><value>Error loading envelope #{0}: {1}</value></data>
|
||||
<data name="SenderEditor_LoadFileError" xml:space="preserve"><value>Error loading file: {0}</value></data>
|
||||
<data name="SenderEditor_Message_Placeholder" xml:space="preserve"><value>Please sign the attached document.</value></data>
|
||||
<data name="SenderEditor_NoDocumentLoaded" xml:space="preserve"><value>No document loaded</value></data>
|
||||
<data name="SenderEditor_NoReceiversAdded" xml:space="preserve"><value>No receivers added yet.</value></data>
|
||||
<data name="SenderEditor_PlaceSignatureField" xml:space="preserve"><value>Place signature field</value></data>
|
||||
<data name="SenderEditor_RemoveAllSignatureFields" xml:space="preserve"><value>Remove all signature fields</value></data>
|
||||
<data name="SenderEditor_SelectPdf" xml:space="preserve"><value>Please select a PDF file.</value></data>
|
||||
<data name="SenderEditor_Sent_Body" xml:space="preserve"><value>The existing draft has been sent.</value></data>
|
||||
<data name="SenderEditor_Sent_Header" xml:space="preserve"><value>Envelope sent</value></data>
|
||||
<data name="SenderEditor_Sent_Title" xml:space="preserve"><value>Envelope was sent successfully.</value></data>
|
||||
<data name="SenderEditor_SignatureField" xml:space="preserve"><value>Signature field</value></data>
|
||||
<data name="SenderEditor_Title_Placeholder" xml:space="preserve"><value>e.g. Employment contract 2025</value></data>
|
||||
<data name="SenderEditor_ToOverview" xml:space="preserve"><value>To overview</value></data>
|
||||
<data name="SenderEditor_UploadPdfToPlaceFields" xml:space="preserve"><value>Upload a PDF file to place signature fields.</value></data>
|
||||
<data name="SenderEditor_Validation_EmailDuplicate" xml:space="preserve"><value>This email address has already been added.</value></data>
|
||||
<data name="SenderEditor_Validation_EmailInvalid" xml:space="preserve"><value>Please enter a valid email address.</value></data>
|
||||
<data name="SenderEditor_Validation_EmailRequired" xml:space="preserve"><value>Please enter an email address.</value></data>
|
||||
<data name="SenderEditor_Validation_FullNameRequired" xml:space="preserve"><value>Please enter first and last name.</value></data>
|
||||
<data name="SenderEditor_Validation_ReceiverRequired" xml:space="preserve"><value>Please add at least one receiver.</value></data>
|
||||
<data name="SenderEditor_Validation_SignatureFieldRequired" xml:space="preserve"><value>Please place at least one signature field.</value></data>
|
||||
<data name="SenderEditor_Validation_TitleRequired" xml:space="preserve"><value>Please enter a title.</value></data>
|
||||
<data name="SenderEditor_Validation_UploadPdf" xml:space="preserve"><value>Please upload a PDF document first.</value></data>
|
||||
<data name="SenderLogin_Description" xml:space="preserve"><value>Please sign in with your credentials to access the sender dashboard.</value></data>
|
||||
<data name="SenderLogin_FooterHelp" xml:space="preserve"><value>If you have issues, please contact the administrator.</value></data>
|
||||
<data name="SenderLogin_InvalidCredentials_Body" xml:space="preserve"><value>Username or password is incorrect. Please try again.</value></data>
|
||||
<data name="SenderLogin_InvalidCredentials_Title" xml:space="preserve"><value>Invalid credentials.</value></data>
|
||||
<data name="SenderLogin_Password_Placeholder" xml:space="preserve"><value>Enter password</value></data>
|
||||
<data name="SenderLogin_Subtitle" xml:space="preserve"><value>Secure access to sender dashboard</value></data>
|
||||
<data name="SenderLogin_Title" xml:space="preserve"><value>Sender login</value></data>
|
||||
<data name="SenderLogin_Username_Placeholder" xml:space="preserve"><value>Enter username</value></data>
|
||||
<data name="ServerError" xml:space="preserve"><value>Server error.</value></data>
|
||||
<data name="Signed" xml:space="preserve"><value>Signed</value></data>
|
||||
<data name="SignedDocument" xml:space="preserve"><value>Signed document</value></data>
|
||||
<data name="System" xml:space="preserve"><value>System</value></data>
|
||||
<data name="UnexpectedErrorTryAgainLater" xml:space="preserve"><value>An unexpected error occurred. Please try again later.</value></data>
|
||||
<data name="UploadPdf" xml:space="preserve"><value>Upload PDF</value></data>
|
||||
<data name="UserName" xml:space="preserve"><value>Username</value></data>
|
||||
<data name="VerifyInProgress" xml:space="preserve"><value>Verifying ...</value></data>
|
||||
</root>
|
||||
|
||||
@@ -651,4 +651,123 @@
|
||||
<data name="Status" xml:space="preserve">
|
||||
<value>Statut</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="ChangedAt" xml:space="preserve"><value>Modifié le</value></data>
|
||||
<data name="Close" xml:space="preserve"><value>Fermer</value></data>
|
||||
<data name="CloseLanguageMenu" xml:space="preserve"><value>Fermer le menu des langues</value></data>
|
||||
<data name="ConfirmSignature" xml:space="preserve"><value>Confirmer la signature</value></data>
|
||||
<data name="Created" xml:space="preserve"><value>Créé</value></data>
|
||||
<data name="Delete" xml:space="preserve"><value>Supprimer</value></data>
|
||||
<data name="Document" xml:space="preserve"><value>Document</value></data>
|
||||
<data name="DocumentCouldNotBeLoaded" xml:space="preserve"><value>Le document n'a pas pu être chargé</value></data>
|
||||
<data name="DocumentLoading" xml:space="preserve"><value>Chargement du document...</value></data>
|
||||
<data name="EmailAddress" xml:space="preserve"><value>Adresse e-mail</value></data>
|
||||
<data name="Envelope" xml:space="preserve"><value>Enveloppe</value></data>
|
||||
<data name="Error" xml:space="preserve"><value>Erreur</value></data>
|
||||
<data name="ErrorLoadingDocument_WithReason" xml:space="preserve"><value>Erreur lors du chargement du document : {0}</value></data>
|
||||
<data name="ErrorPage_DevelopmentMode" xml:space="preserve"><value>Mode développement</value></data>
|
||||
<data name="ErrorPage_DevelopmentMode_Detail" xml:space="preserve"><value>Cela peut afficher des informations sensibles d'exception aux utilisateurs finaux. Pour le débogage local, définissez ASPNETCORE_ENVIRONMENT sur Development et redémarrez l'application.</value></data>
|
||||
<data name="ErrorPage_DevelopmentMode_Info" xml:space="preserve"><value>Le mode développement affiche des informations d'erreur plus détaillées.</value></data>
|
||||
<data name="ErrorPage_DevelopmentMode_Warning" xml:space="preserve"><value>Le mode développement ne doit pas être activé dans les applications déployées.</value></data>
|
||||
<data name="ErrorPage_ProcessingRequest" xml:space="preserve"><value>Une erreur s'est produite lors du traitement de votre demande.</value></data>
|
||||
<data name="ErrorPage_RequestId" xml:space="preserve"><value>ID de requête</value></data>
|
||||
<data name="ErrorWithReason" xml:space="preserve"><value>Erreur : {0}</value></data>
|
||||
<data name="From" xml:space="preserve"><value>De</value></data>
|
||||
<data name="FullName" xml:space="preserve"><value>Nom et prénom</value></data>
|
||||
<data name="History" xml:space="preserve"><value>Historique</value></data>
|
||||
<data name="Index_DigitalSignature" xml:space="preserve"><value>Signature numérique</value></data>
|
||||
<data name="Index_PdfExport" xml:space="preserve"><value>Export PDF</value></data>
|
||||
<data name="Index_SecureAccess" xml:space="preserve"><value>Accès sécurisé</value></data>
|
||||
<data name="Index_Subtitle" xml:space="preserve"><value>Bienvenue sur le portail eSign</value></data>
|
||||
<data name="Index_ToEnvelopeOverview" xml:space="preserve"><value>Aller à l'aperçu des enveloppes</value></data>
|
||||
<data name="Languages" xml:space="preserve"><value>Langues</value></data>
|
||||
<data name="Loading" xml:space="preserve"><value>Chargement...</value></data>
|
||||
<data name="Login" xml:space="preserve"><value>Connexion</value></data>
|
||||
<data name="LoginInProgress" xml:space="preserve"><value>Connexion ...</value></data>
|
||||
<data name="NotAuthenticatedPleaseLoginAgain" xml:space="preserve"><value>Non authentifié. Veuillez vous reconnecter.</value></data>
|
||||
<data name="Only" xml:space="preserve"><value>Uniquement</value></data>
|
||||
<data name="OpenDocument" xml:space="preserve"><value>Ouvrir le document</value></data>
|
||||
<data name="Optional" xml:space="preserve"><value>optionnel</value></data>
|
||||
<data name="Pending" xml:space="preserve"><value>En attente</value></data>
|
||||
<data name="Preview" xml:space="preserve"><value>Aperçu</value></data>
|
||||
<data name="PrivacyPolicy" xml:space="preserve"><value>Politique de confidentialité</value></data>
|
||||
<data name="Receiver" xml:space="preserve"><value>Destinataire</value></data>
|
||||
<data name="ReceiverLogin_AccessCode_Placeholder" xml:space="preserve"><value>Saisir le code d'accès</value></data>
|
||||
<data name="ReceiverLogin_Description" xml:space="preserve"><value>Veuillez saisir le code d'accès reçu par e-mail pour ouvrir le document en toute sécurité.</value></data>
|
||||
<data name="ReceiverLogin_FooterHelp" xml:space="preserve"><value>En cas de problème, veuillez contacter l'expéditeur du document.</value></data>
|
||||
<data name="ReceiverLogin_InvalidCode_Body" xml:space="preserve"><value>Le code saisi est incorrect. Veuillez réessayer.</value></data>
|
||||
<data name="ReceiverLogin_InvalidCode_Title" xml:space="preserve"><value>Code d'accès invalide.</value></data>
|
||||
<data name="ReceiverLogin_NotFound_Body" xml:space="preserve"><value>Le code d'accès indiqué ne correspond à aucun document. Veuillez vérifier le lien dans votre e-mail.</value></data>
|
||||
<data name="ReceiverLogin_NotFound_Title" xml:space="preserve"><value>Document introuvable.</value></data>
|
||||
<data name="ReceiverLogin_Subtitle" xml:space="preserve"><value>Accès sécurisé avec code d'accès</value></data>
|
||||
<data name="ReceiverSigned_ConfirmBody" xml:space="preserve"><value>Cette action est irréversible. En confirmant, vous déclarez avoir signé électroniquement le document affiché ci-dessus. Le document signé sera ensuite transmis à toutes les parties concernées.</value></data>
|
||||
<data name="ReceiverSigned_ConfirmQuestion" xml:space="preserve"><value>Voulez-vous signer ce document de manière contraignante ?</value></data>
|
||||
<data name="ReceiverSigned_EnvelopeKeyMissing" xml:space="preserve"><value>La clé d'enveloppe est manquante.</value></data>
|
||||
<data name="Refresh" xml:space="preserve"><value>Actualiser</value></data>
|
||||
<data name="RequiredField" xml:space="preserve"><value>Champ obligatoire</value></data>
|
||||
<data name="Selected" xml:space="preserve"><value>sélectionnées</value></data>
|
||||
<data name="Send" xml:space="preserve"><value>Envoyer</value></data>
|
||||
<data name="Sender_ActiveEnvelopes" xml:space="preserve"><value>Enveloppes actives</value></data>
|
||||
<data name="Sender_CompletedEnvelopes" xml:space="preserve"><value>Enveloppes terminées</value></data>
|
||||
<data name="Sender_CreateEnvelope" xml:space="preserve"><value>Nouvelle enveloppe</value></data>
|
||||
<data name="Sender_CreateEnvelope_Title" xml:space="preserve"><value>Créer une nouvelle enveloppe</value></data>
|
||||
<data name="Sender_DeleteEnvelope_Header" xml:space="preserve"><value>Supprimer l'enveloppe</value></data>
|
||||
<data name="Sender_DeleteEnvelope_ReasonPlaceholder" xml:space="preserve"><value>Saisir la raison...</value></data>
|
||||
<data name="Sender_DeleteEnvelope_ReasonPrompt" xml:space="preserve"><value>Veuillez indiquer la raison pour laquelle l'enveloppe doit être supprimée/retirée.</value></data>
|
||||
<data name="Sender_DeleteEnvelope_ReasonRequired" xml:space="preserve"><value>Veuillez indiquer un motif de suppression.</value></data>
|
||||
<data name="Sender_DeleteEnvelope_Title" xml:space="preserve"><value>Supprimer l'enveloppe sélectionnée</value></data>
|
||||
<data name="Sender_Edit" xml:space="preserve"><value>Modifier</value></data>
|
||||
<data name="Sender_EditEnvelope_Title" xml:space="preserve"><value>Modifier l'enveloppe sélectionnée</value></data>
|
||||
<data name="Sender_ExportCurrentView_Title" xml:space="preserve"><value>Exporter la vue actuelle</value></data>
|
||||
<data name="Sender_ExportFormat_Aria" xml:space="preserve"><value>Sélectionner le format d'export</value></data>
|
||||
<data name="Sender_ExportView_Title" xml:space="preserve"><value>Exporter la vue actuelle du tableau</value></data>
|
||||
<data name="Sender_LoadError_Title" xml:space="preserve"><value>Erreur lors du chargement des enveloppes</value></data>
|
||||
<data name="Sender_LoadingEnvelopes" xml:space="preserve"><value>Chargement des enveloppes...</value></data>
|
||||
<data name="Sender_NoHistory" xml:space="preserve"><value>Aucun historique disponible</value></data>
|
||||
<data name="Sender_NoReceivers" xml:space="preserve"><value>Aucun destinataire</value></data>
|
||||
<data name="Sender_Preview_Title" xml:space="preserve"><value>Afficher l'enveloppe sélectionnée en PDF</value></data>
|
||||
<data name="Sender_PreviewDocumentNotFound" xml:space="preserve"><value>Le document de l'enveloppe #{0} est introuvable ou vous n'avez pas l'autorisation d'y accéder.</value></data>
|
||||
<data name="SenderEditor_AddSignature" xml:space="preserve"><value>Ajouter une signature</value></data>
|
||||
<data name="SenderEditor_ClickDocumentFor" xml:space="preserve"><value>Cliquez sur l'emplacement dans le document pour</value></data>
|
||||
<data name="SenderEditor_EmailSuggestionsHint" xml:space="preserve"><value>Les adresses e-mail déjà utilisées sont proposées pendant la saisie.</value></data>
|
||||
<data name="SenderEditor_EmailSuggestionsLoading" xml:space="preserve"><value>Chargement des suggestions d'e-mail...</value></data>
|
||||
<data name="SenderEditor_ErrorCreatingEnvelope" xml:space="preserve"><value>Erreur lors de la création de l'enveloppe</value></data>
|
||||
<data name="SenderEditor_LoadEnvelopeError" xml:space="preserve"><value>Erreur lors du chargement de l'enveloppe #{0} : {1}</value></data>
|
||||
<data name="SenderEditor_LoadFileError" xml:space="preserve"><value>Erreur lors du chargement du fichier : {0}</value></data>
|
||||
<data name="SenderEditor_Message_Placeholder" xml:space="preserve"><value>Veuillez signer le document ci-joint.</value></data>
|
||||
<data name="SenderEditor_NoDocumentLoaded" xml:space="preserve"><value>Aucun document chargé</value></data>
|
||||
<data name="SenderEditor_NoReceiversAdded" xml:space="preserve"><value>Aucun destinataire ajouté pour le moment.</value></data>
|
||||
<data name="SenderEditor_PlaceSignatureField" xml:space="preserve"><value>Placer un champ de signature</value></data>
|
||||
<data name="SenderEditor_RemoveAllSignatureFields" xml:space="preserve"><value>Supprimer tous les champs de signature</value></data>
|
||||
<data name="SenderEditor_SelectPdf" xml:space="preserve"><value>Veuillez sélectionner un fichier PDF.</value></data>
|
||||
<data name="SenderEditor_Sent_Body" xml:space="preserve"><value>Le brouillon existant a été envoyé.</value></data>
|
||||
<data name="SenderEditor_Sent_Header" xml:space="preserve"><value>Enveloppe envoyée</value></data>
|
||||
<data name="SenderEditor_Sent_Title" xml:space="preserve"><value>L'enveloppe a été envoyée avec succès.</value></data>
|
||||
<data name="SenderEditor_SignatureField" xml:space="preserve"><value>Champ de signature</value></data>
|
||||
<data name="SenderEditor_Title_Placeholder" xml:space="preserve"><value>p. ex. Contrat de travail 2025</value></data>
|
||||
<data name="SenderEditor_ToOverview" xml:space="preserve"><value>Vers l'aperçu</value></data>
|
||||
<data name="SenderEditor_UploadPdfToPlaceFields" xml:space="preserve"><value>Téléversez un fichier PDF pour placer des champs de signature.</value></data>
|
||||
<data name="SenderEditor_Validation_EmailDuplicate" xml:space="preserve"><value>Cette adresse e-mail a déjà été ajoutée.</value></data>
|
||||
<data name="SenderEditor_Validation_EmailInvalid" xml:space="preserve"><value>Veuillez saisir une adresse e-mail valide.</value></data>
|
||||
<data name="SenderEditor_Validation_EmailRequired" xml:space="preserve"><value>Veuillez saisir une adresse e-mail.</value></data>
|
||||
<data name="SenderEditor_Validation_FullNameRequired" xml:space="preserve"><value>Veuillez saisir le nom et le prénom.</value></data>
|
||||
<data name="SenderEditor_Validation_ReceiverRequired" xml:space="preserve"><value>Veuillez ajouter au moins un destinataire.</value></data>
|
||||
<data name="SenderEditor_Validation_SignatureFieldRequired" xml:space="preserve"><value>Veuillez placer au moins un champ de signature.</value></data>
|
||||
<data name="SenderEditor_Validation_TitleRequired" xml:space="preserve"><value>Veuillez saisir un titre.</value></data>
|
||||
<data name="SenderEditor_Validation_UploadPdf" xml:space="preserve"><value>Veuillez d'abord téléverser un document PDF.</value></data>
|
||||
<data name="SenderLogin_Description" xml:space="preserve"><value>Veuillez vous connecter avec vos identifiants pour accéder au tableau de bord expéditeur.</value></data>
|
||||
<data name="SenderLogin_FooterHelp" xml:space="preserve"><value>En cas de problème, veuillez contacter l'administrateur.</value></data>
|
||||
<data name="SenderLogin_InvalidCredentials_Body" xml:space="preserve"><value>Le nom d'utilisateur ou le mot de passe est incorrect. Veuillez réessayer.</value></data>
|
||||
<data name="SenderLogin_InvalidCredentials_Title" xml:space="preserve"><value>Identifiants invalides.</value></data>
|
||||
<data name="SenderLogin_Password_Placeholder" xml:space="preserve"><value>Saisir le mot de passe</value></data>
|
||||
<data name="SenderLogin_Subtitle" xml:space="preserve"><value>Accès sécurisé au tableau de bord expéditeur</value></data>
|
||||
<data name="SenderLogin_Title" xml:space="preserve"><value>Connexion expéditeur</value></data>
|
||||
<data name="SenderLogin_Username_Placeholder" xml:space="preserve"><value>Saisir le nom d'utilisateur</value></data>
|
||||
<data name="ServerError" xml:space="preserve"><value>Erreur serveur.</value></data>
|
||||
<data name="Signed" xml:space="preserve"><value>Signé</value></data>
|
||||
<data name="SignedDocument" xml:space="preserve"><value>Document signé</value></data>
|
||||
<data name="System" xml:space="preserve"><value>Système</value></data>
|
||||
<data name="UnexpectedErrorTryAgainLater" xml:space="preserve"><value>Une erreur inattendue s'est produite. Veuillez réessayer plus tard.</value></data>
|
||||
<data name="UploadPdf" xml:space="preserve"><value>Téléverser un PDF</value></data>
|
||||
<data name="UserName" xml:space="preserve"><value>Nom d'utilisateur</value></data>
|
||||
<data name="VerifyInProgress" xml:space="preserve"><value>Vérification ...</value></data>
|
||||
</root>
|
||||
|
||||
7
EnvelopeGenerator.Domain/Resources/Model.cs
Normal file
7
EnvelopeGenerator.Domain/Resources/Model.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace EnvelopeGenerator.Domain.Resources
|
||||
{
|
||||
// Marker type for IStringLocalizer lookup of Resources/Model.resx.
|
||||
public class Model
|
||||
{
|
||||
}
|
||||
}
|
||||
116
EnvelopeGenerator.Domain/Resources/Model.fr-FR.resx
Normal file
116
EnvelopeGenerator.Domain/Resources/Model.fr-FR.resx
Normal file
@@ -0,0 +1,116 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
|
||||
<data name="AccessCodeIncorrect" xml:space="preserve">
|
||||
<value>Code d'acces incorrect</value>
|
||||
</data>
|
||||
<data name="DocumentRejected" xml:space="preserve">
|
||||
<value>Document rejete</value>
|
||||
</data>
|
||||
<data name="DocumentSigned" xml:space="preserve">
|
||||
<value>Document signe</value>
|
||||
</data>
|
||||
<data name="Draft" xml:space="preserve">
|
||||
<value>Brouillon</value>
|
||||
</data>
|
||||
<data name="EnvelopeCompletelySigned" xml:space="preserve">
|
||||
<value>Entierement signe</value>
|
||||
</data>
|
||||
<data name="EnvelopeCreated" xml:space="preserve">
|
||||
<value>Enveloppe creee</value>
|
||||
</data>
|
||||
<data name="EnvelopeDeleted" xml:space="preserve">
|
||||
<value>Enveloppe supprimee</value>
|
||||
</data>
|
||||
<data name="EnvelopePartlySigned" xml:space="preserve">
|
||||
<value>Partiellement signee</value>
|
||||
</data>
|
||||
<data name="EnvelopeQueued" xml:space="preserve">
|
||||
<value>Enveloppe en file d'attente</value>
|
||||
</data>
|
||||
<data name="EnvelopeRejected" xml:space="preserve">
|
||||
<value>Enveloppe rejetee</value>
|
||||
</data>
|
||||
<data name="EnvelopeSaved" xml:space="preserve">
|
||||
<value>Enregistree</value>
|
||||
</data>
|
||||
<data name="EnvelopeSent" xml:space="preserve">
|
||||
<value>Envoyee</value>
|
||||
</data>
|
||||
<data name="EnvelopeWithdrawn" xml:space="preserve">
|
||||
<value>Enveloppe retiree</value>
|
||||
</data>
|
||||
<data name="MessageAccessCodeSent" xml:space="preserve">
|
||||
<value>Code d'acces envoye</value>
|
||||
</data>
|
||||
<data name="MessageCompletionSent" xml:space="preserve">
|
||||
<value>E-mail de cloture envoye</value>
|
||||
</data>
|
||||
<data name="MessageConfirmationSent" xml:space="preserve">
|
||||
<value>Confirmation finale envoyee</value>
|
||||
</data>
|
||||
<data name="MessageDeletionSent" xml:space="preserve">
|
||||
<value>Notification de suppression envoyee</value>
|
||||
</data>
|
||||
<data name="MessageInvitationSent" xml:space="preserve">
|
||||
<value>Invitation envoyee</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,93 @@
|
||||
@using EnvelopeGenerator.Server.Client.Services
|
||||
@using EnvelopeGenerator.Application.Resources
|
||||
@using Microsoft.Extensions.Localization
|
||||
@rendermode InteractiveWebAssembly
|
||||
|
||||
@inject LocalizationService LocalizationService
|
||||
@inject NavigationManager Navigation
|
||||
@inject IStringLocalizer<Resource> UiLocalizer
|
||||
|
||||
<div class="sf-lang-combo">
|
||||
<button type="button"
|
||||
class="sf-lang-trigger"
|
||||
@onclick="ToggleMenu"
|
||||
aria-label="@L("Language", "Language")"
|
||||
aria-expanded="@_isOpen">
|
||||
<span class="fi @_selected.FlagClass"></span>
|
||||
</button>
|
||||
|
||||
@if (_isOpen)
|
||||
{
|
||||
<button type="button"
|
||||
class="sf-lang-backdrop"
|
||||
aria-label="@L("CloseLanguageMenu", "Close language menu")"
|
||||
@onclick="CloseMenu">
|
||||
</button>
|
||||
|
||||
<div class="sf-lang-menu" role="listbox" aria-label="@L("Languages", "Languages")">
|
||||
@foreach (var option in _options)
|
||||
{
|
||||
<button type="button"
|
||||
class="sf-lang-option @(_selected.Code == option.Code ? "sf-lang-option--active" : string.Empty)"
|
||||
role="option"
|
||||
aria-selected="@(_selected.Code == option.Code)"
|
||||
@onmousedown:preventDefault="true"
|
||||
@onclick="() => SelectLanguageAsync(option)">
|
||||
<span class="fi @option.FlagClass"></span>
|
||||
<span>@option.ShortName</span>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private readonly IReadOnlyList<LanguageOption> _options =
|
||||
[
|
||||
new("de-DE", "fi-de", "DE"),
|
||||
new("en-US", "fi-us", "EN"),
|
||||
new("fr-FR", "fi-fr", "FR")
|
||||
];
|
||||
|
||||
private LanguageOption _selected = new("de-DE", "fi-de", "DE");
|
||||
private bool _isOpen;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var currentLanguage = await LocalizationService.GetLanguageAsync();
|
||||
_selected = _options.FirstOrDefault(x => x.Code.Equals(currentLanguage, StringComparison.OrdinalIgnoreCase))
|
||||
?? _options[0];
|
||||
}
|
||||
|
||||
private void ToggleMenu()
|
||||
{
|
||||
_isOpen = !_isOpen;
|
||||
}
|
||||
|
||||
private void CloseMenu()
|
||||
{
|
||||
_isOpen = false;
|
||||
}
|
||||
|
||||
private async Task SelectLanguageAsync(LanguageOption option)
|
||||
{
|
||||
if (_selected.Code.Equals(option.Code, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_isOpen = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_selected = option;
|
||||
_isOpen = false;
|
||||
await LocalizationService.SetLanguageAsync(option.Code);
|
||||
Navigation.NavigateTo(Navigation.Uri, forceLoad: true);
|
||||
}
|
||||
|
||||
private sealed record LanguageOption(string Code, string FlagClass, string ShortName);
|
||||
|
||||
string L(string key, string fallback)
|
||||
{
|
||||
var value = UiLocalizer[key];
|
||||
return value.ResourceNotFound ? fallback : value.Value;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
@using EnvelopeGenerator.Server.Client.Services
|
||||
@using EnvelopeGenerator.Application.Resources
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.Extensions.Localization
|
||||
@using System.Globalization
|
||||
@inherits LayoutComponentBase
|
||||
@implements IDisposable
|
||||
|
||||
@inject ThemeService ThemeService
|
||||
@inject IStringLocalizer<Resource> UiLocalizer
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
<HeadContent>
|
||||
<link href="@ThemeService.ThemeCssHref" rel="stylesheet" />
|
||||
@@ -18,10 +24,13 @@
|
||||
<div class="receiver-footer__left">
|
||||
<span>© SignFlow 2023-2024 <a href="https://digitaldata.works" target="_blank" rel="noopener">Digital Data GmbH</a></span>
|
||||
<span class="receiver-footer__sep">|</span>
|
||||
<a href="docs/privacy-policy.de-DE.html" target="_blank" rel="noopener">Datenschutz</a>
|
||||
<a href="@GetPrivacyPolicyPath()" target="_blank" rel="noopener">@L("PrivacyPolicy", "Datenschutz")</a>
|
||||
</div>
|
||||
|
||||
<ThemeControls />
|
||||
<div class="receiver-footer__right">
|
||||
<LanguageControls />
|
||||
<ThemeControls />
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -32,6 +41,7 @@
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Navigation.LocationChanged += OnLocationChanged;
|
||||
await FontLoader.LoadFonts(HttpClientFactory, ["opensans.ttf"]);
|
||||
_themeChangeHandler = () => InvokeAsync(StateHasChanged);
|
||||
ThemeService.OnChange += _themeChangeHandler;
|
||||
@@ -39,5 +49,31 @@
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> ThemeService.OnChange -= _themeChangeHandler;
|
||||
{
|
||||
ThemeService.OnChange -= _themeChangeHandler;
|
||||
Navigation.LocationChanged -= OnLocationChanged;
|
||||
}
|
||||
|
||||
private void OnLocationChanged(object? sender, LocationChangedEventArgs e)
|
||||
=> _ = InvokeAsync(StateHasChanged);
|
||||
|
||||
private string GetPrivacyPolicyPath()
|
||||
{
|
||||
var current = CultureInfo.CurrentUICulture.Name;
|
||||
var code = current switch
|
||||
{
|
||||
"de-DE" => "de-DE",
|
||||
"en-US" => "en-US",
|
||||
"fr-FR" => "fr-FR",
|
||||
_ => "de-DE"
|
||||
};
|
||||
|
||||
return $"docs/privacy-policy.{code}.html";
|
||||
}
|
||||
|
||||
string L(string key, string fallback)
|
||||
{
|
||||
var value = UiLocalizer[key];
|
||||
return value.ResourceNotFound ? fallback : value.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
@page "/"
|
||||
@using EnvelopeGenerator.Application.Resources
|
||||
@using Microsoft.Extensions.Localization
|
||||
@inject IJSRuntime JS
|
||||
@inject NavigationManager Navigation
|
||||
@inject EnvelopeGenerator.Server.Client.Services.CustomImagesService CustomImagesService
|
||||
@inject IStringLocalizer<Resource> UiLocalizer
|
||||
@rendermode InteractiveWebAssembly
|
||||
|
||||
<div class="home-page-wrapper">
|
||||
@@ -24,7 +27,7 @@
|
||||
}
|
||||
<div>
|
||||
<h1 class="home-hero-header__title">SignFlow</h1>
|
||||
<p class="home-hero-header__subtitle">Willkommen im eSign-Portal</p>
|
||||
<p class="home-hero-header__subtitle">@L("Index_Subtitle", "Willkommen im eSign-Portal")</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,7 +46,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4Zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1H2Zm13 2.383-4.708 2.825L15 11.105V5.383Zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741ZM1 11.105l4.708-2.897L1 5.383v5.722Z"/>
|
||||
</svg>
|
||||
Zur Umschlag-Übersicht
|
||||
@L("Index_ToEnvelopeOverview", "Zur Umschlag-Übersicht")
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"/>
|
||||
</svg>
|
||||
@@ -54,20 +57,20 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="me-1" viewBox="0 0 16 16">
|
||||
<path d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z"/>
|
||||
</svg>
|
||||
Sicherer Zugang
|
||||
@L("Index_SecureAccess", "Sicherer Zugang")
|
||||
</div>
|
||||
<div class="home-feature-badge">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" class="me-1" 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>
|
||||
Digitale Unterschrift
|
||||
@L("Index_DigitalSignature", "Digitale Unterschrift")
|
||||
</div>
|
||||
<div class="home-feature-badge">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" 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 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
|
||||
</svg>
|
||||
PDF-Export
|
||||
@L("Index_PdfExport", "PDF-Export")
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -80,10 +83,9 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private const string HomePageDescription =
|
||||
"Das digitale Unterschriftenportal ist eine Plattform, die entwickelt wurde, um Ihre Dokumente sicher zu unterschreiben und zu verwalten. " +
|
||||
"Mit seiner benutzerfreundlichen Oberfläche können Sie Ihre Dokumente schnell hochladen, die Unterschriftsprozesse verfolgen und Ihre digitalen Unterschriftenanwendungen einfach durchführen. " +
|
||||
"Dieses Portal beschleunigt Ihren Arbeitsablauf mit rechtlich gültigen Unterschriften und erhöht gleichzeitig die Sicherheit Ihrer Dokumente.";
|
||||
private string HomePageDescription => L(
|
||||
"HomePageDescription",
|
||||
"Das digitale Unterschriftenportal ist eine Plattform, die entwickelt wurde, um Ihre Dokumente sicher zu unterschreiben und zu verwalten. Mit seiner benutzerfreundlichen Oberfläche können Sie Ihre Dokumente schnell hochladen, die Unterschriftsprozesse verfolgen und Ihre digitalen Unterschriftenanwendungen einfach durchführen. Dieses Portal beschleunigt Ihren Arbeitsablauf mit rechtlich gültigen Unterschriften und erhöht gleichzeitig die Sicherheit Ihrer Dokumente.");
|
||||
|
||||
private EnvelopeGenerator.Server.Client.Services.CustomImageDto? _appLogo;
|
||||
|
||||
@@ -99,4 +101,10 @@
|
||||
if (!firstRender) return;
|
||||
await JS.InvokeVoidAsync("receiverSignature.startTyped", "home-description", HomePageDescription, 15);
|
||||
}
|
||||
|
||||
string L(string key, string fallback)
|
||||
{
|
||||
var value = UiLocalizer[key];
|
||||
return value.ResourceNotFound ? fallback : value.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
@page "/envelope/login/{EnvelopeKey}"
|
||||
@rendermode InteractiveWebAssembly
|
||||
@using EnvelopeGenerator.Server.Client.Services
|
||||
@using EnvelopeGenerator.Application.Resources
|
||||
@using Microsoft.Extensions.Localization
|
||||
@inject AuthService AuthService
|
||||
@inject NavigationManager Navigation
|
||||
@inject IStringLocalizer<Resource> UiLocalizer
|
||||
|
||||
<div class="login-page-wrapper d-flex align-items-center justify-content-center min-vh-100">
|
||||
<div class="login-card card shadow border-0" style="max-width: 440px; width: 100%;">
|
||||
@@ -13,14 +16,14 @@
|
||||
<path d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h5 class="mb-0 fw-semibold">Dokument öffnen</h5>
|
||||
<p class="mb-0 mt-1 opacity-75" style="font-size: 0.85rem;">Sicherer Zugang mit Zugangscode</p>
|
||||
<h5 class="mb-0 fw-semibold">@L("OpenDocument", "Dokument öffnen")</h5>
|
||||
<p class="mb-0 mt-1 opacity-75" style="font-size: 0.85rem;">@L("ReceiverLogin_Subtitle", "Sicherer Zugang mit Zugangscode")</p>
|
||||
</div>
|
||||
|
||||
<div class="card-body p-4">
|
||||
|
||||
<p class="text-muted mb-4" style="font-size: 0.875rem; line-height: 1.5;">
|
||||
Bitte geben Sie den Zugangscode ein, den Sie per E-Mail erhalten haben, um das Dokument sicher zu öffnen.
|
||||
@L("ReceiverLogin_Description", "Bitte geben Sie den Zugangscode ein, den Sie per E-Mail erhalten haben, um das Dokument sicher zu öffnen.")
|
||||
</p>
|
||||
|
||||
@if (LoginResult == EnvelopeLoginResult.NotFound)
|
||||
@@ -30,8 +33,8 @@
|
||||
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z" />
|
||||
</svg>
|
||||
<div>
|
||||
<strong>Dokument nicht gefunden.</strong><br />
|
||||
<span style="font-size:0.85rem;">Der angegebene Zugangscode konnte keinem Dokument zugeordnet werden. Bitte prüfen Sie den Link in Ihrer E-Mail.</span>
|
||||
<strong>@L("ReceiverLogin_NotFound_Title", "Dokument nicht gefunden.")</strong><br />
|
||||
<span style="font-size:0.85rem;">@L("ReceiverLogin_NotFound_Body", "Der angegebene Zugangscode konnte keinem Dokument zugeordnet werden. Bitte prüfen Sie den Link in Ihrer E-Mail.")</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -42,8 +45,8 @@
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
|
||||
</svg>
|
||||
<div>
|
||||
<strong>Ungültiger Zugangscode.</strong><br />
|
||||
<span style="font-size:0.85rem;">Der eingegebene Code ist falsch. Bitte versuchen Sie es erneut.</span>
|
||||
<strong>@L("ReceiverLogin_InvalidCode_Title", "Ungültiger Zugangscode.")</strong><br />
|
||||
<span style="font-size:0.85rem;">@L("ReceiverLogin_InvalidCode_Body", "Der eingegebene Code ist falsch. Bitte versuchen Sie es erneut.")</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -55,15 +58,15 @@
|
||||
<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>
|
||||
<strong>Serverfehler.</strong><br />
|
||||
<span style="font-size:0.85rem;">Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.</span>
|
||||
<strong>@L("ServerError", "Serverfehler.")</strong><br />
|
||||
<span style="font-size:0.85rem;">@L("UnexpectedErrorTryAgainLater", "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.")</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-medium" for="login-access-code">
|
||||
Zugangscode
|
||||
@L("AccessCode", "Zugangscode")
|
||||
<span class="text-danger ms-1">*</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
@@ -75,7 +78,7 @@
|
||||
<input id="login-access-code"
|
||||
type="@(ShowCode ? "text" : "password")"
|
||||
class="form-control border-start-0 border-end-0 @(LoginResult == EnvelopeLoginResult.InvalidCode ? "is-invalid" : null)"
|
||||
placeholder="Zugangscode eingeben"
|
||||
placeholder="@L("ReceiverLogin_AccessCode_Placeholder", "Zugangscode eingeben")"
|
||||
@bind="AccessCode"
|
||||
@bind:event="oninput"
|
||||
@onkeydown="OnKeyDownAsync"
|
||||
@@ -113,21 +116,21 @@
|
||||
@if (IsLoading)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
<span>Überprüfen …</span>
|
||||
<span>@L("VerifyInProgress", "Überprüfen …")</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="me-2" viewBox="0 0 16 16">
|
||||
<path d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z" />
|
||||
</svg>
|
||||
<span>Dokument öffnen</span>
|
||||
<span>@L("OpenDocument", "Dokument öffnen")</span>
|
||||
}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-center text-muted py-3 border-0 bg-transparent" style="font-size: 0.78rem;">
|
||||
Bei Problemen wenden Sie sich bitte an den Absender des Dokuments.
|
||||
@L("ReceiverLogin_FooterHelp", "Bei Problemen wenden Sie sich bitte an den Absender des Dokuments.")
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -167,5 +170,11 @@
|
||||
IsLoading = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
string L(string key, string fallback)
|
||||
{
|
||||
var value = UiLocalizer[key];
|
||||
return value.ResourceNotFound ? fallback : value.Value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
@page "/sender/login"
|
||||
@rendermode InteractiveWebAssembly
|
||||
@using EnvelopeGenerator.Server.Client.Services
|
||||
@using EnvelopeGenerator.Application.Resources
|
||||
@using Microsoft.Extensions.Localization
|
||||
@inject AuthService AuthService
|
||||
@inject NavigationManager Navigation
|
||||
@inject IStringLocalizer<Resource> UiLocalizer
|
||||
|
||||
<div class="login-page-wrapper d-flex align-items-center justify-content-center min-vh-100">
|
||||
<div class="login-card card shadow border-0" style="max-width: 440px; width: 100%;">
|
||||
@@ -14,14 +17,14 @@
|
||||
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h5 class="mb-0 fw-semibold">Sender Anmeldung</h5>
|
||||
<p class="mb-0 mt-1 opacity-75" style="font-size: 0.85rem;">Sicherer Zugang zum Sender-Dashboard</p>
|
||||
<h5 class="mb-0 fw-semibold">@L("SenderLogin_Title", "Sender Anmeldung")</h5>
|
||||
<p class="mb-0 mt-1 opacity-75" style="font-size: 0.85rem;">@L("SenderLogin_Subtitle", "Sicherer Zugang zum Sender-Dashboard")</p>
|
||||
</div>
|
||||
|
||||
<div class="card-body p-4">
|
||||
|
||||
<p class="text-muted mb-4" style="font-size: 0.875rem; line-height: 1.5;">
|
||||
Bitte melden Sie sich mit Ihren Zugangsdaten an, um auf das Sender-Dashboard zuzugreifen.
|
||||
@L("SenderLogin_Description", "Bitte melden Sie sich mit Ihren Zugangsdaten an, um auf das Sender-Dashboard zuzugreifen.")
|
||||
</p>
|
||||
|
||||
@if (LoginResult == SenderLoginResult.InvalidCredentials) {
|
||||
@@ -30,8 +33,8 @@
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"/>
|
||||
</svg>
|
||||
<div>
|
||||
<strong>Ungültige Anmeldedaten.</strong><br />
|
||||
<span style="font-size:0.85rem;">Benutzername oder Passwort ist falsch. Bitte versuchen Sie es erneut.</span>
|
||||
<strong>@L("SenderLogin_InvalidCredentials_Title", "Ungültige Anmeldedaten.")</strong><br />
|
||||
<span style="font-size:0.85rem;">@L("SenderLogin_InvalidCredentials_Body", "Benutzername oder Passwort ist falsch. Bitte versuchen Sie es erneut.")</span>
|
||||
</div>
|
||||
</div>
|
||||
} else if (LoginResult == SenderLoginResult.Error) {
|
||||
@@ -41,15 +44,15 @@
|
||||
<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>
|
||||
<strong>Serverfehler.</strong><br />
|
||||
<span style="font-size:0.85rem;">Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.</span>
|
||||
<strong>@L("ServerError", "Serverfehler.")</strong><br />
|
||||
<span style="font-size:0.85rem;">@L("UnexpectedErrorTryAgainLater", "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.")</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-medium" for="login-username">
|
||||
Benutzername
|
||||
@L("UserName", "Benutzername")
|
||||
<span class="text-danger ms-1">*</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
@@ -61,7 +64,7 @@
|
||||
<input id="login-username"
|
||||
type="text"
|
||||
class="form-control @(LoginResult == SenderLoginResult.InvalidCredentials ? "is-invalid" : null)"
|
||||
placeholder="Benutzername eingeben"
|
||||
placeholder="@L("SenderLogin_Username_Placeholder", "Benutzername eingeben")"
|
||||
@bind="Username"
|
||||
@bind:event="oninput"
|
||||
@onkeydown="OnKeyDownAsync"
|
||||
@@ -72,7 +75,7 @@
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-medium" for="login-password">
|
||||
Passwort
|
||||
@L("Password", "Passwort")
|
||||
<span class="text-danger ms-1">*</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
@@ -84,7 +87,7 @@
|
||||
<input id="login-password"
|
||||
type="@(ShowPassword ? "text" : "password")"
|
||||
class="form-control border-start-0 border-end-0 @(LoginResult == SenderLoginResult.InvalidCredentials ? "is-invalid" : null)"
|
||||
placeholder="Passwort eingeben"
|
||||
placeholder="@L("SenderLogin_Password_Placeholder", "Passwort eingeben")"
|
||||
@bind="Password"
|
||||
@bind:event="oninput"
|
||||
@onkeydown="OnKeyDownAsync"
|
||||
@@ -118,20 +121,20 @@
|
||||
disabled="@(IsLoading || string.IsNullOrWhiteSpace(Username) || string.IsNullOrWhiteSpace(Password))">
|
||||
@if (IsLoading) {
|
||||
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
<span>Anmelden …</span>
|
||||
<span>@L("LoginInProgress", "Anmelden …")</span>
|
||||
} else {
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="me-2" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M10 3.5a.5.5 0 0 0-.5-.5h-8a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5v-2a.5.5 0 0 1 1 0v2A1.5 1.5 0 0 1 9.5 14h-8A1.5 1.5 0 0 1 0 12.5v-9A1.5 1.5 0 0 1 1.5 2h8A1.5 1.5 0 0 1 11 3.5v2a.5.5 0 0 1-1 0v-2z"/>
|
||||
<path fill-rule="evenodd" d="M4.146 8.354a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5H14.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3z"/>
|
||||
</svg>
|
||||
<span>Anmelden</span>
|
||||
<span>@L("Login", "Anmelden")</span>
|
||||
}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-center text-muted py-3 border-0 bg-transparent" style="font-size: 0.78rem;">
|
||||
Bei Problemen wenden Sie sich bitte an den Administrator.
|
||||
@L("SenderLogin_FooterHelp", "Bei Problemen wenden Sie sich bitte an den Administrator.")
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -167,4 +170,10 @@
|
||||
IsLoading = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
string L(string key, string fallback)
|
||||
{
|
||||
var value = UiLocalizer[key];
|
||||
return value.ResourceNotFound ? fallback : value.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,17 @@ using DevExpress.Blazor.Reporting;
|
||||
using DevExpress.XtraReports.Web.Extensions;
|
||||
using DevExpress.DataAccess.Web;
|
||||
using DevExpress.XtraReports.Services;
|
||||
using System.Globalization;
|
||||
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
|
||||
var initialCulture = await ResolveInitialCultureAsync(builder.HostEnvironment.BaseAddress);
|
||||
var cultureInfo = new CultureInfo(initialCulture);
|
||||
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
|
||||
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
|
||||
|
||||
builder.Services.AddLocalization();
|
||||
|
||||
// Named HttpClient for API calls (both for services and DevExpress components)
|
||||
builder.Services.AddHttpClient("EnvelopeGenerator.Server", client =>
|
||||
{
|
||||
@@ -33,6 +41,7 @@ builder.Services.AddScoped<SignatureService>();
|
||||
builder.Services.AddScoped<SignatureCacheService>();
|
||||
builder.Services.AddSingleton<AppVersionService>();
|
||||
builder.Services.AddScoped<EnvelopeService>();
|
||||
builder.Services.AddScoped<LocalizationService>();
|
||||
|
||||
// UI Preferences & Theme
|
||||
builder.Services.AddScoped<UserPreferencesService>();
|
||||
@@ -66,3 +75,23 @@ ReportStorageWebExtension.RegisterExtensionGlobal(new InMemoryReportStorageWebEx
|
||||
var host = builder.Build();
|
||||
await FontLoader.LoadFonts(host.Services.GetRequiredService<IHttpClientFactory>(), new List<string> { "opensans.ttf" });
|
||||
await host.RunAsync();
|
||||
|
||||
static async Task<string> ResolveInitialCultureAsync(string baseAddress)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var http = new HttpClient { BaseAddress = new Uri(baseAddress) };
|
||||
var response = await http.GetAsync("/api/Localization/lang");
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return "de-DE";
|
||||
|
||||
var value = await response.Content.ReadAsStringAsync();
|
||||
var culture = value.Trim().Trim('"');
|
||||
return string.IsNullOrWhiteSpace(culture) ? "de-DE" : culture;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "de-DE";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Net;
|
||||
|
||||
namespace EnvelopeGenerator.Server.Client.Services;
|
||||
|
||||
public class LocalizationService(IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
private HttpClient CreateDefaultClient() => httpClientFactory.CreateClient("EnvelopeGenerator.Server");
|
||||
|
||||
public async Task<string> GetLanguageAsync(CancellationToken cancel = default)
|
||||
{
|
||||
using var http = CreateDefaultClient();
|
||||
var response = await http.GetAsync("/api/Localization/lang", cancel);
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
return "de-DE";
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return "de-DE";
|
||||
|
||||
var language = await response.Content.ReadAsStringAsync(cancel);
|
||||
return string.IsNullOrWhiteSpace(language) ? "de-DE" : language.Trim('"');
|
||||
}
|
||||
|
||||
public async Task SetLanguageAsync(string language, CancellationToken cancel = default)
|
||||
{
|
||||
using var http = CreateDefaultClient();
|
||||
var response = await http.PostAsync($"/api/Localization/lang?language={Uri.EscapeDataString(language)}", null, cancel);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<base href="/" />
|
||||
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flag-icons@7.2.3/css/flag-icons.min.css" />
|
||||
<link rel="stylesheet" href="css/app.css" />
|
||||
<link rel="stylesheet" href="css/envelope-viewer.css" />
|
||||
<link rel="stylesheet" href="EnvelopeGenerator.Server.styles.css" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
@using DevExpress.Blazor.PdfViewer
|
||||
@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
|
||||
@inject EnvelopeGenerator.Server.Client.Services.EnvelopeService EnvelopeService
|
||||
@inject EnvelopeGenerator.Server.Client.Services.AuthService AuthService
|
||||
@inject EnvelopeGenerator.Server.Client.Services.ThemeService ThemeService
|
||||
@@ -19,6 +23,8 @@
|
||||
@inject NavigationManager Navigation
|
||||
@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" />
|
||||
@@ -66,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" />
|
||||
@@ -99,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>
|
||||
@@ -117,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" />
|
||||
@@ -136,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>
|
||||
@@ -164,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>
|
||||
}
|
||||
@@ -180,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>
|
||||
@@ -192,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>
|
||||
@@ -208,15 +214,15 @@
|
||||
</div>
|
||||
|
||||
<div class="sender-grid-wrapper">
|
||||
@if (_activeTab == "active")
|
||||
{
|
||||
<DxGrid Data="@_activeEnvelopes"
|
||||
@ref="_gridActive"
|
||||
<DxGrid @key="_activeTab"
|
||||
Data="@CurrentGridData"
|
||||
@ref="_grid"
|
||||
KeyFieldName="Id"
|
||||
ShowFilterRow="true"
|
||||
ShowFilterRow="false"
|
||||
FilterMenuButtonDisplayMode="GridFilterMenuButtonDisplayMode.Always"
|
||||
ShowSearchBox="true"
|
||||
AllowColumnReorder="true"
|
||||
AllowSort=true
|
||||
AllowSort="true"
|
||||
ColumnResizeMode="GridColumnResizeMode.ColumnsContainer"
|
||||
PageSize="20"
|
||||
PagerVisible="true"
|
||||
@@ -227,22 +233,30 @@
|
||||
RowClick="OnRowClick"
|
||||
RowDoubleClick="OnRowDoubleClick"
|
||||
CustomizeElement="OnCustomizeElement"
|
||||
LayoutAutoLoading="OnActiveGridLayoutAutoLoading"
|
||||
LayoutAutoSaving="OnActiveGridLayoutAutoSaving">
|
||||
LayoutAutoLoading="OnGridLayoutAutoLoading"
|
||||
LayoutAutoSaving="OnGridLayoutAutoSaving">
|
||||
<Columns>
|
||||
<DxGridDataColumn FieldName="Id" Caption="ID"
|
||||
DisplayFormat="0"
|
||||
SortIndex="0"
|
||||
SortOrder="GridColumnSortOrder.Descending">
|
||||
<CellDisplayTemplate Context="cellContext">
|
||||
@((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">
|
||||
<EditSettings>
|
||||
<DxComboBoxSettings Data="@GetStatusFilterItems(CurrentGridData)"
|
||||
ValueFieldName="Value"
|
||||
TextFieldName="Text"
|
||||
SearchMode="ListSearchMode.AutoSearch"
|
||||
SearchFilterCondition="ListSearchFilterCondition.Contains" />
|
||||
</EditSettings>
|
||||
<CellDisplayTemplate Context="cellContext">
|
||||
@{
|
||||
var envelope = cellContext.DataItem as EnvelopeDto;
|
||||
@@ -257,18 +271,18 @@
|
||||
}
|
||||
</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;
|
||||
if (envelope != null)
|
||||
{
|
||||
var receivers = envelope.EnvelopeReceivers?.ToList() ?? [];
|
||||
var signed = receivers.Count(r => r.Signed);
|
||||
var signed = GetSignedReceiverCount(envelope);
|
||||
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)
|
||||
{
|
||||
@@ -281,16 +295,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)
|
||||
@@ -299,13 +313,18 @@
|
||||
@foreach (var receiver in envelope.EnvelopeReceivers)
|
||||
{
|
||||
<div style="display: flex; align-items: center; gap: 1rem; padding: 0.5rem; background: white; border-radius: 6px; border: 1px solid #e5e7eb;">
|
||||
<span class="receiver-badge receiver-badge--@(receiver.Signed ? "signed" : "unsigned")" style="min-width: 100px;">
|
||||
@if (receiver.Signed)
|
||||
@{
|
||||
var isSigned = IsReceiverSigned(envelope, receiver);
|
||||
var accessCode = receiver.AccessCode;
|
||||
var hasAccessCode = !string.IsNullOrWhiteSpace(accessCode);
|
||||
}
|
||||
<span class="receiver-badge receiver-badge--@(isSigned ? "signed" : "unsigned")" style="min-width: 100px;">
|
||||
@if (isSigned)
|
||||
{
|
||||
<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
|
||||
{
|
||||
@@ -313,134 +332,62 @@
|
||||
<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;">
|
||||
<strong style="color: #1f2937;">@receiver.Name</strong>
|
||||
<span style="color: #6b7280; margin-left: 0.5rem;">@receiver.Receiver?.EmailAddress</span>
|
||||
</div>
|
||||
<div class="sender-access-code">
|
||||
<span class="sender-access-code__label">@L("AccessCode", "Access code")</span>
|
||||
<div class="sender-access-code__row">
|
||||
<input class="sender-access-code__value"
|
||||
value="@(hasAccessCode ? accessCode : "-")"
|
||||
readonly
|
||||
title="@L("AccessCode", "Access code")" />
|
||||
<button type="button"
|
||||
class="sender-access-code__copy"
|
||||
disabled="@(!hasAccessCode)"
|
||||
@onclick='() => CopyAccessCodeAsync(accessCode)'
|
||||
title="@L("Copy", "Copy")">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16" aria-hidden="true">
|
||||
<path d="M10 1.5A1.5 1.5 0 0 1 11.5 3v1H13A1.5 1.5 0 0 1 14.5 5.5v8A1.5 1.5 0 0 1 13 15H5A1.5 1.5 0 0 1 3.5 13.5V12H3A1.5 1.5 0 0 1 1.5 10.5v-8A1.5 1.5 0 0 1 3 1h7zm.5 2A.5.5 0 0 0 10 3H3a.5.5 0 0 0-.5.5v7A.5.5 0 0 0 3 11h.5V5.5A1.5 1.5 0 0 1 5 4h5.5v-.5zM5 5a.5.5 0 0 0-.5.5v8a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5v-8A.5.5 0 0 0 13 5H5z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
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>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</DetailRowTemplate>
|
||||
</DxGrid>
|
||||
}
|
||||
else
|
||||
{
|
||||
<DxGrid Data="@_completedEnvelopes"
|
||||
@ref="_gridCompleted"
|
||||
KeyFieldName="Id"
|
||||
ShowFilterRow="true"
|
||||
ShowSearchBox="true"
|
||||
PageSize="20"
|
||||
PagerVisible="true"
|
||||
SelectionMode="GridSelectionMode.Single"
|
||||
AllowSelectRowByClick="true"
|
||||
SelectedDataItem="@_selectedEnvelope"
|
||||
SelectedDataItemChanged="@((object o) => _selectedEnvelope = o as EnvelopeDto)"
|
||||
RowClick="OnRowClick"
|
||||
RowDoubleClick="OnRowDoubleClick"
|
||||
CustomizeElement="OnCustomizeElement"
|
||||
LayoutAutoLoading="OnCompletedGridLayoutAutoLoading"
|
||||
LayoutAutoSaving="OnCompletedGridLayoutAutoSaving">
|
||||
<Columns>
|
||||
<DxGridDataColumn FieldName="Id" Caption="ID"
|
||||
SortIndex="0"
|
||||
SortOrder="GridColumnSortOrder.Descending">
|
||||
<CellDisplayTemplate Context="cellContext">
|
||||
@((cellContext.DataItem as EnvelopeDto)?.Id)
|
||||
</CellDisplayTemplate>
|
||||
</DxGridDataColumn>
|
||||
<DxGridDataColumn FieldName="Title" Caption="Titel">
|
||||
<CellDisplayTemplate Context="cellContext">
|
||||
<strong>@((cellContext.DataItem as EnvelopeDto)?.Title)</strong>
|
||||
</CellDisplayTemplate>
|
||||
</DxGridDataColumn>
|
||||
<DxGridDataColumn FieldName="Status" Caption="Status" ExportEnabled="false">
|
||||
<CellDisplayTemplate Context="cellContext">
|
||||
@{
|
||||
var envelope = cellContext.DataItem as EnvelopeDto;
|
||||
if (envelope != null)
|
||||
{
|
||||
var statusInfo = GetStatusInfo(envelope.Status);
|
||||
<div class="status-badge status-badge--@statusInfo.CssClass">
|
||||
<span class="status-dot status-dot--@statusInfo.DotColor"></span>
|
||||
@statusInfo.Label
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</CellDisplayTemplate>
|
||||
</DxGridDataColumn>
|
||||
<DxGridDataColumn FieldName="EnvelopeReceivers" Caption="Empfänger" ExportEnabled="false">
|
||||
<CellDisplayTemplate Context="cellContext">
|
||||
@{
|
||||
var envelope = cellContext.DataItem as EnvelopeDto;
|
||||
if (envelope != null)
|
||||
{
|
||||
var receivers = envelope.EnvelopeReceivers?.ToList() ?? [];
|
||||
var signed = receivers.Count(r => r.Signed);
|
||||
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
|
||||
</span>
|
||||
@if (total > 0)
|
||||
{
|
||||
<div style="flex: 1; min-width: 60px; max-width: 120px; height: 6px; background: #e5e7eb; border-radius: 3px; overflow: hidden;">
|
||||
<div style="height: 100%; background: linear-gradient(90deg, #81c784 0%, #66bb6a 100%); width: @((signed * 100.0 / total).ToString("F0"))%;"></div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</CellDisplayTemplate>
|
||||
</DxGridDataColumn>
|
||||
<DxGridDataColumn FieldName="EnvelopeTypeTitle" Caption="Typ"
|
||||
Width="120px" AllowSort="true" />
|
||||
<DxGridDataColumn FieldName="AddedWhen" Caption="Erstellt"
|
||||
DisplayFormat="{0:dd.MM.yyyy}" Width="110px" AllowSort="true" />
|
||||
<DxGridDataColumn FieldName="ChangedWhen" Caption="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: 1rem 0 0.75rem;">@L("History", "Historie")</h6>
|
||||
@{
|
||||
var envelope = detailContext.DataItem as EnvelopeDto;
|
||||
if (envelope?.EnvelopeReceivers?.Any() == true)
|
||||
var historyEntries = envelope is null
|
||||
? Enumerable.Empty<HistoryDto>()
|
||||
: GetOrderedHistories(envelope);
|
||||
if (historyEntries.Any())
|
||||
{
|
||||
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
|
||||
@foreach (var receiver in envelope.EnvelopeReceivers)
|
||||
<div class="sender-history-list">
|
||||
@foreach (var history in historyEntries)
|
||||
{
|
||||
<div style="display: flex; align-items: center; gap: 1rem; padding: 0.5rem; background: white; border-radius: 6px; border: 1px solid #e5e7eb;">
|
||||
<span class="receiver-badge receiver-badge--@(receiver.Signed ? "signed" : "unsigned")" style="min-width: 100px;">
|
||||
@if (receiver.Signed)
|
||||
{
|
||||
<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>
|
||||
}
|
||||
else
|
||||
{
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<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>
|
||||
<div style="flex: 1; font-size: 0.875rem;">
|
||||
<strong style="color: #1f2937;">@receiver.Name</strong>
|
||||
<span style="color: #6b7280; margin-left: 0.5rem;">@receiver.Receiver?.EmailAddress</span>
|
||||
var statusInfo = GetStatusInfo(history.Status);
|
||||
<div class="sender-history-item">
|
||||
<div class="sender-history-item__left">
|
||||
<div class="status-badge status-badge--@statusInfo.CssClass">
|
||||
<span class="status-dot status-dot--@statusInfo.DotColor"></span>
|
||||
@statusInfo.Label
|
||||
</div>
|
||||
<span class="sender-history-item__actor">@GetHistoryActor(history)</span>
|
||||
</div>
|
||||
<div class="sender-history-item__right">
|
||||
@GetHistoryDate(history).ToString("dd.MM.yyyy HH:mm")
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -448,22 +395,22 @@
|
||||
}
|
||||
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_NoHistory", "Keine Historie vorhanden")</p>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</DetailRowTemplate>
|
||||
</DxGrid>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@* ── 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"
|
||||
@@ -486,9 +433,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>
|
||||
}
|
||||
@@ -503,7 +450,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>
|
||||
@@ -522,7 +469,7 @@
|
||||
</DxPopup>
|
||||
|
||||
<DxPopup @bind-Visible="@_deletePopupVisible"
|
||||
HeaderText="Umschlag löschen"
|
||||
HeaderText="@L("Sender_DeleteEnvelope_Header", "Umschlag löschen")"
|
||||
Width="560px"
|
||||
MaxWidth="95vw"
|
||||
ShowFooter="true"
|
||||
@@ -534,16 +481,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" />
|
||||
@@ -557,11 +504,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)" />
|
||||
@@ -578,8 +525,7 @@
|
||||
private bool _isLoading = true;
|
||||
private bool _isLoggingOut = false;
|
||||
private string? _errorMessage;
|
||||
private DxGrid? _gridActive;
|
||||
private DxGrid? _gridCompleted;
|
||||
private DxGrid? _grid;
|
||||
|
||||
// Logo config loaded from /api/CustomImages after auth
|
||||
private EnvelopeGenerator.Server.Client.Services.CustomImageDto? _appLogo;
|
||||
@@ -602,10 +548,15 @@
|
||||
private string _deleteReason = string.Empty;
|
||||
private string? _deleteErrorMessage;
|
||||
|
||||
// Stable keys used to store/restore each grid's layout in UserPreferences.GridLayouts
|
||||
// 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";
|
||||
|
||||
private IEnumerable<EnvelopeDto> CurrentGridData =>
|
||||
(_activeTab == "completed" ? _completedEnvelopes : _activeEnvelopes) ?? Enumerable.Empty<EnvelopeDto>();
|
||||
|
||||
private string CurrentGridKey => _activeTab == "completed" ? GridKeyCompleted : GridKeyActive;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var hasAccess = await AuthService.CheckSenderAccessAsync();
|
||||
@@ -670,7 +621,7 @@
|
||||
|
||||
async Task ExportSelectedFormatAsync()
|
||||
{
|
||||
var grid = _activeTab == "active" ? _gridActive : _gridCompleted;
|
||||
var grid = _grid;
|
||||
if (grid is null)
|
||||
return;
|
||||
|
||||
@@ -757,7 +708,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;
|
||||
}
|
||||
|
||||
@@ -806,21 +757,124 @@
|
||||
|
||||
(string Label, string CssClass, string DotColor) GetStatusInfo(EnvelopeStatus status)
|
||||
{
|
||||
var label = GetStatusLabel(status);
|
||||
|
||||
return status switch
|
||||
{
|
||||
EnvelopeStatus.EnvelopePartlySigned => ("Teilweise unterschrieben", "partly-signed", "green"),
|
||||
EnvelopeStatus.EnvelopeQueued => ("In Warteschlange", "queued", "orange"),
|
||||
EnvelopeStatus.EnvelopeSent => ("Gesendet", "sent", "orange"),
|
||||
EnvelopeStatus.EnvelopeCompletelySigned => ("Vollständig unterschrieben", "completed", "green"),
|
||||
EnvelopeStatus.EnvelopeDeleted => ("Gelöscht", "deleted", "red"),
|
||||
EnvelopeStatus.EnvelopeRejected => ("Abgelehnt", "rejected", "red"),
|
||||
EnvelopeStatus.EnvelopeWithdrawn => ("Zurückgezogen", "withdrawn", "red"),
|
||||
EnvelopeStatus.EnvelopeCreated => ("Erstellt", "created", "blue"),
|
||||
EnvelopeStatus.EnvelopeSaved => ("Gespeichert", "saved", "blue"),
|
||||
_ => ("Unbekannt", "unknown", "blue")
|
||||
EnvelopeStatus.EnvelopePartlySigned => (label, "partly-signed", "green"),
|
||||
EnvelopeStatus.EnvelopeCompletelySigned => (label, "completed", "green"),
|
||||
EnvelopeStatus.DocumentSigned => (label, "partly-signed", "green"),
|
||||
EnvelopeStatus.MessageCompletionSent => (label, "completed", "green"),
|
||||
|
||||
EnvelopeStatus.EnvelopeQueued => (label, "queued", "orange"),
|
||||
EnvelopeStatus.EnvelopeSent => (label, "sent", "orange"),
|
||||
EnvelopeStatus.MessageInvitationSent => (label, "queued", "orange"),
|
||||
EnvelopeStatus.MessageAccessCodeSent => (label, "queued", "orange"),
|
||||
EnvelopeStatus.MessageConfirmationSent => (label, "queued", "orange"),
|
||||
|
||||
EnvelopeStatus.EnvelopeDeleted => (label, "deleted", "red"),
|
||||
EnvelopeStatus.EnvelopeRejected => (label, "rejected", "red"),
|
||||
EnvelopeStatus.EnvelopeWithdrawn => (label, "withdrawn", "red"),
|
||||
EnvelopeStatus.DocumentRejected => (label, "rejected", "red"),
|
||||
EnvelopeStatus.AccessCodeIncorrect => (label, "rejected", "red"),
|
||||
EnvelopeStatus.MessageDeletionSent => (label, "deleted", "red"),
|
||||
|
||||
_ => (label, "created", "blue")
|
||||
};
|
||||
}
|
||||
|
||||
string GetStatusLabel(EnvelopeStatus status)
|
||||
{
|
||||
var key = status.ToString();
|
||||
var localized = ModelLocalizer[key];
|
||||
return localized.ResourceNotFound ? key : localized.Value;
|
||||
}
|
||||
|
||||
IEnumerable<HistoryDto> GetOrderedHistories(EnvelopeDto envelope)
|
||||
{
|
||||
return (envelope.Histories ?? Enumerable.Empty<HistoryDto>())
|
||||
.OrderByDescending(GetHistoryDate)
|
||||
.Take(20);
|
||||
}
|
||||
|
||||
IReadOnlyList<StatusFilterItem> GetStatusFilterItems(IEnumerable<EnvelopeDto>? source)
|
||||
{
|
||||
return (source ?? Enumerable.Empty<EnvelopeDto>())
|
||||
.Select(x => x.Status)
|
||||
.Distinct()
|
||||
.OrderBy(x => x)
|
||||
.Select(x => new StatusFilterItem(x, GetStatusLabel(x)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
int GetSignedReceiverCount(EnvelopeDto envelope)
|
||||
{
|
||||
var receivers = envelope.EnvelopeReceivers?.ToList() ?? [];
|
||||
if (receivers.Count == 0)
|
||||
return 0;
|
||||
|
||||
return receivers.Count(r => IsReceiverSigned(envelope, r));
|
||||
}
|
||||
|
||||
bool IsReceiverSigned(EnvelopeDto envelope, EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver.EnvelopeReceiverDto receiver)
|
||||
{
|
||||
var status = (EnvelopeStatus)envelope.Status;
|
||||
if (status is EnvelopeStatus.EnvelopeCompletelySigned or EnvelopeStatus.MessageCompletionSent)
|
||||
return true;
|
||||
|
||||
var histories = envelope.Histories?.Where(h => h.Status == EnvelopeStatus.DocumentSigned).ToList() ?? [];
|
||||
if (histories.Count == 0)
|
||||
return false;
|
||||
|
||||
if (histories.Any(h => h.Receiver?.Id == receiver.ReceiverId))
|
||||
return true;
|
||||
|
||||
var receiverEmail = receiver.Receiver?.EmailAddress;
|
||||
if (!string.IsNullOrWhiteSpace(receiverEmail) &&
|
||||
histories.Any(h => string.Equals(h.UserReference, receiverEmail, StringComparison.OrdinalIgnoreCase)))
|
||||
return true;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(receiver.Name) &&
|
||||
histories.Any(h => string.Equals(h.UserReference, receiver.Name, StringComparison.OrdinalIgnoreCase)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DateTime GetHistoryDate(HistoryDto history)
|
||||
{
|
||||
return history.ActionDate ?? history.AddedWhen;
|
||||
}
|
||||
|
||||
string GetHistoryActor(HistoryDto history)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(history.UserReference))
|
||||
return history.UserReference;
|
||||
|
||||
return L("System", "System");
|
||||
}
|
||||
|
||||
string L(string key, string fallback)
|
||||
{
|
||||
var value = UiLocalizer[key];
|
||||
return value.ResourceNotFound ? fallback : value.Value;
|
||||
}
|
||||
|
||||
async Task CopyAccessCodeAsync(string? accessCode)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(accessCode))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", accessCode);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("console.warn", "[Sender] Failed to copy access code to clipboard.");
|
||||
}
|
||||
}
|
||||
|
||||
void OnCustomizeElement(GridCustomizeElementEventArgs e)
|
||||
{
|
||||
// Future: Add custom row coloring based on status if needed
|
||||
@@ -872,7 +926,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;
|
||||
}
|
||||
|
||||
@@ -883,12 +937,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
|
||||
@@ -919,21 +977,21 @@
|
||||
// UserPreferencesService.GetAsync() uses the 1h in-memory cache → 0 HTTP on restore.
|
||||
// UserPreferencesService.SaveGridLayoutAsync() merges the single key → 1 PUT.
|
||||
|
||||
async Task OnActiveGridLayoutAutoLoading(GridPersistentLayoutEventArgs e)
|
||||
async Task OnGridLayoutAutoLoading(GridPersistentLayoutEventArgs e)
|
||||
{
|
||||
// Use cached preferences only — avoid HTTP during grid initialization.
|
||||
// If cache is empty (LoadFromServerAsync failed), skip layout restore silently.
|
||||
var prefs = UserPreferencesService.GetCached();
|
||||
if (prefs is not null && prefs.GridLayouts.TryGetValue(GridKeyActive, out var json))
|
||||
if (prefs is not null && prefs.GridLayouts.TryGetValue(CurrentGridKey, out var json))
|
||||
e.Layout = JsonSerializer.Deserialize<GridPersistentLayout>(json);
|
||||
}
|
||||
|
||||
async Task OnActiveGridLayoutAutoSaving(GridPersistentLayoutEventArgs e)
|
||||
async Task OnGridLayoutAutoSaving(GridPersistentLayoutEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = JsonSerializer.Serialize(e.Layout);
|
||||
await UserPreferencesService.SaveGridLayoutAsync(GridKeyActive, json);
|
||||
await UserPreferencesService.SaveGridLayoutAsync(CurrentGridKey, json);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -941,16 +999,5 @@
|
||||
}
|
||||
}
|
||||
|
||||
async Task OnCompletedGridLayoutAutoLoading(GridPersistentLayoutEventArgs e)
|
||||
{
|
||||
var prefs = UserPreferencesService.GetCached();
|
||||
if (prefs is not null && prefs.GridLayouts.TryGetValue(GridKeyCompleted, out var json))
|
||||
e.Layout = JsonSerializer.Deserialize<GridPersistentLayout>(json);
|
||||
}
|
||||
|
||||
async Task OnCompletedGridLayoutAutoSaving(GridPersistentLayoutEventArgs e)
|
||||
{
|
||||
var json = JsonSerializer.Serialize(e.Layout);
|
||||
await UserPreferencesService.SaveGridLayoutAsync(GridKeyCompleted, json);
|
||||
}
|
||||
private sealed record StatusFilterItem(EnvelopeStatus Value, string Text);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using EnvelopeGenerator.Application.Resources;
|
||||
|
||||
namespace EnvelopeGenerator.Server.Controllers;
|
||||
|
||||
@@ -20,7 +19,6 @@ public class LocalizationController : ControllerBase
|
||||
|
||||
private readonly ILogger<LocalizationController> _logger;
|
||||
private readonly IStringLocalizer<Resource> _mLocalizer;
|
||||
private readonly IStringLocalizer<Resource> _localizer;
|
||||
private readonly IMemoryCache _cache;
|
||||
|
||||
/// <summary>
|
||||
@@ -32,14 +30,12 @@ public class LocalizationController : ControllerBase
|
||||
/// <param name="_modelLocalizer">Lokalisierungsdienst für Modelle.</param>
|
||||
public LocalizationController(
|
||||
ILogger<LocalizationController> logger,
|
||||
IStringLocalizer<Resource> localizer,
|
||||
IMemoryCache memoryCache,
|
||||
IStringLocalizer<Resource> _modelLocalizer)
|
||||
IStringLocalizer<Resource> modelLocalizer)
|
||||
{
|
||||
_logger = logger;
|
||||
_localizer = localizer;
|
||||
_cache = memoryCache;
|
||||
_mLocalizer = _modelLocalizer;
|
||||
_mLocalizer = modelLocalizer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -92,10 +88,10 @@ public class LocalizationController : ControllerBase
|
||||
var cookieValue = Request.Cookies[CookieRequestCultureProvider.DefaultCookieName];
|
||||
|
||||
if (string.IsNullOrEmpty(cookieValue))
|
||||
return null;
|
||||
return "de-DE";
|
||||
|
||||
var culture = CookieRequestCultureProvider.ParseCookieValue(cookieValue)?.Cultures[0];
|
||||
return culture?.Value ?? null;
|
||||
return culture?.Value ?? "de-DE";
|
||||
}
|
||||
set
|
||||
{
|
||||
@@ -118,4 +114,4 @@ public class LocalizationController : ControllerBase
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,6 +337,7 @@ try
|
||||
builder.Services.AddScoped<EnvelopeGenerator.Server.Client.Services.UserPreferencesService>();
|
||||
builder.Services.AddScoped<EnvelopeGenerator.Server.Client.Services.ThemeService>();
|
||||
builder.Services.AddScoped<EnvelopeGenerator.Server.Client.Services.CustomImagesService>();
|
||||
builder.Services.AddScoped<EnvelopeGenerator.Server.Client.Services.LocalizationService>();
|
||||
|
||||
// Business Services (Server specific)
|
||||
builder.Services.AddScoped<DocumentService>();
|
||||
@@ -410,16 +411,22 @@ try
|
||||
// Set CORS policy
|
||||
app.UseCors("AllowSpecificOriginsPolicy");
|
||||
|
||||
// Localization
|
||||
string[] supportedCultureNames = ["de-DE", "en-US"];
|
||||
IList<CultureInfo> list = [.. supportedCultureNames.Select(cn => new CultureInfo(cn))];
|
||||
var cultureInfo = list.FirstOrDefault() ?? throw new InvalidOperationException("There is no supported culture.");
|
||||
// Localization (default German; can be switched via cookie)
|
||||
var defaultCulture = new CultureInfo("de-DE");
|
||||
IList<CultureInfo> list =
|
||||
[
|
||||
defaultCulture,
|
||||
new CultureInfo("en-US"),
|
||||
new CultureInfo("fr-FR")
|
||||
];
|
||||
var requestLocalizationOptions = new RequestLocalizationOptions
|
||||
{
|
||||
DefaultRequestCulture = new RequestCulture(defaultCulture),
|
||||
SupportedCultures = list,
|
||||
SupportedUICultures = list
|
||||
SupportedUICultures = list,
|
||||
RequestCultureProviders = [new CookieRequestCultureProvider()]
|
||||
};
|
||||
requestLocalizationOptions.RequestCultureProviders.Add(new QueryStringRequestCultureProvider());
|
||||
requestLocalizationOptions.ApplyCurrentCultureToResponseHeaders = true;
|
||||
app.UseRequestLocalization(requestLocalizationOptions);
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
@@ -404,6 +404,89 @@ article {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.receiver-footer__right {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.sf-lang-combo {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sf-lang-trigger {
|
||||
width: 28px;
|
||||
height: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.28);
|
||||
border-radius: 5px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sf-lang-trigger:hover {
|
||||
background: rgba(255, 255, 255, 0.16);
|
||||
}
|
||||
|
||||
.sf-lang-menu {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 28px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
min-width: 64px;
|
||||
padding: 6px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
background: rgba(29, 38, 55, 0.98);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
|
||||
z-index: 110;
|
||||
}
|
||||
|
||||
.sf-lang-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
cursor: default;
|
||||
z-index: 105;
|
||||
}
|
||||
|
||||
.sf-lang-option {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: rgba(255, 255, 255, 0.88);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
padding: 4px 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sf-lang-option:hover,
|
||||
.sf-lang-option--active {
|
||||
background: rgba(255, 255, 255, 0.14);
|
||||
}
|
||||
|
||||
.sf-lang-trigger .fi,
|
||||
.sf-lang-option .fi {
|
||||
width: 16px;
|
||||
height: 12px;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.25) inset;
|
||||
}
|
||||
|
||||
/* ── Footer: Theme Slider ────────────────────────────────────────────────── */
|
||||
/* 🌙 blazing-dark ──◇──·──◇── purple ☀️ */
|
||||
/* Left group: copyright | datenschutz → Right group: slider */
|
||||
@@ -613,4 +696,4 @@ article {
|
||||
|
||||
[data-sf-theme="blazing-dark"] .login-card .text-muted {
|
||||
color: rgba(255, 255, 255, 0.50) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +238,7 @@
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
||||
.sender-grid-container {
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
backdrop-filter: blur(20px);
|
||||
@@ -381,6 +382,109 @@
|
||||
color: #c62828;
|
||||
}
|
||||
|
||||
.sender-access-code {
|
||||
min-width: 120px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.sender-access-code__label {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: uppercase;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.sender-access-code__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.sender-access-code__value {
|
||||
width: 72px;
|
||||
height: 28px;
|
||||
border: 1px solid rgba(126, 34, 206, 0.22);
|
||||
border-right: none;
|
||||
border-radius: 6px 0 0 6px;
|
||||
padding: 0 0.55rem;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.sender-access-code__value:focus {
|
||||
outline: 2px solid rgba(126, 34, 206, 0.18);
|
||||
border-color: rgba(126, 34, 206, 0.45);
|
||||
}
|
||||
|
||||
.sender-access-code__copy {
|
||||
height: 28px;
|
||||
border: 1px solid rgba(126, 34, 206, 0.3);
|
||||
border-radius: 0 6px 6px 0;
|
||||
width: 30px;
|
||||
min-width: 30px;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #4c1d95;
|
||||
background: rgba(126, 34, 206, 0.08);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.sender-access-code__copy:hover:not(:disabled) {
|
||||
border-color: rgba(126, 34, 206, 0.45);
|
||||
background: rgba(126, 34, 206, 0.16);
|
||||
}
|
||||
|
||||
.sender-access-code__copy:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.sender-history-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.sender-history-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.5rem;
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.sender-history-item__left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sender-history-item__actor {
|
||||
color: #4b5563;
|
||||
font-size: 0.8rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.sender-history-item__right {
|
||||
color: #6b7280;
|
||||
font-size: 0.8rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 1320px) {
|
||||
.sender-action-bar__inner {
|
||||
flex-wrap: wrap;
|
||||
@@ -482,6 +586,10 @@
|
||||
padding: 0.875rem 1rem;
|
||||
font-size: 0.813rem;
|
||||
}
|
||||
|
||||
.sender-access-code {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
@@ -652,6 +760,7 @@
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
|
||||
[data-sf-theme="blazing-dark"] .sender-grid-container::before {
|
||||
background: linear-gradient(90deg, #6366f1 0%, #4338ca 100%);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user