SF-74: Server — CustomImagesController (GET /api/CustomImages), DI registration, appsettings logo config, App.razor

This commit is contained in:
2026-09-04 01:07:04 +02:00
parent 119ee3ec16
commit 5b7157f422
4 changed files with 79 additions and 3 deletions

View File

@@ -10,15 +10,58 @@
<link rel="stylesheet" href="css/envelope-viewer.css" /> <link rel="stylesheet" href="css/envelope-viewer.css" />
<link rel="stylesheet" href="EnvelopeGenerator.Server.styles.css" /> <link rel="stylesheet" href="EnvelopeGenerator.Server.styles.css" />
<HeadOutlet /> <HeadOutlet />
<!-- Apply saved theme immediately on page load — before WASM boots.
Reads localStorage["signflow.theme"] to avoid flash of default theme. -->
<script>
(function () {
var allowed = { 'blazing-dark': true, 'blazing-berry': true, 'purple': true };
var theme = localStorage.getItem('signflow.theme');
if (!theme || !allowed[theme]) theme = 'blazing-berry';
var bsTheme = theme === 'blazing-dark' ? 'dark' : 'light';
document.documentElement.setAttribute('data-sf-theme', theme);
document.documentElement.setAttribute('data-bs-theme', bsTheme);
var link = document.createElement('link');
link.id = 'dx-theme-link';
link.rel = 'stylesheet';
link.href = '_content/DevExpress.Blazor.Themes/' + theme + '.bs5.min.css';
document.head.appendChild(link);
})();
</script>
</head> </head>
<body> <body>
<!-- WASM boot loading overlay — hidden once Blazor circuit connects -->
<div id="sf-boot-overlay">
<div class="sf-boot-spinner">
<div class="sf-boot-ring"></div>
</div>
</div>
<Routes /> <Routes />
<script src="_content/DevExpress.Blazor.Resources/js/preload-script.js"></script> <script src="_content/DevExpress.Blazor.Resources/js/preload-script.js"></script>
<script src="js/typed.umd.js"></script> <script src="js/typed.umd.js"></script>
<script src="js/receiver-signature.js?v=9"></script> <script src="js/receiver-signature.js?v=9"></script>
<script src="js/signflow-theme.js"></script> <script src="js/signflow-theme.js"></script>
<script src="_framework/blazor.web.js"></script> <script src="_framework/blazor.web.js"></script>
<script>
// Remove the boot overlay once Blazor has started and enhanced navigation is active.
Blazor.addEventListener('enhancedload', function onFirstLoad() {
var overlay = document.getElementById('sf-boot-overlay');
if (overlay) overlay.style.opacity = '0';
setTimeout(function () { if (overlay) overlay.remove(); }, 250);
Blazor.removeEventListener('enhancedload', onFirstLoad);
});
// Also remove when WASM circuit is ready (for non-enhanced pages like /sender)
document.addEventListener('blazor-circuit-connected', function () {
var overlay = document.getElementById('sf-boot-overlay');
if (overlay) { overlay.style.opacity = '0'; setTimeout(function () { if (overlay) overlay.remove(); }, 250); }
}, { once: true });
// Fallback: remove after 4s regardless
setTimeout(function () {
var overlay = document.getElementById('sf-boot-overlay');
if (overlay) overlay.remove();
}, 4000);
</script>
</body> </body>
</html> </html>

View File

@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Server.Models;
namespace EnvelopeGenerator.Server.Controllers;
/// <summary>
/// Exposes the CustomImages configuration (signFLOW logo + company logo) to WASM clients.
/// Values are sourced from appsettings.json "CustomImages" section via Options Pattern.
///
/// To change logos per deployment: update "CustomImages.App.Src", "App.Height",
/// "Company.Src", "Company.Height" in appsettings.json — no code change required.
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Authorize(Policy = AuthPolicy.SenderOrReceiver)]
public class CustomImagesController(IOptions<CustomImagesOptions> options) : ControllerBase
{
/// <summary>
/// Returns the signFLOW app logo and company logo configuration.
/// </summary>
[HttpGet]
public ActionResult<CustomImagesOptions> Get() => Ok(options.Value);
}

View File

@@ -326,12 +326,17 @@ try
// User Preferences (theme, dark mode, grid layouts — stored in TBDD_CACHE) // User Preferences (theme, dark mode, grid layouts — stored in TBDD_CACHE)
// Registration handled by Infrastructure DependencyInjection (AddEnvelopeGeneratorInfrastructureServices) // Registration handled by Infrastructure DependencyInjection (AddEnvelopeGeneratorInfrastructureServices)
// CustomImages — logo sources and heights, configurable per deployment via appsettings.json
builder.Services.Configure<EnvelopeGenerator.Server.Models.CustomImagesOptions>(
config.GetSection(EnvelopeGenerator.Server.Models.CustomImagesOptions.SectionName));
// Client-side services that MainLayout depends on must also be registered here // Client-side services that MainLayout depends on must also be registered here
// so that Blazor Auto prerender (SSR pass) can resolve them on the server. // so that Blazor Auto prerender (SSR pass) can resolve them on the server.
// ThemeService.InitializeAsync() uses IJSRuntime but is only called in OnAfterRenderAsync // ThemeService.InitializeAsync() uses IJSRuntime but is only called in OnAfterRenderAsync
// (never during prerender), so registering it server-side is safe. // (never during prerender), so registering it server-side is safe.
builder.Services.AddScoped<EnvelopeGenerator.Server.Client.Services.UserPreferencesService>(); builder.Services.AddScoped<EnvelopeGenerator.Server.Client.Services.UserPreferencesService>();
builder.Services.AddScoped<EnvelopeGenerator.Server.Client.Services.ThemeService>(); builder.Services.AddScoped<EnvelopeGenerator.Server.Client.Services.ThemeService>();
builder.Services.AddScoped<EnvelopeGenerator.Server.Client.Services.CustomImagesService>();
// Business Services (Server specific) // Business Services (Server specific)
builder.Services.AddScoped<DocumentService>(); builder.Services.AddScoped<DocumentService>();

View File

@@ -145,15 +145,17 @@
"CustomImages": { "CustomImages": {
"App": { "App": {
"Src": "/img/DD_signFLOW_LOGO.png", "Src": "/img/DD_signFLOW_LOGO.png",
"Height": "28px",
"Classes": { "Classes": {
"Main": "signFlow-logo" "Main": "signflow-app-logo"
} }
}, },
"Company": { "Company": {
"Src": "/img/digital_data.svg", "Src": "/img/digital_data.svg",
"Height": "33px",
"Classes": { "Classes": {
"Show": "dd-show-logo", "Show": "signflow-company-logo",
"Locked": "dd-locked-logo" "Locked": "signflow-company-logo"
} }
} }
}, },