From 8d736cdc5edbb63e70fb00c75b16fa48674b525c Mon Sep 17 00:00:00 2001 From: TekH Date: Fri, 12 Jun 2026 15:14:51 +0200 Subject: [PATCH 01/43] Refactor EnvelopeDto property for receiver handling Replaced the `Receivers` property with `EnvelopeReceivers` in the `EnvelopeDto` class to improve clarity and better align with the updated data model. The new property uses `IEnumerable?` instead of `IEnumerable?`. --- EnvelopeGenerator.Application/Common/Dto/EnvelopeDto.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EnvelopeGenerator.Application/Common/Dto/EnvelopeDto.cs b/EnvelopeGenerator.Application/Common/Dto/EnvelopeDto.cs index 739ce86f..66e41b61 100644 --- a/EnvelopeGenerator.Application/Common/Dto/EnvelopeDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/EnvelopeDto.cs @@ -133,5 +133,5 @@ public record EnvelopeDto : IEnvelope /// /// /// - public IEnumerable? Receivers { get; set; } + public IEnumerable? EnvelopeReceivers { get; set; } } \ No newline at end of file From a3c653ddb36d0180d005f28bc5ffb0d6ab068691 Mon Sep 17 00:00:00 2001 From: TekH Date: Fri, 12 Jun 2026 15:15:09 +0200 Subject: [PATCH 02/43] Simplify Envelope to EnvelopeDto mapping Removed custom mapping logic for the `Receivers` property in the `Envelope` to `EnvelopeDto` mapping within the `MappingProfile` class. The mapping now uses default behavior without projecting `EnvelopeReceivers` to `Receivers`. --- EnvelopeGenerator.Application/Common/Dto/MappingProfile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EnvelopeGenerator.Application/Common/Dto/MappingProfile.cs b/EnvelopeGenerator.Application/Common/Dto/MappingProfile.cs index d46be867..152cb02b 100644 --- a/EnvelopeGenerator.Application/Common/Dto/MappingProfile.cs +++ b/EnvelopeGenerator.Application/Common/Dto/MappingProfile.cs @@ -26,7 +26,7 @@ public class MappingProfile : Profile CreateMap(); CreateMap(); CreateMap(); - CreateMap().ForMember(dest => dest.Receivers, opt => opt.MapFrom(src => src.EnvelopeReceivers.Select(er => er.Receiver))); + CreateMap(); CreateMap(); CreateMap().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen)); CreateMap().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen)); From 1326407462d79df002a710a67843637badb423ef Mon Sep 17 00:00:00 2001 From: TekH Date: Fri, 12 Jun 2026 15:15:44 +0200 Subject: [PATCH 03/43] Update AuthController to use specific auth scheme The `[Authorize]` attribute on the `Check` method was updated to specify the `AuthScheme.Sender` authentication scheme. This change ensures that the `Check` endpoint now requires authentication using this specific scheme, enhancing security and supporting multiple authentication schemes within the application. --- EnvelopeGenerator.API/Controllers/AuthController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EnvelopeGenerator.API/Controllers/AuthController.cs b/EnvelopeGenerator.API/Controllers/AuthController.cs index 6a3500c9..b6044216 100644 --- a/EnvelopeGenerator.API/Controllers/AuthController.cs +++ b/EnvelopeGenerator.API/Controllers/AuthController.cs @@ -69,7 +69,7 @@ public partial class AuthController(IOptions authTokenKeyOptions, [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] [HttpGet("check")] - [Authorize] + [Authorize(AuthenticationSchemes = AuthScheme.Sender)] public IActionResult Check(string? role = null) => role is not null && !User.IsInRole(role) ? Unauthorized() From fa354a05cc8e5ca58e14f5734f14a7e38badabcb Mon Sep 17 00:00:00 2001 From: TekH Date: Fri, 12 Jun 2026 15:21:29 +0200 Subject: [PATCH 04/43] Update authorization policy in ConfigController Replaced the generic [Authorize] attribute with a more specific [Authorize(Policy = AuthPolicy.SenderOrReceiver)] to enforce a stricter authorization policy. Added a `using` directive for `EnvelopeGenerator.Domain.Constants` to support the new policy. --- EnvelopeGenerator.API/Controllers/ConfigController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/EnvelopeGenerator.API/Controllers/ConfigController.cs b/EnvelopeGenerator.API/Controllers/ConfigController.cs index 81aa23c0..117252a2 100644 --- a/EnvelopeGenerator.API/Controllers/ConfigController.cs +++ b/EnvelopeGenerator.API/Controllers/ConfigController.cs @@ -1,4 +1,5 @@ using EnvelopeGenerator.API.Models.PsPdfKitAnnotation; +using EnvelopeGenerator.Domain.Constants; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; @@ -13,7 +14,7 @@ namespace EnvelopeGenerator.API.Controllers; /// [Route("api/[controller]")] [ApiController] -[Authorize] +[Authorize(Policy = AuthPolicy.SenderOrReceiver)] public class ConfigController(IOptionsMonitor annotationParamsOptions) : ControllerBase { private readonly AnnotationParams _annotationParams = annotationParamsOptions.CurrentValue; From 151c785af983f2d466ae57951095330759a9b88d Mon Sep 17 00:00:00 2001 From: TekH Date: Fri, 12 Jun 2026 15:21:50 +0200 Subject: [PATCH 05/43] Enhance JSON options and authorization policies Added JSON serialization options to ignore reference cycles in the `AddControllers` method by configuring `ReferenceHandler` to `IgnoreCycles`. Updated the `AddAuthorizationBuilder` to include authentication schemes for the `SenderOrReceiver` policy, requiring roles and schemes for enhanced security. --- EnvelopeGenerator.API/Program.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/EnvelopeGenerator.API/Program.cs b/EnvelopeGenerator.API/Program.cs index 2ea3ce27..464f5c23 100644 --- a/EnvelopeGenerator.API/Program.cs +++ b/EnvelopeGenerator.API/Program.cs @@ -44,7 +44,11 @@ try var deferredProvider = new DeferredServiceProvider(); - builder.Services.AddControllers(); + builder.Services.AddControllers() + .AddJsonOptions(options => + { + options.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles; + }); builder.Services.AddHttpClient(); builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); @@ -238,8 +242,9 @@ try }); builder.Services.AddAuthorizationBuilder() - .AddPolicy(AuthPolicy.SenderOrReceiver, policy => policy.RequireRole(Role.Sender, Role.Receiver.Full)) - + .AddPolicy(AuthPolicy.SenderOrReceiver, policy => policy + .RequireRole(Role.Sender, Role.Receiver.Full) + .AddAuthenticationSchemes(AuthScheme.Sender, AuthScheme.Receiver)) .AddPolicy(AuthPolicy.Sender, policy => policy .RequireRole(Role.Sender) .AddAuthenticationSchemes(AuthScheme.Sender)) From 011960be752eec3cb47e6f60ad9b6c7418f5edf3 Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 15 Jun 2026 14:55:01 +0200 Subject: [PATCH 06/43] Add EnvelopeStatus extensions and update documentation URL The `System` namespace was added to `EnvelopeStatus.cs` to enable additional functionality. A documentation URL in the comments was updated to point to a new location, replacing the outdated link. Introduced a new static class `EnvelopeStatusExtensions` with two extension methods for the `EnvelopeStatus` enum: - `IsActive`: Checks if the status is active (between `EnvelopeCreated` and `EnvelopePartlySigned`). - `IsCompleted`: Checks if the status is completed (between `EnvelopeCompletelySigned` and `EnvelopeWithdrawn`). --- EnvelopeGenerator.API/Properties/launchSettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EnvelopeGenerator.API/Properties/launchSettings.json b/EnvelopeGenerator.API/Properties/launchSettings.json index 1b89a1c9..2ab4f3f4 100644 --- a/EnvelopeGenerator.API/Properties/launchSettings.json +++ b/EnvelopeGenerator.API/Properties/launchSettings.json @@ -22,7 +22,7 @@ "https": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": false, + "launchBrowser": true, "launchUrl": "swagger", "applicationUrl": "https://localhost:8088;http://localhost:5131", "environmentVariables": { From 561b844e59ac0cb7eeb1b4ed74a295e1bb3edb5d Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 15 Jun 2026 15:07:12 +0200 Subject: [PATCH 07/43] Add filtering for active and completed envelopes Added `OnlyActive` and `OnlyCompleted` properties to the `ReadEnvelopeQuery` class to enable filtering envelopes by their active or completed status. Updated the `ReadEnvelopeQueryHandler` to apply these filters when the properties are set. Enhanced the `EnvelopeStatus` class by introducing `Active` and `Completed` status lists and adding extension methods (`IsActive` and `IsCompleted`) to determine status categories. Included necessary `using` directives for `System` and `System.Linq`. --- .../Envelopes/Queries/ReadEnvelopeQuery.cs | 16 ++++++++++ .../Constants/EnvelopeStatus.cs | 29 +++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/EnvelopeGenerator.Application/Envelopes/Queries/ReadEnvelopeQuery.cs b/EnvelopeGenerator.Application/Envelopes/Queries/ReadEnvelopeQuery.cs index 0719279e..8af36db6 100644 --- a/EnvelopeGenerator.Application/Envelopes/Queries/ReadEnvelopeQuery.cs +++ b/EnvelopeGenerator.Application/Envelopes/Queries/ReadEnvelopeQuery.cs @@ -14,6 +14,16 @@ namespace EnvelopeGenerator.Application.Envelopes.Queries; /// public record ReadEnvelopeQuery : EnvelopeQueryBase, IRequest> { + /// + /// + /// + public bool OnlyActive { get; init; } = false; + + /// + /// + /// + public bool OnlyCompleted { get; init; } = false; + /// /// Abfrage des Include des Umschlags /// @@ -132,6 +142,12 @@ public class ReadEnvelopeQueryHandler : IRequestHandler !status.Ignore.Contains(e.Status)); } + if(request is { OnlyActive: true }) + query = query.Where(e => Status.Active.Contains(e.Status)); + + if (request is { OnlyCompleted: true }) + query = query.Where(e => Status.Completed.Contains(e.Status)); + var envelopes = await query .Include(e => e.EnvelopeReceivers).ThenInclude(er => er.Receiver) .ToListAsync(cancel); diff --git a/EnvelopeGenerator.Domain/Constants/EnvelopeStatus.cs b/EnvelopeGenerator.Domain/Constants/EnvelopeStatus.cs index 5242a5f8..f096fd09 100644 --- a/EnvelopeGenerator.Domain/Constants/EnvelopeStatus.cs +++ b/EnvelopeGenerator.Domain/Constants/EnvelopeStatus.cs @@ -1,8 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; namespace EnvelopeGenerator.Domain.Constants { - // http://wiki.dd/xwiki13/bin/view/Anwendungen/Produkt-Handbuch/Sonstiges/SignFlow/Envelope%20Status/ + // http://wiki.dd/xwiki_prod/bin/view/Anwendungen/Produkt-Handbuch/Sonstiges/signFLOW/signFLOW%20-%20Enwickler-Handbuch/4.%20Anhang/4.3%20Historie%20und%20Status%20der%20Umschl%C3%A4ge/ public enum EnvelopeStatus { Invalid = 0, @@ -49,5 +51,28 @@ namespace EnvelopeGenerator.Domain.Constants EnvelopeStatus.EnvelopeCreated, EnvelopeStatus.DocumentMod_Rotation }; + + public static readonly List Active = Enum.GetValues(typeof(EnvelopeStatus)) + .Cast() + .Where(status => status.IsActive()) + .ToList(); + + public static readonly List Completed = Enum.GetValues(typeof(EnvelopeStatus)) + .Cast() + .Where(status => status.IsCompleted()) + .ToList(); + } + + public static class EnvelopeStatusExtensions + { + public static bool IsActive(this EnvelopeStatus status) + { + return status >= EnvelopeStatus.EnvelopeCreated && status < EnvelopeStatus.EnvelopePartlySigned; + } + + public static bool IsCompleted(this EnvelopeStatus status) + { + return status >= EnvelopeStatus.EnvelopeCompletelySigned && status <= EnvelopeStatus.EnvelopeWithdrawn; + } } } \ No newline at end of file From 95c8e15887ae3980b43ea64314d4e15cc57eff18 Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 15 Jun 2026 15:40:59 +0200 Subject: [PATCH 08/43] Add EnvelopeDto and EnvelopeService for API integration Introduced the `EnvelopeDto` class to represent envelope data with JSON property mappings. Added the `EnvelopeService` class to handle API interactions, including fetching envelopes with optional filters and query string construction using `Microsoft.AspNetCore.WebUtilities`. Updated the project file to include the required package reference for query string manipulation. --- .../EnvelopeGenerator.ReceiverUI.csproj | 1 + .../Models/EnvelopeDto.cs | 24 +++++++ .../Services/EnvelopeService.cs | 72 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 EnvelopeGenerator.ReceiverUI/Models/EnvelopeDto.cs create mode 100644 EnvelopeGenerator.ReceiverUI/Services/EnvelopeService.cs diff --git a/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj b/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj index aa225212..b8190cc9 100644 --- a/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj +++ b/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj @@ -29,6 +29,7 @@ + diff --git a/EnvelopeGenerator.ReceiverUI/Models/EnvelopeDto.cs b/EnvelopeGenerator.ReceiverUI/Models/EnvelopeDto.cs new file mode 100644 index 00000000..787742d1 --- /dev/null +++ b/EnvelopeGenerator.ReceiverUI/Models/EnvelopeDto.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; + +namespace EnvelopeGenerator.ReceiverUI.Models; + +public class EnvelopeDto +{ + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("uuid")] + public string? Uuid { get; set; } + + [JsonPropertyName("title")] + public string? Title { get; set; } + + [JsonPropertyName("status")] + public int Status { get; set; } + + [JsonPropertyName("docResult")] + public byte[]? DocResult { get; set; } + + [JsonPropertyName("envelopeReceivers")] + public List EnvelopeReceivers { get; set; } = new(); +} diff --git a/EnvelopeGenerator.ReceiverUI/Services/EnvelopeService.cs b/EnvelopeGenerator.ReceiverUI/Services/EnvelopeService.cs new file mode 100644 index 00000000..2e61f47d --- /dev/null +++ b/EnvelopeGenerator.ReceiverUI/Services/EnvelopeService.cs @@ -0,0 +1,72 @@ +using System.Net.Http.Json; +using System.Text.Json; +using EnvelopeGenerator.ReceiverUI.Models; +using EnvelopeGenerator.ReceiverUI.Options; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Options; + +namespace EnvelopeGenerator.ReceiverUI.Services; + +/// +/// Retrieves s from the API. +/// +public class EnvelopeService +{ + private readonly HttpClient _http; + private readonly ApiOptions _apiOptions; + private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web); + + public EnvelopeService(HttpClient http, IOptions apiOptions) + { + _http = http; + _apiOptions = apiOptions.Value; + } + + /// + /// Fetches envelopes from the API with optional filters. + /// + /// Thrown when the API request fails. + public async Task?> GetAsync( + int? id = null, + string? uuid = null, + bool? onlyActive = null, + bool? onlyCompleted = null, + CancellationToken cancel = default) + { + var baseUrl = $"{_apiOptions.BaseUrl}/api/Envelope"; + var queryParams = new Dictionary(); + + if (id.HasValue) + { + queryParams["Id"] = id.Value.ToString(); + } + if (!string.IsNullOrEmpty(uuid)) + { + queryParams["Uuid"] = uuid; + } + if (onlyActive.HasValue) + { + queryParams["OnlyActive"] = onlyActive.Value.ToString(); + } + if (onlyCompleted.HasValue) + { + queryParams["OnlyCompleted"] = onlyCompleted.Value.ToString(); + } + + var url = QueryHelpers.AddQueryString(baseUrl, queryParams); + + var response = await _http.GetAsync(url, cancel); + + if (!response.IsSuccessStatusCode) + { + var statusCode = (int)response.StatusCode; + var reasonPhrase = response.ReasonPhrase ?? "Unknown error"; + throw new HttpRequestException( + $"Failed to load envelopes. Status: {statusCode} ({reasonPhrase})", + null, + response.StatusCode); + } + + return await response.Content.ReadFromJsonAsync>(_jsonOptions, cancel); + } +} From c5db676e01bde1e815b81519f1b25434d8a37572 Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 15 Jun 2026 15:56:49 +0200 Subject: [PATCH 09/43] Add EnvelopeService to DI container in Program.cs Registered EnvelopeService with a scoped lifetime in the dependency injection container by adding `builder.Services.AddScoped();` to Program.cs. This ensures a new instance is created per HTTP request. --- EnvelopeGenerator.ReceiverUI/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/EnvelopeGenerator.ReceiverUI/Program.cs b/EnvelopeGenerator.ReceiverUI/Program.cs index 1c825e94..fa88134a 100644 --- a/EnvelopeGenerator.ReceiverUI/Program.cs +++ b/EnvelopeGenerator.ReceiverUI/Program.cs @@ -23,6 +23,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddSingleton(); +builder.Services.AddScoped(); builder.Services.AddDevExpressWebAssemblyBlazorReportViewer(); builder.Services.AddDevExpressWebAssemblyBlazorPdfViewer(); From 83cdb9dfe909f605c7ce696b8f05a257a432e3ab Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 15 Jun 2026 16:20:28 +0200 Subject: [PATCH 10/43] Update launch settings for HTTPS and ReceiverUI profiles Changed the `launchUrl` for the HTTPS profile in `launchSettings.json` from `"swagger"` to `"sender"`, updating the default URL path. Disabled `launchBrowser` for the `EnvelopeGenerator.ReceiverUI` profile, preventing the browser from automatically opening when this profile is executed. --- EnvelopeGenerator.API/Properties/launchSettings.json | 2 +- EnvelopeGenerator.ReceiverUI/Properties/launchSettings.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/EnvelopeGenerator.API/Properties/launchSettings.json b/EnvelopeGenerator.API/Properties/launchSettings.json index 2ab4f3f4..365fa0b2 100644 --- a/EnvelopeGenerator.API/Properties/launchSettings.json +++ b/EnvelopeGenerator.API/Properties/launchSettings.json @@ -23,7 +23,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "launchUrl": "swagger", + "launchUrl": "sender", "applicationUrl": "https://localhost:8088;http://localhost:5131", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/EnvelopeGenerator.ReceiverUI/Properties/launchSettings.json b/EnvelopeGenerator.ReceiverUI/Properties/launchSettings.json index 166bbda2..227c2ec4 100644 --- a/EnvelopeGenerator.ReceiverUI/Properties/launchSettings.json +++ b/EnvelopeGenerator.ReceiverUI/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "EnvelopeGenerator.ReceiverUI": { "commandName": "Project", - "launchBrowser": true, + "launchBrowser": false, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, From e4ebb29969a769eb1ea8858118a60e1bbb50b58c Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 15 Jun 2026 16:24:45 +0200 Subject: [PATCH 11/43] Add authorization and data fetching to sender page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added an `[Authorize]` attribute with the "Sender" policy to restrict access to the `EnvelopeSenderPage.razor`. Updated the page title to "Umschläge" and added placeholder text for data loading. Injected `EnvelopeService` and `IJSRuntime` to fetch and log active and completed envelopes. Introduced `_activeEnvelopes` and `_completedEnvelopes` fields to store fetched data. Configured `JsonSerializerOptions` for consistent JSON handling. Implemented `OnInitializedAsync` to fetch data asynchronously, log results to the console, and handle errors gracefully. --- .../Pages/EnvelopeSenderPage.razor | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor index 2b0b9f0e..a91c9290 100644 --- a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor +++ b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor @@ -1,7 +1,37 @@ @page "/sender" +@attribute [Microsoft.AspNetCore.Authorization.Authorize(Policy = "Sender")] -

