From a73b72f8318689f303d5d64c04c05c9bb0215ff4 Mon Sep 17 00:00:00 2001 From: Loretta Date: Fri, 6 Mar 2026 14:27:22 +0100 Subject: [PATCH] Refactor SignalR, pallet editing, and error handling - Refactored SignalRMessageToClientWithText to use explicit constructors and properties. - Added Shipping.Comment column to GridShippingDocument.razor. - Updated PalletItemComponent: renamed audit callback, added pre-save/audit callback, improved UI refresh logic. - MeasuringOut now uses new callbacks and _enablePalletItems flag to control editability during save/audit. - Wrapped SignalR message handling and logout logic in try/catch for better error logging. - MainLayout and MeasuringOut now implement IDisposable to unsubscribe SignalR events. - UI editability and state updates improved for thread safety and responsiveness. --- .../SignalRMessageToClientWithText.cs | 14 +- .../Components/GridShippingDocument.razor | 1 + .../Components/PalletItemComponent.razor | 22 ++- .../Layout/MainLayout.razor.cs | 74 +++++--- .../Pages/MeasuringOut.razor | 8 +- .../Pages/MeasuringOut.razor.cs | 178 ++++++++++-------- 6 files changed, 179 insertions(+), 118 deletions(-) diff --git a/FruitBank.Common/Models/SignalRs/SignalRMessageToClientWithText.cs b/FruitBank.Common/Models/SignalRs/SignalRMessageToClientWithText.cs index 3ef3d991..8fa31263 100644 --- a/FruitBank.Common/Models/SignalRs/SignalRMessageToClientWithText.cs +++ b/FruitBank.Common/Models/SignalRs/SignalRMessageToClientWithText.cs @@ -2,8 +2,16 @@ namespace FruitBank.Common.Models.SignalRs; -public class SignalRMessageToClientWithText(string? message, T? content) +public class SignalRMessageToClientWithText { - public string? Message { get; set; } = message; - public T? Content { get; set; } = content; + public SignalRMessageToClientWithText() + {} + public SignalRMessageToClientWithText(string? message, T? content) : this() + { + Message = message; + Content = content; + } + + public string? Message { get; set; } + public T? Content { get; set; } } \ No newline at end of file diff --git a/FruitBankHybrid.Shared/Components/GridShippingDocument.razor b/FruitBankHybrid.Shared/Components/GridShippingDocument.razor index faa475a9..08c783a9 100644 --- a/FruitBankHybrid.Shared/Components/GridShippingDocument.razor +++ b/FruitBankHybrid.Shared/Components/GridShippingDocument.razor @@ -57,6 +57,7 @@ + diff --git a/FruitBankHybrid.Shared/Components/PalletItemComponent.razor b/FruitBankHybrid.Shared/Components/PalletItemComponent.razor index 14321490..8da64c72 100644 --- a/FruitBankHybrid.Shared/Components/PalletItemComponent.razor +++ b/FruitBankHybrid.Shared/Components/PalletItemComponent.razor @@ -60,7 +60,7 @@ } -@code { + @code { [Inject] public required IEnumerable LogWriters { get; set; } [Inject] public required FruitBankSignalRClient FruitBankSignalRClient { get; set; } [Inject] public required LoggedInModel LoggedInModel { get; set; } @@ -75,10 +75,14 @@ [Parameter] public int? MaxTrayQuantity { get; set; } = null; [Parameter] public bool Editable { get; set; } = true; - //[Parameter] public EventCallback OnPalletItemSaveClick { get; set; } [Parameter] public Func? OnPalletItemSaved { get; set; } [Parameter] public Func? OnPalletItemValueChanged { get; set; } - [Parameter] public Func? OnPalletItemAuditedClick { get; set; } + [Parameter] public Func? OnPalletItemAudited { get; set; } + + /// + /// Before Save or Audit + /// + [Parameter] public Func? OnPalletItemSaveOrAuditClick { get; set; } //public bool LoadingPanelVisible { get; set; } = false; //public bool Editable => !HasAuditButton || (OrderItemPallet.IsAudited); @@ -119,6 +123,8 @@ PalletItem.ModifierId = LoggedInModel.CustomerDto?.Id; + if (OnPalletItemSaveOrAuditClick != null) await OnPalletItemSaveOrAuditClick.Invoke(PalletItem); + var responseShippingItemPallet = await FruitBankSignalRClient.PostDataAsync(AddOrUpdateSignalRTag!.Value, PalletItem); if (responseShippingItemPallet != null) PalletItem.Id = responseShippingItemPallet.Id; //Az UpdateCollection miatt kell, hogy megtalálja mit kell kicserélni! - J. //else _logger.Error($"Sikertelen volt a raklap adatainak mentése!"); @@ -126,7 +132,7 @@ if (OnPalletItemSaved != null) await OnPalletItemSaved.Invoke(responseShippingItemPallet); //LoadingPanelVisible = false; - StateHasChanged(); + await InvokeAsync(StateHasChanged); } protected async Task OnItemUpdating(string fieldName, object newValue, TPalletItem palletItem) @@ -172,15 +178,17 @@ private async Task PalletItemAuditedClick() { - if (OnPalletItemAuditedClick != null) + if (OnPalletItemAudited != null) { if (OrderItemPallet == null) throw new Exception($"PalletItemComponent->PalletItemAuditedClick(); OrderItemPallet == null"); if (await DialogService.ShowConfirmBoxAsync("Megerősítés", "Biztoan jóváhagyja a mérést? Jóváhagyás után a mérés nem módosítható!", MessageBoxRenderStyle.Info)) { + if (OnPalletItemSaveOrAuditClick != null) await OnPalletItemSaveOrAuditClick.Invoke(PalletItem); + OrderItemPallet.RevisorId = LoggedInModel.CustomerDto!.Id; - StateHasChanged(); //Az Audit button miatt kell a StateHasChanged(), most már van RevisorId és emiatt disabled lesz... + await InvokeAsync(StateHasChanged); //Az Audit button miatt kell a StateHasChanged(), most már van RevisorId és emiatt disabled lesz... PalletItem.SetParentPropToNull(); @@ -191,7 +199,7 @@ await DialogService.ShowMessageBoxAsync("Hiba", "Adatok mentése sikertelen volt, ellenőrizze a mérés adatait!", MessageBoxRenderStyle.Danger); } - if (OnPalletItemAuditedClick != null) await OnPalletItemAuditedClick.Invoke(responseShippingItemPallet); + await OnPalletItemAudited.Invoke(responseShippingItemPallet); } } } diff --git a/FruitBankHybrid.Shared/Layout/MainLayout.razor.cs b/FruitBankHybrid.Shared/Layout/MainLayout.razor.cs index 42821e27..55e9bb7a 100644 --- a/FruitBankHybrid.Shared/Layout/MainLayout.razor.cs +++ b/FruitBankHybrid.Shared/Layout/MainLayout.razor.cs @@ -16,7 +16,7 @@ using Microsoft.AspNetCore.Components; namespace FruitBankHybrid.Shared.Layout; -public partial class MainLayout : LayoutComponentBase +public partial class MainLayout : LayoutComponentBase, IDisposable { [Inject] public required IEnumerable LogWriters { get; set; } [Inject] public required NavigationManager NavManager { get; set; } @@ -70,43 +70,57 @@ public partial class MainLayout : LayoutComponentBase { if (messageTag != SignalRTags.NotificationReceived || !LoggedInModel.IsLoggedIn) return; - var notificationMessage = responseDataMessage?.GetResponseData>(); - if (notificationMessage == null) + try { - _logger.Error($"notificationMessage == null"); - return; + var notificationMessage = responseDataMessage?.GetResponseData>(); + if (notificationMessage == null) + { + _logger.Error($"MainLayout.SignalRClientOnMessageReceived; notificationMessage == null; messageTag: {messageTag}"); + return; + } + + toastOrderNumber = null; + toastDateOfReceipt = null; + + toastMessage = notificationMessage.Message; + + var orderDto = notificationMessage.Content; + var hasPermission = orderDto == null || (orderDto.HasMeasuringAccess(LoggedInModel.CustomerDto!.Id, LoggedInModel.IsRevisor) || orderDto.MeasurementOwnerId == 0); + + if (orderDto != null && hasPermission) + { + toastOrderNumber = orderDto.CustomOrderNumber; + toastDateOfReceipt = orderDto.DateOfReceipt; + } + + if (!hasPermission) return; + + _logger.Debug($"MainLayout.SignalRClientOnMessageReceived; NotificationMessage received. {toastMessage}"); + + await InvokeAsync(() => + { + orderNotificationToast?.Show(); + StateHasChanged(); + }); } - - toastOrderNumber = null; - toastDateOfReceipt = null; - - toastMessage = notificationMessage.Message; - - var orderDto = notificationMessage.Content; - var hasPermission = orderDto == null || (orderDto.HasMeasuringAccess(LoggedInModel.CustomerDto!.Id, LoggedInModel.IsRevisor) || orderDto.MeasurementOwnerId == 0); - - if (orderDto != null && hasPermission) + catch (Exception ex) { - toastOrderNumber = orderDto.CustomOrderNumber; - toastDateOfReceipt = orderDto.DateOfReceipt; + _logger.Error($"MainLayout.SignalRClientOnMessageReceived ERROR; messageTag: {messageTag}", ex); } - - if (!hasPermission) return; - - _logger.Debug($"NotificationMessage received. {toastMessage}"); - - await InvokeAsync(() => - { - orderNotificationToast?.Show(); - StateHasChanged(); - }); } private async void OnLogoutClick() { - await LoggedInModel.LogOutAsync(); - RefreshMainLayout(); - NavManager.NavigateTo("/Login"); + try + { + await LoggedInModel.LogOutAsync(); + RefreshMainLayout(); + NavManager.NavigateTo("/Login"); + } + catch (Exception e) + { + _logger.Error($"OnLogoutClick error"); + } } public void RefreshMainLayout() diff --git a/FruitBankHybrid.Shared/Pages/MeasuringOut.razor b/FruitBankHybrid.Shared/Pages/MeasuringOut.razor index e26c8de0..65ec04bc 100644 --- a/FruitBankHybrid.Shared/Pages/MeasuringOut.razor +++ b/FruitBankHybrid.Shared/Pages/MeasuringOut.razor @@ -176,7 +176,8 @@ { var selectedOrderItem = (OrderItemDto)(context.DataItem); - + @for (var index = 0; index < (selectedOrderItem?.OrderItemPallets?.Count ?? 0); index++) { @@ -185,14 +186,15 @@ + OnPalletItemAudited="pallet => OnOrderItemAudited(pallet, selectedOrderItem)"> } diff --git a/FruitBankHybrid.Shared/Pages/MeasuringOut.razor.cs b/FruitBankHybrid.Shared/Pages/MeasuringOut.razor.cs index 116dbd59..85858aa0 100644 --- a/FruitBankHybrid.Shared/Pages/MeasuringOut.razor.cs +++ b/FruitBankHybrid.Shared/Pages/MeasuringOut.razor.cs @@ -17,9 +17,10 @@ using Nop.Core.Domain.Orders; namespace FruitBankHybrid.Shared.Pages { - public partial class MeasuringOut : ComponentBase + public partial class MeasuringOut : ComponentBase, IDisposable { private readonly Lock _lock = new Lock(); + [Inject] public required IEnumerable LogWriters { get; set; } [Inject] public required FruitBankSignalRClient FruitBankSignalRClient { get; set; } [Inject] public required NavigationManager NavManager{ get; set; } @@ -29,6 +30,7 @@ namespace FruitBankHybrid.Shared.Pages private LoggerClient _logger = null!; private string _errorText; + private bool _enablePalletItems = true; private int _lastDaysCount = 1; public bool HasMeasuringAccess; public bool LoadingPanelVisible { get; set; } = true; @@ -59,99 +61,106 @@ namespace FruitBankHybrid.Shared.Pages if (messageTag != SignalRTags.SendOrderItemDeleted && messageTag != SignalRTags.SendOrderChanged && messageTag != SignalRTags.SendOrderItemChanged && messageTag != SignalRTags.SendOrderItemPalletChanged && messageTag != SignalRTags.SendProductChanged) return; - _logger.DebugConditional($"SignalRClientOnMessageReceived received. {responseDataMessage}"); + _logger.DebugConditional($"MeasuringOut.SignalRClientOnMessageReceived received. {responseDataMessage}"); - OrderDto? orderDto; - OrderItem? orderItem; - OrderItemDto? orderItemDto; - - switch (messageTag) + try { - case SignalRTags.SendOrderItemDeleted: - orderItem = responseDataMessage?.GetResponseData(); - if (orderItem == null) break; + OrderDto? orderDto; + OrderItem? orderItem; + OrderItemDto? orderItemDto; - lock (_lock) - { - orderDto = SelectedDayOrders.FirstOrDefault(o => o.OrderItemDtos.Any(oi => oi.Id == orderItem.Id)); - orderDto?.OrderItemDtos.RemoveAll(oi => oi.Id == orderItem.Id); - } + switch (messageTag) + { + case SignalRTags.SendOrderItemDeleted: + orderItem = responseDataMessage?.GetResponseData(); + if (orderItem == null) break; - await InvokeAsync(StateHasChanged); - return; - case SignalRTags.SendOrderChanged: - orderDto = responseDataMessage?.GetResponseData(); - if (orderDto == null) break; + lock (_lock) + { + orderDto = SelectedDayOrders.FirstOrDefault(o => o.OrderItemDtos.Any(oi => oi.Id == orderItem.Id)); + orderDto?.OrderItemDtos.RemoveAll(oi => oi.Id == orderItem.Id); + } - if (orderDto.DateOfReceipt == null) return; + await InvokeAsync(StateHasChanged); + return; + case SignalRTags.SendOrderChanged: + orderDto = responseDataMessage?.GetResponseData(); + if (orderDto == null) break; - lock (_lock) - { - if (_measuringDates.All(x => x.DateTime.Date != orderDto.DateOfReceipt.Value.Date)) - _measuringDates.Add(new MeasuringDateSelectorModel(orderDto.Id, orderDto.DateOfReceipt.Value.Date, orderDto.IsMeasured)); + if (orderDto.DateOfReceipt == null) return; - if (SelectedDate != orderDto.DateOfReceipt.Value.Date) return; + lock (_lock) + { + if (_measuringDates.All(x => x.DateTime.Date != orderDto.DateOfReceipt.Value.Date)) + _measuringDates.Add(new MeasuringDateSelectorModel(orderDto.Id, orderDto.DateOfReceipt.Value.Date, orderDto.IsMeasured)); - //Elég lenne ez is, csak a CopyTo a Collection - ökben lévő elemeket hozzáfűzi és duplikálva lesznek... -J. - if (SelectedOrder?.Id == orderDto.Id) orderDto.CopyTo(SelectedOrder); - else SelectedDayOrders.UpdateCollection(orderDto, false); + if (SelectedDate != orderDto.DateOfReceipt.Value.Date) return; - //var selectedOrderId = SelectedOrder?.Id; - //SelectedDayOrders.UpdateCollection(orderDto, false); + //Elég lenne ez is, csak a CopyTo a Collection - ökben lévő elemeket hozzáfűzi és duplikálva lesznek... -J. + if (SelectedOrder?.Id == orderDto.Id) orderDto.CopyTo(SelectedOrder); + else SelectedDayOrders.UpdateCollection(orderDto, false); - //if (selectedOrderId.GetValueOrDefault(-1) == orderDto.Id) SelectedOrder = orderDto; - } + //var selectedOrderId = SelectedOrder?.Id; + //SelectedDayOrders.UpdateCollection(orderDto, false); - await InvokeAsync(StateHasChanged); - return; - case SignalRTags.SendOrderItemChanged: - orderItemDto = responseDataMessage?.GetResponseData(); - if (orderItemDto == null) break; + //if (selectedOrderId.GetValueOrDefault(-1) == orderDto.Id) SelectedOrder = orderDto; + } - lock (_lock) - { - var localOrderDto = SelectedDayOrders.FirstOrDefault(o => o.OrderItemDtos.Any(oi => oi.Id == orderItemDto.Id)); - var localOrderItemDto = localOrderDto?.OrderItemDtos.FirstOrDefault(x => x.Id == orderItemDto.Id); - - if (localOrderItemDto == null) return; + await InvokeAsync(StateHasChanged); + return; + case SignalRTags.SendOrderItemChanged: + orderItemDto = responseDataMessage?.GetResponseData(); + if (orderItemDto == null) break; - //orderItemDto.OrderDto = localOrderDto!; + lock (_lock) + { + var localOrderDto = SelectedDayOrders.FirstOrDefault(o => o.OrderItemDtos.Any(oi => oi.Id == orderItemDto.Id)); + var localOrderItemDto = localOrderDto?.OrderItemDtos.FirstOrDefault(x => x.Id == orderItemDto.Id); - localOrderItemDto.Quantity = orderItemDto.Quantity; - localOrderItemDto.GenericAttributes.UpdateBaseEntityCollection(orderItemDto.GenericAttributes, false); - } + if (localOrderItemDto == null) return; - await InvokeAsync(StateHasChanged); - return; - case SignalRTags.SendOrderItemPalletChanged: - var orderItemPallet = responseDataMessage?.GetResponseData(); - if (orderItemPallet == null) break; + //orderItemDto.OrderDto = localOrderDto!; - lock (_lock) - { - var orderItemDtos = SelectedDayOrders.FirstOrDefault(x => x.OrderItemDtos.Any(oi => oi.Id == orderItemPallet.OrderItemId))?.OrderItemDtos; - - orderItemDto = orderItemDtos?.FirstOrDefault(oi => oi.Id == orderItemPallet.OrderItemId); - if (orderItemDto == null) return; + localOrderItemDto.Quantity = orderItemDto.Quantity; + localOrderItemDto.GenericAttributes.UpdateBaseEntityCollection(orderItemDto.GenericAttributes, false); + } - orderItemPallet.OrderItemDto = orderItemDto; - var orderItemPalletsCount = orderItemDto.OrderItemPallets.Count; + await InvokeAsync(StateHasChanged); + return; + case SignalRTags.SendOrderItemPalletChanged: + var orderItemPallet = responseDataMessage?.GetResponseData(); + if (orderItemPallet == null) break; - if (orderItemDto.OrderItemPallets[orderItemPalletsCount - 1].Id == 0) orderItemDto.OrderItemPallets.Insert(orderItemPalletsCount - 1, orderItemPallet); - else orderItemDto.OrderItemPallets.UpdateCollection(orderItemPallet, false); - } + lock (_lock) + { + var orderItemDtos = SelectedDayOrders.FirstOrDefault(x => x.OrderItemDtos.Any(oi => oi.Id == orderItemPallet.OrderItemId))?.OrderItemDtos; - await InvokeAsync(StateHasChanged); - return; - case SignalRTags.SendProductChanged: - var productDto = responseDataMessage?.GetResponseData(); - if (productDto == null) break; + orderItemDto = orderItemDtos?.FirstOrDefault(oi => oi.Id == orderItemPallet.OrderItemId); + if (orderItemDto == null) return; - await InvokeAsync(StateHasChanged); - return; + orderItemPallet.OrderItemDto = orderItemDto; + var orderItemPalletsCount = orderItemDto.OrderItemPallets.Count; + + if (orderItemDto.OrderItemPallets[orderItemPalletsCount - 1].Id == 0) orderItemDto.OrderItemPallets.Insert(orderItemPalletsCount - 1, orderItemPallet); + else orderItemDto.OrderItemPallets.UpdateCollection(orderItemPallet, false); + } + + await InvokeAsync(StateHasChanged); + return; + case SignalRTags.SendProductChanged: + var productDto = responseDataMessage?.GetResponseData(); + if (productDto == null) break; + + await InvokeAsync(StateHasChanged); + return; + } + + _logger.Error($"MeasuringOut.SignalRClientOnMessageReceived message == null; messageTag: {messageTag}"); + } + catch (Exception ex) + { + _logger.Error($"MeasuringOut.SignalRClientOnMessageReceived ERROR; messageTag: {messageTag}", ex); } - - _logger.Error($"SignalRClientOnMessageReceived message == null"); } private async Task RefreshOrdersFromDb(DateTime dateTime, int lastDaysCount) @@ -241,12 +250,25 @@ namespace FruitBankHybrid.Shared.Pages return Task.CompletedTask; } - private async Task OnPalletItemAuditedClick(OrderItemPallet? orderItemPallet, OrderItemDto selectedOrderItemDto) + private async Task OnOrderItemAudited(OrderItemPallet? orderItemPallet, OrderItemDto selectedOrderItemDto) { if (orderItemPallet == null) return; + + _enablePalletItems = false; + + await InvokeAsync(StateHasChanged); await OnOrderItemPalletSaved(orderItemPallet, selectedOrderItemDto); } + private async Task OnOrderItemSaveOrAuditClick(OrderItemPallet? orderItemPallet, OrderItemDto selectedOrderItemDto) + { + if (orderItemPallet != null) + { + _enablePalletItems = false; + await InvokeAsync(StateHasChanged); + } + } + private async Task OnOrderItemPalletSaved(OrderItemPallet? orderItemPallet, OrderItemDto selectedOrderItemDto) { if (orderItemPallet != null) @@ -258,7 +280,8 @@ namespace FruitBankHybrid.Shared.Pages } else await DialogService.ShowMessageBoxAsync("Hiba", "Adatok mentése sikertelen volt, ellenőrizze a mérés adatait!", MessageBoxRenderStyle.Danger); - StateHasChanged(); + _enablePalletItems = true; + await InvokeAsync(StateHasChanged); } private Task AddNewPalletItemClick(OrderItemDto selectedOrderItemDto) @@ -359,5 +382,10 @@ namespace FruitBankHybrid.Shared.Pages orderDto.GenericAttributes.UpdateBaseEntityCollection(genericAttributes, false); } } + + public void Dispose() + { + FruitBankSignalRClient.OnMessageReceived -= SignalRClientOnMessageReceived; + } } }