Compare commits

...

11 Commits

Author SHA1 Message Date
3a2fa77862 refactor(WebUI): configure HtpClient 2026-06-15 11:33:03 +02:00
cfa6dbd2de remove deprecated parameter 2026-06-15 11:18:34 +02:00
eb2603f389 remove wwwroot/app.css and add js and css dependencies 2026-06-15 11:04:35 +02:00
456178bee1 migrate shared components 2026-06-15 10:54:08 +02:00
2c41c74510 refactor(COPILOT_CONTEXT): update to be compatible with the migration from ReceiverUI to WebUI 2026-06-15 10:44:19 +02:00
bb73795d68 remove MIGRATION_CONTEXT.md
Migrate ReceiverUI to hybrid Blazor WebUI architecture

Migrated the `EnvelopeGenerator.ReceiverUI` project to a new
hybrid Blazor architecture (`EnvelopeGenerator.WebUI`) that
supports both Blazor Server and WebAssembly (WASM) modes.

- Added `WebUI` (server) and `WebUI.Client` (WASM) projects.
- Migrated client-side pages to `WebUI.Client` with
  `@rendermode InteractiveWebAssembly`.
- Migrated server-side pages to `WebUI` with
  `@rendermode InteractiveServer`.
- Added YARP reverse proxy (`yarp.json`) to `WebUI` for API
  and Swagger routing.
- Registered DevExpress server-side services in `WebUI` to
  enable backend rendering for `DxPdfViewer`.
- Migrated services, models, options, and data files to
  `WebUI.Client` with updated namespaces.
- Merged static files (JS, CSS, configuration) from
  `ReceiverUI/wwwroot` to `WebUI/wwwroot`.
- Retained `ReceiverUI` project for rollback safety.

This migration resolves the issue where the DevExpress
`DxPdfViewer` failed to render PDFs in a pure Blazor
WebAssembly environment due to missing server-side rendering
services.
2026-06-15 10:41:53 +02:00
207992d95a fix(WebUI.Client.Services): resolve doc comment icons 2026-06-15 10:27:51 +02:00
d6bafc64a6 Fix BOM issue in using directives in Report.cs and ReportsFactory.cs
The `using DevExpress.XtraReports.UI;` directive was modified in
both `Report.cs` and `ReportsFactory.cs` due to the addition of a
non-visible character (likely a Byte Order Mark or BOM) at the
beginning of the line. This change does not affect functionality
but may resolve potential issues with tools or version control
systems sensitive to such characters.
2026-06-15 10:18:31 +02:00
3090711892 resolve comment icons 2026-06-15 10:13:22 +02:00
9dbd8f7952 remove Home.razor 2026-06-15 09:59:42 +02:00
48a41f2987 refactor(wwwroot): move docs, fonts, fake-data, images and js files.
- update appsettings.json to fix
2026-06-15 09:59:18 +02:00
36 changed files with 837 additions and 1390 deletions

View File