EnvelopeSender

+@using System.Text.Json +@using EnvelopeGenerator.ReceiverUI.Models +@inject EnvelopeGenerator.ReceiverUI.Services.EnvelopeService EnvelopeService +@inject IJSRuntime JSRuntime + +

Umschläge

+ +

Daten werden geladen und in der Konsole ausgegeben...

@code { + private IEnumerable? _activeEnvelopes; + private IEnumerable? _completedEnvelopes; -} + private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web); + + protected override async Task OnInitializedAsync() + { + try + { + _activeEnvelopes = await EnvelopeService.GetAsync(onlyActive: true); + _completedEnvelopes = await EnvelopeService.GetAsync(onlyCompleted: true); + + await JSRuntime.InvokeVoidAsync("console.log", "----- Aktive Umschläge -----"); + await JSRuntime.InvokeVoidAsync("console.log", _activeEnvelopes); + + await JSRuntime.InvokeVoidAsync("console.log", "----- Abgeschlossene Umschläge -----"); + await JSRuntime.InvokeVoidAsync("console.log", _completedEnvelopes); + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("console.error", "Fehler beim Laden der Umschläge:", ex.ToString()); + } + } +} \ No newline at end of file From ef246bae32117cc74dc92e1ce94d96c6fbe470f6 Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 15 Jun 2026 16:59:16 +0200 Subject: [PATCH 12/43] Add LogoutSenderAsync method to AuthService A new asynchronous method `LogoutSenderAsync` was added to the `AuthService` class to handle sender user logout. The method sends a POST request to the `/api/auth/logout` endpoint and removes the authentication cookie. It accepts an optional `CancellationToken` parameter and returns a `bool` indicating the success of the operation. XML documentation comments were included to describe the method's functionality. --- EnvelopeGenerator.ReceiverUI/Services/AuthService.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/EnvelopeGenerator.ReceiverUI/Services/AuthService.cs b/EnvelopeGenerator.ReceiverUI/Services/AuthService.cs index c0b020dd..4fa759ee 100644 --- a/EnvelopeGenerator.ReceiverUI/Services/AuthService.cs +++ b/EnvelopeGenerator.ReceiverUI/Services/AuthService.cs @@ -78,4 +78,16 @@ public class AuthService(HttpClient http, IOptions apiOptions) _ => SenderLoginResult.Error }; } + + /// + /// Logs out the sender user by removing the authentication cookie. + /// Calls POST /api/auth/logout. + /// + public async Task LogoutSenderAsync(CancellationToken cancel = default) + { + var response = await http.PostAsync( + $"{_api.BaseUrl}/api/auth/logout", + null, cancel); + return response.IsSuccessStatusCode; + } } From 9f6004ba8c404f7a8f0ed2276b9e6993486d2421 Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 15 Jun 2026 16:59:37 +0200 Subject: [PATCH 13/43] Refactor EnvelopeDto and add EnvelopeStatus enum Updated the `EnvelopeDto` class to use a simplified receiver model (`EnvelopeReceiverSimpleDto`) for streamlined data handling. Added the `EnvelopeReceiverSimpleDto` class to represent basic receiver information (`Name`, `Email`, `Signed`). Introduced the `EnvelopeStatus` enumeration in `EnvelopeStatus.cs` to define envelope lifecycle statuses, repurposed for the `ReceiverUI` context. Added `EnvelopeStatusExtensions` with `IsActive` and `IsCompleted` methods to evaluate envelope status states. --- .../Models/EnvelopeDto.cs | 17 +++++++++- .../Models/EnvelopeStatus.cs | 33 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 EnvelopeGenerator.ReceiverUI/Models/EnvelopeStatus.cs diff --git a/EnvelopeGenerator.ReceiverUI/Models/EnvelopeDto.cs b/EnvelopeGenerator.ReceiverUI/Models/EnvelopeDto.cs index 787742d1..eb6fb910 100644 --- a/EnvelopeGenerator.ReceiverUI/Models/EnvelopeDto.cs +++ b/EnvelopeGenerator.ReceiverUI/Models/EnvelopeDto.cs @@ -20,5 +20,20 @@ public class EnvelopeDto public byte[]? DocResult { get; set; } [JsonPropertyName("envelopeReceivers")] - public List EnvelopeReceivers { get; set; } = new(); + public List EnvelopeReceivers { get; set; } = new(); +} + +/// +/// Simplified receiver model for envelope list display +/// +public class EnvelopeReceiverSimpleDto +{ + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("email")] + public string? Email { get; set; } + + [JsonPropertyName("signed")] + public bool Signed { get; set; } } diff --git a/EnvelopeGenerator.ReceiverUI/Models/EnvelopeStatus.cs b/EnvelopeGenerator.ReceiverUI/Models/EnvelopeStatus.cs new file mode 100644 index 00000000..cb19eab5 --- /dev/null +++ b/EnvelopeGenerator.ReceiverUI/Models/EnvelopeStatus.cs @@ -0,0 +1,33 @@ +namespace EnvelopeGenerator.ReceiverUI.Models; + +/// +/// Envelope status enumeration (copied from Domain for ReceiverUI) +/// +public enum EnvelopeStatus +{ + Invalid = 0, + EnvelopeCreated = 1001, + EnvelopeSaved = 1002, + EnvelopeQueued = 1003, + EnvelopeSent = 1004, + EnvelopePartlySigned = 1005, + EnvelopeCompletelySigned = 1006, + EnvelopeReportCreated = 1007, + EnvelopeArchived = 1008, + EnvelopeDeleted = 1009, + EnvelopeRejected = 10007, + EnvelopeWithdrawn = 10009 +} + +public static class EnvelopeStatusExtensions +{ + public static bool IsActive(this EnvelopeStatus status) + { + return status >= EnvelopeStatus.EnvelopeCreated && status < EnvelopeStatus.EnvelopePartlySigned; + } + + public static bool IsCompleted(this EnvelopeStatus status) + { + return status >= EnvelopeStatus.EnvelopeCompletelySigned && status <= EnvelopeStatus.EnvelopeWithdrawn; + } +} From 3b66de0691d07e7a8622a016d94c7e50983b5da4 Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 15 Jun 2026 17:00:23 +0200 Subject: [PATCH 14/43] Enhance EnvelopeSenderPage with new UI and features Integrated DevExpress Blazor components and added a responsive, modern UI for the sender dashboard. Replaced placeholder content with a functional layout, including a grid-based envelope viewer with filtering, pagination, and detailed row templates. Added status badges, progress indicators, and a sender action bar with buttons for creating, editing, deleting, refreshing envelopes, and logging out. Introduced loading and error handling states for better user experience. Refactored data loading with `LoadEnvelopesAsync` to fetch and categorize envelopes. Added methods for envelope management and logout functionality. Improved state management and removed unused code. These changes lay the groundwork for future enhancements. --- .../Pages/EnvelopeSenderPage.razor | 702 +++++++++++++++++- 1 file changed, 691 insertions(+), 11 deletions(-) diff --git a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor index a91c9290..6b6dfcc8 100644 --- a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor +++ b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor @@ -3,35 +3,715 @@ @using System.Text.Json @using EnvelopeGenerator.ReceiverUI.Models +@using DevExpress.Blazor @inject EnvelopeGenerator.ReceiverUI.Services.EnvelopeService EnvelopeService +@inject EnvelopeGenerator.ReceiverUI.Services.AuthService AuthService +@inject NavigationManager Navigation @inject IJSRuntime JSRuntime -

Umschläge

+ + -

Daten werden geladen und in der Konsole ausgegeben...

