From a3604d7333acc63a413701ebabbf8b7997642c07 Mon Sep 17 00:00:00 2001 From: Loretta Date: Sun, 12 Jul 2026 14:32:05 +0200 Subject: [PATCH] Update MgGrid reload logic, add IsProd env flag, doc fixes - MgGridBase: call Reload after item changes to refresh grid view - Add IsProd environment flag (FruitBankConst, IFruitBankDataControllerCommon, SignalRTags, client/server wiring) - Logger DI: fix constructor param, default AppType to Web in configs and DI setup - Docs: clarify MgGrid edit flow, editor requirements, companion component structure, and disposal logic - Add PowerShell test command for EkaerTradeCardValidatorTests --- .claude/settings.local.json | 3 ++- FruitBank.Common.Server/FruitBankConst.cs | 24 +++++++++++++++++++ .../Loggers/LoggerToLoggerApiController.cs | 5 +++- .../IFruitBankDataControllerCommon.cs | 6 +++++ FruitBank.Common/SignalRs/SignalRTags.cs | 2 ++ .../SignalRs/FruitBankSignalRClient.cs | 2 ++ FruitBankHybrid.Shared/appsettings.json | 2 +- FruitBankHybrid.Web.Client/Program.cs | 6 +++-- .../wwwroot/appsettings.json | 2 +- FruitBankHybrid.Web/Program.cs | 6 +++-- FruitBankHybrid.Web/appsettings.json | 2 +- FruitBankHybrid/MauiProgram.cs | 8 ++++--- 12 files changed, 56 insertions(+), 12 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index fd77ad4b..35b4e8d2 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -85,7 +85,8 @@ "PowerShell($xml = Get-ChildItem \"$env:USERPROFILE\\\\.nuget\\\\packages\\\\devexpress.blazor\" -Recurse -Filter \"DevExpress.Blazor.xml\" -ErrorAction SilentlyContinue | Select-Object -Last 1; if \\($xml\\) { $xml.FullName; Select-String -Path $xml.FullName -Pattern \"MessageBoxOptions\\\\.\\(BodyTemplate|CssClass|Text|Title\\)\" | Select-Object -First 8 | ForEach-Object { $_.Line.Trim\\(\\).Substring\\(0, [Math]::Min\\(120, $_.Line.Trim\\(\\).Length\\)\\) } } else { \"DevExpress.Blazor.xml nem talalhato a NuGet cache-ben\" })", "PowerShell($lib = Get-ChildItem \"$env:USERPROFILE\\\\.nuget\\\\packages\\\\devexpress.blazor\" -Directory | Sort-Object Name | Select-Object -Last 1; \"Verzio: $\\($lib.Name\\)\"; $dll = Get-ChildItem $lib.FullName -Recurse -Filter \"DevExpress.Blazor.dll\" | Select-Object -First 1; $asm = [System.Reflection.Assembly]::LoadFile\\($dll.FullName\\); $t = $asm.GetType\\(\"DevExpress.Blazor.MessageBoxOptions\"\\); if \\($t\\) { $t.GetProperties\\(\\) | Select-Object -ExpandProperty Name } else { \"MessageBoxOptions tipus nem talalhato\" })", "PowerShell($dll = Get-ChildItem \"$env:USERPROFILE\\\\.nuget\\\\packages\\\\devexpress.blazor\\\\25.1.5\" -Recurse -Filter \"DevExpress.Blazor.dll\" | Select-Object -First 1; $dll.FullName; $bytes = [System.IO.File]::ReadAllBytes\\($dll.FullName\\); $text = [System.Text.Encoding]::ASCII.GetString\\($bytes\\); @\\(\"BodyTemplate\",\"CssClass\",\"MessageBoxOptions\"\\) | ForEach-Object { \"$\\($_\\): $\\($text.Contains\\($_\\)\\)\" })", - "PowerShell($dll = \"C:\\\\Users\\\\Fullepi\\\\.nuget\\\\packages\\\\devexpress.blazor\\\\25.1.5\\\\lib\\\\net8.0\\\\DevExpress.Blazor.v25.1.dll\"; $bytes = [System.IO.File]::ReadAllBytes\\($dll\\); $text = [System.Text.Encoding]::ASCII.GetString\\($bytes\\); @\\(\"BodyTemplate\",\"get_CssClass\",\"MessageBoxOptions\",\"TitleTemplate\"\\) | ForEach-Object { \"$\\($_\\): $\\($text.Contains\\($_\\)\\)\" })" + "PowerShell($dll = \"C:\\\\Users\\\\Fullepi\\\\.nuget\\\\packages\\\\devexpress.blazor\\\\25.1.5\\\\lib\\\\net8.0\\\\DevExpress.Blazor.v25.1.dll\"; $bytes = [System.IO.File]::ReadAllBytes\\($dll\\); $text = [System.Text.Encoding]::ASCII.GetString\\($bytes\\); @\\(\"BodyTemplate\",\"get_CssClass\",\"MessageBoxOptions\",\"TitleTemplate\"\\) | ForEach-Object { \"$\\($_\\): $\\($text.Contains\\($_\\)\\)\" })", + "PowerShell(dotnet test \"H:\\\\Applications\\\\Aycode\\\\Source\\\\AyCode.Core\\\\AyCode.Services.Tests\\\\AyCode.Services.Tests.csproj\" --filter \"FullyQualifiedName~EkaerTradeCardValidatorTests\" --nologo -v quiet 2>$null)" ] } } diff --git a/FruitBank.Common.Server/FruitBankConst.cs b/FruitBank.Common.Server/FruitBankConst.cs index c60e6ca0..ab279d71 100644 --- a/FruitBank.Common.Server/FruitBankConst.cs +++ b/FruitBank.Common.Server/FruitBankConst.cs @@ -27,6 +27,30 @@ namespace FruitBank.Common.Server /// public const string PreOrderWindowEnd = "PreOrderWindowEnd"; + // ── Environment flag — method-only surface on purpose: no property to bind/serialize/read accidentally ── + + private static bool? _isProd; + + /// + /// Set once by the plugin startup after DB-catalog classification. + /// A second call with a different value throws — the flag cannot flip at runtime. + /// + public static void SetIsProd(bool isProd) + { + if (_isProd.HasValue && _isProd.Value != isProd) + throw new InvalidOperationException( + $"IsProd is already set to {_isProd.Value}; refusing to change it to {isProd}."); + + _isProd = isProd; + } + + /// + /// True = PROD database. Throws until SetIsProd ran — an uninitialized read must never return a default. + /// + public static bool IsProd() + => _isProd ?? throw new InvalidOperationException( + "IsProd has not been initialized — SetIsProd must run at startup before any read."); + static FruitBankConst() { ProjectId = Guid.Parse(ProjectIdString); diff --git a/FruitBank.Common.Server/Services/Loggers/LoggerToLoggerApiController.cs b/FruitBank.Common.Server/Services/Loggers/LoggerToLoggerApiController.cs index 6ba04041..ef89e8ee 100644 --- a/FruitBank.Common.Server/Services/Loggers/LoggerToLoggerApiController.cs +++ b/FruitBank.Common.Server/Services/Loggers/LoggerToLoggerApiController.cs @@ -6,6 +6,9 @@ namespace FruitBank.Common.Server.Services.Loggers; public class LoggerToLoggerApiController : Logger { - public LoggerToLoggerApiController(params IAcLogWriterBase[] logWriters) : base(logWriters) + // IEnumerable — a MS DI tömböt ("params IAcLogWriterBase[]") NEM tud feloldani (InvalidOperationException: + // Unable to resolve service for type 'IAcLogWriterBase[]'), amitől a LoggerSignalRHub aktiválása minden + // kliens-kapcsolódásnál elszállt, és a felküldött kliens-logok némán elvesztek. + public LoggerToLoggerApiController(IEnumerable logWriters) : base(logWriters.ToArray()) { } } \ No newline at end of file diff --git a/FruitBank.Common/Interfaces/IFruitBankDataControllerCommon.cs b/FruitBank.Common/Interfaces/IFruitBankDataControllerCommon.cs index 7e1277b4..ffc7e4dc 100644 --- a/FruitBank.Common/Interfaces/IFruitBankDataControllerCommon.cs +++ b/FruitBank.Common/Interfaces/IFruitBankDataControllerCommon.cs @@ -111,6 +111,12 @@ public interface IFruitBankDataControllerCommon Task LoginMeasuringUser(MgLoginModelRequest loginModelRequest); Task?> ProcessAndSaveFullShippingJson(string fullShippingJson, int customerId); + /// + /// True if the server runs against the PROD database. The value is definite by construction + /// (an ambiguous DB catalog fails the server startup); consumers must fail closed on any error. + /// + public Task IsProdEnvironment(); + public Task?> GetGenericAttributeDtosByEntityIdAndKeyGroup(int productId, string keyGroup, int storeId); public Task AddGenericAttributeDto(GenericAttributeDto genericAttributeDto); diff --git a/FruitBank.Common/SignalRs/SignalRTags.cs b/FruitBank.Common/SignalRs/SignalRTags.cs index d62c2ec3..791cf2ab 100644 --- a/FruitBank.Common/SignalRs/SignalRTags.cs +++ b/FruitBank.Common/SignalRs/SignalRTags.cs @@ -131,6 +131,8 @@ public class SignalRTags : AcSignalRTags public const int AuthenticateUser = 195; public const int RefreshToken = 200; + public const int IsProdEnvironment = 210; + #region SendToClient public const int SendOrderChanged = 500; public const int SendOrderItemChanged = 501; diff --git a/FruitBankHybrid.Shared/Services/SignalRs/FruitBankSignalRClient.cs b/FruitBankHybrid.Shared/Services/SignalRs/FruitBankSignalRClient.cs index a441c500..02bea86d 100644 --- a/FruitBankHybrid.Shared/Services/SignalRs/FruitBankSignalRClient.cs +++ b/FruitBankHybrid.Shared/Services/SignalRs/FruitBankSignalRClient.cs @@ -58,6 +58,8 @@ namespace FruitBankHybrid.Shared.Services.SignalRs public Task?> GetMeasuringModels() => GetAllAsync>(SignalRTags.GetMeasuringModels); + public Task IsProdEnvironment() => GetAllAsync(SignalRTags.IsProdEnvironment); + #region Partner public Task?> GetPartners() => GetAllAsync>(SignalRTags.GetPartners); public Task GetPartnerById(int id) => GetByIdAsync(SignalRTags.GetPartnerById, id); diff --git a/FruitBankHybrid.Shared/appsettings.json b/FruitBankHybrid.Shared/appsettings.json index 47f5ae5c..3687fc31 100644 --- a/FruitBankHybrid.Shared/appsettings.json +++ b/FruitBankHybrid.Shared/appsettings.json @@ -14,7 +14,7 @@ //"ApiBaseUrl": "https://shop.fruitbank.hu" }, "Logger": { - "AppType": "Server", + "AppType": "Web", "LogLevel": "Detail", "LogWriters": [ { diff --git a/FruitBankHybrid.Web.Client/Program.cs b/FruitBankHybrid.Web.Client/Program.cs index 83cfc671..1cf2d9ba 100644 --- a/FruitBankHybrid.Web.Client/Program.cs +++ b/FruitBankHybrid.Web.Client/Program.cs @@ -1,3 +1,4 @@ +using AyCode.Core.Enums; using AyCode.Core.Loggers; using AyCode.Core.Serializers.Binaries; using AyCode.Services.SignalRs; @@ -43,9 +44,10 @@ builder.Services.Configure(opts => }); // Logger options + framework factory. LoggerClient instances are created per caller category, -// with AppType+LogLevel from appsettings and writers resolved from DI via IAcLogWriterClientBase. +// with LogLevel from appsettings and writers resolved from DI via IAcLogWriterClientBase. +// AppType: a WASM kliens kód-defaultja Web — az appsettings (AyCode:Logger:AppType) felülírhatja, ha megadja. builder.Services.Configure(builder.Configuration.GetSection("AyCode:Logger")); -builder.Services.AddAcLoggerFactory(); +builder.Services.AddAcLoggerFactory(AppType.Web); // HubConnectionBuilder — transient so each consumer gets a fresh builder to Build(). // All connection and protocol configuration flows from appsettings.json via IOptions; diff --git a/FruitBankHybrid.Web.Client/wwwroot/appsettings.json b/FruitBankHybrid.Web.Client/wwwroot/appsettings.json index 47f5ae5c..3687fc31 100644 --- a/FruitBankHybrid.Web.Client/wwwroot/appsettings.json +++ b/FruitBankHybrid.Web.Client/wwwroot/appsettings.json @@ -14,7 +14,7 @@ //"ApiBaseUrl": "https://shop.fruitbank.hu" }, "Logger": { - "AppType": "Server", + "AppType": "Web", "LogLevel": "Detail", "LogWriters": [ { diff --git a/FruitBankHybrid.Web/Program.cs b/FruitBankHybrid.Web/Program.cs index 56965a95..05d85dff 100644 --- a/FruitBankHybrid.Web/Program.cs +++ b/FruitBankHybrid.Web/Program.cs @@ -1,3 +1,4 @@ +using AyCode.Core.Enums; using AyCode.Core.Loggers; using AyCode.Core.Serializers.Binaries; using AyCode.Services.SignalRs; @@ -26,9 +27,10 @@ builder.Services.AddSingleton(); // Logger options + framework factory. LoggerClient instances are created per caller category, -// with AppType+LogLevel from appsettings and writers resolved from DI via IAcLogWriterBase. +// with LogLevel from appsettings and writers resolved from DI via IAcLogWriterBase. +// AppType: a hybrid WEB-host kód-defaultja Web (az [S] a nop-szerveré) — az appsettings felülírhatja, ha megadja. builder.Services.Configure(builder.Configuration.GetSection("AyCode:Logger")); -builder.Services.AddAcLoggerFactory(); +builder.Services.AddAcLoggerFactory(AppType.Web); builder.Services.AddSingleton(sp => new LoggedInModel(sp.GetRequiredService())); diff --git a/FruitBankHybrid.Web/appsettings.json b/FruitBankHybrid.Web/appsettings.json index 47f5ae5c..3687fc31 100644 --- a/FruitBankHybrid.Web/appsettings.json +++ b/FruitBankHybrid.Web/appsettings.json @@ -14,7 +14,7 @@ //"ApiBaseUrl": "https://shop.fruitbank.hu" }, "Logger": { - "AppType": "Server", + "AppType": "Web", "LogLevel": "Detail", "LogWriters": [ { diff --git a/FruitBankHybrid/MauiProgram.cs b/FruitBankHybrid/MauiProgram.cs index f310f25c..24f7c1f0 100644 --- a/FruitBankHybrid/MauiProgram.cs +++ b/FruitBankHybrid/MauiProgram.cs @@ -1,4 +1,5 @@ -using AyCode.Core.Loggers; +using AyCode.Core.Enums; +using AyCode.Core.Loggers; using AyCode.Services.SignalRs; using FruitBank.Common.Loggers; using FruitBank.Common.Models; @@ -51,9 +52,10 @@ namespace FruitBankHybrid builder.Services.AddSingleton(); // Logger options + framework factory. LoggerClient instances are created per caller category, - // with AppType+LogLevel from appsettings and writers resolved from DI via IAcLogWriterClientBase. + // with LogLevel from appsettings and writers resolved from DI via IAcLogWriterClientBase. + // AppType: a MAUI kliens kód-defaultja Web — az appsettings felülírhatja (pl. Mobile-ra), ha megadja. builder.Services.Configure(builder.Configuration.GetSection("AyCode:Logger")); - builder.Services.AddAcLoggerFactory(); + builder.Services.AddAcLoggerFactory(AppType.Web); // Bind SignalR options from configuration. // Precedence: code default → appsettings.json (this line) → any later Configure action.