From d35a35c75eeebfbeeac15447abdf6bddcf7eb92c Mon Sep 17 00:00:00 2001 From: TekH Date: Fri, 12 Jun 2026 12:48:32 +0200 Subject: [PATCH] feat(webui): migrate to hybrid Blazor architecture Migrated `EnvelopeGenerator.ReceiverUI` to a new hybrid Blazor architecture (`EnvelopeGenerator.WebUI`) combining Blazor Server and WebAssembly modes. This resolves the issue with `DxPdfViewer` requiring server-side rendering. Key changes: - Introduced `WebUI` (Blazor Server) and `WebUI.Client` (Blazor WebAssembly) projects. - Added YARP reverse proxy to `WebUI` for API routing. - Migrated client-side pages to `WebUI.Client` with `@rendermode InteractiveWebAssembly`. - Migrated server-side pages (e.g., PDF viewer) to `WebUI` with `@rendermode InteractiveServer`. - Copied services, models, and static files from `ReceiverUI`. - Configured DevExpress server-side and WASM components. Includes detailed migration documentation, rollback plan, and testing strategies to ensure stability. --- EnvelopeGenerator.sln | 1 + MIGRATION_CONTEXT.md | 781 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 782 insertions(+) create mode 100644 MIGRATION_CONTEXT.md diff --git a/EnvelopeGenerator.sln b/EnvelopeGenerator.sln index 9b155f71..cb626349 100644 --- a/EnvelopeGenerator.sln +++ b/EnvelopeGenerator.sln @@ -23,6 +23,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{134D4164-B29 ProjectSection(SolutionItems) = preProject COPILOT_CONTEXT.md = COPILOT_CONTEXT.md FORM_APPLICATION_CONTEXT.md = FORM_APPLICATION_CONTEXT.md + MIGRATION_CONTEXT.md = MIGRATION_CONTEXT.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0CBC2432-A561-4440-89BC-671B66A24146}" diff --git a/MIGRATION_CONTEXT.md b/MIGRATION_CONTEXT.md new file mode 100644 index 00000000..ab37be43 --- /dev/null +++ b/MIGRATION_CONTEXT.md @@ -0,0 +1,781 @@ +# EnvelopeGenerator.ReceiverUI ? WebUI Migration Context + +## ?? Migration Purpose + +### Problem Statement +**DevExpress `DxPdfViewer` component does NOT work in pure Blazor WebAssembly (standalone) mode.** + +**Symptoms:** +- PDF file loads successfully (byte array received) +- Component renders (no errors in console) +- **Result: Blank/white screen** - PDF is not displayed + +**Root Cause:** +DevExpress `DxPdfViewer` requires **backend server-side rendering services** that are NOT available in pure WebAssembly projects (`Microsoft.NET.Sdk.BlazorWebAssembly`). + +**Solution:** +Migrate from **pure Blazor WebAssembly** (`ReceiverUI`) to **Blazor Auto (Server+WASM hybrid)** (`WebUI`) architecture. + +--- + +## ?? Current vs. Target Architecture + +### Current Architecture (PROBLEMATIC) +``` +Client Browser + ? +EnvelopeGenerator.API:8088 (YARP Proxy) + ? +EnvelopeGenerator.ReceiverUI:52936 (Pure Blazor WebAssembly) + ??? SDK: Microsoft.NET.Sdk.BlazorWebAssembly + ??? DxPdfViewer ? ? WHITE SCREEN (no backend) + ??? All pages run client-side only +``` + +### Target Architecture (SOLUTION) +``` +Client Browser + ? +EnvelopeGenerator.WebUI:XXXX (Blazor Auto - Server+WASM) + ??? YARP Proxy: /api/* ? API:8088 + ??? YARP Proxy: /swagger/* ? API:8088 + ??? Server-side Pages (@rendermode InteractiveServer) + ? ??? DxPdfViewer pages ? ? WORKS (backend available) + ??? Client-side Pages (@rendermode InteractiveWebAssembly) + ??? Login, Sender, Index pages +``` + +--- + +## ??? Project Structure + +### EnvelopeGenerator.WebUI (Server Project) +**Path:** `EnvelopeGenerator.WebUI\EnvelopeGenerator.WebUI\` +**SDK:** `Microsoft.NET.Sdk.Web` +**Target:** `net8.0` + +**Responsibilities:** +1. **YARP Reverse Proxy** + - Routes `/api/*` to `API:8088` + - Routes `/swagger/*`, `/openapi/*`, `/scalar/*` to `API:8088` + - All other routes handled by Blazor components + +2. **DevExpress Server-Side Services** + - `AddDevExpressServerSideBlazorPdfViewer()` - **CRITICAL for DxPdfViewer** + - `AddDevExpressBlazorReportViewer()` - For DxReportViewer + - Provides backend rendering engine + +3. **Static File Hosting** + - Serves `wwwroot/` (JS, CSS, PDF.js) + - Hosts PDF viewer assets + +4. **Server-Side Components** + - `@rendermode InteractiveServer` pages + - PDF viewer pages (EnvelopeReceiverPage*.razor) + +**Key Files:** +- `Program.cs` - YARP + DevExpress server configuration +- `yarp.json` - Proxy route definitions +- `Components/App.razor` - Root component +- `Components/Routes.razor` - Routing configuration +- `Pages/EnvelopeReceiverPage*.razor` - Server-side PDF viewers + +**NuGet Packages:** +```xml + + + + + +``` + +--- + +### EnvelopeGenerator.WebUI.Client (WASM Project) +**Path:** `EnvelopeGenerator.WebUI\EnvelopeGenerator.WebUI.Client\` +**SDK:** `Microsoft.NET.Sdk.BlazorWebAssembly` +**Target:** `net8.0` + +**Responsibilities:** +1. **Client-Side Pages** + - `@rendermode InteractiveWebAssembly` components + - Authentication pages (Login) + - Public pages (Index) + - Sender dashboard + +2. **Business Logic Services** + - All services (AuthService, DocumentService, etc.) + - API communication via HttpClient + - Signature caching + +3. **DevExpress WASM Components** + - Client-side DevExpress components + - Reporting tools + +**Key Files:** +- `Program.cs` - Service registration + DevExpress WASM +- `Pages/Index.razor` - Landing page +- `Pages/LoginSenderPage.razor` - Sender authentication +- `Pages/LoginReceiverPage.razor` - Receiver authentication +- `Pages/EnvelopeSenderPage.razor` - Sender dashboard +- `Services/*` - All business logic services + +**NuGet Packages:** +```xml + + + + + + + + +``` + +--- + +### EnvelopeGenerator.ReceiverUI (OLD - Will Remain for Now) +**Path:** `EnvelopeGenerator.ReceiverUI\` +**Status:** ?? **DEPRECATED but NOT deleted during migration** +**SDK:** `Microsoft.NET.Sdk.BlazorWebAssembly` + +**Migration Plan:** +- Files will be **COPIED** to WebUI/WebUI.Client (not moved) +- Original ReceiverUI project will **remain untouched** during migration +- Can be deleted later after successful migration verification + +--- + +## ?? File Migration Map + +### Client-Side Pages (ReceiverUI ? WebUI.Client) +| Source File | Destination | Render Mode | Action | +|-------------|-------------|-------------|--------| +| `ReceiverUI/Pages/Index.razor` | `WebUI.Client/Pages/Index.razor` | `@rendermode InteractiveWebAssembly` | **COPY** | +| `ReceiverUI/Pages/EnvelopeSenderPage.razor` | `WebUI.Client/Pages/EnvelopeSenderPage.razor` | `@rendermode InteractiveWebAssembly` | **COPY** | +| `ReceiverUI/Pages/LoginSenderPage.razor` | `WebUI.Client/Pages/LoginSenderPage.razor` | `@rendermode InteractiveWebAssembly` | **COPY** | +| `ReceiverUI/Pages/LoginReceiverPage.razor` | `WebUI.Client/Pages/LoginReceiverPage.razor` | `@rendermode InteractiveWebAssembly` | **COPY** | + +**Post-Migration Edit:** +Add `@rendermode InteractiveWebAssembly` to the top of each file (after `@page` directive). + +--- + +### Server-Side PDF Viewer Pages (ReceiverUI ? WebUI) +| Source File | Destination | Render Mode | Action | +|-------------|-------------|-------------|--------| +| `ReceiverUI/Pages/EnvelopeReceiverPage.razor` | `WebUI/Pages/EnvelopeReceiverPage.razor` | `@rendermode InteractiveServer` | **COPY** | +| `ReceiverUI/Pages/EnvelopeReceiverPage_DxPdfViewer.razor` | `WebUI/Pages/EnvelopeReceiverPage_DxPdfViewer.razor` | `@rendermode InteractiveServer` | **COPY** | +| `ReceiverUI/Pages/EnvelopeReceiverPage_DxReportViewer.razor` | `WebUI/Pages/EnvelopeReceiverPage_DxReportViewer.razor` | `@rendermode InteractiveServer` | **COPY** | +| `ReceiverUI/Pages/EnvelopeReceiverPage_embed.razor` | `WebUI/Pages/EnvelopeReceiverPage_embed.razor` | `@rendermode InteractiveServer` | **COPY** | + +**Why Server-Side?** +- DevExpress `DxPdfViewer` **requires** backend rendering +- `DxReportViewer` also needs server-side services +- `@rendermode InteractiveServer` provides SignalR connection to backend + +**Post-Migration Edit:** +1. Add `@rendermode InteractiveServer` to top of file +2. Update `@using` directives to reference `WebUI.Client` namespaces + +--- + +### Services (ReceiverUI ? WebUI.Client) +| Source Directory | Destination | Action | +|------------------|-------------|--------| +| `ReceiverUI/Services/AuthService.cs` | `WebUI.Client/Services/AuthService.cs` | **COPY** | +| `ReceiverUI/Services/DocumentService.cs` | `WebUI.Client/Services/DocumentService.cs` | **COPY** | +| `ReceiverUI/Services/SignatureService.cs` | `WebUI.Client/Services/SignatureService.cs` | **COPY** | +| `ReceiverUI/Services/SignatureCacheService.cs` | `WebUI.Client/Services/SignatureCacheService.cs` | **COPY** | +| `ReceiverUI/Services/EnvelopeReceiverService.cs` | `WebUI.Client/Services/EnvelopeReceiverService.cs` | **COPY** | +| `ReceiverUI/Services/AnnotationService.cs` | `WebUI.Client/Services/AnnotationService.cs` | **COPY** | +| `ReceiverUI/Services/AppVersionService.cs` | `WebUI.Client/Services/AppVersionService.cs` | **COPY** | +| `ReceiverUI/Services/CustomDataSourceWizardJsonDataConnectionStorage.cs` | `WebUI.Client/Services/CustomDataSourceWizardJsonDataConnectionStorage.cs` | **COPY** | +| `ReceiverUI/Services/CustomJsonDataConnectionProviderFactory.cs` | `WebUI.Client/Services/CustomJsonDataConnectionProviderFactory.cs` | **COPY** | +| `ReceiverUI/Services/CustomReportProvider.cs` | `WebUI.Client/Services/CustomReportProvider.cs` | **COPY** | +| `ReceiverUI/Services/FontLoader.cs` | `WebUI.Client/Services/FontLoader.cs` | **COPY** | +| `ReceiverUI/Services/InMemoryReportStorageWebExtension.cs` | `WebUI.Client/Services/InMemoryReportStorageWebExtension.cs` | **COPY** | +| `ReceiverUI/Services/ObjectDataSourceWizardCustomTypeProvider.cs` | `WebUI.Client/Services/ObjectDataSourceWizardCustomTypeProvider.cs` | **COPY** | + +**Post-Migration Edit:** +Update namespace from `EnvelopeGenerator.ReceiverUI.Services` to `EnvelopeGenerator.WebUI.Client.Services` + +--- + +### Models, Options, Data (ReceiverUI ? WebUI.Client) +| Source Directory | Destination | Action | +|------------------|-------------|--------| +| `ReceiverUI/Models/*` | `WebUI.Client/Models/*` | **COPY ALL FILES** | +| `ReceiverUI/Options/*` | `WebUI.Client/Options/*` | **COPY ALL FILES** | +| `ReceiverUI/Data/*` | `WebUI.Client/Data/*` | **COPY ALL FILES** | +| `ReceiverUI/Shared/*` | `WebUI.Client/Shared/*` | **COPY ALL FILES** | + +**Post-Migration Edit:** +Update namespaces to `EnvelopeGenerator.WebUI.Client.*` + +--- + +### Static Files (ReceiverUI ? WebUI) +| Source | Destination | Action | +|--------|-------------|--------| +| `ReceiverUI/wwwroot/js/*` | `WebUI/wwwroot/js/*` | **MERGE** (keep both if conflict) | +| `ReceiverUI/wwwroot/css/*` | `WebUI/wwwroot/css/*` | **MERGE** | +| `ReceiverUI/wwwroot/docs/*` | `WebUI/wwwroot/docs/*` | **MERGE** | +| `ReceiverUI/wwwroot/appsettings.json` | `WebUI/wwwroot/appsettings.json` | **MERGE** (combine PdfViewerOptions) | +| `ReceiverUI/wwwroot/appsettings.Development.json` | `WebUI/wwwroot/appsettings.Development.json` | **MERGE** | + +**Critical Files:** +- `js/pdf-viewer.js` - PDF.js wrapper +- `js/receiver-signature.js` - Signature pad +- `css/envelope-viewer.css` - Viewer styles +- `appsettings.json` - PdfViewerOptions configuration + +--- + +### _Imports.razor (ReceiverUI ? WebUI.Client) +| Source | Destination | Action | +|--------|-------------|--------| +| `ReceiverUI/_Imports.razor` | `WebUI.Client/_Imports.razor` | **MERGE** (combine using directives) | + +**Post-Migration Content:** +```razor +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.AspNetCore.Components.WebAssembly.Http +@using Microsoft.JSInterop +@using EnvelopeGenerator.WebUI.Client +@using EnvelopeGenerator.WebUI.Client.Services +@using EnvelopeGenerator.WebUI.Client.Models +@using EnvelopeGenerator.WebUI.Client.Options +@using DevExpress.Blazor +@using DevExpress.Blazor.PdfViewer +@using DevExpress.Blazor.Reporting +``` + +--- + +### Excluded Files (NOT Migrated) +| Source Directory | Reason | +|------------------|--------| +| `ReceiverUI/Pages/Example/*` | Test pages, not needed in production | +| `ReceiverUI/PredefinedReports/*` | Deprecated, reports moved to WebUI.Client/Data | + +--- + +## ?? YARP Configuration + +### WebUI/yarp.json (NEW FILE) +```json +{ + "ReverseProxy": { + "Routes": { + "api-route": { + "ClusterId": "api-cluster", + "Match": { + "Path": "/api/{**catch-all}" + } + }, + "swagger-route": { + "ClusterId": "api-cluster", + "Match": { + "Path": "/swagger/{**catch-all}" + } + }, + "openapi-route": { + "ClusterId": "api-cluster", + "Match": { + "Path": "/openapi/{**catch-all}" + } + }, + "scalar-route": { + "ClusterId": "api-cluster", + "Match": { + "Path": "/scalar/{**catch-all}" + } + } + }, + "Clusters": { + "api-cluster": { + "Destinations": { + "api-destination": { + "Address": "https://localhost:8088" + } + } + } + } + } +} +``` + +**Purpose:** +- Route all `/api/*` requests to backend API +- Route Swagger/OpenAPI to API (development only) +- All other requests handled by Blazor components + +**Configuration Location:** +Place `yarp.json` in `EnvelopeGenerator.WebUI\EnvelopeGenerator.WebUI\` directory. + +**Important:** +Set **Copy to Output Directory** to `Copy if newer` in file properties. + +--- + +### API/yarp.json (MODIFICATION) +**Current State:** +API currently proxies requests to ReceiverUI:52936 + +**Target State:** +API will **NOT have YARP** - all proxying moves to WebUI + +**Action:** +- **DO NOT DELETE** `API/yarp.json` during migration +- Keep for rollback safety +- Can comment out YARP code in `API/Program.cs` instead of deleting + +--- + +## ?? Configuration Files + +### WebUI/appsettings.json +```json +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ApiOptions": { + "BaseUrl": "" + }, + "PdfViewerOptions": { + "ThumbnailBaseScale": 0.75, + "ThumbnailEnableHiDPI": true, + "MainCanvasEnableHiDPI": true, + "ZoomStepPercentage": 5 + } +} +``` + +**Critical Setting:** +`"BaseUrl": ""` - Empty because YARP handles proxy automatically (requests to `/api/*` are proxied) + +--- + +### WebUI/wwwroot/appsettings.json (Client-Side Config) +```json +{ + "ApiOptions": { + "BaseUrl": "" + }, + "PdfViewerOptions": { + "ThumbnailBaseScale": 0.75, + "ThumbnailEnableHiDPI": true, + "ThumbnailMaxDPR": 4.0, + "MainCanvasEnableHiDPI": true, + "MainCanvasMaxDPR": 4.0, + "EnableSmoothZoom": true, + "ZoomTransitionDuration": 200, + "RenderingOpacity": 1.0, + "ZoomStepPercentage": 5, + "ThumbnailRenderDelay": 25 + } +} +``` + +**Purpose:** +Configures PDF.js quality settings for optimal rendering. + +--- + +## ?? Render Mode Strategy + +### When to Use `@rendermode InteractiveServer` +**Use Cases:** +- PDF viewer pages (DxPdfViewer, DxReportViewer) +- Any page using DevExpress components requiring backend +- Pages with heavy server-side logic + +**Example:** +```razor +@page "/envelope/{EnvelopeKey}" +@rendermode InteractiveServer +@using DevExpress.Blazor.PdfViewer +@using EnvelopeGenerator.WebUI.Client.Services +... +``` + +**Why:** +DevExpress `DxPdfViewer` calls server-side APIs for PDF rendering. Server render mode provides SignalR connection to backend. + +--- + +### When to Use `@rendermode InteractiveWebAssembly` +**Use Cases:** +- Authentication pages (no sensitive data on client) +- Public pages (landing, index) +- Pages with pure client-side logic +- Sender dashboard (calls API via HttpClient) + +**Example:** +```razor +@page "/sender/login" +@rendermode InteractiveWebAssembly +@using EnvelopeGenerator.WebUI.Client.Services +... +``` + +**Why:** +No server-side dependencies, reduces server load, works offline (PWA). + +--- + +## ?? Service Registration + +### WebUI/Program.cs (Server) +```csharp +using DevExpress.Blazor; + +var builder = WebApplication.CreateBuilder(args); + +// Load YARP configuration +builder.Configuration.AddJsonFile("yarp.json", optional: false, reloadOnChange: true); + +// Blazor Components +builder.Services.AddRazorComponents() + .AddInteractiveServerComponents() + .AddInteractiveWebAssemblyComponents(); + +// YARP Reverse Proxy +builder.Services.AddReverseProxy() + .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); + +// DevExpress Server-Side Services (CRITICAL for DxPdfViewer) +builder.Services.AddDevExpressBlazor(configure => configure.BootstrapVersion = BootstrapVersion.v5); +builder.Services.AddDevExpressServerSideBlazorPdfViewer(); +builder.Services.AddDevExpressBlazorReportViewer(); + +// Configuration Options +builder.Services.Configure( + builder.Configuration.GetSection("ApiOptions")); +builder.Services.Configure( + builder.Configuration.GetSection("PdfViewerOptions")); + +var app = builder.Build(); + +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error"); + app.UseHsts(); +} + +app.UseHttpsRedirection(); +app.UseStaticFiles(); +app.UseAntiforgery(); + +// Blazor routing (BEFORE YARP) +app.MapRazorComponents() + .AddInteractiveServerRenderMode() + .AddInteractiveWebAssemblyRenderMode() + .AddAdditionalAssemblies(typeof(EnvelopeGenerator.WebUI.Client._Imports).Assembly); + +// YARP proxy (AFTER Blazor - catch-all) +app.MapReverseProxy(); + +app.Run(); +``` + +**Critical Order:** +1. `MapRazorComponents` first (Blazor routes) +2. `MapReverseProxy` last (catch-all for `/api/*`) + +--- + +### WebUI.Client/Program.cs (WASM) +```csharp +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using EnvelopeGenerator.WebUI.Client.Services; +using EnvelopeGenerator.WebUI.Client.Options; +using DevExpress.Blazor.Reporting; +using DevExpress.XtraReports.Web.Extensions; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); + +// HTTP Client (uses WebUI's YARP proxy) +builder.Services.AddScoped(sp => new HttpClient { + BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) +}); + +// Configuration Options +builder.Services.Configure( + builder.Configuration.GetSection(ApiOptions.SectionName)); +builder.Services.Configure( + builder.Configuration.GetSection(PdfViewerOptions.SectionName)); + +// Business Services +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddSingleton(); + +// DevExpress WASM +builder.Services.AddDevExpressWebAssemblyBlazorPdfViewer(); +builder.Services.AddDevExpressWebAssemblyBlazorReportViewer(); + +builder.Services.AddDevExpressBlazorReportingWebAssembly(configure => { + configure.UseDevelopmentMode(); +}); + +// Reporting Services +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +DevExpress.Utils.DeserializationSettings.RegisterTrustedClass(typeof(EnvelopeGenerator.WebUI.Client.Data.DataItemList)); +DevExpress.Utils.DeserializationSettings.RegisterTrustedClass(typeof(EnvelopeGenerator.WebUI.Client.PredefinedReports.Report)); + +builder.Services.AddSingleton(); +builder.Services.AddSingleton(sp => sp.GetRequiredService()); +builder.Services.AddScoped(); + +ReportStorageWebExtension.RegisterExtensionGlobal(new InMemoryReportStorageWebExtension()); + +await builder.Build().RunAsync(); +``` + +--- + +## ? Migration Checklist + +### Phase 1: WebUI YARP Setup +- [ ] Create `WebUI/yarp.json` +- [ ] Add YARP NuGet package to `WebUI.csproj` +- [ ] Update `WebUI/Program.cs` with YARP configuration +- [ ] Add DevExpress server packages to `WebUI.csproj` +- [ ] Create `WebUI/appsettings.json` with `ApiOptions.BaseUrl = ""` + +### Phase 2: File Migration - Client Pages +- [ ] Copy `ReceiverUI/Pages/Index.razor` ? `WebUI.Client/Pages/` +- [ ] Copy `ReceiverUI/Pages/EnvelopeSenderPage.razor` ? `WebUI.Client/Pages/` +- [ ] Copy `ReceiverUI/Pages/LoginSenderPage.razor` ? `WebUI.Client/Pages/` +- [ ] Copy `ReceiverUI/Pages/LoginReceiverPage.razor` ? `WebUI.Client/Pages/` +- [ ] Add `@rendermode InteractiveWebAssembly` to each file + +### Phase 3: File Migration - Server Pages +- [ ] Copy `ReceiverUI/Pages/EnvelopeReceiverPage.razor` ? `WebUI/Pages/` +- [ ] Copy `ReceiverUI/Pages/EnvelopeReceiverPage_DxPdfViewer.razor` ? `WebUI/Pages/` +- [ ] Copy `ReceiverUI/Pages/EnvelopeReceiverPage_DxReportViewer.razor` ? `WebUI/Pages/` +- [ ] Copy `ReceiverUI/Pages/EnvelopeReceiverPage_embed.razor` ? `WebUI/Pages/` +- [ ] Add `@rendermode InteractiveServer` to each file +- [ ] Update `@using` directives to `WebUI.Client` namespaces + +### Phase 4: File Migration - Services +- [ ] Copy `ReceiverUI/Services/*` ? `WebUI.Client/Services/` +- [ ] Update namespaces to `EnvelopeGenerator.WebUI.Client.Services` + +### Phase 5: File Migration - Models/Options/Data +- [ ] Copy `ReceiverUI/Models/*` ? `WebUI.Client/Models/` +- [ ] Copy `ReceiverUI/Options/*` ? `WebUI.Client/Options/` +- [ ] Copy `ReceiverUI/Data/*` ? `WebUI.Client/Data/` +- [ ] Copy `ReceiverUI/Shared/*` ? `WebUI.Client/Shared/` +- [ ] Update all namespaces to `EnvelopeGenerator.WebUI.Client.*` + +### Phase 6: File Migration - Static Files +- [ ] Merge `ReceiverUI/wwwroot/js/*` ? `WebUI/wwwroot/js/` +- [ ] Merge `ReceiverUI/wwwroot/css/*` ? `WebUI/wwwroot/css/` +- [ ] Merge `ReceiverUI/wwwroot/docs/*` ? `WebUI/wwwroot/docs/` +- [ ] Merge `ReceiverUI/wwwroot/appsettings.json` ? `WebUI/wwwroot/appsettings.json` + +### Phase 7: Configuration +- [ ] Add DevExpress WASM packages to `WebUI.Client.csproj` +- [ ] Update `WebUI.Client/Program.cs` with service registrations +- [ ] Merge `ReceiverUI/_Imports.razor` ? `WebUI.Client/_Imports.razor` +- [ ] Set `yarp.json` to "Copy if newer" in file properties + +### Phase 8: Testing +- [ ] Build `WebUI` project ? No errors +- [ ] Build `WebUI.Client` project ? No errors +- [ ] Run `WebUI` ? Application starts +- [ ] Test YARP: `/api/*` routes to API:8088 +- [ ] Test `/sender/login` ? Login page works +- [ ] Test `/envelope/DxPdfViewer` ? **PDF displays (not blank!)** +- [ ] Test `/envelope/{key}` ? PDF.js viewer works +- [ ] Test signature caching +- [ ] Test authentication cookies +- [ ] No CORS errors in browser console + +--- + +## ?? Critical Notes + +### 1. DO NOT DELETE ReceiverUI During Migration +- Keep `EnvelopeGenerator.ReceiverUI` project untouched +- Files are **COPIED**, not moved +- Allows rollback if migration fails +- Can be deleted after successful verification + +### 2. API YARP Modification Strategy +- **DO NOT delete** `API/yarp.json` immediately +- Comment out YARP code in `API/Program.cs` instead +- Keep for rollback safety +- Remove only after WebUI proven stable + +### 3. Namespace Updates Are CRITICAL +- All copied files must update namespaces +- `EnvelopeGenerator.ReceiverUI.*` ? `EnvelopeGenerator.WebUI.Client.*` +- Missing namespace updates = compilation errors + +### 4. Render Mode MUST Match Component Type +- Server-side DevExpress ? `@rendermode InteractiveServer` +- Client-side pages ? `@rendermode InteractiveWebAssembly` +- Wrong render mode = component won't work + +### 5. Service Registration Must Be Complete +- All ReceiverUI services ? `WebUI.Client/Program.cs` +- Missing service = runtime injection errors +- Check DI container for all dependencies + +### 6. YARP Route Order Matters +```csharp +app.MapRazorComponents()...; // FIRST - Blazor routes +app.MapReverseProxy(); // LAST - Catch-all API proxy +``` +Wrong order = `/api/*` requests handled by Blazor (404 errors) + +### 7. wwwroot/ Merge Strategy +- **DO NOT overwrite** existing WebUI files +- Compare manually before merge +- Prioritize ReceiverUI files (they're tested) +- Keep both if conflict, rename WebUI version + +--- + +## ?? Rollback Plan + +If migration fails: + +### Immediate Rollback +1. Stop `WebUI` application +2. Restore `API/Program.cs` YARP code +3. Start `ReceiverUI` + `API` as before +4. System operational again + +### Permanent Rollback +1. Checkout previous Git commit +2. Discard all `WebUI` changes +3. Delete copied files from `WebUI.Client` +4. Resume development on `ReceiverUI` + +### Partial Rollback +1. Keep `WebUI` structure +2. Revert YARP changes only +3. Continue using `ReceiverUI` while debugging `WebUI` + +--- + +## ?? Success Criteria + +Migration is successful when: + +- [ ] `WebUI` application starts without errors +- [ ] `/api/*` requests proxied to API:8088 +- [ ] `/envelope/DxPdfViewer` displays PDF (not blank!) +- [ ] `/envelope/{key}` PDF.js viewer works +- [ ] Login pages functional (Sender, Receiver) +- [ ] Sender dashboard loads +- [ ] Signature caching works +- [ ] Authentication cookies work +- [ ] No CORS errors +- [ ] No console errors +- [ ] Performance acceptable (not slower than ReceiverUI) + +--- + +## ?? Known Issues & Workarounds + +### Issue 1: DxPdfViewer Still Blank +**Cause:** Missing DevExpress server-side services +**Fix:** Verify `AddDevExpressServerSideBlazorPdfViewer()` in `WebUI/Program.cs` + +### Issue 2: 404 on /api/* Requests +**Cause:** YARP not configured or wrong route order +**Fix:** Check `yarp.json` exists and `MapReverseProxy()` is AFTER `MapRazorComponents()` + +### Issue 3: Services Not Injected +**Cause:** Missing service registration in `WebUI.Client/Program.cs` +**Fix:** Copy ALL service registrations from `ReceiverUI/Program.cs` + +### Issue 4: Namespace Errors +**Cause:** Namespace not updated after file copy +**Fix:** Find/Replace `EnvelopeGenerator.ReceiverUI` ? `EnvelopeGenerator.WebUI.Client` + +### Issue 5: Static Files Not Loading +**Cause:** `wwwroot/` not merged correctly +**Fix:** Check file paths, ensure files copied to `WebUI/wwwroot/` + +--- + +## ?? Git Workflow + +### Branch Strategy +- **Current Branch:** `bugfix/devexpress-pdf-not-displaying` +- **Remote:** `https://git.dd/AppStd/EnvelopeGenerator` + +### Commit Strategy +1. Commit after each phase (incremental changes) +2. Descriptive commit messages +3. Tag final working version: `v1.0.0-webui-migration` + +### Example Commits +``` +feat(webui): add YARP configuration for API proxy +feat(webui): migrate client-side pages from ReceiverUI +feat(webui): migrate server-side PDF viewer pages +feat(webui): migrate services and models +feat(webui): merge static files and configuration +fix(webui): update namespaces to WebUI.Client +test(webui): verify DxPdfViewer rendering +docs(migration): add MIGRATION_CONTEXT.md +``` + +--- + +## ?? Learning Resources + +### DevExpress Blazor Render Modes +https://docs.devexpress.com/Blazor/403507/blazor-components-render-modes + +### YARP Documentation +https://microsoft.github.io/reverse-proxy/ + +### Blazor Auto (InteractiveAuto) Mode +https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes + +--- + +**Migration Status:** ? **READY TO START** +**Last Updated:** 2025-01-XX +**Responsible:** AI Agent + Developer +**Estimated Time:** 4-6 hours +**Risk Level:** Medium (rollback available) + +--- + +## ?? Support + +**Questions?** Contact the development team or refer to: +- `COPILOT_CONTEXT.md` - Overall project context +- `FORM_APPLICATION_CONTEXT.md` - Legacy VB.NET app context +- DevExpress Support Portal + +--- + +**END OF MIGRATION CONTEXT**