# 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**