Compare commits
11 Commits
96688a951c
...
3a2fa77862
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a2fa77862 | |||
| cfa6dbd2de | |||
| eb2603f389 | |||
| 456178bee1 | |||
| 2c41c74510 | |||
| bb73795d68 | |||
| 207992d95a | |||
| d6bafc64a6 | |||
| 3090711892 | |||
| 9dbd8f7952 | |||
| 48a41f2987 |
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>© SignFlow 2023-2024 <a href="https://digitaldata.works" target="_blank" rel="noopener">Digital Data GmbH</a></span>
|
||||||
|
<span class="receiver-footer__sep">|</span>
|
||||||
|
<a href="docs/privacy-policy.de-DE.html" target="_blank" rel="noopener">Datenschutz</a>
|
||||||
|
</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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace EnvelopeGenerator.WebUI.Client.Models.Constants
|
namespace EnvelopeGenerator.WebUI.Client.Models.Constants
|
||||||
{
|
{
|
||||||
public enum SenderAppType
|
public enum SenderAppType
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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; }
|
||||||
}
|
}
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
@page "/"
|
|
||||||
|
|
||||||
<PageTitle>Home</PageTitle>
|
|
||||||
|
|
||||||
<h1>Hello, world!</h1>
|
|
||||||
|
|
||||||
Welcome to your new app.
|
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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) { }
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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,
|
||||||
|
|||||||
Binary file not shown.
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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 d’utilisateur, 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 d’accès ou tentatives d’accè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 d’un 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 d’un 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 d’accè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 d’irrégularités, telles qu’un 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 à l’utilisateur final de signer un document numériquement. Cela nécessite l’identification 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 l’information, 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 à l’article 8 (2) VDG.</p>
|
||||||
|
|
||||||
|
<h3>3.5 Licéité du traitement</h3>
|
||||||
|
<p>Vos données sont collectées sur la base d’une relation commerciale existante ou imminente.</p>
|
||||||
|
<p>La base légale pour la transmission aux autorités compétentes est l’article 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 à l’article 6 (1) (f) RGPD existe dans les cas suivants :</p>
|
||||||
|
<p>Des mesures sont prises pour la sécurité de l’information, incluant à la fois des mesures techniques et organisationnelles préventives ainsi que la gestion des incidents. L’objectif est d’évaluer et d’éviter les dommages potentiels pour l’entreprise, 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 l’authentification 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 n’a 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 l’inté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 l’utilisateur, n’est 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 à l’adresse indiquée ci-dessus. Cela s’applique également si vous souhaitez vous opposer au traitement conformément à l’article 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 d’autres 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>
|
||||||
@@ -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.
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
@@ -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 |
@@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
|||||||
1161
MIGRATION_CONTEXT.md
1161
MIGRATION_CONTEXT.md
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user