Files
EnvelopeGenerator/MIGRATION_CONTEXT.md
TekH d35a35c75e 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.
2026-06-12 12:48:32 +02:00

26 KiB

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:

<PackageReference Include="Yarp.ReverseProxy" Version="2.1.0" />
<PackageReference Include="DevExpress.Blazor" Version="25.2.3" />
<PackageReference Include="DevExpress.Blazor.PdfViewer" Version="25.2.3" />
<PackageReference Include="DevExpress.Blazor.Reporting.Viewer" Version="25.2.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.22" />

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:

<PackageReference Include="DevExpress.Blazor.PdfViewer" Version="25.2.3" />
<PackageReference Include="DevExpress.Blazor.Reporting.JSBasedControls" Version="25.2.3" />
<PackageReference Include="DevExpress.Blazor.Reporting.Viewer" Version="25.2.3" />
<PackageReference Include="DevExpress.Drawing.Skia" Version="25.2.3" />
<PackageReference Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="8.3.1.2" />
<PackageReference Include="SkiaSharp.NativeAssets.WebAssembly" Version="3.119.1" />
<PackageReference Include="SkiaSharp.Views.Blazor" Version="3.119.1" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.11" />

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:

@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)

{
  "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

{
  "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)

{
  "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:

@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:

@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)

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<EnvelopeGenerator.WebUI.Client.Options.ApiOptions>(
    builder.Configuration.GetSection("ApiOptions"));
builder.Services.Configure<EnvelopeGenerator.WebUI.Client.Options.PdfViewerOptions>(
    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<App>()
    .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)

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<ApiOptions>(
    builder.Configuration.GetSection(ApiOptions.SectionName));
builder.Services.Configure<PdfViewerOptions>(
    builder.Configuration.GetSection(PdfViewerOptions.SectionName));

// Business Services
builder.Services.AddScoped<DocumentService>();
builder.Services.AddScoped<AuthService>();
builder.Services.AddScoped<AnnotationService>();
builder.Services.AddScoped<EnvelopeReceiverService>();
builder.Services.AddScoped<SignatureService>();
builder.Services.AddScoped<SignatureCacheService>();
builder.Services.AddSingleton<AppVersionService>();

// DevExpress WASM
builder.Services.AddDevExpressWebAssemblyBlazorPdfViewer();
builder.Services.AddDevExpressWebAssemblyBlazorReportViewer();

builder.Services.AddDevExpressBlazorReportingWebAssembly(configure => {
    configure.UseDevelopmentMode();
});

// Reporting Services
builder.Services.AddScoped<IDataSourceWizardJsonConnectionStorage, CustomDataSourceWizardJsonDataConnectionStorage>();
builder.Services.AddScoped<IJsonDataConnectionProviderFactory, CustomJsonDataConnectionProviderFactory>();
builder.Services.AddScoped<IObjectDataSourceWizardTypeProvider, ObjectDataSourceWizardCustomTypeProvider>();

DevExpress.Utils.DeserializationSettings.RegisterTrustedClass(typeof(EnvelopeGenerator.WebUI.Client.Data.DataItemList));
DevExpress.Utils.DeserializationSettings.RegisterTrustedClass(typeof(EnvelopeGenerator.WebUI.Client.PredefinedReports.Report));

builder.Services.AddSingleton<InMemoryReportStorageWebExtension>();
builder.Services.AddSingleton<ReportStorageWebExtension>(sp => sp.GetRequiredService<InMemoryReportStorageWebExtension>());
builder.Services.AddScoped<IReportProviderAsync, CustomReportProvider>();

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

app.MapRazorComponents<App>()...;  // 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