+ + +
+
+
+
+ +
Umschlag-Übersicht
+
+ +
+ + + + + + + + + +
+
+
+ +
+ @if (_isLoading && _allEnvelopes == null) { +
+
+
+ Lädt... +
+

Umschläge werden geladen...

+
+
+ } else if (_errorMessage != null) { +
+
+
+ + + + +
+
Fehler beim Laden der Umschläge
+

@_errorMessage

+
+
+
+
+ } else { +
+
+ + +
+ +
+ @if (_activeTab == "active") { + + + + + + @((cellContext.DataItem as EnvelopeDto)?.Title) + + + + + @{ + var envelope = cellContext.DataItem as EnvelopeDto; + if (envelope != null) { + var statusInfo = GetStatusInfo(envelope.Status); +
+ + @statusInfo.Label +
+ } + } +
+
+ + + @{ + var envelope = cellContext.DataItem as EnvelopeDto; + if (envelope != null) { + var receivers = envelope.EnvelopeReceivers ?? new List(); + var signed = receivers.Count(r => r.Signed); + var total = receivers.Count; +
+ + @signed / @total unterschrieben + + @if (total > 0) { +
+
+
+ } +
+ } + } +
+
+
+ +
+
Empfänger
+ @{ + var envelope = detailContext.DataItem as EnvelopeDto; + if (envelope?.EnvelopeReceivers?.Any() == true) { +
+ @foreach (var receiver in envelope.EnvelopeReceivers) { +
+ + @if (receiver.Signed) { + + + + Unterschrieben + } else { + + + + + Ausstehend + } + +
+ @receiver.Name + @receiver.Email +
+
+ } +
+ } else { +

Keine Empfänger

+ } + } +
+
+
+ } else { + + + + + + @((cellContext.DataItem as EnvelopeDto)?.Title) + + + + + @{ + var envelope = cellContext.DataItem as EnvelopeDto; + if (envelope != null) { + var statusInfo = GetStatusInfo(envelope.Status); +
+ + @statusInfo.Label +
+ } + } +
+
+ + + @{ + var envelope = cellContext.DataItem as EnvelopeDto; + if (envelope != null) { + var receivers = envelope.EnvelopeReceivers ?? new List(); + var signed = receivers.Count(r => r.Signed); + var total = receivers.Count; +
+ + @signed / @total unterschrieben + + @if (total > 0) { +
+
+
+ } +
+ } + } +
+
+
+ +
+
Empfänger
+ @{ + var envelope = detailContext.DataItem as EnvelopeDto; + if (envelope?.EnvelopeReceivers?.Any() == true) { +
+ @foreach (var receiver in envelope.EnvelopeReceivers) { +
+ + @if (receiver.Signed) { + + + + Unterschrieben + } else { + + + + + Ausstehend + } + +
+ @receiver.Name + @receiver.Email +
+
+ } +
+ } else { +

Keine Empfänger

+ } + } +
+
+
+ } +
+
+ } +
+
@code { + private IEnumerable? _allEnvelopes; private IEnumerable? _activeEnvelopes; private IEnumerable? _completedEnvelopes; - - private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web); + private EnvelopeDto? _selectedEnvelope; + private string _activeTab = "active"; + private bool _isLoading = true; + private bool _isLoggingOut = false; + private string? _errorMessage; + private DxGrid? _gridActive; + private DxGrid? _gridCompleted; protected override async Task OnInitializedAsync() { + await LoadEnvelopesAsync(); + } + + async Task LoadEnvelopesAsync() + { + _isLoading = true; + _errorMessage = null; + await InvokeAsync(StateHasChanged); + try { - _activeEnvelopes = await EnvelopeService.GetAsync(onlyActive: true); - _completedEnvelopes = await EnvelopeService.GetAsync(onlyCompleted: true); + _allEnvelopes = await EnvelopeService.GetAsync(); + + // Split into active and completed based on status + var envelopes = _allEnvelopes.ToList(); + _activeEnvelopes = envelopes.Where(e => ((EnvelopeStatus)e.Status).IsActive()).ToList(); + _completedEnvelopes = envelopes.Where(e => ((EnvelopeStatus)e.Status).IsCompleted()).ToList(); - await JSRuntime.InvokeVoidAsync("console.log", "----- Aktive Umschläge -----"); - await JSRuntime.InvokeVoidAsync("console.log", _activeEnvelopes); - - await JSRuntime.InvokeVoidAsync("console.log", "----- Abgeschlossene Umschläge -----"); - await JSRuntime.InvokeVoidAsync("console.log", _completedEnvelopes); + await JSRuntime.InvokeVoidAsync("console.log", $"Loaded {_activeEnvelopes.Count()} active and {_completedEnvelopes.Count()} completed envelopes"); } catch (Exception ex) { + _errorMessage = ex.Message; await JSRuntime.InvokeVoidAsync("console.error", "Fehler beim Laden der Umschläge:", ex.ToString()); } + finally + { + _isLoading = false; + await InvokeAsync(StateHasChanged); + } + } + + async Task RefreshEnvelopes() + { + await LoadEnvelopesAsync(); + } + + void CreateEnvelope() + { + // TODO: Navigate to envelope creation page + JSRuntime.InvokeVoidAsync("console.log", "Create envelope clicked - not yet implemented"); + } + + void EditEnvelope() + { + if (_selectedEnvelope == null) return; + // TODO: Navigate to envelope editor + JSRuntime.InvokeVoidAsync("console.log", $"Edit envelope {_selectedEnvelope.Id} clicked - not yet implemented"); + } + + void DeleteEnvelope() + { + if (_selectedEnvelope == null) return; + // TODO: Show delete confirmation dialog + JSRuntime.InvokeVoidAsync("console.log", $"Delete envelope {_selectedEnvelope.Id} clicked - not yet implemented"); + } + + async Task LogoutAsync() + { + _isLoggingOut = true; + await InvokeAsync(StateHasChanged); + await AuthService.LogoutSenderAsync(); + Navigation.NavigateTo("/sender/login", forceLoad: true); + } + + bool IsEnvelopeSent(EnvelopeDto envelope) + { + var status = (EnvelopeStatus)envelope.Status; + return status >= EnvelopeStatus.EnvelopeQueued; + } + + (string Label, string CssClass, string DotColor) GetStatusInfo(int statusCode) + { + var status = (EnvelopeStatus)statusCode; + 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") + }; + } + + void OnCustomizeElement(GridCustomizeElementEventArgs e) + { + // Future: Add custom row coloring based on status if needed + } + + void OnSelectedEnvelopeChanged(object envelope) + { + _selectedEnvelope = envelope as EnvelopeDto; } } \ No newline at end of file From bb81920d44ef44fb9efd958c7122e9e746abc412 Mon Sep 17 00:00:00 2001 From: TekH Date: Tue, 16 Jun 2026 15:05:00 +0200 Subject: [PATCH 15/43] Refactor sender page styles and add versioned URLs Moved inline styles from `EnvelopeSenderPage.razor` to a new `sender-page.css` file for better maintainability and separation of concerns. Updated `EnvelopeSenderPage.razor` to use versioned URLs for stylesheets via the newly injected `AppVersionService`, enabling cache-busting. Added responsive design support in `sender-page.css` to improve layout on smaller screens. --- .../Pages/EnvelopeSenderPage.razor | 302 +----------------- .../wwwroot/css/sender-page.css | 292 +++++++++++++++++ 2 files changed, 296 insertions(+), 298 deletions(-) create mode 100644 EnvelopeGenerator.ReceiverUI/wwwroot/css/sender-page.css diff --git a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor index 6b6dfcc8..b9b8818b 100644 --- a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor +++ b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor @@ -4,310 +4,16 @@ @using System.Text.Json @using EnvelopeGenerator.ReceiverUI.Models @using DevExpress.Blazor +@using EnvelopeGenerator.ReceiverUI.Services @inject EnvelopeGenerator.ReceiverUI.Services.EnvelopeService EnvelopeService @inject EnvelopeGenerator.ReceiverUI.Services.AuthService AuthService @inject NavigationManager Navigation @inject IJSRuntime JSRuntime +@inject AppVersionService AppVersion - - - + +
diff --git a/EnvelopeGenerator.ReceiverUI/wwwroot/css/sender-page.css b/EnvelopeGenerator.ReceiverUI/wwwroot/css/sender-page.css new file mode 100644 index 00000000..cff932bd --- /dev/null +++ b/EnvelopeGenerator.ReceiverUI/wwwroot/css/sender-page.css @@ -0,0 +1,292 @@ +.sender-dashboard-layout { + display: flex; + flex-direction: column; + height: 100vh; + overflow: hidden; + background: linear-gradient(135deg, #1e3c72 0%, #2a5298 50%, #7e22ce 100%); +} + +.sender-action-bar { + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(20px); + border-bottom: 3px solid rgba(126, 34, 206, 0.3); + padding: 1rem 2rem; + flex-shrink: 0; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); +} + +.sender-action-bar__inner { + max-width: 1600px; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1.5rem; +} + +.sender-title-section { + display: flex; + align-items: center; + gap: 1rem; +} + +.sender-logo svg { + filter: drop-shadow(0 2px 4px rgba(126, 34, 206, 0.3)); + color: #7e22ce; +} + +.sender-title { + font-size: 1.25rem; + font-weight: 700; + color: #1e293b; + letter-spacing: -0.025em; +} + +.sender-toolbar { + display: flex; + align-items: center; + gap: 0.75rem; + flex-wrap: wrap; +} + +.sender-btn { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.625rem 1.125rem; + background: linear-gradient(135deg, rgba(126, 34, 206, 0.05) 0%, rgba(42, 82, 152, 0.05) 100%); + border: 1px solid rgba(126, 34, 206, 0.2); + border-radius: 8px; + font-size: 0.875rem; + font-weight: 600; + color: #1e293b; + cursor: pointer; + transition: all 0.2s ease; + white-space: nowrap; +} + + .sender-btn:hover:not(:disabled) { + background: linear-gradient(135deg, rgba(126, 34, 206, 0.1) 0%, rgba(42, 82, 152, 0.1) 100%); + border-color: rgba(126, 34, 206, 0.4); + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(126, 34, 206, 0.2); + } + + .sender-btn:disabled { + opacity: 0.4; + cursor: not-allowed; + background: rgba(0, 0, 0, 0.02); + border-color: rgba(0, 0, 0, 0.1); + } + +.sender-btn--primary { + background: linear-gradient(135deg, #7e22ce 0%, #2a5298 100%); + border-color: transparent; + color: white; +} + + .sender-btn--primary:hover:not(:disabled) { + background: linear-gradient(135deg, #6b1cb0 0%, #1e3a72 100%); + transform: translateY(-1px); + box-shadow: 0 4px 16px rgba(126, 34, 206, 0.3); + } + +.sender-btn--danger { + background: linear-gradient(135deg, rgba(239, 68, 68, 0.08) 0%, rgba(220, 38, 38, 0.08) 100%); + border-color: rgba(239, 68, 68, 0.3); + color: #dc2626; +} + + .sender-btn--danger:hover:not(:disabled) { + background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); + border-color: transparent; + color: white; + } + +.sender-btn--logout { + padding: 0.5rem; + min-width: 38px; +} + +.sender-content { + flex: 1; + min-height: 0; + padding: 1.5rem; + position: relative; + overflow: auto; +} + +.sender-grid-container { + background: rgba(255, 255, 255, 0.98); + backdrop-filter: blur(20px); + border-radius: 16px; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.1); + overflow: hidden; + position: relative; + max-width: 1600px; + margin: 0 auto; +} + + .sender-grid-container::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, #7e22ce 0%, #2a5298 100%); + z-index: 1; + border-radius: 16px 16px 0 0; + } + +.sender-tabs { + display: flex; + border-bottom: 2px solid rgba(126, 34, 206, 0.1); + padding: 0 2rem; + background: rgba(126, 34, 206, 0.02); +} + +.sender-tab { + padding: 1rem 1.5rem; + font-size: 0.875rem; + font-weight: 600; + color: #6b7280; + background: transparent; + border: none; + border-bottom: 3px solid transparent; + cursor: pointer; + transition: all 0.2s ease; + white-space: nowrap; +} + + .sender-tab:hover { + color: #7e22ce; + background: rgba(126, 34, 206, 0.05); + } + +.sender-tab--active { + color: #7e22ce; + border-bottom-color: #7e22ce; + background: white; +} + +.sender-grid-wrapper { + padding: 1.5rem 2rem 2rem; +} + +.status-badge { + display: inline-flex; + align-items: center; + gap: 0.375rem; + padding: 0.25rem 0.625rem; + border-radius: 6px; + font-size: 0.75rem; + font-weight: 600; + white-space: nowrap; +} + +.status-badge--partly-signed, +.status-badge--completed { + background: rgba(129, 199, 132, 0.15); + color: #2e7d32; +} + +.status-badge--queued, +.status-badge--sent { + background: rgba(255, 183, 77, 0.15); + color: #e65100; +} + +.status-badge--deleted, +.status-badge--rejected, +.status-badge--withdrawn { + background: rgba(229, 115, 115, 0.15); + color: #c62828; +} + +.status-badge--created, +.status-badge--saved { + background: rgba(100, 181, 246, 0.15); + color: #1565c0; +} + +.status-dot { + width: 6px; + height: 6px; + border-radius: 50%; +} + +.status-dot--green { + background: #81c784; +} + +.status-dot--orange { + background: #ffb74d; +} + +.status-dot--red { + background: #e57373; +} + +.status-dot--blue { + background: #64b5f6; +} + +.receiver-badge { + display: inline-flex; + align-items: center; + gap: 0.25rem; + padding: 0.125rem 0.5rem; + background: #f3f4f6; + border-radius: 4px; + font-size: 0.75rem; + color: #374151; + white-space: nowrap; +} + +.receiver-badge--signed { + background: rgba(129, 199, 132, 0.15); + color: #2e7d32; +} + +.receiver-badge--unsigned { + background: rgba(229, 115, 115, 0.15); + color: #c62828; +} + +@@media (max-width: 768px) { + .sender-action-bar { + padding: 1rem 1.25rem; + } + + .sender-action-bar__inner { + flex-wrap: wrap; + } + + .sender-toolbar { + width: 100%; + justify-content: flex-start; + } + + .sender-title { + font-size: 1.125rem; + } + + .sender-content { + padding: 0.75rem; + } + + .sender-grid-wrapper { + padding: 1rem; + } + + .sender-tabs { + padding: 0 1rem; + overflow-x: auto; + } + + .sender-tab { + padding: 0.875rem 1rem; + font-size: 0.813rem; + } +} From b3a70d7259e29f0efecaa9ebe4ca9aeafe5aedec Mon Sep 17 00:00:00 2001 From: TekH Date: Tue, 16 Jun 2026 15:55:59 +0200 Subject: [PATCH 16/43] Add sender authentication check to EnvelopeSenderPage Added an authentication check in `EnvelopeSenderPage.razor` to verify sender access before loading envelopes. Redirects unauthorized users to the sender login page. Introduced `CheckSenderAsync` in `AuthService` to validate sender tokens via the `/api/auth/check` endpoint. Updated `OnInitializedAsync` to use this method, enhancing security by ensuring only authorized users can access envelope-related functionality. --- .../Pages/EnvelopeSenderPage.razor | 7 +++++++ EnvelopeGenerator.ReceiverUI/Services/AuthService.cs | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor index b9b8818b..bbe36e27 100644 --- a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor +++ b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor @@ -322,6 +322,13 @@ protected override async Task OnInitializedAsync() { + var hasAccess = await AuthService.CheckSenderAsync(); + if (!hasAccess) + { + Navigation.NavigateTo($"/sender/login"); + return; + } + await LoadEnvelopesAsync(); } diff --git a/EnvelopeGenerator.ReceiverUI/Services/AuthService.cs b/EnvelopeGenerator.ReceiverUI/Services/AuthService.cs index 4fa759ee..9bff4bd1 100644 --- a/EnvelopeGenerator.ReceiverUI/Services/AuthService.cs +++ b/EnvelopeGenerator.ReceiverUI/Services/AuthService.cs @@ -58,6 +58,16 @@ public class AuthService(HttpClient http, IOptions apiOptions) return response.IsSuccessStatusCode; } + /// + /// Checks whether the current user holds a valid receiver token for the given envelope key. + /// Calls GET /api/auth/check/envelope/{envelopeKey}. + /// + public async Task CheckSenderAsync(CancellationToken cancel = default) + { + var response = await http.GetAsync($"{_api.BaseUrl}/api/auth/check", cancel); + return response.StatusCode == HttpStatusCode.OK; + } + /// /// Authenticates a sender user with username and password. /// Calls POST /api/auth?cookie=true with JSON body. From 4572e20c51d79c65e6b6c40037a6f4ef3f3b290a Mon Sep 17 00:00:00 2001 From: TekH Date: Tue, 16 Jun 2026 16:32:53 +0200 Subject: [PATCH 17/43] Restrict Logout method to Sender auth scheme The `[Authorize]` attribute on the `Logout` method in the `AuthController` class was updated to use the `AuthenticationSchemes = AuthScheme.Sender` instead of the `Policy = AuthPolicy.SenderOrReceiver`. This change narrows the authorization requirement, ensuring only users under the `Sender` authentication scheme can access the `Logout` functionality. --- EnvelopeGenerator.API/Controllers/AuthController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EnvelopeGenerator.API/Controllers/AuthController.cs b/EnvelopeGenerator.API/Controllers/AuthController.cs index b6044216..56e710d8 100644 --- a/EnvelopeGenerator.API/Controllers/AuthController.cs +++ b/EnvelopeGenerator.API/Controllers/AuthController.cs @@ -40,7 +40,7 @@ public partial class AuthController(IOptions authTokenKeyOptions, /// Wenn es kein zugelassenes Cookie gibt, wird „nicht zugelassen“ zurückgegeben. [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] - [Authorize(Policy = AuthPolicy.SenderOrReceiver)] + [Authorize(AuthenticationSchemes = AuthScheme.Sender)] [HttpPost("logout")] public async Task Logout() { From 3302be93480f91d83b46c031a8e35721b35d2514 Mon Sep 17 00:00:00 2001 From: TekH Date: Tue, 16 Jun 2026 16:47:11 +0200 Subject: [PATCH 18/43] Refactor grid columns and improve UI styling Removed fixed column widths in `EnvelopeSenderPage.razor` for dynamic sizing. Added `CellDisplayTemplate` to `Title`, `Status`, and `EnvelopeReceivers` columns for custom data rendering. Fixed gradient typo in progress bar CSS. Added a new CSS rule to hide empty DevExpress grid cells and updated `sender-page.css` to include this rule while preserving existing styles. --- .../Pages/EnvelopeSenderPage.razor | 18 +++++++++--------- .../wwwroot/css/sender-page.css | 5 +++++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor index bbe36e27..2ed8afcd 100644 --- a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor +++ b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor @@ -129,13 +129,13 @@ SelectedDataItemChanged="@OnSelectedEnvelopeChanged" CustomizeElement="OnCustomizeElement"> - - + + @((cellContext.DataItem as EnvelopeDto)?.Title) - + @{ var envelope = cellContext.DataItem as EnvelopeDto; @@ -149,7 +149,7 @@ } - + @{ var envelope = cellContext.DataItem as EnvelopeDto; @@ -221,13 +221,13 @@ SelectedDataItemChanged="@OnSelectedEnvelopeChanged" CustomizeElement="OnCustomizeElement"> - - + + @((cellContext.DataItem as EnvelopeDto)?.Title) - + @{ var envelope = cellContext.DataItem as EnvelopeDto; @@ -241,7 +241,7 @@ } - + @{ var envelope = cellContext.DataItem as EnvelopeDto; @@ -255,7 +255,7 @@ @if (total > 0) {
-
+
}
diff --git a/EnvelopeGenerator.ReceiverUI/wwwroot/css/sender-page.css b/EnvelopeGenerator.ReceiverUI/wwwroot/css/sender-page.css index cff932bd..9195af48 100644 --- a/EnvelopeGenerator.ReceiverUI/wwwroot/css/sender-page.css +++ b/EnvelopeGenerator.ReceiverUI/wwwroot/css/sender-page.css @@ -174,6 +174,11 @@ padding: 1.5rem 2rem 2rem; } +/* Hide DevExpress empty cells */ +.dxbl-grid-empty-cell { + display: none !important; +} + .status-badge { display: inline-flex; align-items: center; From 4237f0a8151529044423a967d2a5e2cbc91e3b91 Mon Sep 17 00:00:00 2001 From: TekH Date: Tue, 16 Jun 2026 17:01:38 +0200 Subject: [PATCH 19/43] Add CellDisplayTemplate for ID column in grid Updated the `DxGridDataColumn` for the `Id` field in `EnvelopeSenderPage.razor` to include a `CellDisplayTemplate`. This enables customized rendering of the `Id` property from the `EnvelopeDto` object, allowing for additional formatting or logic during display. --- .../Pages/EnvelopeSenderPage.razor | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor index 2ed8afcd..b1f36080 100644 --- a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor +++ b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor @@ -129,7 +129,11 @@ SelectedDataItemChanged="@OnSelectedEnvelopeChanged" CustomizeElement="OnCustomizeElement"> - + + + @((cellContext.DataItem as EnvelopeDto)?.Id) + + @((cellContext.DataItem as EnvelopeDto)?.Title) @@ -221,7 +225,11 @@ SelectedDataItemChanged="@OnSelectedEnvelopeChanged" CustomizeElement="OnCustomizeElement"> - + + + @((cellContext.DataItem as EnvelopeDto)?.Id) + + @((cellContext.DataItem as EnvelopeDto)?.Title) From 6ca7767e4d71d37f9a2b94cf14ec0aeeed3654ed Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 09:41:46 +0200 Subject: [PATCH 20/43] Enhance grid functionality in EnvelopeSenderPage Added support for column reordering, sorting, and resizing in the grid: - Enabled `AllowColumnReorder` for column reordering. - Enabled `AllowSort` to allow sorting of grid columns. - Set `ColumnResizeMode` to `GridColumnResizeMode.ColumnsContainer` for improved column resizing behavior. --- EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor | 3 +++ 1 file changed, 3 insertions(+) diff --git a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor index b1f36080..599a72e8 100644 --- a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor +++ b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor @@ -122,6 +122,9 @@ @ref="_gridActive" ShowFilterRow="true" ShowSearchBox="true" + AllowColumnReorder="true" + AllowSort=true + ColumnResizeMode="GridColumnResizeMode.ColumnsContainer" PageSize="20" PagerVisible="true" SelectionMode="GridSelectionMode.Single" From 3a4f449b593133b74b1bd972bd54f3043fc5f611 Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 09:45:22 +0200 Subject: [PATCH 21/43] Add EnvelopeGenerator.Dto project to the solution Introduce a new project, `EnvelopeGenerator.Dto`, targeting .NET 8.0. The project is configured with implicit `using` directives and nullable reference types enabled. Update the solution file to include the new project, its build configurations (Debug/Release for Any CPU), and its hierarchical relationship in the `NestedProjects` section. --- EnvelopeGenerator.Dto/EnvelopeGenerator.Dto.csproj | 9 +++++++++ EnvelopeGenerator.sln | 7 +++++++ 2 files changed, 16 insertions(+) create mode 100644 EnvelopeGenerator.Dto/EnvelopeGenerator.Dto.csproj diff --git a/EnvelopeGenerator.Dto/EnvelopeGenerator.Dto.csproj b/EnvelopeGenerator.Dto/EnvelopeGenerator.Dto.csproj new file mode 100644 index 00000000..fa71b7ae --- /dev/null +++ b/EnvelopeGenerator.Dto/EnvelopeGenerator.Dto.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/EnvelopeGenerator.sln b/EnvelopeGenerator.sln index 1158a000..2d54c875 100644 --- a/EnvelopeGenerator.sln +++ b/EnvelopeGenerator.sln @@ -43,6 +43,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Dependenc EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.ReceiverUI", "EnvelopeGenerator.ReceiverUI\EnvelopeGenerator.ReceiverUI.csproj", "{FB2D306B-1042-4A70-31ED-F991A1599371}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Dto", "EnvelopeGenerator.Dto\EnvelopeGenerator.Dto.csproj", "{94470CB8-1BFA-4639-8DEA-8D8470837E0E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -101,6 +103,10 @@ Global {FB2D306B-1042-4A70-31ED-F991A1599371}.Debug|Any CPU.Build.0 = Debug|Any CPU {FB2D306B-1042-4A70-31ED-F991A1599371}.Release|Any CPU.ActiveCfg = Release|Any CPU {FB2D306B-1042-4A70-31ED-F991A1599371}.Release|Any CPU.Build.0 = Release|Any CPU + {94470CB8-1BFA-4639-8DEA-8D8470837E0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94470CB8-1BFA-4639-8DEA-8D8470837E0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94470CB8-1BFA-4639-8DEA-8D8470837E0E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94470CB8-1BFA-4639-8DEA-8D8470837E0E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -122,6 +128,7 @@ Global {EC768913-6270-14F4-1DD3-69C87A659462} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} {5DCCF9A1-C03F-90E6-87D3-E96DB25250C2} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} {FB2D306B-1042-4A70-31ED-F991A1599371} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} + {94470CB8-1BFA-4639-8DEA-8D8470837E0E} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {73E60370-756D-45AD-A19A-C40A02DACCC7} From c5e97ee30b9962fd373cb8746a617e6ca592d3ac Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 09:46:27 +0200 Subject: [PATCH 22/43] move dto to common dir --- .../Common/{Dto => }/MappingProfile.cs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) rename EnvelopeGenerator.Application/Common/{Dto => }/MappingProfile.cs (63%) diff --git a/EnvelopeGenerator.Application/Common/Dto/MappingProfile.cs b/EnvelopeGenerator.Application/Common/MappingProfile.cs similarity index 63% rename from EnvelopeGenerator.Application/Common/Dto/MappingProfile.cs rename to EnvelopeGenerator.Application/Common/MappingProfile.cs index 152cb02b..361cd0cd 100644 --- a/EnvelopeGenerator.Application/Common/Dto/MappingProfile.cs +++ b/EnvelopeGenerator.Application/Common/MappingProfile.cs @@ -7,7 +7,9 @@ using EnvelopeGenerator.Application.Common.Dto.Receiver; using EnvelopeGenerator.Application.Common.Extensions; using EnvelopeGenerator.Domain.Entities; -namespace EnvelopeGenerator.Application.Common.Dto; +using EnvelopeGenerator.Application.Common.Dto; + +namespace EnvelopeGenerator.Application.Common; /// /// Represents the AutoMapper profile configuration for mapping between @@ -28,13 +30,13 @@ public class MappingProfile : Profile CreateMap(); CreateMap(); CreateMap(); - CreateMap().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen)); - CreateMap().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen)); - CreateMap(); - CreateMap(); + CreateMap().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen)); + CreateMap().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen)); + CreateMap(); + CreateMap(); CreateMap(); - CreateMap(); - CreateMap(); + CreateMap(); + CreateMap(); CreateMap(); // DTO to Entity mappings @@ -47,13 +49,13 @@ public class MappingProfile : Profile CreateMap(); CreateMap(); CreateMap(); - CreateMap().ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(src => src.ActionDate)); - CreateMap().ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(src => src.ActionDate)); - CreateMap(); + CreateMap().ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(src => src.ActionDate)); + CreateMap().ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(src => src.ActionDate)); + CreateMap(); CreateMap(); - CreateMap().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore()); - CreateMap(); - CreateMap(); + CreateMap().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore()); + CreateMap(); + CreateMap(); CreateMap() .MapAddedWhen(); From 65bb68feef35de7a2fbb90ab8e28b22b0e1ad8ba Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 11:05:53 +0200 Subject: [PATCH 23/43] Update DevExpress package versions to 25.2.8 Updated the following DevExpress package references in the project: - `DevExpress.Blazor.PdfViewer` from 25.2.3 to 25.2.8 - `DevExpress.Blazor.Reporting.JSBasedControls` from 25.2.3 to 25.2.8 - `DevExpress.Blazor.Reporting.Viewer` from 25.2.3 to 25.2.8 - `DevExpress.Drawing.Skia` from 25.2.3 to 25.2.8 No changes were made to other package references or the `NativeFileReference` entry. --- .../EnvelopeGenerator.ReceiverUI.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj b/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj index b8190cc9..fbdaa2e6 100644 --- a/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj +++ b/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj @@ -24,10 +24,10 @@ - - - - + + + + From 73d793f0a0e33d049665a9f570dfe625025c4e06 Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 11:53:01 +0200 Subject: [PATCH 24/43] remove ApiExplorerSettings attribute --- .../Common/Dto/ConfigDto.cs | 5 +--- .../Common/Dto/DocReceiverElementDto.cs | 3 --- .../Common/Dto/DocumentDto.cs | 5 +--- .../Common/Dto/DocumentStatusDto.cs | 2 -- .../Common/Dto/EnvelopeDto.cs | 5 ---- .../EnvelopeReceiver/EnvelopeReceiverDto.cs | 2 -- .../EnvelopeReceiverSecretDto.cs | 5 +--- .../EnvelopeReceiverReadOnlyCreateDto.cs | 4 +-- .../EnvelopeReceiverReadOnlyDto.cs | 5 +--- .../EnvelopeReceiverReadOnlyUpdateDto.cs | 5 +--- .../Common/Dto/EnvelopeTypeDto.cs | 5 +--- .../Dto/Messaging/GtxMessagingResponse.cs | 5 +--- .../Common/Dto/Messaging/SmsResponse.cs | 5 +--- .../Common/Dto/Receiver/ReceiverDto.cs | 2 -- .../EnvelopeGenerator.Application.csproj | 27 +++++++++---------- .../Commands/CreateReceiverCommand.cs | 2 -- .../Commands/UpdateReceiverCommand.cs | 5 +--- 17 files changed, 23 insertions(+), 69 deletions(-) diff --git a/EnvelopeGenerator.Application/Common/Dto/ConfigDto.cs b/EnvelopeGenerator.Application/Common/Dto/ConfigDto.cs index 8b9505a4..5a05e3fa 100644 --- a/EnvelopeGenerator.Application/Common/Dto/ConfigDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/ConfigDto.cs @@ -1,11 +1,8 @@ -using Microsoft.AspNetCore.Mvc; - -namespace EnvelopeGenerator.Application.Common.Dto; +namespace EnvelopeGenerator.Application.Common.Dto; /// /// Data Transfer Object representing configuration settings. /// -[ApiExplorerSettings(IgnoreApi = true)] public class ConfigDto { /// diff --git a/EnvelopeGenerator.Application/Common/Dto/DocReceiverElementDto.cs b/EnvelopeGenerator.Application/Common/Dto/DocReceiverElementDto.cs index d24cee32..808561f6 100644 --- a/EnvelopeGenerator.Application/Common/Dto/DocReceiverElementDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/DocReceiverElementDto.cs @@ -1,14 +1,11 @@ using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Interfaces; -using Microsoft.AspNetCore.Mvc; -using System.ComponentModel.DataAnnotations.Schema; namespace EnvelopeGenerator.Application.Common.Dto; /// /// Data Transfer Object representing a positioned element assigned to a document receiver. /// -[ApiExplorerSettings(IgnoreApi = true)] public class DocReceiverElementDto : IDocReceiverElement { /// diff --git a/EnvelopeGenerator.Application/Common/Dto/DocumentDto.cs b/EnvelopeGenerator.Application/Common/Dto/DocumentDto.cs index 4fa3394e..34403d8f 100644 --- a/EnvelopeGenerator.Application/Common/Dto/DocumentDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/DocumentDto.cs @@ -1,11 +1,8 @@ -using Microsoft.AspNetCore.Mvc; - -namespace EnvelopeGenerator.Application.Common.Dto; +namespace EnvelopeGenerator.Application.Common.Dto; /// /// Data Transfer Object representing a document within an envelope, including optional binary data and form elements. /// -[ApiExplorerSettings(IgnoreApi = true)] public class DocumentDto { /// diff --git a/EnvelopeGenerator.Application/Common/Dto/DocumentStatusDto.cs b/EnvelopeGenerator.Application/Common/Dto/DocumentStatusDto.cs index dd37172f..0bb8a35e 100644 --- a/EnvelopeGenerator.Application/Common/Dto/DocumentStatusDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/DocumentStatusDto.cs @@ -1,12 +1,10 @@ using EnvelopeGenerator.Domain.Constants; -using Microsoft.AspNetCore.Mvc; namespace EnvelopeGenerator.Application.Common.Dto; /// /// Data Transfer Object representing the status of a document for a specific receiver. /// -[ApiExplorerSettings(IgnoreApi = true)] public class DocumentStatusDto { /// diff --git a/EnvelopeGenerator.Application/Common/Dto/EnvelopeDto.cs b/EnvelopeGenerator.Application/Common/Dto/EnvelopeDto.cs index 66e41b61..601ac1b0 100644 --- a/EnvelopeGenerator.Application/Common/Dto/EnvelopeDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/EnvelopeDto.cs @@ -1,19 +1,14 @@ using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes; using DigitalData.UserManager.Application.DTOs.User; using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver; -using EnvelopeGenerator.Application.Common.Dto.Receiver; using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Interfaces; -using Microsoft.AspNetCore.Mvc; -using System.Text.Json.Serialization; - namespace EnvelopeGenerator.Application.Common.Dto; /// /// /// -[ApiExplorerSettings(IgnoreApi = true)] public record EnvelopeDto : IEnvelope { /// diff --git a/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiver/EnvelopeReceiverDto.cs b/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiver/EnvelopeReceiverDto.cs index 3ec59e15..34a74c2b 100644 --- a/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiver/EnvelopeReceiverDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiver/EnvelopeReceiverDto.cs @@ -1,13 +1,11 @@ using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes; using EnvelopeGenerator.Application.Common.Dto.Receiver; -using Microsoft.AspNetCore.Mvc; namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver; /// /// /// -[ApiExplorerSettings(IgnoreApi = true)] public record EnvelopeReceiverDto { /// diff --git a/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiver/EnvelopeReceiverSecretDto.cs b/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiver/EnvelopeReceiverSecretDto.cs index 717a358f..ad2c0f20 100644 --- a/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiver/EnvelopeReceiverSecretDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiver/EnvelopeReceiverSecretDto.cs @@ -1,11 +1,8 @@ -using Microsoft.AspNetCore.Mvc; - -namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver; +namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver; /// /// /// -[ApiExplorerSettings(IgnoreApi = true)] public record EnvelopeReceiverSecretDto : EnvelopeReceiverDto { /// diff --git a/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiverReadOnly/EnvelopeReceiverReadOnlyCreateDto.cs b/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiverReadOnly/EnvelopeReceiverReadOnlyCreateDto.cs index 506f7f6b..212aa3f1 100644 --- a/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiverReadOnly/EnvelopeReceiverReadOnlyCreateDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiverReadOnly/EnvelopeReceiverReadOnlyCreateDto.cs @@ -1,5 +1,4 @@ -using Microsoft.AspNetCore.Mvc; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly; @@ -8,7 +7,6 @@ namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly; /// /// /// -[ApiExplorerSettings(IgnoreApi = true)] public record EnvelopeReceiverReadOnlyCreateDto( DateTime DateValid) { diff --git a/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiverReadOnly/EnvelopeReceiverReadOnlyDto.cs b/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiverReadOnly/EnvelopeReceiverReadOnlyDto.cs index 89ff3716..5a9de959 100644 --- a/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiverReadOnly/EnvelopeReceiverReadOnlyDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiverReadOnly/EnvelopeReceiverReadOnlyDto.cs @@ -1,6 +1,4 @@ -using EnvelopeGenerator.Application.Common.Dto; -using EnvelopeGenerator.Application.Common.Dto.Receiver; -using Microsoft.AspNetCore.Mvc; +using EnvelopeGenerator.Application.Common.Dto.Receiver; namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly; @@ -8,7 +6,6 @@ namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly; /// Represents a read-only Data Transfer Object (DTO) for an envelope receiver. /// Contains information about the receiver, associated envelope, and audit details. /// -[ApiExplorerSettings(IgnoreApi = true)] public class EnvelopeReceiverReadOnlyDto { /// diff --git a/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiverReadOnly/EnvelopeReceiverReadOnlyUpdateDto.cs b/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiverReadOnly/EnvelopeReceiverReadOnlyUpdateDto.cs index 2e2a384a..3db17e10 100644 --- a/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiverReadOnly/EnvelopeReceiverReadOnlyUpdateDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/EnvelopeReceiverReadOnly/EnvelopeReceiverReadOnlyUpdateDto.cs @@ -1,11 +1,8 @@ -using Microsoft.AspNetCore.Mvc; - -namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly; +namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly; /// /// Data Transfer Object for updating a read-only envelope receiver. /// -[ApiExplorerSettings(IgnoreApi = true)] public class EnvelopeReceiverReadOnlyUpdateDto { /// diff --git a/EnvelopeGenerator.Application/Common/Dto/EnvelopeTypeDto.cs b/EnvelopeGenerator.Application/Common/Dto/EnvelopeTypeDto.cs index ccbdd9db..cb2e4578 100644 --- a/EnvelopeGenerator.Application/Common/Dto/EnvelopeTypeDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/EnvelopeTypeDto.cs @@ -1,11 +1,8 @@ -using Microsoft.AspNetCore.Mvc; - -namespace EnvelopeGenerator.Application.Common.Dto; +namespace EnvelopeGenerator.Application.Common.Dto; /// /// Data Transfer Object representing a type of envelope with its configuration settings. /// -[ApiExplorerSettings(IgnoreApi = true)] public class EnvelopeTypeDto { /// diff --git a/EnvelopeGenerator.Application/Common/Dto/Messaging/GtxMessagingResponse.cs b/EnvelopeGenerator.Application/Common/Dto/Messaging/GtxMessagingResponse.cs index 614da32c..5d8b008c 100644 --- a/EnvelopeGenerator.Application/Common/Dto/Messaging/GtxMessagingResponse.cs +++ b/EnvelopeGenerator.Application/Common/Dto/Messaging/GtxMessagingResponse.cs @@ -1,9 +1,6 @@ -using Microsoft.AspNetCore.Mvc; - -namespace EnvelopeGenerator.Application.Common.Dto.Messaging; +namespace EnvelopeGenerator.Application.Common.Dto.Messaging; /// /// /// -[ApiExplorerSettings(IgnoreApi = true)] public class GtxMessagingResponse : Dictionary { } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Common/Dto/Messaging/SmsResponse.cs b/EnvelopeGenerator.Application/Common/Dto/Messaging/SmsResponse.cs index f1f71123..36c6f774 100644 --- a/EnvelopeGenerator.Application/Common/Dto/Messaging/SmsResponse.cs +++ b/EnvelopeGenerator.Application/Common/Dto/Messaging/SmsResponse.cs @@ -1,11 +1,8 @@ -using Microsoft.AspNetCore.Mvc; - -namespace EnvelopeGenerator.Application.Common.Dto.Messaging; +namespace EnvelopeGenerator.Application.Common.Dto.Messaging; /// /// /// -[ApiExplorerSettings(IgnoreApi = true)] public record SmsResponse { /// diff --git a/EnvelopeGenerator.Application/Common/Dto/Receiver/ReceiverDto.cs b/EnvelopeGenerator.Application/Common/Dto/Receiver/ReceiverDto.cs index 534a33f5..956868ce 100644 --- a/EnvelopeGenerator.Application/Common/Dto/Receiver/ReceiverDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/Receiver/ReceiverDto.cs @@ -1,5 +1,4 @@ using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver; -using Microsoft.AspNetCore.Mvc; using System.Text.Json.Serialization; namespace EnvelopeGenerator.Application.Common.Dto.Receiver; @@ -7,7 +6,6 @@ namespace EnvelopeGenerator.Application.Common.Dto.Receiver; /// /// /// -[ApiExplorerSettings(IgnoreApi = true)] public class ReceiverDto { /// diff --git a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj index ba181291..5ea118b7 100644 --- a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj +++ b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj @@ -7,7 +7,7 @@ true bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml - + @@ -21,7 +21,6 @@ - @@ -79,25 +78,25 @@ - - - + + + 7.0.5 - + - - - 8.1.1 - + + + 8.1.1 + - - - 8.1.1 - + + + 8.1.1 + diff --git a/EnvelopeGenerator.Application/Receivers/Commands/CreateReceiverCommand.cs b/EnvelopeGenerator.Application/Receivers/Commands/CreateReceiverCommand.cs index 333f4a35..712e78ca 100644 --- a/EnvelopeGenerator.Application/Receivers/Commands/CreateReceiverCommand.cs +++ b/EnvelopeGenerator.Application/Receivers/Commands/CreateReceiverCommand.cs @@ -3,7 +3,6 @@ using DigitalData.Core.Abstraction.Application.Repository; using EnvelopeGenerator.Application.Common.Dto.Receiver; using EnvelopeGenerator.Domain.Entities; using MediatR; -using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations; using System.Security.Cryptography; @@ -14,7 +13,6 @@ namespace EnvelopeGenerator.Application.Receivers.Commands; /// /// /// -[ApiExplorerSettings(IgnoreApi = true)] public record CreateReceiverCommand : IRequest<(ReceiverDto Receiver, bool AlreadyExists)> { /// diff --git a/EnvelopeGenerator.Application/Receivers/Commands/UpdateReceiverCommand.cs b/EnvelopeGenerator.Application/Receivers/Commands/UpdateReceiverCommand.cs index b9e211c2..9623d6d7 100644 --- a/EnvelopeGenerator.Application/Receivers/Commands/UpdateReceiverCommand.cs +++ b/EnvelopeGenerator.Application/Receivers/Commands/UpdateReceiverCommand.cs @@ -1,11 +1,8 @@ -using Microsoft.AspNetCore.Mvc; - -namespace EnvelopeGenerator.Application.Receivers.Commands; +namespace EnvelopeGenerator.Application.Receivers.Commands; /// /// Data Transfer Object for updating a receiver's information. /// -[ApiExplorerSettings(IgnoreApi = true)] public class UpdateReceiverCommand { /// From 9f57baf2e56301ad5a2e75260846fdb1603318ec Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 14:05:01 +0200 Subject: [PATCH 25/43] refactor(ReceiverUI/Models): update to use Application layer's DTO --- .../EnvelopeGenerator.ReceiverUI.csproj | 3 ++ .../Models/AnnotationDto.cs | 32 ------------------- .../Pages/Example/ReportViewer.razor | 6 +++- .../Services/AnnotationService.cs | 1 + 4 files changed, 9 insertions(+), 33 deletions(-) delete mode 100644 EnvelopeGenerator.ReceiverUI/Models/AnnotationDto.cs diff --git a/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj b/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj index fbdaa2e6..9f4ee25e 100644 --- a/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj +++ b/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj @@ -41,6 +41,9 @@ + + + Always diff --git a/EnvelopeGenerator.ReceiverUI/Models/AnnotationDto.cs b/EnvelopeGenerator.ReceiverUI/Models/AnnotationDto.cs deleted file mode 100644 index ced9c039..00000000 --- a/EnvelopeGenerator.ReceiverUI/Models/AnnotationDto.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace EnvelopeGenerator.ReceiverUI.Models; - -/// -/// Represents a pre-assigned signature annotation position on a specific page. -///

-/// Coordinate unit (X, Y): Inches (GdPicture14 native unit), -/// origin at the top-left corner of the page, both axes increase downward/rightward. -///

-/// Conversion to DevExpress: Multiply by 100 (DX uses 1/100 inch). -/// Convert: xDX = xInches * 100.0 -///
-/// Conversion to PDF Points: Multiply by 72 (1 inch = 72 points). -/// Convert: xPt = xInches * 72.0 -///
-/// Y-axis for PDF (bottom-left origin): Flip required for iText7. -/// Convert: yPt = (pageHeightInches - yInches - elemHeightInches) * 72.0 -///
-[Obsolete("Use SignatureDto with SignatureService.")] -public record AnnotationDto -{ - /// Unique identifier of the annotation. - public long Id { get; init; } - - /// 1-based page number within the document. - public int Page { get; init; } - - /// Horizontal position in INCHES from the left edge of the page. - public double X { get; init; } - - /// Vertical position in INCHES from the top edge of the page. - public double Y { get; init; } -} diff --git a/EnvelopeGenerator.ReceiverUI/Pages/Example/ReportViewer.razor b/EnvelopeGenerator.ReceiverUI/Pages/Example/ReportViewer.razor index 6e5f275e..42bfc0d7 100644 --- a/EnvelopeGenerator.ReceiverUI/Pages/Example/ReportViewer.razor +++ b/EnvelopeGenerator.ReceiverUI/Pages/Example/ReportViewer.razor @@ -6,6 +6,7 @@ @using DevExpress.Utils @using DevExpress.XtraPrinting @using DevExpress.XtraPrinting.Drawing +@using EnvelopeGenerator.Application.Common.Dto @using Microsoft.JSInterop @using XtraReport = DevExpress.XtraReports.UI.XtraReport @using BottomMarginBand = DevExpress.XtraReports.UI.BottomMarginBand @@ -301,7 +302,10 @@ Shown="OnPopupShownAsync"> bool IsLoggingOut; IReadOnlyList _annotations = []; - IEnumerable AnnotationPages => _annotations.Select(a => a.Page).Distinct().OrderBy(p => p); + IEnumerable AnnotationPages => _annotations + .Select(a => a.Page ?? throw new InvalidOperationException($"Annotation page is missing for annotation ID {a.Id}. Annotation details: X={a.X}, Y={a.Y}")) + .Distinct() + .OrderBy(p => p); EnvelopeReceiverDto? _envelopeReceiver; record SignatureCapture(string DataUrl, string FullName, string Position, string Place); SignatureCapture? _capturedSignature; diff --git a/EnvelopeGenerator.ReceiverUI/Services/AnnotationService.cs b/EnvelopeGenerator.ReceiverUI/Services/AnnotationService.cs index ead7437c..74bb3960 100644 --- a/EnvelopeGenerator.ReceiverUI/Services/AnnotationService.cs +++ b/EnvelopeGenerator.ReceiverUI/Services/AnnotationService.cs @@ -1,5 +1,6 @@ using System.Net.Http.Json; using System.Text.Json; +using EnvelopeGenerator.Application.Common.Dto; using EnvelopeGenerator.ReceiverUI.Models; using EnvelopeGenerator.ReceiverUI.Options; using Microsoft.Extensions.Options; From 06c8af2ed844a64d2a4e45dca25e722dcdc487cd Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 14:38:30 +0200 Subject: [PATCH 26/43] Remove EnvelopeGenerator.Dto project from solution The `EnvelopeGenerator.Dto.csproj` file has been removed entirely, including its `` XML structure. References to `EnvelopeGenerator.Dto` have been removed from `EnvelopeGenerator.sln`, including the project declaration, build configurations in `SolutionConfigurationPlatforms`, and nested project mappings in `NestedProjects`. This change fully removes the `EnvelopeGenerator.Dto` project from the solution. --- EnvelopeGenerator.Dto/EnvelopeGenerator.Dto.csproj | 9 --------- EnvelopeGenerator.sln | 7 ------- 2 files changed, 16 deletions(-) delete mode 100644 EnvelopeGenerator.Dto/EnvelopeGenerator.Dto.csproj diff --git a/EnvelopeGenerator.Dto/EnvelopeGenerator.Dto.csproj b/EnvelopeGenerator.Dto/EnvelopeGenerator.Dto.csproj deleted file mode 100644 index fa71b7ae..00000000 --- a/EnvelopeGenerator.Dto/EnvelopeGenerator.Dto.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net8.0 - enable - enable - - - diff --git a/EnvelopeGenerator.sln b/EnvelopeGenerator.sln index 2d54c875..1158a000 100644 --- a/EnvelopeGenerator.sln +++ b/EnvelopeGenerator.sln @@ -43,8 +43,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Dependenc EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.ReceiverUI", "EnvelopeGenerator.ReceiverUI\EnvelopeGenerator.ReceiverUI.csproj", "{FB2D306B-1042-4A70-31ED-F991A1599371}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Dto", "EnvelopeGenerator.Dto\EnvelopeGenerator.Dto.csproj", "{94470CB8-1BFA-4639-8DEA-8D8470837E0E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -103,10 +101,6 @@ Global {FB2D306B-1042-4A70-31ED-F991A1599371}.Debug|Any CPU.Build.0 = Debug|Any CPU {FB2D306B-1042-4A70-31ED-F991A1599371}.Release|Any CPU.ActiveCfg = Release|Any CPU {FB2D306B-1042-4A70-31ED-F991A1599371}.Release|Any CPU.Build.0 = Release|Any CPU - {94470CB8-1BFA-4639-8DEA-8D8470837E0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {94470CB8-1BFA-4639-8DEA-8D8470837E0E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {94470CB8-1BFA-4639-8DEA-8D8470837E0E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {94470CB8-1BFA-4639-8DEA-8D8470837E0E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -128,7 +122,6 @@ Global {EC768913-6270-14F4-1DD3-69C87A659462} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} {5DCCF9A1-C03F-90E6-87D3-E96DB25250C2} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} {FB2D306B-1042-4A70-31ED-F991A1599371} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} - {94470CB8-1BFA-4639-8DEA-8D8470837E0E} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {73E60370-756D-45AD-A19A-C40A02DACCC7} From 31548728cd0e7e335d3f5ea7e890b7182aaa3fc5 Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 15:10:17 +0200 Subject: [PATCH 27/43] Refactor EnvelopeStatus to shared domain library Moved the `EnvelopeStatus` enum and its extension methods from `ReceiverUI.Models` to `Domain.Constants` to improve code reuse and maintainability. Updated `EnvelopeSenderPage.razor` to reference the new namespace. --- .../Models/EnvelopeStatus.cs | 33 ------------------- .../Pages/EnvelopeSenderPage.razor | 1 + 2 files changed, 1 insertion(+), 33 deletions(-) delete mode 100644 EnvelopeGenerator.ReceiverUI/Models/EnvelopeStatus.cs diff --git a/EnvelopeGenerator.ReceiverUI/Models/EnvelopeStatus.cs b/EnvelopeGenerator.ReceiverUI/Models/EnvelopeStatus.cs deleted file mode 100644 index cb19eab5..00000000 --- a/EnvelopeGenerator.ReceiverUI/Models/EnvelopeStatus.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace EnvelopeGenerator.ReceiverUI.Models; - -/// -/// Envelope status enumeration (copied from Domain for ReceiverUI) -/// -public enum EnvelopeStatus -{ - Invalid = 0, - EnvelopeCreated = 1001, - EnvelopeSaved = 1002, - EnvelopeQueued = 1003, - EnvelopeSent = 1004, - EnvelopePartlySigned = 1005, - EnvelopeCompletelySigned = 1006, - EnvelopeReportCreated = 1007, - EnvelopeArchived = 1008, - EnvelopeDeleted = 1009, - EnvelopeRejected = 10007, - EnvelopeWithdrawn = 10009 -} - -public static class EnvelopeStatusExtensions -{ - public static bool IsActive(this EnvelopeStatus status) - { - return status >= EnvelopeStatus.EnvelopeCreated && status < EnvelopeStatus.EnvelopePartlySigned; - } - - public static bool IsCompleted(this EnvelopeStatus status) - { - return status >= EnvelopeStatus.EnvelopeCompletelySigned && status <= EnvelopeStatus.EnvelopeWithdrawn; - } -} diff --git a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor index 599a72e8..b2f8ad44 100644 --- a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor +++ b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor @@ -2,6 +2,7 @@ @attribute [Microsoft.AspNetCore.Authorization.Authorize(Policy = "Sender")] @using System.Text.Json +@using EnvelopeGenerator.Domain.Constants @using EnvelopeGenerator.ReceiverUI.Models @using DevExpress.Blazor @using EnvelopeGenerator.ReceiverUI.Services From 746635979b7ad48f3b125315589e1e61f173dfa2 Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 15:10:31 +0200 Subject: [PATCH 28/43] Ensure `_allEnvelopes` is never null after assignment Modified the assignment of `_allEnvelopes` to use the result of `EnvelopeService.GetAsync()` or an empty list (`[]`) if the result is null. This change prevents potential null reference issues when `_allEnvelopes` is used later in the code. --- EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor index b2f8ad44..3f9017a6 100644 --- a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor +++ b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeSenderPage.razor @@ -352,7 +352,7 @@ try { - _allEnvelopes = await EnvelopeService.GetAsync(); + _allEnvelopes = await EnvelopeService.GetAsync() ?? []; // Split into active and completed based on status var envelopes = _allEnvelopes.ToList(); From 315a022cb82004c0a8196c23ef3f9d531aa89820 Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 15:55:13 +0200 Subject: [PATCH 29/43] feat: Add sender-side UI resource strings for all languages Added 46 new localized strings to support sender-side UI implementation: Resources added: - Resource.de-DE.resx (German) - Resource.en-US.resx (English) - Resource.fr-FR.resx (French) Categories covered: - Dashboard actions (NewEnvelope, LoadEnvelope, DeleteEnvelope, etc.) - Grid columns (Receivers, Status, Type, CreatedOn, etc.) - Toolbar actions (RefreshData, ShowDocument, ResendInvitation, etc.) - Envelope editor (AddFile, MergeFiles, EditFields, SendEnvelope, etc.) - Settings (Language, UseAccessCode, TwoFactorEnabled, etc.) - Reminders & expiry (FirstReminderDays, ExpiresWhenDays, etc.) Source: Migrated from EnvelopeGenerator.Form resx files Purpose: Support upcoming ReceiverUI sender pages implementation --- .../Resources/Resource.de-DE.resx | 174 ++++++++++++++++++ .../Resources/Resource.en-US.resx | 174 ++++++++++++++++++ .../Resources/Resource.fr-FR.resx | 174 ++++++++++++++++++ 3 files changed, 522 insertions(+) diff --git a/EnvelopeGenerator.Application/Resources/Resource.de-DE.resx b/EnvelopeGenerator.Application/Resources/Resource.de-DE.resx index 16fb00f5..223b8e72 100644 --- a/EnvelopeGenerator.Application/Resources/Resource.de-DE.resx +++ b/EnvelopeGenerator.Application/Resources/Resource.de-DE.resx @@ -477,4 +477,178 @@ Bestätigungen + + Neuer Umschlag + + + Umschlag laden + + + Umschlag zurückrufen/löschen + + + Daten Aktualisieren + + + Aktualisiert: {0} + + + Dokument anzeigen + + + Empfänger kontaktieren + + + Umschlag-ID: {0} + + + Öffne Log Verzeichnis + + + Ergebnisbericht anzeigen + + + Support Mail + + + Einladung manuell versenden + + + Export + + + Empfänger + + + Email Anrede + + + Unterschrieben wann + + + Zugangscode + + + Benutzer + + + Typ + + + Titel + + + Erstellt am + + + Zuletzt geändert am + + + Offene Umschläge + + + Abgeschlossene Umschläge + + + Zugangscode senden + + + 2-Faktor Eigenschaften + + + Name + + + Telefonnummer + + + Empfänger hinzufügen + + + Empfänger löschen + + + Datei hinzufügen + + + Dateien zusammenführen + + + Datei löschen + + + Datei anzeigen + + + Felder bearbeiten + + + Daten bearbeiten + + + Speichern + + + Umschlag versenden + + + Abbrechen + + + Signatur hinzufügen + + + Signatur löschen + + + Sprache + + + Zugangscode verwenden + + + 2-Faktor-Authentifizierung aktiviert + + + Zertifizierungstyp + + + Finale E-Mail an Ersteller + + + Finale E-Mail an Empfänger + + + Erinnerungs-E-Mails senden + + + Erste Erinnerung (Tage) + + + Erinnerungsintervall (Tage) + + + Läuft ab nach (Tage) + + + Ablaufwarnung (Tage) + + + Nachricht + + + Umschlagtyp + + + Alle Optionen + + + Grund für Löschung + + + Bitte geben Sie einen Grund an + + + Status + \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Resources/Resource.en-US.resx b/EnvelopeGenerator.Application/Resources/Resource.en-US.resx index fa6c5afd..0db372f4 100644 --- a/EnvelopeGenerator.Application/Resources/Resource.en-US.resx +++ b/EnvelopeGenerator.Application/Resources/Resource.en-US.resx @@ -477,4 +477,178 @@ Confirmations + + New Envelope + + + Load Envelope + + + Delete Envelope + + + Reload Data + + + Refreshed: {0} + + + Show Document + + + Contact Receiver + + + Envelope-ID: {0} + + + Open Log Directory + + + Show Results Report + + + Support Mail + + + Send Invitation Again + + + Export + + + Receivers + + + Email Salutation + + + Signed When + + + Access Code + + + User + + + Type + + + Title + + + Created On + + + Last Modified + + + Open Envelopes + + + Completed Envelopes + + + Send Access Code + + + 2-Factor Properties + + + Name + + + Phone Number + + + Add Receiver + + + Delete Receiver + + + Add File + + + Merge Files + + + Delete File + + + Show File + + + Edit Fields + + + Edit Data + + + Save + + + Send Envelope + + + Cancel + + + Add Signature + + + Delete Signature + + + Language + + + Use Access Code + + + 2-Factor Authentication Enabled + + + Certification Type + + + Final Email to Creator + + + Final Email to Receivers + + + Send Reminder Emails + + + First Reminder (Days) + + + Reminder Interval (Days) + + + Expires After (Days) + + + Expiry Warning (Days) + + + Message + + + Envelope Type + + + All Options + + + Deletion Reason + + + Please provide a reason + + + Status + \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Resources/Resource.fr-FR.resx b/EnvelopeGenerator.Application/Resources/Resource.fr-FR.resx index be60d7bb..75834e1b 100644 --- a/EnvelopeGenerator.Application/Resources/Resource.fr-FR.resx +++ b/EnvelopeGenerator.Application/Resources/Resource.fr-FR.resx @@ -477,4 +477,178 @@ Confirmations + + Nouvelle enveloppe + + + Charger l'enveloppe + + + Supprimer l'enveloppe + + + Actualiser les données + + + Actualisé : {0} + + + Afficher le document + + + Contacter le destinataire + + + ID d'enveloppe : {0} + + + Ouvrir le répertoire des logs + + + Afficher le rapport de résultats + + + E-mail de support + + + Renvoyer l'invitation + + + Exporter + + + Destinataires + + + Formule de politesse + + + Signé quand + + + Code d'accès + + + Utilisateur + + + Type + + + Titre + + + Créé le + + + Dernière modification + + + Enveloppes ouvertes + + + Enveloppes terminées + + + Envoyer le code d'accès + + + Propriétés 2-facteurs + + + Nom + + + Numéro de téléphone + + + Ajouter un destinataire + + + Supprimer le destinataire + + + Ajouter un fichier + + + Fusionner les fichiers + + + Supprimer le fichier + + + Afficher le fichier + + + Modifier les champs + + + Modifier les données + + + Enregistrer + + + Envoyer l'enveloppe + + + Annuler + + + Ajouter une signature + + + Supprimer la signature + + + Langue + + + Utiliser un code d'accès + + + Authentification à 2 facteurs activée + + + Type de certification + + + E-mail final au créateur + + + E-mail final aux destinataires + + + Envoyer des e-mails de rappel + + + Premier rappel (jours) + + + Intervalle de rappel (jours) + + + Expire après (jours) + + + Avertissement d'expiration (jours) + + + Message + + + Type d'enveloppe + + + Toutes les options + + + Motif de suppression + + + Veuillez indiquer une raison + + + Statut + \ No newline at end of file From 714cb9555f774f825913d470b3e060d4b56db8bc Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 15:58:30 +0200 Subject: [PATCH 30/43] feat(i18n): add sender-side UI resources and extension methods - Added 46 resource strings (de-DE, en-US, fr-FR) for sender UI - Added corresponding extension methods in Resource.cs - Migrated from Form app for ReceiverUI sender pages --- .../Resources/Resource.cs | 408 ++++++++++++++++++ 1 file changed, 408 insertions(+) diff --git a/EnvelopeGenerator.Application/Resources/Resource.cs b/EnvelopeGenerator.Application/Resources/Resource.cs index 13223106..630b45b1 100644 --- a/EnvelopeGenerator.Application/Resources/Resource.cs +++ b/EnvelopeGenerator.Application/Resources/Resource.cs @@ -397,4 +397,412 @@ public static class Extensions /// /// public static string LockedFooterBody(this IStringLocalizer localizer, string suffix) => localizer[nameof(LockedFooterBody) + suffix].Value; + + // Sender-side UI resources + + /// + /// + /// + /// + /// + public static string NewEnvelope(this IStringLocalizer localizer) => localizer[nameof(NewEnvelope)].Value; + + /// + /// + /// + /// + /// + public static string LoadEnvelope(this IStringLocalizer localizer) => localizer[nameof(LoadEnvelope)].Value; + + /// + /// + /// + /// + /// + public static string DeleteEnvelope(this IStringLocalizer localizer) => localizer[nameof(DeleteEnvelope)].Value; + + /// + /// + /// + /// + /// + public static string RefreshData(this IStringLocalizer localizer) => localizer[nameof(RefreshData)].Value; + + /// + /// + /// + /// + /// + public static string RefreshedAt(this IStringLocalizer localizer) => localizer[nameof(RefreshedAt)].Value; + + /// + /// + /// + /// + /// + public static string ShowDocument(this IStringLocalizer localizer) => localizer[nameof(ShowDocument)].Value; + + /// + /// + /// + /// + /// + public static string ContactReceiver(this IStringLocalizer localizer) => localizer[nameof(ContactReceiver)].Value; + + /// + /// + /// + /// + /// + public static string EnvelopeId(this IStringLocalizer localizer) => localizer[nameof(EnvelopeId)].Value; + + /// + /// + /// + /// + /// + public static string OpenLogDirectory(this IStringLocalizer localizer) => localizer[nameof(OpenLogDirectory)].Value; + + /// + /// + /// + /// + /// + public static string ShowResultsReport(this IStringLocalizer localizer) => localizer[nameof(ShowResultsReport)].Value; + + /// + /// + /// + /// + /// + public static string SupportMail(this IStringLocalizer localizer) => localizer[nameof(SupportMail)].Value; + + /// + /// + /// + /// + /// + public static string ResendInvitation(this IStringLocalizer localizer) => localizer[nameof(ResendInvitation)].Value; + + /// + /// + /// + /// + /// + public static string Export(this IStringLocalizer localizer) => localizer[nameof(Export)].Value; + + /// + /// + /// + /// + /// + public static string Receivers(this IStringLocalizer localizer) => localizer[nameof(Receivers)].Value; + + /// + /// + /// + /// + /// + public static string EmailSalutation(this IStringLocalizer localizer) => localizer[nameof(EmailSalutation)].Value; + + /// + /// + /// + /// + /// + public static string SignedWhen(this IStringLocalizer localizer) => localizer[nameof(SignedWhen)].Value; + + /// + /// + /// + /// + /// + public static string AccessCode(this IStringLocalizer localizer) => localizer[nameof(AccessCode)].Value; + + /// + /// + /// + /// + /// + public static string User(this IStringLocalizer localizer) => localizer[nameof(User)].Value; + + /// + /// + /// + /// + /// + public static string Type(this IStringLocalizer localizer) => localizer[nameof(Type)].Value; + + /// + /// + /// + /// + /// + public static string Title(this IStringLocalizer localizer) => localizer[nameof(Title)].Value; + + /// + /// + /// + /// + /// + public static string CreatedOn(this IStringLocalizer localizer) => localizer[nameof(CreatedOn)].Value; + + /// + /// + /// + /// + /// + public static string LastModified(this IStringLocalizer localizer) => localizer[nameof(LastModified)].Value; + + /// + /// + /// + /// + /// + public static string OpenEnvelopes(this IStringLocalizer localizer) => localizer[nameof(OpenEnvelopes)].Value; + + /// + /// + /// + /// + /// + public static string CompletedEnvelopes(this IStringLocalizer localizer) => localizer[nameof(CompletedEnvelopes)].Value; + + /// + /// + /// + /// + /// + public static string SendAccessCode(this IStringLocalizer localizer) => localizer[nameof(SendAccessCode)].Value; + + /// + /// + /// + /// + /// + public static string TwoFactorProperties(this IStringLocalizer localizer) => localizer[nameof(TwoFactorProperties)].Value; + + /// + /// + /// + /// + /// + public static string Name(this IStringLocalizer localizer) => localizer[nameof(Name)].Value; + + /// + /// + /// + /// + /// + public static string PhoneNumber(this IStringLocalizer localizer) => localizer[nameof(PhoneNumber)].Value; + + /// + /// + /// + /// + /// + public static string AddReceiver(this IStringLocalizer localizer) => localizer[nameof(AddReceiver)].Value; + + /// + /// + /// + /// + /// + public static string DeleteReceiver(this IStringLocalizer localizer) => localizer[nameof(DeleteReceiver)].Value; + + /// + /// + /// + /// + /// + public static string AddFile(this IStringLocalizer localizer) => localizer[nameof(AddFile)].Value; + + /// + /// + /// + /// + /// + public static string MergeFiles(this IStringLocalizer localizer) => localizer[nameof(MergeFiles)].Value; + + /// + /// + /// + /// + /// + public static string DeleteFile(this IStringLocalizer localizer) => localizer[nameof(DeleteFile)].Value; + + /// + /// + /// + /// + /// + public static string ShowFile(this IStringLocalizer localizer) => localizer[nameof(ShowFile)].Value; + + /// + /// + /// + /// + /// + public static string EditFields(this IStringLocalizer localizer) => localizer[nameof(EditFields)].Value; + + /// + /// + /// + /// + /// + public static string EditData(this IStringLocalizer localizer) => localizer[nameof(EditData)].Value; + + /// + /// + /// + /// + /// + public static string Save(this IStringLocalizer localizer) => localizer[nameof(Save)].Value; + + /// + /// + /// + /// + /// + public static string SendEnvelope(this IStringLocalizer localizer) => localizer[nameof(SendEnvelope)].Value; + + /// + /// + /// + /// + /// + public static string Cancel(this IStringLocalizer localizer) => localizer[nameof(Cancel)].Value; + + /// + /// + /// + /// + /// + public static string AddSignature(this IStringLocalizer localizer) => localizer[nameof(AddSignature)].Value; + + /// + /// + /// + /// + /// + public static string DeleteSignature(this IStringLocalizer localizer) => localizer[nameof(DeleteSignature)].Value; + + /// + /// + /// + /// + /// + public static string Language(this IStringLocalizer localizer) => localizer[nameof(Language)].Value; + + /// + /// + /// + /// + /// + public static string UseAccessCode(this IStringLocalizer localizer) => localizer[nameof(UseAccessCode)].Value; + + /// + /// + /// + /// + /// + public static string TwoFactorEnabled(this IStringLocalizer localizer) => localizer[nameof(TwoFactorEnabled)].Value; + + /// + /// + /// + /// + /// + public static string CertificationType(this IStringLocalizer localizer) => localizer[nameof(CertificationType)].Value; + + /// + /// + /// + /// + /// + public static string FinalEmailToCreator(this IStringLocalizer localizer) => localizer[nameof(FinalEmailToCreator)].Value; + + /// + /// + /// + /// + /// + public static string FinalEmailToReceivers(this IStringLocalizer localizer) => localizer[nameof(FinalEmailToReceivers)].Value; + + /// + /// + /// + /// + /// + public static string SendReminderEmails(this IStringLocalizer localizer) => localizer[nameof(SendReminderEmails)].Value; + + /// + /// + /// + /// + /// + public static string FirstReminderDays(this IStringLocalizer localizer) => localizer[nameof(FirstReminderDays)].Value; + + /// + /// + /// + /// + /// + public static string ReminderIntervalDays(this IStringLocalizer localizer) => localizer[nameof(ReminderIntervalDays)].Value; + + /// + /// + /// + /// + /// + public static string ExpiresWhenDays(this IStringLocalizer localizer) => localizer[nameof(ExpiresWhenDays)].Value; + + /// + /// + /// + /// + /// + public static string ExpiresWarningDays(this IStringLocalizer localizer) => localizer[nameof(ExpiresWarningDays)].Value; + + /// + /// + /// + /// + /// + public static string Message(this IStringLocalizer localizer) => localizer[nameof(Message)].Value; + + /// + /// + /// + /// + /// + public static string EnvelopeType(this IStringLocalizer localizer) => localizer[nameof(EnvelopeType)].Value; + + /// + /// + /// + /// + /// + public static string AllOptions(this IStringLocalizer localizer) => localizer[nameof(AllOptions)].Value; + + /// + /// + /// + /// + /// + public static string DeleteReason(this IStringLocalizer localizer) => localizer[nameof(DeleteReason)].Value; + + /// + /// + /// + /// + /// + public static string PleaseProvideReason(this IStringLocalizer localizer) => localizer[nameof(PleaseProvideReason)].Value; + + /// + /// + /// + /// + /// + public static string Status(this IStringLocalizer localizer) => localizer[nameof(Status)].Value; } \ No newline at end of file From d61fe796135734771151b77eb045a8f89f8ea150 Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 16:05:26 +0200 Subject: [PATCH 31/43] Add localization support to the application Added a package reference for `Microsoft.Extensions.Localization` to enable localization support. Registered localization services in `Program.cs` using `builder.Services.AddLocalization()`. Introduced the `Microsoft.Extensions.Localization` and `EnvelopeGenerator.Application.Resources` namespaces to support localized resources. These changes allow the application to provide content in multiple languages or regions. --- .../EnvelopeGenerator.ReceiverUI.csproj | 1 + EnvelopeGenerator.ReceiverUI/Program.cs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj b/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj index 9f4ee25e..c367f14c 100644 --- a/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj +++ b/EnvelopeGenerator.ReceiverUI/EnvelopeGenerator.ReceiverUI.csproj @@ -30,6 +30,7 @@ + diff --git a/EnvelopeGenerator.ReceiverUI/Program.cs b/EnvelopeGenerator.ReceiverUI/Program.cs index fa88134a..42ab0fa7 100644 --- a/EnvelopeGenerator.ReceiverUI/Program.cs +++ b/EnvelopeGenerator.ReceiverUI/Program.cs @@ -7,6 +7,8 @@ using EnvelopeGenerator.ReceiverUI.Options; using DevExpress.XtraReports.Services; using DevExpress.Blazor.Reporting; using DevExpress.XtraReports.Web.Extensions; +using EnvelopeGenerator.Application.Resources; +using Microsoft.Extensions.Localization; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); @@ -25,6 +27,9 @@ builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddScoped(); +// Localization services +builder.Services.AddLocalization(); + builder.Services.AddDevExpressWebAssemblyBlazorReportViewer(); builder.Services.AddDevExpressWebAssemblyBlazorPdfViewer(); From f2356b3ce42192a1ef98548ba2c7cac6ab905a93 Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 16:07:43 +0200 Subject: [PATCH 32/43] Refactor SignatureService to DocReceiverElementService Renamed the `SignatureService` class to `DocReceiverElementService` to reflect its updated purpose. Updated the `GetAsync` method to use the `DocReceiverElement` API endpoint (`/api/DocReceiverElement/{envelopeKey}`) instead of the `Signature` API endpoint. These changes align the service with the new `DocReceiverElement` context. --- .../{SignatureService.cs => DocReceiverElementService.cs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename EnvelopeGenerator.ReceiverUI/Services/{SignatureService.cs => DocReceiverElementService.cs} (81%) diff --git a/EnvelopeGenerator.ReceiverUI/Services/SignatureService.cs b/EnvelopeGenerator.ReceiverUI/Services/DocReceiverElementService.cs similarity index 81% rename from EnvelopeGenerator.ReceiverUI/Services/SignatureService.cs rename to EnvelopeGenerator.ReceiverUI/Services/DocReceiverElementService.cs index 0955ed5b..34ef1916 100644 --- a/EnvelopeGenerator.ReceiverUI/Services/SignatureService.cs +++ b/EnvelopeGenerator.ReceiverUI/Services/DocReceiverElementService.cs @@ -6,13 +6,13 @@ using Microsoft.Extensions.Options; namespace EnvelopeGenerator.ReceiverUI.Services; -public class SignatureService(HttpClient http, IOptions apiOptions) +public class DocReceiverElementService(HttpClient http, IOptions apiOptions) { private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web); public async Task> GetAsync(string envelopeKey, CancellationToken cancel = default) { - var url = $"{apiOptions.Value.BaseUrl}/api/Signature/{Uri.EscapeDataString(envelopeKey)}"; + var url = $"{apiOptions.Value.BaseUrl}/api/DocReceiverElement/{Uri.EscapeDataString(envelopeKey)}"; var response = await http.GetAsync(url, cancel); if (!response.IsSuccessStatusCode) From ca4ec7cb6fa6f41198cbce81963650d6513420ea Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 16:09:26 +0200 Subject: [PATCH 33/43] Rename SignatureController to DocReceiverElementController The class `SignatureController` has been renamed to `DocReceiverElementController` to better reflect its purpose. The constructor and its XML documentation have been updated accordingly. The `[Authorize]`, `[ApiController]`, and `[Route]` attributes remain unchanged. A `TODO` comment has been added to indicate future updates for using a signature query. --- ...gnatureController.cs => DocReceiverElementController.cs} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename EnvelopeGenerator.API/Controllers/{SignatureController.cs => DocReceiverElementController.cs} (88%) diff --git a/EnvelopeGenerator.API/Controllers/SignatureController.cs b/EnvelopeGenerator.API/Controllers/DocReceiverElementController.cs similarity index 88% rename from EnvelopeGenerator.API/Controllers/SignatureController.cs rename to EnvelopeGenerator.API/Controllers/DocReceiverElementController.cs index bd3ff352..6c234117 100644 --- a/EnvelopeGenerator.API/Controllers/SignatureController.cs +++ b/EnvelopeGenerator.API/Controllers/DocReceiverElementController.cs @@ -15,14 +15,14 @@ namespace EnvelopeGenerator.API.Controllers; [Authorize(Policy = AuthPolicy.Receiver)] [ApiController] [Route("api/[controller]")] -public class SignatureController : ControllerBase +public class DocReceiverElementController : ControllerBase { private readonly IMediator _mediator; /// - /// Initializes a new instance of . + /// Initializes a new instance of . /// - public SignatureController(IMediator mediator) + public DocReceiverElementController(IMediator mediator) { _mediator = mediator; } From 02b857382c7257a5b43bf0ea54839431e9d898e2 Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 16:09:57 +0200 Subject: [PATCH 34/43] Replace SignatureService with DocReceiverElementService Refactor EnvelopeReceiverPage.razor to inject DocReceiverElementService instead of SignatureService. Update Program.cs to remove SignatureService from the dependency injection configuration and add DocReceiverElementService as a scoped service. This change reflects a shift in architecture where DocReceiverElementService now handles functionality previously managed by SignatureService. --- EnvelopeGenerator.ReceiverUI/Pages/EnvelopeReceiverPage.razor | 2 +- EnvelopeGenerator.ReceiverUI/Program.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeReceiverPage.razor b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeReceiverPage.razor index b1525967..b58ac15b 100644 --- a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeReceiverPage.razor +++ b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeReceiverPage.razor @@ -11,7 +11,7 @@ @inject IOptions AppOptions @inject IOptions PdfViewerOptions @inject IJSRuntime JSRuntime -@inject SignatureService SignatureService +@inject DocReceiverElementService SignatureService @inject SignatureCacheService SignatureCacheService @inject EnvelopeGenerator.ReceiverUI.Services.AuthService AuthService @inject EnvelopeGenerator.ReceiverUI.Services.EnvelopeReceiverService EnvelopeReceiverService diff --git a/EnvelopeGenerator.ReceiverUI/Program.cs b/EnvelopeGenerator.ReceiverUI/Program.cs index 42ab0fa7..2ca70aab 100644 --- a/EnvelopeGenerator.ReceiverUI/Program.cs +++ b/EnvelopeGenerator.ReceiverUI/Program.cs @@ -22,7 +22,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddScoped(); From 1e963ea21502707fdafd195b75134a6c793afacb Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 16:25:57 +0200 Subject: [PATCH 35/43] Add support for JWT authentication Added `Microsoft.AspNetCore.Authentication.JwtBearer` package to the project to enable JWT authentication. Updated `Program.cs` to include the necessary `using` directive for JWT authentication. No functional changes were made beyond the integration of the new package. --- EnvelopeGenerator.API/EnvelopeGenerator.API.csproj | 1 + EnvelopeGenerator.API/Program.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/EnvelopeGenerator.API/EnvelopeGenerator.API.csproj b/EnvelopeGenerator.API/EnvelopeGenerator.API.csproj index ba382a62..96154a76 100644 --- a/EnvelopeGenerator.API/EnvelopeGenerator.API.csproj +++ b/EnvelopeGenerator.API/EnvelopeGenerator.API.csproj @@ -34,6 +34,7 @@ + diff --git a/EnvelopeGenerator.API/Program.cs b/EnvelopeGenerator.API/Program.cs index 464f5c23..8e0b570e 100644 --- a/EnvelopeGenerator.API/Program.cs +++ b/EnvelopeGenerator.API/Program.cs @@ -13,7 +13,6 @@ using EnvelopeGenerator.Application; using DigitalData.Auth.Client; using DigitalData.Core.Abstractions; using EnvelopeGenerator.API.Models; -using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using DigitalData.Core.Abstractions.Security.Extensions; using EnvelopeGenerator.API.Middleware; @@ -22,6 +21,7 @@ using NLog.Web; using NLog; using DigitalData.Auth.Claims; using EnvelopeGenerator.API; +using Microsoft.AspNetCore.Authentication.JwtBearer; var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); logger.Info("Logging initialized!"); From a88a26c248a72b14a2970e51d6eba766faefcccb Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 16:26:24 +0200 Subject: [PATCH 36/43] Remove EGDbContextFactory and related configuration code The `EGDbContextFactory` class, which implemented the `IDesignTimeDbContextFactory` interface, has been removed. This includes the `CreateDbContext` method, which handled design-time DbContext creation by loading configuration from `appsettings.migration.json`, setting up `DbContextOptions`, and constructing `DbTriggerParams`. Additionally, the `#if NET` preprocessor directive and its corresponding `#endif` have been removed, along with all associated `using` directives. This change suggests that the factory is no longer needed due to a shift in the application's architecture or design-time DbContext handling. --- .../EGDbContextFactory.cs | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 EnvelopeGenerator.Infrastructure/EGDbContextFactory.cs diff --git a/EnvelopeGenerator.Infrastructure/EGDbContextFactory.cs b/EnvelopeGenerator.Infrastructure/EGDbContextFactory.cs deleted file mode 100644 index fea2b764..00000000 --- a/EnvelopeGenerator.Infrastructure/EGDbContextFactory.cs +++ /dev/null @@ -1,48 +0,0 @@ -#if NET -using EnvelopeGenerator.Application.Common.Configurations; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Design; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; - -namespace EnvelopeGenerator.Infrastructure -{ - public class EGDbContextFactory : IDesignTimeDbContextFactory - { - public EGDbContext CreateDbContext(string[] args) - { - var config = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.migration.json") - .Build(); - - // create DbContextOptions - var optionsBuilder = new DbContextOptionsBuilder(); - optionsBuilder.UseSqlServer(config.GetConnectionString("Default")); - - // create DbTriggerParams - var triggerLists = config.GetSection("DbTriggerParams").Get>>(); - var dbTriggerParams = new DbTriggerParams(); - if (triggerLists is not null) - foreach (var triggerList in triggerLists) - { - if (triggerList.Value.Count == 0) - continue; // Skip empty trigger lists - - var tableName = triggerList.Key; - - dbTriggerParams[tableName] = new List(); - - foreach (var trigger in triggerList.Value) - { - dbTriggerParams[tableName].Add(trigger); - } - } - - var dbContext = new EGDbContext(optionsBuilder.Options, Options.Create(dbTriggerParams)); - dbContext.IsMigration = true; - return dbContext; - } - } -} -#endif \ No newline at end of file From c93a056ca56036fa2b63858e1662360b2357cc6c Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 16:54:40 +0200 Subject: [PATCH 37/43] Add CultureService and localization support Added a new `CultureService` to manage application culture and localization. The service supports retrieving and setting the application culture, storing it in `localStorage`, and initializing it based on stored values, browser settings, or a default fallback. Registered `CultureService` in the dependency injection container and added localization support in `Program.cs` using `builder.Services.AddLocalization()`. --- EnvelopeGenerator.ReceiverUI/Program.cs | 1 + .../Services/CultureService.cs | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 EnvelopeGenerator.ReceiverUI/Services/CultureService.cs diff --git a/EnvelopeGenerator.ReceiverUI/Program.cs b/EnvelopeGenerator.ReceiverUI/Program.cs index 2ca70aab..6e725a86 100644 --- a/EnvelopeGenerator.ReceiverUI/Program.cs +++ b/EnvelopeGenerator.ReceiverUI/Program.cs @@ -26,6 +26,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddScoped(); +builder.Services.AddScoped(); // Localization services builder.Services.AddLocalization(); diff --git a/EnvelopeGenerator.ReceiverUI/Services/CultureService.cs b/EnvelopeGenerator.ReceiverUI/Services/CultureService.cs new file mode 100644 index 00000000..2b4e4aeb --- /dev/null +++ b/EnvelopeGenerator.ReceiverUI/Services/CultureService.cs @@ -0,0 +1,74 @@ +using System.Globalization; +using Microsoft.JSInterop; + +namespace EnvelopeGenerator.ReceiverUI.Services; + +/// +/// Service for managing application culture/localization. +/// +public class CultureService +{ + private readonly IJSRuntime _jsRuntime; + private const string CULTURE_KEY = "AppCulture"; + + public CultureService(IJSRuntime jsRuntime) + { + _jsRuntime = jsRuntime; + } + + /// + /// Gets the list of supported cultures. + /// + public static CultureInfo[] SupportedCultures { get; } = new[] + { + new CultureInfo("de-DE"), + new CultureInfo("en-US"), + new CultureInfo("fr-FR") + }; + + /// + /// Sets the application culture and stores it in localStorage. + /// + public async Task SetCultureAsync(string culture) + { + if (!SupportedCultures.Any(c => c.Name == culture)) + throw new ArgumentException($"Culture '{culture}' is not supported.", nameof(culture)); + + await _jsRuntime.InvokeVoidAsync("localStorage.setItem", CULTURE_KEY, culture); + } + + /// + /// Gets the stored culture from localStorage. + /// + public async Task GetCultureAsync() + { + try + { + return await _jsRuntime.InvokeAsync("localStorage.getItem", CULTURE_KEY); + } + catch + { + return null; + } + } + + /// + /// Initializes the culture from localStorage or browser settings. + /// + public async Task InitializeCultureAsync() + { + var storedCulture = await GetCultureAsync(); + + if (!string.IsNullOrEmpty(storedCulture) && + SupportedCultures.Any(c => c.Name == storedCulture)) + { + return new CultureInfo(storedCulture); + } + + // Fallback to browser culture or default + var browserCulture = CultureInfo.CurrentCulture.Name; + var matchedCulture = SupportedCultures.FirstOrDefault(c => c.Name == browserCulture); + + return matchedCulture ?? SupportedCultures[0]; // Default to German + } +} From 9d962708c40e09e66ab61b098afbade7d149b6f8 Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 16:55:44 +0200 Subject: [PATCH 38/43] Initialize culture settings in App.razor Added dependency injection for `CultureService` in `App.razor` to dynamically initialize culture settings during the component's lifecycle. Included `System.Globalization` and `EnvelopeGenerator.ReceiverUI.Services` namespaces to support culture-related functionality. Injected `CultureService` and added an `OnInitializedAsync` method to set `CultureInfo.DefaultThreadCurrentCulture` and `CultureInfo.DefaultThreadCurrentUICulture` based on the service's initialization. Reformatted surrounding `` code for clarity. --- EnvelopeGenerator.ReceiverUI/App.razor | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/EnvelopeGenerator.ReceiverUI/App.razor b/EnvelopeGenerator.ReceiverUI/App.razor index c5ac499c..6176d21c 100644 --- a/EnvelopeGenerator.ReceiverUI/App.razor +++ b/EnvelopeGenerator.ReceiverUI/App.razor @@ -1,4 +1,8 @@ - +@using System.Globalization +@using EnvelopeGenerator.ReceiverUI.Services +@inject CultureService CultureService + + @@ -7,4 +11,13 @@

Sorry, there's nothing at this address.

-
\ No newline at end of file +
+ +@code { + protected override async Task OnInitializedAsync() + { + var culture = await CultureService.InitializeCultureAsync(); + CultureInfo.DefaultThreadCurrentCulture = culture; + CultureInfo.DefaultThreadCurrentUICulture = culture; + } +} From ba9f233993d0a373b0afc9c28059e9aac1feea81 Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 17 Jun 2026 16:57:39 +0200 Subject: [PATCH 39/43] Add Language Selector component to footer Introduced a Language Selector component (`LanguageSelector.razor`) to enable users to switch between German, English, and French. The component includes a dropdown UI with flag icons and language labels, along with logic for toggling the dropdown and changing the app's culture. Injected `IJSRuntime`, `NavigationManager`, and `CultureService` into the component to handle culture changes and navigation updates. Updated `MainLayout.razor` to include the Language Selector in the footer. Refactored footer layout in `MainLayout.razor.css` to use flexbox for better alignment and spacing. Added styles for the Language Selector in `app.css` to ensure a clean and responsive design. Integrated the `flag-icons` library in `index.html` to display flag icons for language representation. --- .../Shared/LanguageSelector.razor | 62 ++++++++++++++++ .../Shared/LanguageSelector.razor.css | 0 .../Shared/MainLayout.razor | 9 ++- .../Shared/MainLayout.razor.css | 15 ++++ .../wwwroot/css/app.css | 73 ++++++++++++++++++- .../wwwroot/index.html | 1 + 6 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 EnvelopeGenerator.ReceiverUI/Shared/LanguageSelector.razor create mode 100644 EnvelopeGenerator.ReceiverUI/Shared/LanguageSelector.razor.css diff --git a/EnvelopeGenerator.ReceiverUI/Shared/LanguageSelector.razor b/EnvelopeGenerator.ReceiverUI/Shared/LanguageSelector.razor new file mode 100644 index 00000000..a24a4f4e --- /dev/null +++ b/EnvelopeGenerator.ReceiverUI/Shared/LanguageSelector.razor @@ -0,0 +1,62 @@ +@using System.Globalization +@using EnvelopeGenerator.ReceiverUI.Services +@inject IJSRuntime JSRuntime +@inject NavigationManager Navigation +@inject CultureService CultureService + +
+ + + @if (isOpen) + { +
+ + + +
+ } +
+ +@code { + private bool isOpen = false; + private string CurrentCulture => CultureInfo.CurrentCulture.Name; + + private void ToggleDropdown() + { + isOpen = !isOpen; + } + + private async Task ChangeLanguageAsync(string culture) + { + if (CultureInfo.CurrentCulture.Name != culture) + { + await CultureService.SetCultureAsync(culture); + Navigation.NavigateTo(Navigation.Uri, forceLoad: true); + } + + isOpen = false; + } + + private string GetFlagCode(string culture) + { + return culture switch + { + "de-DE" => "de", + "en-US" => "us", + "fr-FR" => "fr", + _ => "de" + }; + } +} diff --git a/EnvelopeGenerator.ReceiverUI/Shared/LanguageSelector.razor.css b/EnvelopeGenerator.ReceiverUI/Shared/LanguageSelector.razor.css new file mode 100644 index 00000000..e69de29b diff --git a/EnvelopeGenerator.ReceiverUI/Shared/MainLayout.razor b/EnvelopeGenerator.ReceiverUI/Shared/MainLayout.razor index e667fbac..873ea018 100644 --- a/EnvelopeGenerator.ReceiverUI/Shared/MainLayout.razor +++ b/EnvelopeGenerator.ReceiverUI/Shared/MainLayout.razor @@ -8,9 +8,12 @@
diff --git a/EnvelopeGenerator.ReceiverUI/Shared/MainLayout.razor.css b/EnvelopeGenerator.ReceiverUI/Shared/MainLayout.razor.css index d662d7ea..ef51f3a9 100644 --- a/EnvelopeGenerator.ReceiverUI/Shared/MainLayout.razor.css +++ b/EnvelopeGenerator.ReceiverUI/Shared/MainLayout.razor.css @@ -15,3 +15,18 @@ article { padding: 0 !important; margin: 0 !important; } + +.receiver-footer { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem 2rem; + gap: 1rem; +} + +.receiver-footer__content { + display: flex; + align-items: center; + gap: 0.5rem; +} + diff --git a/EnvelopeGenerator.ReceiverUI/wwwroot/css/app.css b/EnvelopeGenerator.ReceiverUI/wwwroot/css/app.css index 9f967169..48de5029 100644 --- a/EnvelopeGenerator.ReceiverUI/wwwroot/css/app.css +++ b/EnvelopeGenerator.ReceiverUI/wwwroot/css/app.css @@ -365,4 +365,75 @@ article { .receiver-footer__sep { opacity: 0.4; -} \ No newline at end of file +} + +/* ── Language Selector (Footer) ──────────────────────────────────────────── */ +.language-selector { + position: relative; + display: inline-block; +} + +.language-selector__trigger { + display: flex; + align-items: center; + gap: 0.4rem; + padding: 0.25rem 0.5rem; + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 4px; + color: rgba(255, 255, 255, 0.9); + cursor: pointer; + font-size: 1rem; + transition: all 0.2s ease; +} + +.language-selector__trigger:hover { + background: rgba(255, 255, 255, 0.15); + border-color: rgba(255, 255, 255, 0.3); +} + +.language-selector__arrow { + font-size: 0.6rem; + transition: transform 0.2s ease; + opacity: 0.7; +} + +.language-selector__dropdown { + position: absolute; + bottom: calc(100% + 0.5rem); + right: 0; + background: white; + border: 1px solid #ddd; + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + min-width: 160px; + z-index: 1000; + overflow: hidden; +} + +.language-selector__option { + display: flex; + align-items: center; + gap: 0.75rem; + width: 100%; + padding: 0.65rem 1rem; + background: white; + border: none; + color: #333; + cursor: pointer; + font-size: 0.9rem; + transition: background-color 0.2s ease; + text-align: left; +} + +.language-selector__option:hover { + background-color: #f5f5f5; +} + +.language-selector__option .fi { + font-size: 1.25rem; +} + +.language-selector__option span:last-child { + font-weight: 500; +} diff --git a/EnvelopeGenerator.ReceiverUI/wwwroot/index.html b/EnvelopeGenerator.ReceiverUI/wwwroot/index.html index ce8ae2fe..ec48b8fa 100644 --- a/EnvelopeGenerator.ReceiverUI/wwwroot/index.html +++ b/EnvelopeGenerator.ReceiverUI/wwwroot/index.html @@ -12,6 +12,7 @@ +