@@ -1,72 +1,107 @@
# EnvelopeGenerator — AI Context Reference # EnvelopeGenerator — AI Context Reference
## Purpose ## Purpose
Digital document signing system with **unified Blazor WASM frontend** for both Senders and Receivers. Senders create envelopes and place signature fields. Receivers view PDFs, sign documents, export stamped PDFs. Digital document signing system with **unified Blazor Auto (Server+WASM hybrid) frontend** for both Senders and Receivers. Senders create envelopes and place signature fields. Receivers view PDFs, sign documents, export stamped PDFs.
**Primary Libraries:** DevExpress + PDF.js (PSPDFKit removed) **Primary Libraries:** DevExpress + PDF.js (PSPDFKit removed)
--- ---
## Migration Notice
**EnvelopeGenerator.ReceiverUI ? EnvelopeGenerator.WebUI Migration**
The project has been migrated from pure Blazor WebAssembly (`ReceiverUI`) to **Blazor Auto (Server+WASM hybrid)** architecture (`WebUI`) to resolve DevExpress `DxPdfViewer` compatibility issues.
**Reason:** DevExpress `DxPdfViewer` requires backend server-side rendering services that are NOT available in pure WebAssembly projects.
**New Structure:**
- **WebUI** (Server project): Hosts server-side components, YARP proxy, DevExpress backend services
- **WebUI.Client** (WASM project): Client-side components, business logic, services
**Migration Details:** See `MIGRATION_CONTEXT.md`
---
## Deployment Architecture ## Deployment Architecture
**Two Presentation Projects (Both Required):** **Two Presentation Projects (Both Required):**
1. **EnvelopeGenerator.API** (ASP.NET Core Web API) 1. **EnvelopeGenerator.API** (ASP.NET Core Web API)
- Runs independently (development & production) - Runs independently (development & production)
- **YARP Reverse Proxy** configured via `yarp.json` - Backend services for document management, authentication, signature endpoints
- Proxies requests to: - Serves as API endpoint for WebUI
- `EnvelopeGenerator.ReceiverUI` (Blazor WASM)
- External Auth.API service
- Serves as single entry point for all requests
2. **EnvelopeGenerator.ReceiverUI** (Blazor WebAssembly) 2. **EnvelopeGenerator.WebUI** (Blazor Auto - Server+WASM Hybrid)
- Runs on separate host/port - **Server Project (`EnvelopeGenerator.WebUI`):**
- Accessed **only through API proxy** (not directly) - **YARP Reverse Proxy** configured via `yarp.json`
- Serves static files (HTML, JS, CSS, WASM) - Proxies `/api/*` requests to `API:8088`
- Hosts server-side components (`@rendermode InteractiveServer`)
- DevExpress server-side services (DxPdfViewer backend)
- **Client Project (`EnvelopeGenerator.WebUI.Client`):**
- Client-side components (`@rendermode InteractiveWebAssembly`)
- Business logic services (AuthService, DocumentService, etc.)
- WASM runtime
**Request Flow:** **Request Flow:**
``` ```
Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM) Client ? WebUI:XXXX (Blazor Auto)
? Auth.API:9090 (External Auth Service) ?? Server-side Pages (DxPdfViewer)
?? Client-side Pages (WASM)
?? YARP Proxy: /api/* ? API:8088
``` ```
**Configuration:** `EnvelopeGenerator.API/yarp.json` **Configuration:** `EnvelopeGenerator.WebUI/yarp.json`
--- ---
## ReceiverUI Route Structure ## WebUI Route Structure
### Root Route ### Root Route
| Route | File | Purpose | | Route | File | Location | Render Mode |
|---|---|---| |---|---|---|---|
| `/` | `Index.razor` | Application entry point (landing page). | | `/` | `Index.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
### Sender Routes ### Sender Routes
| Route | File | Purpose | | Route | File | Location | Render Mode |
|---|---|---| |---|---|---|---|
| `/sender/login` | `LoginSenderPage.razor` | Username/password authentication | | `/sender/login` | `LoginSenderPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
| `/sender` | `EnvelopeSenderPage.razor` | Sender dashboard (envelope list) | | `/sender` | `EnvelopeSenderPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
### Receiver Routes ### Receiver Routes (PDF Viewers)
| Route | File | Purpose | | Route | File | Location | Render Mode |
|---|---|---| |---|---|---|---|
| `/envelope/login/{EnvelopeKey}` | `LoginReceiverPage.razor` | Access code authentication for specific envelope | | `/envelope/login/{EnvelopeKey}` | `LoginReceiverPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
| `/envelope/{EnvelopeKey}` | `EnvelopeReceiverPage.razor` | View & sign envelope (PDF.js viewer) | | `/envelope/{EnvelopeKey}` | `EnvelopeReceiverPage.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
| `/envelope/DxPdfViewer` | `EnvelopeReceiverPage_DxPdfViewer.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
| `/envelope/{EnvelopeKey}/DxReportViewer` | `EnvelopeReceiverPage_DxReportViewer.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
| `/envelope/Embed` | `EnvelopeReceiverPage_embed.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
**Multi-Envelope Support:** Receivers can login to multiple envelopes simultaneously (per-envelope cookie authentication). **Multi-Envelope Support:** Receivers can login to multiple envelopes simultaneously (per-envelope cookie authentication).
**Render Mode Strategy:**
- **Client-side Pages (WASM):** Login, Sender dashboard, Index (no DevExpress backend required)
- **Server-side Pages (Server):** PDF viewers (DevExpress DxPdfViewer requires backend)
--- ---
## Architecture Evolution ## Architecture Evolution
### Old Architecture (Deprecated) ### Old Architecture (Deprecated v1)
- **Sender UI:** `EnvelopeGenerator.Web` (Razor Pages + PSPDFKit) - **Sender UI:** `EnvelopeGenerator.Web` (Razor Pages + PSPDFKit)
- **Receiver UI:** `EnvelopeGenerator.ReceiverUI` (Blazor WASM + PDF.js) - **Receiver UI:** Separate project
- **Backend:** `EnvelopeGenerator.API` - **Backend:** `EnvelopeGenerator.API`
### Current Architecture ### Intermediate Architecture (Deprecated v2)
- **Unified Frontend:** `EnvelopeGenerator.ReceiverUI` (Blazor WASM)**Both Senders & Receivers** - **Unified Frontend:** `EnvelopeGenerator.ReceiverUI` (Pure Blazor WASM)
- **Backend:** `EnvelopeGenerator.API`**Both Senders & Receivers** - **Backend:** `EnvelopeGenerator.API`
- **Issue:** DevExpress `DxPdfViewer` displayed blank screen (no backend services in WASM)
### Current Architecture (Active)
- **Frontend:** `EnvelopeGenerator.WebUI` (Blazor Auto - Server+WASM Hybrid)
- **WebUI** (Server): Server-side components, YARP proxy, DevExpress backend
- **WebUI.Client** (WASM): Client-side components, services, business logic
- **Backend:** `EnvelopeGenerator.API`
- **Libraries:** DevExpress + PDF.js - **Libraries:** DevExpress + PDF.js
- **PSPDFKit:** **REMOVED** - **PSPDFKit:** **REMOVED**
@@ -77,12 +112,14 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
| Project | Target | Purpose | | Project | Target | Purpose |
|---|---|---| |---|---|---|
| `EnvelopeGenerator.API` | net8.0 | ASP.NET Core Web API. Backend for **both Senders & Receivers**. Auth, PDF serving, signature endpoints. | | `EnvelopeGenerator.API` | net8.0 | ASP.NET Core Web API. Backend for **both Senders & Receivers**. Auth, PDF serving, signature endpoints. |
| `EnvelopeGenerator.ReceiverUI` | net8.0 WASM | **Unified Blazor WebAssembly Frontend**. UI for **both Senders & Receivers**. YARP proxy to API. | | `EnvelopeGenerator.WebUI` | net8.0 | **Blazor Auto Server Project**. YARP proxy, server-side components, DevExpress backend services. |
| `EnvelopeGenerator.WebUI.Client` | net8.0 WASM | **Blazor Auto Client Project**. Client-side components, services, business logic. |
| `EnvelopeGenerator.ReceiverUI` | net8.0 WASM | **DEPRECATED.** Pure Blazor WASM (migrated to WebUI). |
| `EnvelopeGenerator.Web` | net7/8/9 | **DEPRECATED.** Legacy Razor Pages (Sender UI). No longer used. | | `EnvelopeGenerator.Web` | net7/8/9 | **DEPRECATED.** Legacy Razor Pages (Sender UI). No longer used. |
| `EnvelopeGenerator.Application` | multi | MediatR CQRS handlers. Business logic. | | `EnvelopeGenerator.Application` | multi | MediatR CQRS handlers. Business logic. |
| `EnvelopeGenerator.Domain` | multi | Domain models, constants, interfaces. | | `EnvelopeGenerator.Domain` | multi | Domain models, constants, interfaces. |
| `EnvelopeGenerator.Infrastructure` | multi | EF Core repos, DB context. | | `EnvelopeGenerator.Infrastructure` | multi | EF Core repos, DB context. |
| `EnvelopeGenerator.PdfEditor` | multi | iText7 utilities (NOT used in ReceiverUI). | | `EnvelopeGenerator.PdfEditor` | multi | iText7 utilities (NOT used in WebUI). |
| `EnvelopeGenerator.DependencyInjection` | multi | DI registration helpers. | | `EnvelopeGenerator.DependencyInjection` | multi | DI registration helpers. |
| **VB.NET projects** (Service/Form/BBTests) | net462 | **Legacy. Do NOT touch.** | | **VB.NET projects** (Service/Form/BBTests) | net462 | **Legacy. Do NOT touch.** |
@@ -90,18 +127,31 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
## Key Files & Routes ## Key Files & Routes
| File | Route/Purpose | ### Client-Side Pages (WebUI.Client)
| File | Route | Purpose |
|---|---|---|
| `WebUI.Client/Pages/Index.razor` | `/` | Application entry point (landing page). |
| `WebUI.Client/Pages/EnvelopeSenderPage.razor` | `/sender` | Sender dashboard (envelope list). |
| `WebUI.Client/Pages/LoginSenderPage.razor` | `/sender/login` | Sender username/password auth. |
| `WebUI.Client/Pages/LoginReceiverPage.razor` | `/envelope/login/{EnvelopeKey}` | Receiver access code auth. |
### Server-Side Pages (WebUI)
| File | Route | Purpose |
|---|---|---|
| `WebUI/Components/Pages/EnvelopeReceiverPage.razor` | `/envelope/{key}` | Receiver PDF viewer & signing (PDF.js). |
| `WebUI/Components/Pages/EnvelopeReceiverPage_DxPdfViewer.razor` | `/envelope/DxPdfViewer` | DevExpress PDF Viewer (test page). |
| `WebUI/Components/Pages/EnvelopeReceiverPage_DxReportViewer.razor` | `/envelope/{key}/DxReportViewer` | DevExpress Report Viewer. |
| `WebUI/Components/Pages/EnvelopeReceiverPage_embed.razor` | `/envelope/Embed` | Embedded PDF viewer (iframe). |
### Services & Assets
| File | Purpose |
|---|---| |---|---|
| `ReceiverUI/Pages/Index.razor` | `/` — Application entry point (landing page). | | `WebUI.Client/Services/AuthService.cs` | Receiver + Sender authentication. |
| `ReceiverUI/Pages/EnvelopeSenderPage.razor` | `/sender` — Sender dashboard (envelope list). | | `WebUI.Client/Services/SignatureCacheService.cs` | Signature caching (Redis/SQL). |
| `ReceiverUI/Pages/EnvelopeReceiverPage.razor` | `/envelope/{key}` — Receiver PDF viewer & signing. | | `WebUI.Client/Services/DocumentService.cs` | PDF document retrieval. |
| `ReceiverUI/Pages/LoginSenderPage.razor` | `/sender/login` — Sender username/password auth. | | `WebUI/wwwroot/js/pdf-viewer.js` | PDF.js wrapper (zoom, pagination, thumbnails). |
| `ReceiverUI/Pages/LoginReceiverPage.razor` | `/envelope/login/{EnvelopeKey}` — Receiver access code auth. | | `WebUI/wwwroot/js/receiver-signature.js` | Signature pad (draw/type/image). |
| `ReceiverUI/wwwroot/js/pdf-viewer.js` | PDF.js wrapper (zoom, pagination, thumbnails). | | `WebUI/wwwroot/css/envelope-viewer.css` | EnvelopeViewer styles. |
| `ReceiverUI/wwwroot/js/receiver-signature.js` | Signature pad (draw/type/image). |
| `ReceiverUI/wwwroot/css/envelope-viewer.css` | EnvelopeViewer styles. |
| `ReceiverUI/Services/AuthService.cs` | Receiver + Sender authentication. |
| `ReceiverUI/Services/SignatureCacheService.cs` | Signature caching (Redis/SQL). |
| `API/Controllers/CacheController.cs` | Signature cache endpoints. | | `API/Controllers/CacheController.cs` | Signature cache endpoints. |
--- ---
@@ -138,8 +188,8 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
## EnvelopeReceiver — PDF.js Viewer & Signing ## EnvelopeReceiver — PDF.js Viewer & Signing
**Route:** `/envelope/{EnvelopeKey}` **Route:** `/envelope/{EnvelopeKey}`
**Tech:** PDF.js 3.11.174 + Blazor WASM + configurable quality **Tech:** PDF.js 3.11.174 + Blazor Server (`@rendermode InteractiveServer`) + configurable quality
**File:** `ReceiverUI/Pages/EnvelopeReceiverPage.razor` **File:** `WebUI/Components/Pages/EnvelopeReceiverPage.razor`
### Key Features ### Key Features
1. HiDPI/Retina support (4x quality) 1. HiDPI/Retina support (4x quality)
@@ -150,7 +200,7 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
6. Responsive (desktop/mobile) 6. Responsive (desktop/mobile)
### Configuration ### Configuration
**File:** `ReceiverUI/wwwroot/appsettings.json` **File:** `WebUI/wwwroot/appsettings.json`
```json ```json
{ {
@@ -164,7 +214,7 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
``` ```
### JavaScript API ### JavaScript API
**File:** `ReceiverUI/wwwroot/js/pdf-viewer.js` **File:** `WebUI/wwwroot/js/pdf-viewer.js`
```javascript ```javascript
window.pdfViewer = { window.pdfViewer = {
@@ -211,7 +261,7 @@ window.pdfViewer = {
- Session state: `_capturedSignature` (lost on refresh) - Session state: `_capturedSignature` (lost on refresh)
### Data Model ### Data Model
**File:** `ReceiverUI/Models/SignatureCaptureDto.cs` **File:** `WebUI.Client/Models/SignatureCaptureDto.cs`
```csharp ```csharp
public sealed record SignatureCaptureDto { public sealed record SignatureCaptureDto {
@@ -250,7 +300,7 @@ signature:91751687-8ae6-4777-bf5f-b8846085e62e:{envelopeKey}
``` ```
### Service ### Service
**File:** `ReceiverUI/Services/SignatureCacheService.cs` **File:** `WebUI.Client/Services/SignatureCacheService.cs`
```csharp ```csharp
public class SignatureCacheService { public class SignatureCacheService {
@@ -267,11 +317,11 @@ public class SignatureCacheService {
## Sender Login ## Sender Login
**Route:** `/sender/login` **Route:** `/sender/login`
**File:** `ReceiverUI/Pages/LoginSenderPage.razor` **File:** `WebUI.Client/Pages/LoginSenderPage.razor`
**Tech:** Bootstrap 5 + DevExpress Blazing Berry theme **Tech:** Bootstrap 5 + DevExpress Blazing Berry theme
### AuthService Extension ### AuthService Extension
**File:** `ReceiverUI/Services/AuthService.cs` **File:** `WebUI.Client/Services/AuthService.cs`
```csharp ```csharp
public enum SenderLoginResult { Success, InvalidCredentials, Error } public enum SenderLoginResult { Success, InvalidCredentials, Error }
@@ -316,7 +366,7 @@ public async Task<SenderLoginResult> LoginSenderAsync(string username, string pa
## Receiver Login ## Receiver Login
**Route:** `/envelope/login/{EnvelopeKey}` **Route:** `/envelope/login/{EnvelopeKey}`
**File:** `ReceiverUI/Pages/LoginReceiverPage.razor` **File:** `WebUI.Client/Pages/LoginReceiverPage.razor`
**Multi-Envelope Support:** Cookies are stored per-envelope (e.g., `AuthTokenSignFLOWReceiver.{envelopeKey}`), allowing simultaneous authentication for multiple envelopes in the same browser session. **Multi-Envelope Support:** Cookies are stored per-envelope (e.g., `AuthTokenSignFLOWReceiver.{envelopeKey}`), allowing simultaneous authentication for multiple envelopes in the same browser session.
@@ -344,7 +394,7 @@ public async Task<EnvelopeLoginResult> LoginEnvelopeReceiverAsync(string key, st
--- ---
## NuGet Packages (ReceiverUI) ## NuGet Packages (WebUI.Client)
| Package | Version | Purpose | | Package | Version | Purpose |
|---|---|---| |---|---|---|
@@ -376,7 +426,8 @@ public async Task<EnvelopeLoginResult> LoginEnvelopeReceiverAsync(string key, st
### Deprecated Projects ### Deprecated Projects
**DO NOT USE:** **DO NOT USE:**
- `EnvelopeGenerator.Web` (Razor Pages) — Replaced by unified ReceiverUI - `EnvelopeGenerator.ReceiverUI` (Pure Blazor WASM) — Migrated to WebUI (DevExpress compatibility issue)
- `EnvelopeGenerator.Web` (Razor Pages) — Replaced by unified WebUI
- PSPDFKit — Removed, use PDF.js + DevExpress instead - PSPDFKit — Removed, use PDF.js + DevExpress instead
### Legacy Projects (VB.NET) ### Legacy Projects (VB.NET)
@@ -411,14 +462,16 @@ Proves database uses INCHES natively.
2. Prefer simplicity over complexity 2. Prefer simplicity over complexity
3. Use `appsettings.json` for configuration 3. Use `appsettings.json` for configuration
4. Keep consistent with existing design (Bootstrap 5 + Blazing Berry) 4. Keep consistent with existing design (Bootstrap 5 + Blazing Berry)
5. **Unified frontend:** ReceiverUI serves both Senders and Receivers 5. **Unified frontend:** WebUI serves both Senders and Receivers
6. **Render mode:** Client-side (WASM) for login/dashboard, Server-side for PDF viewers
### When debugging: ### When debugging:
1. **Coordinates:** Always check unit system (inches/points/pixels) 1. **Coordinates:** Always check unit system (inches/points/pixels)
2. **Authentication:** Check cookie name/domain/SameSite 2. **Authentication:** Check cookie name/domain/SameSite
3. **Cache:** Check Redis/SQL connection + key format 3. **Cache:** Check Redis/SQL connection + key format
4. **Frontend confusion:** Only use ReceiverUI (Web is deprecated) 4. **Frontend confusion:** Only use WebUI (ReceiverUI/Web are deprecated)
5. **Blank DxPdfViewer:** Ensure page has `@rendermode InteractiveServer`
--- ---
**Last Updated:** Session 19 (Razor file naming convention + Index route proxy) **Last Updated:** 2025-01-27 (ReceiverUI ? WebUI migration complete)

View File

@@ -0,0 +1,21 @@
<nav class="navbar header-navbar p-0">
<button class="navbar-toggler bg-primary d-block" @onclick="OnToggleClick">
<span class="navbar-toggler-icon"></span>
</button>
<div class="ms-3 fw-bold title pe-4">EnvelopeGenerator.ReceiverUI</div>
</nav>
@code {
[Parameter] public bool ToggleOn { get; set; }
[Parameter] public EventCallback<bool> ToggleOnChanged { get; set; }
async Task OnToggleClick() => await Toggle();
async Task Toggle(bool? value = null) {
var newValue = value ?? !ToggleOn;
if(ToggleOn != newValue) {
ToggleOn = newValue;
await ToggleOnChanged.InvokeAsync(ToggleOn);
}
}
}

View File

@@ -1,9 +1,27 @@
@inherits LayoutComponentBase @using EnvelopeGenerator.WebUI.Client.Services;
@inherits LayoutComponentBase
@Body <div class="page">
<main>
<div id="blazor-error-ui"> <article class="content">
An unhandled error has occurred. @Body
<a href="" class="reload">Reload</a> </article>
<a class="dismiss">🗙</a> </main>
<footer class="receiver-footer">
<span>&copy; SignFlow 2023-2024 <a href="https://digitaldata.works" target="_blank" rel="noopener">Digital Data GmbH</a></span>
<span class="receiver-footer__sep">&#124;</span>
<a href="docs/privacy-policy.de-DE.html" target="_blank" rel="noopener">Datenschutz</a>
</footer>
</div> </div>
@code {
[Inject] HttpClient Http { get; set; }
List<string> RequiredFonts = new() {
"opensans.ttf"
};
protected async override Task OnInitializedAsync() {
await FontLoader.LoadFonts(Http, RequiredFonts);
await base.OnInitializedAsync();
}
}

View File

@@ -0,0 +1,46 @@
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">EnvelopeGenerator.ReceiverUI</a>
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<nav class="flex-column">
@*
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="documentviewer">
<span class="oi oi-plus" aria-hidden="true"></span> Document Viewer (JS-Based)
</NavLink>
</div>
*@
<div class="nav-item px-3">
<NavLink class="nav-link" href="receiver">
<span class="oi oi-plus" aria-hidden="true"></span> Empfänger-UI
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="sender">
<span class="oi oi-plus" aria-hidden="true"></span> Umschlag-UI
</NavLink>
</div>
</nav>
</div>
@code {
private bool collapseNavMenu = true;
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}

View File

@@ -1,4 +1,4 @@
namespace EnvelopeGenerator.WebUI.Client.Models.Constants namespace EnvelopeGenerator.WebUI.Client.Models.Constants
{ {
public enum SenderAppType public enum SenderAppType
{ {

View File

@@ -1,4 +1,4 @@
namespace EnvelopeGenerator.WebUI.Client.Models.Constants; namespace EnvelopeGenerator.WebUI.Client.Models.Constants;
/// <summary> /// <summary>
/// Represents the unit of measurement for coordinate values in signature positioning. /// Represents the unit of measurement for coordinate values in signature positioning.
@@ -16,13 +16,13 @@ public enum UnitOfLength
/// <br/> /// <br/>
/// <b>Evidence:</b> VB.NET code directly assigns database values to annotation properties without conversion: /// <b>Evidence:</b> VB.NET code directly assigns database values to annotation properties without conversion:
/// <code> /// <code>
/// oAnnotation.Left = CSng(pElement.X) ' Direct assignment ? INCHES /// oAnnotation.Left = CSng(pElement.X) ' Direct assignment INCHES
/// oAnnotation.Top = CSng(pElement.Y) /// oAnnotation.Top = CSng(pElement.Y)
/// </code> /// </code>
/// <b>Standard Page Dimensions:</b> /// <b>Standard Page Dimensions:</b>
/// <list type="bullet"> /// <list type="bullet">
/// <item>A4: 8.27" × 11.69" (210mm × 297mm)</item> /// <item>A4: 8.27" × 11.69" (210mm × 297mm)</item>
/// <item>Letter: 8.5" × 11"</item> /// <item>Letter: 8.5" × 11"</item>
/// </list> /// </list>
/// </remarks> /// </remarks>
Inch = 0, Inch = 0,
@@ -41,18 +41,18 @@ public enum UnitOfLength
/// points = inches * 72.0 /// points = inches * 72.0
/// inches = points / 72.0 /// inches = points / 72.0
/// </code> /// </code>
/// <b>Important:</b> Point ? Pixel! /// <b>Important:</b> Point Pixel!
/// <list type="bullet"> /// <list type="bullet">
/// <item><b>Point (pt):</b> Device-independent unit (always 1/72 inch)</item> /// <item><b>Point (pt):</b> Device-independent unit (always 1/72 inch)</item>
/// <item><b>Pixel (px):</b> Device-dependent unit (varies with screen DPI)</item> /// <item><b>Pixel (px):</b> Device-dependent unit (varies with screen DPI)</item>
/// <item>At 72 DPI: 1 point = 1 pixel (coincidence)</item> /// <item>At 72 DPI: 1 point = 1 pixel (coincidence)</item>
/// <item>At 96 DPI: 1 point ? 1.33 pixels</item> /// <item>At 96 DPI: 1 point 1.33 pixels</item>
/// <item>At 300 DPI: 1 point ? 4.17 pixels</item> /// <item>At 300 DPI: 1 point 4.17 pixels</item>
/// </list> /// </list>
/// <b>Standard Page Dimensions (in points):</b> /// <b>Standard Page Dimensions (in points):</b>
/// <list type="bullet"> /// <list type="bullet">
/// <item>A4: 595 × 842 points (8.27" × 11.69" × 72)</item> /// <item>A4: 595 × 842 points (8.27" × 11.69" × 72)</item>
/// <item>Letter: 612 × 792 points (8.5" × 11" × 72)</item> /// <item>Letter: 612 × 792 points (8.5" × 11" × 72)</item>
/// </list> /// </list>
/// <b>Usage in EnvelopeGenerator:</b> /// <b>Usage in EnvelopeGenerator:</b>
/// <list type="bullet"> /// <list type="bullet">

View File

@@ -102,4 +102,4 @@ public record ReceiverClientDto
public string? Signature { get; init; } public string? Signature { get; init; }
public DateTime AddedWhen { get; init; } public DateTime AddedWhen { get; init; }
public DateTime? TfaRegDeadline { get; init; } public DateTime? TfaRegDeadline { get; init; }
} }

View File

@@ -31,6 +31,7 @@ public sealed record SignatureCaptureDto
/// Full name of the signer (first and last name). /// Full name of the signer (first and last name).
/// <br/> /// <br/>
/// <b>Required:</b> Yes (validated in popup) /// <b>Required:</b> Yes (validated in popup)
/// <br/>
/// <b>Display:</b> Bold text in applied signature block /// <b>Display:</b> Bold text in applied signature block
/// <br/> /// <br/>
/// <b>Example:</b> "Max Mustermann" /// <b>Example:</b> "Max Mustermann"

View File

@@ -1,4 +1,4 @@
using EnvelopeGenerator.WebUI.Client.Models.Constants; using EnvelopeGenerator.WebUI.Client.Models.Constants;
namespace EnvelopeGenerator.WebUI.Client.Models; namespace EnvelopeGenerator.WebUI.Client.Models;
@@ -58,8 +58,8 @@ public class SignatureDto
// LegacyFormApp uses GdPicture14 with INCHES // LegacyFormApp uses GdPicture14 with INCHES
return _unitOfLength switch return _unitOfLength switch
{ {
UnitOfLength.Inch => 1.0, // No conversion needed: INCHES ? INCHES UnitOfLength.Inch => 1.0, // No conversion needed: INCHES INCHES
UnitOfLength.Point => 72.0, // INCHES ? PDF Points: 1 inch = 72 points (PDF standard, NOT pixels!) UnitOfLength.Point => 72.0, // INCHES PDF Points: 1 inch = 72 points (PDF standard, NOT pixels!)
_ => throw new InvalidOperationException( _ => throw new InvalidOperationException(
$"Unknown UnitOfLength: {_unitOfLength}. Expected '{nameof(UnitOfLength.Inch)}' or '{nameof(UnitOfLength.Point)}'.") $"Unknown UnitOfLength: {_unitOfLength}. Expected '{nameof(UnitOfLength.Inch)}' or '{nameof(UnitOfLength.Point)}'.")
}; };
@@ -98,4 +98,4 @@ public static class SignatureDtoExtensions
return signatures; return signatures;
} }
} }

View File

@@ -2,7 +2,7 @@ namespace EnvelopeGenerator.WebUI.Client.Options;
public class ApiOptions public class ApiOptions
{ {
public const string SectionName = "ApiOptions"; public const string SectionName = "Api";
public string BaseUrl { get; set; } = string.Empty; public string BaseUrl { get; set; } = string.Empty;

View File

@@ -2,7 +2,7 @@ namespace EnvelopeGenerator.WebUI.Client.Options;
public class PdfViewerOptions public class PdfViewerOptions
{ {
public const string SectionName = "PdfViewerOptions"; public const string SectionName = "PdfViewer";
/// <summary> /// <summary>
/// Base scale for thumbnail rendering (0.2 - 1.5 recommended) /// Base scale for thumbnail rendering (0.2 - 1.5 recommended)

View File

@@ -1,7 +0,0 @@
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.

View File

@@ -1,4 +1,4 @@
using DevExpress.XtraReports.UI; using DevExpress.XtraReports.UI;
namespace EnvelopeGenerator.WebUI.Client.PredefinedReports { namespace EnvelopeGenerator.WebUI.Client.PredefinedReports {
public class Report : XtraReport { public class Report : XtraReport {
private TopMarginBand topMarginBand1; private TopMarginBand topMarginBand1;

View File

@@ -1,4 +1,4 @@
using DevExpress.XtraReports.UI; using DevExpress.XtraReports.UI;
namespace EnvelopeGenerator.WebUI.Client.PredefinedReports { namespace EnvelopeGenerator.WebUI.Client.PredefinedReports {
public static class ReportsFactory public static class ReportsFactory

View File

@@ -12,7 +12,7 @@ namespace EnvelopeGenerator.WebUI.Client.Services;
/// During development, <c>BaseUrl</c> is empty so the request resolves to the /// During development, <c>BaseUrl</c> is empty so the request resolves to the
/// YARP-proxied route on the same origin, which currently serves /// YARP-proxied route on the same origin, which currently serves
/// <c>fake-data/annotations.json</c>. To switch to real data, update the /// <c>fake-data/annotations.json</c>. To switch to real data, update the
/// YARP route in <c>yarp.json</c> no code change required. /// YARP route in <c>yarp.json</c> no code change required.
/// </summary> /// </summary>
[Obsolete("Use SignatureService.")] [Obsolete("Use SignatureService.")]
public class AnnotationService(HttpClient http, IOptions<ApiOptions> apiOptions) public class AnnotationService(HttpClient http, IOptions<ApiOptions> apiOptions)

View File

@@ -1,40 +1,39 @@
using DevExpress.DataAccess.Json; using DevExpress.DataAccess.Json;
using DevExpress.DataAccess.Web; using DevExpress.DataAccess.Web;
using DevExpress.DataAccess.Wizard.Services; using DevExpress.DataAccess.Wizard.Services;
namespace EnvelopeGenerator.WebUI.Client.Services namespace EnvelopeGenerator.WebUI.Client.Services;
public class CustomDataSourceWizardJsonDataConnectionStorage : IDataSourceWizardJsonConnectionStorage
{ {
public class CustomDataSourceWizardJsonDataConnectionStorage : IDataSourceWizardJsonConnectionStorage public static JsonDataConnection GetDefaultConnection() {
{ var uriJsonSource = new UriJsonSource() {
public static JsonDataConnection GetDefaultConnection() { Uri = new Uri(@"https://raw.githubusercontent.com/DevExpress-Examples/DataSources/master/JSON/customers.json"),
var uriJsonSource = new UriJsonSource() { };
Uri = new Uri(@"https://raw.githubusercontent.com/DevExpress-Examples/DataSources/master/JSON/customers.json"), return new JsonDataConnection(uriJsonSource) { StoreConnectionNameOnly = true, Name = "NWindProductsJson" };
};
return new JsonDataConnection(uriJsonSource) { StoreConnectionNameOnly = true, Name = "NWindProductsJson" };
}
public static List<JsonDataConnection> GetConnections() {
var connections = new List<JsonDataConnection> {
GetDefaultConnection()
};
return connections;
}
bool IJsonConnectionStorageService.CanSaveConnection => false;
bool IJsonConnectionStorageService.ContainsConnection(string connectionName) {
return GetConnections().Any(x => x.Name == connectionName);
}
IEnumerable<JsonDataConnection> IJsonConnectionStorageService.GetConnections() {
return GetConnections();
}
JsonDataConnection IJsonDataConnectionProviderService.GetJsonDataConnection(string name) {
var connection = GetConnections().FirstOrDefault(x => x.Name == name);
if(connection == null)
throw new InvalidOperationException();
return connection;
}
void IJsonConnectionStorageService.SaveConnection(string connectionName, JsonDataConnection connection, bool saveCredentials) { }
} }
} public static List<JsonDataConnection> GetConnections() {
var connections = new List<JsonDataConnection> {
GetDefaultConnection()
};
return connections;
}
bool IJsonConnectionStorageService.CanSaveConnection => false;
bool IJsonConnectionStorageService.ContainsConnection(string connectionName) {
return GetConnections().Any(x => x.Name == connectionName);
}
IEnumerable<JsonDataConnection> IJsonConnectionStorageService.GetConnections() {
return GetConnections();
}
JsonDataConnection IJsonDataConnectionProviderService.GetJsonDataConnection(string name) {
var connection = GetConnections().FirstOrDefault(x => x.Name == name);
if(connection == null)
throw new InvalidOperationException();
return connection;
}
void IJsonConnectionStorageService.SaveConnection(string connectionName, JsonDataConnection connection, bool saveCredentials) { }
}

View File

@@ -1,24 +1,23 @@
using DevExpress.DataAccess.Json; using DevExpress.DataAccess.Json;
using DevExpress.DataAccess.Web; using DevExpress.DataAccess.Web;
namespace EnvelopeGenerator.WebUI.Client.Services namespace EnvelopeGenerator.WebUI.Client.Services;
{
public class CustomJsonDataConnectionProviderFactory : IJsonDataConnectionProviderFactory {
public IJsonDataConnectionProviderService Create() {
return new WebDocumentViewerJsonDataConnectionProvider(CustomDataSourceWizardJsonDataConnectionStorage.GetConnections());
}
}
public class WebDocumentViewerJsonDataConnectionProvider : IJsonDataConnectionProviderService public class CustomJsonDataConnectionProviderFactory : IJsonDataConnectionProviderFactory {
{ public IJsonDataConnectionProviderService Create() {
readonly List<JsonDataConnection> jsonDataConnections; return new WebDocumentViewerJsonDataConnectionProvider(CustomDataSourceWizardJsonDataConnectionStorage.GetConnections());
public WebDocumentViewerJsonDataConnectionProvider(List<JsonDataConnection> jsonDataConnections) {
this.jsonDataConnections = jsonDataConnections;
}
public JsonDataConnection GetJsonDataConnection(string name) {
var connection = jsonDataConnections.FirstOrDefault(x => x.Name == name);
if(connection == null)
throw new InvalidOperationException();
return connection;
}
} }
} }
public class WebDocumentViewerJsonDataConnectionProvider : IJsonDataConnectionProviderService
{
readonly List<JsonDataConnection> jsonDataConnections;
public WebDocumentViewerJsonDataConnectionProvider(List<JsonDataConnection> jsonDataConnections) {
this.jsonDataConnections = jsonDataConnections;
}
public JsonDataConnection GetJsonDataConnection(string name) {
var connection = jsonDataConnections.FirstOrDefault(x => x.Name == name);
if(connection == null)
throw new InvalidOperationException();
return connection;
}
}

View File

@@ -1,21 +1,20 @@
using DevExpress.XtraReports.UI; using DevExpress.XtraReports.UI;
using DevExpress.XtraReports.Services; using DevExpress.XtraReports.Services;
using EnvelopeGenerator.WebUI.Client.PredefinedReports; using EnvelopeGenerator.WebUI.Client.PredefinedReports;
namespace EnvelopeGenerator.WebUI.Client.Services namespace EnvelopeGenerator.WebUI.Client.Services;
{
public class CustomReportProvider : IReportProviderAsync {
private readonly InMemoryReportStorageWebExtension reportStorage;
public CustomReportProvider(InMemoryReportStorageWebExtension reportStorage) { public class CustomReportProvider : IReportProviderAsync {
this.reportStorage = reportStorage; private readonly InMemoryReportStorageWebExtension reportStorage;
}
public Task<XtraReport> GetReportAsync(string id, ReportProviderContext context) { public CustomReportProvider(InMemoryReportStorageWebExtension reportStorage) {
if(reportStorage.TryGetReport(id, out var savedReport)) this.reportStorage = reportStorage;
return Task.FromResult(savedReport);
return Task.FromResult(ReportsFactory.GetReport(id));
}
} }
}
public Task<XtraReport> GetReportAsync(string id, ReportProviderContext context) {
if(reportStorage.TryGetReport(id, out var savedReport))
return Task.FromResult(savedReport);
return Task.FromResult(ReportsFactory.GetReport(id));
}
}

View File

@@ -1,12 +1,12 @@
using DevExpress.Drawing; using DevExpress.Drawing;
namespace EnvelopeGenerator.WebUI.Client.Services { namespace EnvelopeGenerator.WebUI.Client.Services;
public static class FontLoader {
public async static Task LoadFonts(HttpClient httpClient, List<string> fontNames) { public static class FontLoader {
foreach(var fontName in fontNames) { public async static Task LoadFonts(HttpClient httpClient, List<string> fontNames) {
var fontBytes = await httpClient.GetByteArrayAsync($"fonts/{fontName}"); foreach(var fontName in fontNames) {
DXFontRepository.Instance.AddFont(fontBytes); var fontBytes = await httpClient.GetByteArrayAsync($"fonts/{fontName}");
} DXFontRepository.Instance.AddFont(fontBytes);
} }
} }
} }

View File

@@ -1,9 +1,9 @@
using DevExpress.DataAccess.Web; using DevExpress.DataAccess.Web;
namespace EnvelopeGenerator.WebUI.Client.Services { namespace EnvelopeGenerator.WebUI.Client.Services;
public class ObjectDataSourceWizardCustomTypeProvider : IObjectDataSourceWizardTypeProvider {
public IEnumerable<Type> GetAvailableTypes(string context) { public class ObjectDataSourceWizardCustomTypeProvider : IObjectDataSourceWizardTypeProvider {
return new[] { typeof(Data.DataItemList) }; public IEnumerable<Type> GetAvailableTypes(string context) {
} return new[] { typeof(Data.DataItemList) };
} }
} }

View File

@@ -5,13 +5,18 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" /> <base href="/" />
<link rel="stylesheet" href="app.css" /> <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="css/app.css" />
<link rel="stylesheet" href="css/envelope-viewer.css" />
<link rel="stylesheet" href="EnvelopeGenerator.WebUI.styles.css" /> <link rel="stylesheet" href="EnvelopeGenerator.WebUI.styles.css" />
<HeadOutlet @rendermode="InteractiveAuto" /> <HeadOutlet @rendermode="InteractiveAuto" />
</head> </head>
<body> <body>
<Routes @rendermode="InteractiveAuto" /> <Routes @rendermode="InteractiveAuto" />
<script src="_content/DevExpress.Blazor.Resources/js/preload-script.js"></script>
<script src="js/typed.umd.js"></script>
<script src="js/receiver-signature.js?v=9"></script>
<script src="_framework/blazor.web.js"></script> <script src="_framework/blazor.web.js"></script>
</body> </body>

View File

@@ -16,6 +16,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Update="wwwroot\docs\privacy-policy.en-US.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\docs\privacy-policy.fr-FR.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="yarp.json"> <Content Update="yarp.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>

View File

@@ -11,12 +11,19 @@ builder.Services.AddRazorComponents()
.AddInteractiveServerComponents() .AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents(); .AddInteractiveWebAssemblyComponents();
// HttpClient for server-side components (e.g., MainLayout with FontLoader)
builder.Services.AddScoped<HttpClient>(sp => {
var httpClientFactory = sp.GetRequiredService<IHttpClientFactory>();
return httpClientFactory.CreateClient();
});
builder.Services.AddHttpClient();
// YARP Reverse Proxy // YARP Reverse Proxy
builder.Services.AddReverseProxy() builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
// DevExpress Server-Side Services (CRITICAL for DxPdfViewer) // DevExpress Server-Side Services (CRITICAL for DxPdfViewer)
builder.Services.AddDevExpressBlazor(configure => configure.BootstrapVersion = BootstrapVersion.v5); builder.Services.AddDevExpressBlazor();
builder.Services.AddDevExpressServerSideBlazorPdfViewer(); builder.Services.AddDevExpressServerSideBlazorPdfViewer();
// Configuration Options // Configuration Options

View File

@@ -1,29 +0,0 @@
h1:focus {
outline: none;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid #e50000;
}
.validation-message {
color: #e50000;
}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}
.darker-border-checkbox.form-check-input {
border-color: #929292;
}

View File

@@ -1,8 +1,9 @@
{ {
"ApiOptions": { "Api": {
"BaseUrl": "" "BaseUrl": "",
"UsePredefinedReports": false
}, },
"PdfViewerOptions": { "PdfViewer": {
"ThumbnailBaseScale": 0.75, "ThumbnailBaseScale": 0.75,
"ThumbnailEnableHiDPI": true, "ThumbnailEnableHiDPI": true,
"ThumbnailMaxDPR": 2.0, "ThumbnailMaxDPR": 2.0,

View File

@@ -0,0 +1,167 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Datenschutzinformation für das Fernsignatursystem: signFLOW</title>
<link rel="stylesheet" href="../css/privacy-policy.min.css">
</head>
<body>
<header>
<h1>Datenschutzinformation für das Fernsignatursystem signFLOW</h1>
<p><strong>Stand:</strong> 18.11.2025</p>
</header>
<section>
<h2>1. Allgemeine Informationen</h2>
<p>In der heutigen schnelllebigen und zunehmend digitalen Welt, sind personenbezogene Daten eine wichtige
Ressource. Ihre Daten sind wertvoll und müssen daher mit der durch diverse Gesetze und Vorschriften (DSGVO,
TDDDG, ...) gebotenen Sorgfalt behandelt werden.</p>
<p>Als Anbieter von lokalen Lösungen (OnPremise) setzt der Hersteller des signFLOWs, die Digital Data GmbH,
einen klaren Schwerpunkt auf Datenschutz und Datensicherheit. Für Sie bedeutet dies, dass nur die nötigsten
Daten erhoben und gespeichert werden (Datensparsamkeit bzw. Datenminimierung). Außerdem kommen bei der
Verarbeitung aktuelle und als sicher geltende Technologien zum Einsatz.</p>
<p><strong>Kontaktdaten des Herstellers:</strong></p>
<address>
Digital Data GmbH<br>
Ludwig-Rinn-Straße 16<br>
35452 Heuchelheim<br>
<a href="https://digitaldata.works">https://digitaldata.works</a><br>
<a href="mailto:info-flow@digitaldata.works">info-flow@digitaldata.works</a><br>
Telefon: 0049 641 202360<br>
</address>
<p><strong>Kontakt zum Datenschutzbeauftragten:</strong> <a
href="mailto:privacy-flow@digitaldata.works">privacy-flow@digitaldata.works</a></p>
</section>
<section>
<h2>2. Verantwortliche Stelle der Datenverarbeitung</h2>
<p>Ihre Daten werden vertrauensvoll verarbeitet von:</p>
<address>
Digital Data GmbH<br>
Ludwig-Rinn-Straße 16<br>
35452 Heuchelheim<br>
<a href="https://digitaldata.works">https://digitaldata.works</a><br>
<a href="mailto:info-flow@digitaldata.works">info-flow@digitaldata.works</a><br>
Telefon: 0049 641 202360<br>
</address>
<p><strong>Kontakt zu unserer Datenschutzbeauftragten:</strong> <a
href="mailto:privacy-flow@digitaldata.works">privacy-flow@digitaldata.works</a></p>
</section>
<section>
<h2>3. Datenerhebung</h2>
<h3>3.1 Die folgenden Kategorien personenbezogener Daten werden verarbeitet</h3>
<ul>
<li>Namen: Benutzername, Vor- und Zunamen sowie Ihre digitale Unterschrift</li>
<li>Kontaktdaten: Telefonnummer, Mobilfunknummer und E-Mail-Adresse</li>
<li>Technische Daten: IP-Adresse, Zeitpunkt des Zugriffs oder Zugriffsversuchs</li>
</ul>
<h3>3.2 Ursprung der personenbezogenen Daten</h3>
<p>Sie haben die unter 3.1 stehenden Daten vorab an Ihren Geschäftspartner (die verantwortliche Stelle)
übermittelt. Diese Übermittlung kann mündlich per Telefon, im persönlichen Kontakt, per E-Mail oder per
Kontaktformular geschehen sein.</p>
<p>Ihre digitale Signatur übermitteln Sie eigenständig, bei der Unterzeichnung eines Dokuments.</p>
<h3>3.3 Aufbewahrungsfristen / Speicherungsdauer</h3>
<ul>
<li>Die automatische E-Mail Korrespondenz wird für 6 Jahre aufbewahrt.</li>
<li>Unterzeichnete Verträge werden für die Dauer ihrer Laufzeit + 10 Jahre aufbewahrt.</li>
<li>Der technische Vorgang wird in der signFLOW Softwarelösung je nach Dokumentart bzw. Vertragsart
unbegrenzt aufbewahrt.</li>
</ul>
<p>Ihre personenbezogenen Daten werden aber grundsätzlich anonymisiert, wenn:</p>
<ul>
<li>Der Vertrag ausgelaufen ist und die gesetzliche Aufbewahrungszeit vorbei ist.</li>
<li>Der Vertrag von Ihnen abgelehnt wurde bzw. nie unterschrieben wurde.</li>
</ul>
<p>Die gesetzlichen Grundlagen dieser Aufbewahrungsfristen bieten unter anderen:</p>
<ul>
<li>Handelsgesetzbuch (HGB)</li>
<li>Abgabenordnung (AO)</li>
<li>Grundsätze zur ordnungsgemäßen Führung und Aufbewahrung von Büchern, Aufzeichnungen und Unterlagen in
elektronischer Form sowie zum Datenzugriff (GoBD)</li>
</ul>
<p>
Abhängig von der spezifischen Dokumentart, kann die Aufbewahrungsfrist variieren. Außerdem können sich
Zeiten
verlängern, falls Unregelmäßigkeiten auftreten. Z.B.: eine rechtliche Auseinandersetzung droht oder aktuell
läuft.
</p>
<h3>3.4 Verarbeitungszweck</h3>
<p>Die unter 3.1 definierten personenbezogenen Daten werden verarbeitet um:</p>
<ul>
<li>Den technisch notwendigen Prozess abzubilden bzw. anbieten zu können.</li>
<li>Damit Sie als Endbenutzer in der Lage sind, ein zu unterzeichnendes Dokument, digital zu unterschreiben,
ist die Feststellung der Identität des Antragstellers, Antragsprüfung und -abwicklung, Abrechnung sowie
die Wahrung der Dokumentationspflichten notwendig.</li>
</ul>
<p>In Einzelfällen werden Daten zur Fehlerbehebung insbesondere bei Supportanfragen von der IT-Abteilung
gesondert behandelt oder ggf. an den Hersteller weitergegeben.</p>
<p>Im Zuge der Maßnahmen zur Sicherstellung der Informationssicherheit, insbesondere zur Identifizierung und
Abwehr von Angriffen, sowie zur Durchführung interner und externer Audits, der Exportkontrolle und der
Prüfung von Sanktionslisten, erfolgt ebenfalls eine Datenverarbeitung. Bei Anfragen gemäß §8 Abs. 2 VDG
werden die entsprechenden Informationen an die zuständigen Stellen übermittelt.</p>
<h3>3.5 Rechtmäßigkeit der Verarbeitung</h3>
<p>Ihre Daten werden auf Grundlage einer sich anbahnenden oder bereits vorhandenen Geschäftsbeziehung erhoben.
</p>
<p>Die rechtliche Grundlage für die Übermittlung an zuständige Stellen bildet §8 Abs. 2 VDG. Anfragen von
Betroffenen werden gemäß den Artikeln 12 bis 23 der DSGVO sowie den Paragraphen 32 bis 37 des BDSG
bearbeitet.</p>
<h3>3.6 Berechtigte Interessen</h3>
<p>Ein berechtigtes Interesse der verantwortlichen Stelle gemäß Art. 6 Abs. 1 lit. f DSGVO besteht in den
folgenden Fällen:</p>
<p>Es werden Maßnahmen zur Informationssicherheit ergriffen, die sowohl präventive technische als auch
organisatorische Maßnahmen sowie die Behandlung von Vorfällen umfassen. Ziel ist es, potenzielle Schäden für
das Unternehmen, die von der Datenverarbeitung betroffenen Personen und die Nutzer der Vertrauensdienste zu
bewerten und zu vermeiden.</p>
<h3>3.7 Erforderlichkeit der Daten</h3>
<p>Die erhobenen Daten stellen das Minimum der nötigen Daten zwecks digitaler Unterschrift dar. Ohne die unter
3.1 genannten Daten, kann der Dienst nicht betrieben werden.</p>
<p>Besonders wichtig ist die Angabe einer Mobilfunknummer oder einer deutschen Festnetznummer, da diese für die
Authentifizierung und die Signaturauslösung als zweiter Faktor verwendet wird. Ohne diesen
Sicherheitsmechanismus kann der Dienst leider nicht bereitgestellt werden.</p>
<h3>3.8 Weitergabe von Daten</h3>
<p>Eine systemische Übermittlung von Daten findet nicht statt.</p>
<p>Daten werden nur in Ausnahmefällen, zwecks Supportleistungen, an den Hersteller weitergegeben. Mit der
Hersteller besteht ein gültiger Auftragsdatenverarbeitungsvertrag (AVV), welcher die Sicherheit und
Integrität des Umgangs mit Ihren Daten gewährleistet.</p>
</section>
<section>
<h2>4. Verwendung von Cookies</h2>
<p>
Beim Besuch bestimmter Seiten kommen temporäre Cookies zum Einsatz, die für die technische Bereitstellung
der Dienste notwendig sind. Diese sogenannten Session-Cookies enthalten keine personenbezogenen Daten und
werden nach Beendigung der Sitzung automatisch gelöscht. Methoden wie Java-Applets oder Active-X-Controls,
die das Nutzerverhalten nachvollziehbar machen könnten, werden nicht verwendet.
</p>
</section>
<section>
<h2>5. Betroffenenrechte</h2>
<p>
Wenn Sie Fragen zu Ihren Daten haben oder eine Berichtigung, Löschung oder Einschränkung der Verarbeitung
wünschen, senden Sie bitte Ihre Anfrage per Post oder E-Mail an die oben angegebene Adresse. Dies gilt auch,
wenn Sie gemäß Art. 21 DSGVO Widerspruch gegen die Verarbeitung einlegen möchten oder eine Anfrage zur
Datenübertragbarkeit haben.
</p>
<p>
Bei Fragen oder Beschwerden zu einem Verfahren können Sie sich ebenfalls über die genannten
Kontaktmöglichkeiten an uns wenden. Sollten Sie darüber hinaus einen Grund zur Beschwerde haben, haben Sie
die Möglichkeit, sich an unsere Aufsichtsbehörde zu wenden. Welche Aufsichtsbehörde für Sie zuständig ist,
erfahren Sie hier:
<a href="https://www.bfdi.bund.de/DE/Service/Anschriften/Laender/Laender-node.html">Laender-node.html</a>
</p>
</section>
</body>
</html>

View File

@@ -0,0 +1,158 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data Protection Information for the Remote Signature System signFLOW</title>
<link rel="stylesheet" href="css/privacy-policy.min.css">
</head>
<body>
<header>
<h1>Data Protection Information for the Remote Signature System: signFLOW</h1>
<p><strong>As of:</strong> 18.11.2025</p>
</header>
<section>
<h2>1. General Information</h2>
<p>In today's fast-paced and increasingly digital world, personal data is an important resource. Your data is
valuable and must therefore be handled with the care required by various laws and regulations (GDPR, TDDDG,
...).</p>
<p>As a provider of local solutions (OnPremise), the manufacturer of signFLOW, Digital Data GmbH, places a clear
focus on data protection and data security. For you, this means that only the necessary data is collected
and stored (data minimization). Furthermore, current and secure technologies are used in processing.</p>
<p><strong>Contact details of the manufacturer:</strong></p>
<address>
Digital Data GmbH<br>
Ludwig-Rinn-Straße 16<br>
35452 Heuchelheim<br>
<a href="https://digitaldata.works">https://digitaldata.works</a><br>
<a href="mailto:info-flow@digitaldata.works">info-flow@digitaldata.works</a><br>
Phone: 0049 641 202360<br>
</address>
<p><strong>Contact the Data Protection Officer:</strong> <a
href="mailto:privacy-flow@digitaldata.works">privacy-flow@digitaldata.works</a></p>
</section>
<section>
<h2>2. Responsible Entity for Data Processing</h2>
<p>Your data is processed with confidence by:</p>
<address>
Digital Data GmbH<br>
Ludwig-Rinn-Straße 16<br>
35452 Heuchelheim<br>
<a href="https://digitaldata.works">https://digitaldata.works</a><br>
<a href="mailto:info-flow@digitaldata.works">info-flow@digitaldata.works</a><br>
Phone: 0049 641 202360<br>
</address>
<p><strong>Contact our Data Protection Officer:</strong> <a
href="mailto:privacy-flow@digitaldata.works">privacy-flow@digitaldata.works</a></p>
</section>
<section>
<h2>3. Data Collection</h2>
<h3>3.1 The following categories of personal data are processed</h3>
<ul>
<li>Names: Username, first and last names as well as your digital signature</li>
<li>Contact details: Phone number, mobile phone number, and email address</li>
<li>Technical data: IP address, time of access, or access attempts</li>
</ul>
<h3>3.2 Source of the personal data</h3>
<p>You have previously provided the data mentioned under 3.1 to your business partner (the responsible entity).
This transmission may have occurred verbally over the phone, in personal contact, via email, or via a
contact form.</p>
<p>You transmit your digital signature independently when signing a document.</p>
<h3>3.3 Retention periods / Storage duration</h3>
<ul>
<li>Automatic email correspondence is stored for 6 years.</li>
<li>Signed contracts are retained for the duration of their term + 10 years.</li>
<li>The technical process is stored in the signFLOW software solution indefinitely, depending on the
document or contract type.</li>
</ul>
<p>Your personal data will generally be anonymized when:</p>
<ul>
<li>The contract has expired, and the statutory retention period is over.</li>
<li>The contract was rejected by you or never signed.</li>
</ul>
<p>The legal basis for these retention periods includes:</p>
<ul>
<li>Commercial Code (HGB)</li>
<li>Tax Code (AO)</li>
<li>Principles for the Proper Keeping and Retention of Books, Records, and Documents in Electronic Form and
for Data Access (GoBD)</li>
</ul>
<p>
Depending on the specific type of document, the retention period may vary. Additionally, the periods may be
extended in case of irregularities, such as a pending or ongoing legal dispute.
</p>
<h3>3.4 Purpose of processing</h3>
<p>The personal data defined under 3.1 is processed to:</p>
<ul>
<li>Support or provide the technically necessary process.</li>
<li>Enable you, as the end user, to sign a document digitally. This requires the identification of the
applicant, application verification and processing, billing, and compliance with documentation
requirements.</li>
</ul>
<p>In individual cases, data is processed separately by the IT department, particularly in response to support
requests, or possibly forwarded to the manufacturer for further processing.</p>
<p>Data processing also occurs to ensure information security, especially for the identification and prevention
of attacks, and for conducting internal and external audits, export controls, and sanctions list checks.
Information may also be transmitted to the relevant authorities in accordance with Section 8 (2) VDG.</p>
<h3>3.5 Legality of processing</h3>
<p>Your data is collected based on an impending or already existing business relationship.</p>
<p>The legal basis for the transmission to competent authorities is Section 8 (2) VDG. Requests from data
subjects are processed in accordance with Articles 12 to 23 of the GDPR and Sections 32 to 37 of the Federal
Data Protection Act (BDSG).</p>
<h3>3.6 Legitimate interests</h3>
<p>A legitimate interest of the responsible entity in accordance with Article 6 (1) (f) GDPR exists in the
following cases:</p>
<p>Measures are taken for information security, which include both preventive technical and organizational
measures as well as incident handling. The aim is to assess and avoid potential harm to the company, the
individuals affected by data processing, and the users of trust services.</p>
<h3>3.7 Necessity of data</h3>
<p>The collected data represents the minimum necessary for the digital signature. Without the data mentioned
under 3.1, the service cannot be operated.</p>
<p>It is particularly important to provide a mobile number or a German landline number, as this is used for
authentication and signature triggering as a second factor. Without this security mechanism, the service
cannot be provided.</p>
<h3>3.8 Data transfer</h3>
<p>Systematic data transmission does not take place.</p>
<p>Data is only forwarded to the manufacturer for support services in exceptional cases. A valid data processing
agreement (DPA) exists with the manufacturer, which ensures the security and integrity of the handling of
your data.</p>
</section>
<section>
<h2>4. Use of Cookies</h2>
<p>
When visiting certain pages, temporary cookies are used, which are necessary for the technical provision of
the services. These so-called session cookies do not contain any personal data and are automatically deleted
after the session ends. Methods such as Java applets or Active-X controls that could track user behavior are
not used.
</p>
</section>
<section>
<h2>5. Rights of Affected Persons</h2>
<p>
If you have questions about your data or wish to request correction, deletion, or restriction of processing,
please send your request by mail or email to the address provided above. This also applies if you wish to
object to the processing in accordance with Article 21 GDPR or request data portability.
</p>
<p>
If you have questions or complaints about a procedure, you can also contact us using the contact details
provided. If you have further grounds for complaint, you can contact our supervisory authority. You can find
out which supervisory authority is responsible for you here:
<a href="https://www.bfdi.bund.de/DE/Service/Anschriften/Laender/Laender-node.html">Laender-node.html</a>
</p>
</section>
</body>
</html>

View File

@@ -0,0 +1,126 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Informations sur la protection des données pour le système de signature à distance signFLOW</title>
<link rel="stylesheet" href="css/privacy-policy.min.css">
</head>
<body>
<header>
<h1>Informations sur la protection des données pour le système de signature à distance : signFLOW</h1>
<p><strong>À jour au :</strong> 18.11.2025</p>
</header>
<section>
<h2>1. Informations générales</h2>
<p>Dans le monde actuel, rapide et de plus en plus numérique, les données personnelles sont une ressource précieuse. Vos données sont importantes et doivent donc être traitées avec le soin requis par les différentes lois et réglementations (RGPD, TDDDG, ...).</p>
<p>En tant que fournisseur de solutions locales (OnPremise), le fabricant de signFLOW, Digital Data GmbH, accorde une attention particulière à la protection et à la sécurité des données. Pour vous, cela signifie que seules les données nécessaires sont collectées et stockées (minimisation des données). De plus, des technologies actuelles et sécurisées sont utilisées lors du traitement.</p>
<p><strong>Coordonnées du fabricant :</strong></p>
<address>
Digital Data GmbH<br>
Ludwig-Rinn-Straße 16<br>
35452 Heuchelheim<br>
<a href="https://digitaldata.works">https://digitaldata.works</a><br>
<a href="mailto:info-flow@digitaldata.works">info-flow@digitaldata.works</a><br>
Téléphone : 0049 641 202360<br>
</address>
<p><strong>Contactez le délégué à la protection des données :</strong> <a href="mailto:privacy-flow@digitaldata.works">privacy-flow@digitaldata.works</a></p>
</section>
<section>
<h2>2. Responsable du traitement des données</h2>
<p>Vos données sont traitées en toute confiance par :</p>
<address>
Digital Data GmbH<br>
Ludwig-Rinn-Straße 16<br>
35452 Heuchelheim<br>
<a href="https://digitaldata.works">https://digitaldata.works</a><br>
<a href="mailto:info-flow@digitaldata.works">info-flow@digitaldata.works</a><br>
Téléphone : 0049 641 202360<br>
</address>
<p><strong>Contactez notre délégué à la protection des données :</strong> <a href="mailto:privacy-flow@digitaldata.works">privacy-flow@digitaldata.works</a></p>
</section>
<section>
<h2>3. Collecte des données</h2>
<h3>3.1 Catégories de données personnelles traitées</h3>
<ul>
<li>Noms : nom dutilisateur, prénom et nom ainsi que votre signature numérique</li>
<li>Coordonnées : numéro de téléphone, numéro de mobile et adresse e-mail</li>
<li>Données techniques : adresse IP, heure daccès ou tentatives daccès</li>
</ul>
<h3>3.2 Source des données personnelles</h3>
<p>Vous avez précédemment fourni les données mentionnées en 3.1 à votre partenaire commercial (le responsable du traitement). Cette transmission a pu se faire verbalement par téléphone, lors dun contact personnel, par e-mail ou via un formulaire de contact.</p>
<p>Vous transmettez votre signature numérique de manière autonome lors de la signature dun document.</p>
<h3>3.3 Durée de conservation / stockage</h3>
<ul>
<li>La correspondance par e-mail automatique est conservée pendant 6 ans.</li>
<li>Les contrats signés sont conservés pendant leur durée + 10 ans.</li>
<li>Le processus technique est stocké dans la solution logicielle signFLOW indéfiniment, selon le type de document ou de contrat.</li>
</ul>
<p>Vos données personnelles seront généralement anonymisées lorsque :</p>
<ul>
<li>Le contrat a expiré et la période légale de conservation est terminée.</li>
<li>Le contrat a été refusé par vous ou jamais signé.</li>
</ul>
<p>La base légale de ces périodes de conservation comprend :</p>
<ul>
<li>Code de commerce (HGB)</li>
<li>Code fiscal (AO)</li>
<li>Principes de tenue correcte et de conservation des livres, registres et documents sous forme électronique et daccès aux données (GoBD)</li>
</ul>
<p>
Selon le type spécifique de document, la période de conservation peut varier. De plus, ces périodes peuvent être prolongées en cas dirrégularités, telles quun litige en cours ou imminent.
</p>
<h3>3.4 Finalité du traitement</h3>
<p>Les données personnelles définies en 3.1 sont traitées pour :</p>
<ul>
<li>Supporter ou fournir le processus techniquement nécessaire.</li>
<li>Permettre à lutilisateur final de signer un document numériquement. Cela nécessite lidentification du demandeur, la vérification et le traitement de la demande, la facturation et le respect des obligations documentaires.</li>
</ul>
<p>Dans certains cas, les données sont traitées séparément par le département informatique, notamment en réponse à des demandes de support, ou éventuellement transmises au fabricant pour un traitement supplémentaire.</p>
<p>Le traitement des données intervient également pour garantir la sécurité de linformation, notamment pour identifier et prévenir les attaques, et pour effectuer des audits internes et externes, le contrôle des exportations et les vérifications des listes de sanctions. Les informations peuvent également être transmises aux autorités compétentes conformément à larticle 8 (2) VDG.</p>
<h3>3.5 Licéité du traitement</h3>
<p>Vos données sont collectées sur la base dune relation commerciale existante ou imminente.</p>
<p>La base légale pour la transmission aux autorités compétentes est larticle 8 (2) VDG. Les demandes des personnes concernées sont traitées conformément aux articles 12 à 23 du RGPD et aux articles 32 à 37 de la loi fédérale sur la protection des données (BDSG).</p>
<h3>3.6 Intérêts légitimes</h3>
<p>Un intérêt légitime du responsable du traitement conformément à larticle 6 (1) (f) RGPD existe dans les cas suivants :</p>
<p>Des mesures sont prises pour la sécurité de linformation, incluant à la fois des mesures techniques et organisationnelles préventives ainsi que la gestion des incidents. Lobjectif est dévaluer et déviter les dommages potentiels pour lentreprise, les personnes concernées par le traitement des données et les utilisateurs des services de confiance.</p>
<h3>3.7 Nécessité des données</h3>
<p>Les données collectées représentent le minimum nécessaire pour la signature numérique. Sans les données mentionnées en 3.1, le service ne peut pas être opéré.</p>
<p>Il est particulièrement important de fournir un numéro de mobile ou un numéro fixe allemand, car cela est utilisé pour lauthentification et le déclenchement de la signature comme second facteur. Sans ce mécanisme de sécurité, le service ne peut être fourni.</p>
<h3>3.8 Transfert de données</h3>
<p>Aucun transfert systématique de données na lieu.</p>
<p>Les données ne sont transmises au fabricant pour le support que dans des cas exceptionnels. Un accord de traitement des données (DPA) valide existe avec le fabricant, garantissant la sécurité et lintégrité de la gestion de vos données.</p>
</section>
<section>
<h2>4. Utilisation des cookies</h2>
<p>
Lors de la visite de certaines pages, des cookies temporaires sont utilisés, nécessaires à la fourniture technique des services. Ces cookies de session ne contiennent aucune donnée personnelle et sont automatiquement supprimés à la fin de la session. Aucune méthode telle que les applets Java ou les contrôles Active-X, pouvant suivre le comportement de lutilisateur, nest utilisée.
</p>
</section>
<section>
<h2>5. Droits des personnes concernées</h2>
<p>
Si vous avez des questions concernant vos données ou souhaitez demander une correction, suppression ou limitation du traitement, veuillez envoyer votre demande par courrier ou e-mail à ladresse indiquée ci-dessus. Cela sapplique également si vous souhaitez vous opposer au traitement conformément à larticle 21 RGPD ou demander la portabilité des données.
</p>
<p>
Si vous avez des questions ou des plaintes concernant une procédure, vous pouvez également nous contacter via les coordonnées fournies. Si vous avez dautres motifs de plainte, vous pouvez contacter notre autorité de contrôle. Vous pouvez vérifier quelle autorité de contrôle est compétente pour vous ici :
<a href="https://www.bfdi.bund.de/DE/Service/Anschriften/Laender/Laender-node.html">Laender-node.html</a>
</p>
</section>
</body>
</html>

View File

@@ -0,0 +1,20 @@
[
{
"id": 1,
"page": 1,
"x": 390.0,
"y": 380.0
},
{
"id": 2,
"page": 2,
"x": 390.0,
"y": 680.0
},
{
"id": 3,
"page": 3,
"x": 390.0,
"y": 980.0
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="_x31_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 62 62" style="enable-background:new 0 0 62 62;" xml:space="preserve">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;}
</style>
<path id="_x32_" class="st0" d="M31,61C14.4,61,1,47.6,1,31S14.4,1,31,1s30,13.4,30,30S47.6,61,31,61z M31,5C16.6,5,5,16.6,5,31
s11.6,26,26,26s26-11.6,26-26S45.4,5,31,5z M52,34.5L49.5,37L46,33.5L42.5,37L40,34.5l3.5-3.5L40,27.5l2.5-2.5l3.5,3.5l3.5-3.5
l2.5,2.5L48.5,31L52,34.5z M43,44c0,1.1-0.9,2-2,2H21c-1.1,0-2-0.9-2-2s0.9-2,2-2h20C42.1,42,43,42.9,43,44z M19.5,37L16,33.5
L12.5,37L10,34.5l3.5-3.5L10,27.5l2.5-2.5l3.5,3.5l3.5-3.5l2.5,2.5L18.5,31l3.5,3.5L19.5,37z"/>
</svg>

After

Width:  |  Height:  |  Size: 893 B

View File

@@ -481,7 +481,7 @@ window.pdfViewer = {
// Signature button functionality // Signature button functionality
signatureButtons: [], signatureButtons: [],
appliedSignatures: [], // Track which signatures have been applied (ID list) appliedSignatures: [], // Track which signatures have been applied (ID list)
appliedSignatureElements: [], // ? NEW: Track applied signature DOM elements appliedSignatureElements: [], // NEW: Track applied signature DOM elements
_lastViewedSignatureId: null, // Track last viewed signature for navigation _lastViewedSignatureId: null, // Track last viewed signature for navigation
/** /**
@@ -743,7 +743,7 @@ window.pdfViewer = {
// CRITICAL: Filter OUT already applied signatures! // CRITICAL: Filter OUT already applied signatures!
const appliedIds = new Set(this.appliedSignatures.map(s => s.id)); const appliedIds = new Set(this.appliedSignatures.map(s => s.id));
const pageSignatures = signatures.filter(sig => const pageSignatures = signatures.filter(sig =>
sig.page === currentPageNum && !appliedIds.has(sig.id) // ?? Skip applied ones! sig.page === currentPageNum && !appliedIds.has(sig.id) // ? Skip applied ones!
); );
if (pageSignatures.length === 0) { if (pageSignatures.length === 0) {
@@ -777,7 +777,7 @@ window.pdfViewer = {
const xPx = (sig.x * this.scale); const xPx = (sig.x * this.scale);
const yPx = (sig.y * this.scale); const yPx = (sig.y * this.scale);
// ? FIXED: Scale button size proportionally with zoom // FIXED: Scale button size proportionally with zoom
const baseScale = 1.5; // Reference scale (initial load) const baseScale = 1.5; // Reference scale (initial load)
const scaleFactor = this.scale / baseScale; const scaleFactor = this.scale / baseScale;
const baseWidth = 150; const baseWidth = 150;
@@ -799,8 +799,8 @@ window.pdfViewer = {
button.style.position = 'absolute'; button.style.position = 'absolute';
button.style.left = `${xPx}px`; button.style.left = `${xPx}px`;
button.style.top = `${yPx}px`; button.style.top = `${yPx}px`;
button.style.width = `${scaledWidth}px`; // ? Scaled button.style.width = `${scaledWidth}px`; // Scaled
button.style.height = `${scaledHeight}px`; // ? Scaled button.style.height = `${scaledHeight}px`; // Scaled
button.style.backgroundColor = '#4F46E5'; button.style.backgroundColor = '#4F46E5';
button.style.color = 'white'; button.style.color = 'white';
button.style.border = 'none'; button.style.border = 'none';
@@ -820,14 +820,14 @@ window.pdfViewer = {
// Add text // Add text
const textDiv = document.createElement('div'); const textDiv = document.createElement('div');
textDiv.textContent = 'Unterschreiben'; textDiv.textContent = 'Unterschreiben';
textDiv.style.fontSize = `${scaledFontSize}px`; // ? Scaled textDiv.style.fontSize = `${scaledFontSize}px`; // Scaled
textDiv.style.fontWeight = '700'; textDiv.style.fontWeight = '700';
// Add SVG icon // Add SVG icon
const svgNS = 'http://www.w3.org/2000/svg'; const svgNS = 'http://www.w3.org/2000/svg';
const svg = document.createElementNS(svgNS, 'svg'); const svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('width', scaledIconSize); // ? Scaled svg.setAttribute('width', scaledIconSize); // Scaled
svg.setAttribute('height', scaledIconSize); // ? Scaled svg.setAttribute('height', scaledIconSize); // Scaled
svg.setAttribute('viewBox', '0 8 32 36'); svg.setAttribute('viewBox', '0 8 32 36');
svg.setAttribute('fill', 'none'); svg.setAttribute('fill', 'none');
svg.style.filter = 'drop-shadow(0 1px 2px rgba(0,0,0,0.2))'; svg.style.filter = 'drop-shadow(0 1px 2px rgba(0,0,0,0.2))';
@@ -929,14 +929,14 @@ window.pdfViewer = {
const signature = this._allSignatures.find(s => s.id === signatureId); const signature = this._allSignatures.find(s => s.id === signatureId);
if (signature) { if (signature) {
// ? Position calculation (same as renderSignatureButtons) // Position calculation (same as renderSignatureButtons)
const xPx = signature.x * this.scale; const xPx = signature.x * this.scale;
const yPx = signature.y * this.scale; const yPx = signature.y * this.scale;
container.style.left = `${xPx}px`; container.style.left = `${xPx}px`;
container.style.top = `${yPx}px`; container.style.top = `${yPx}px`;
// ? FIXED: Apply comprehensive scaling using helper method // FIXED: Apply comprehensive scaling using helper method
this.scaleAppliedSignature(container, this.scale); this.scaleAppliedSignature(container, this.scale);
// Show/hide based on current page // Show/hide based on current page
@@ -958,7 +958,7 @@ window.pdfViewer = {
}); });
this.signatureButtons = []; this.signatureButtons = [];
// ? FIXED: Update applied signatures (position + scaling) // FIXED: Update applied signatures (position + scaling)
this.appliedSignatureElements.forEach(container => { this.appliedSignatureElements.forEach(container => {
const signatureId = parseInt(container.getAttribute('data-signature-id')); const signatureId = parseInt(container.getAttribute('data-signature-id'));
const signature = this._allSignatures?.find(s => s.id === signatureId); const signature = this._allSignatures?.find(s => s.id === signatureId);
@@ -1034,7 +1034,7 @@ window.pdfViewer = {
signatureContainer.className = 'applied-signature'; signatureContainer.className = 'applied-signature';
signatureContainer.setAttribute('data-signature-id', signatureId); signatureContainer.setAttribute('data-signature-id', signatureId);
// ? FIXED: Store base values for scaling // FIXED: Store base values for scaling
signatureContainer.setAttribute('data-base-width', '230'); signatureContainer.setAttribute('data-base-width', '230');
signatureContainer.setAttribute('data-base-padding', '12'); signatureContainer.setAttribute('data-base-padding', '12');
signatureContainer.setAttribute('data-base-font-size', '9'); signatureContainer.setAttribute('data-base-font-size', '9');
@@ -1072,8 +1072,8 @@ window.pdfViewer = {
// Text information container // Text information container
const infoContainer = document.createElement('div'); const infoContainer = document.createElement('div');
infoContainer.className = 'signature-info-text'; // ? Add class (for querySelector) infoContainer.className = 'signature-info-text'; // Add class (for querySelector)
infoContainer.setAttribute('data-base-font-size', '9'); // ? Store base font size infoContainer.setAttribute('data-base-font-size', '9'); // Store base font size
infoContainer.style.fontSize = '9px'; infoContainer.style.fontSize = '9px';
infoContainer.style.lineHeight = '1.4'; infoContainer.style.lineHeight = '1.4';
infoContainer.style.color = '#495057'; infoContainer.style.color = '#495057';
@@ -1107,10 +1107,10 @@ window.pdfViewer = {
// Add to signature layer // Add to signature layer
signatureLayer.appendChild(signatureContainer); signatureLayer.appendChild(signatureContainer);
// ? FIXED: Track applied signature element for zoom updates // FIXED: Track applied signature element for zoom updates
this.appliedSignatureElements.push(signatureContainer); this.appliedSignatureElements.push(signatureContainer);
// ? FIXED: Apply initial scaling based on current zoom // FIXED: Apply initial scaling based on current zoom
this.scaleAppliedSignature(signatureContainer, this.scale); this.scaleAppliedSignature(signatureContainer, this.scale);
console.log(`Signature #${signatureId} applied successfully`); console.log(`Signature #${signatureId} applied successfully`);
@@ -1129,3 +1129,10 @@ window.pdfViewer = {
return div.innerHTML; return div.innerHTML;
} }
}; };

View File

@@ -23,7 +23,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{134D4164-B29
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
COPILOT_CONTEXT.md = COPILOT_CONTEXT.md COPILOT_CONTEXT.md = COPILOT_CONTEXT.md
FORM_APPLICATION_CONTEXT.md = FORM_APPLICATION_CONTEXT.md FORM_APPLICATION_CONTEXT.md = FORM_APPLICATION_CONTEXT.md
MIGRATION_CONTEXT.md = MIGRATION_CONTEXT.md
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0CBC2432-A561-4440-89BC-671B66A24146}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0CBC2432-A561-4440-89BC-671B66A24146}"

File diff suppressed because it is too large Load Diff