From ff1a5b63c1044096078010e61a6a797dfd57c4b0 Mon Sep 17 00:00:00 2001 From: Loretta Date: Sun, 22 Mar 2026 16:04:17 +0100 Subject: [PATCH 1/5] Allow info panel collapse, improve grids, update SignalR - Enable collapsing of info panel in MgGridWithInfoPanel for better UX - Refactor GridStockTakingItem columns to use nameof(), clarify bindings, and improve maintainability - Enhance MeasuringOut order selector with advanced search and clear button - Update SignalR.Core reference to 9.0.14 in project file - Minor cleanup of commented group display logic in grid --- .../FruitBank.Common.Server.csproj | 2 +- .../GridStockTakingItem.razor | 46 ++++++++----------- .../Pages/MeasuringOut.razor | 7 +++ 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/FruitBank.Common.Server/FruitBank.Common.Server.csproj b/FruitBank.Common.Server/FruitBank.Common.Server.csproj index c31b4f30..9caa2d68 100644 --- a/FruitBank.Common.Server/FruitBank.Common.Server.csproj +++ b/FruitBank.Common.Server/FruitBank.Common.Server.csproj @@ -41,7 +41,7 @@ ..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\Debug\net9.0\AyCode.Services.Server.dll - C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref\9.0.13\ref\net9.0\Microsoft.AspNetCore.SignalR.Core.dll + C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref\9.0.14\ref\net9.0\Microsoft.AspNetCore.SignalR.Core.dll ..\..\NopCommerce.Common\4.70\Libraries\Mango.Nop.Core\bin\FruitBank\Debug\net9.0\Mango.Nop.Core.dll diff --git a/FruitBankHybrid.Shared/Components/Grids/StockTakingItems/GridStockTakingItem.razor b/FruitBankHybrid.Shared/Components/Grids/StockTakingItems/GridStockTakingItem.razor index 868288d5..15cc8b49 100644 --- a/FruitBankHybrid.Shared/Components/Grids/StockTakingItems/GridStockTakingItem.razor +++ b/FruitBankHybrid.Shared/Components/Grids/StockTakingItems/GridStockTakingItem.razor @@ -16,23 +16,21 @@ + CssClass="@GridCss" ValidationEnabled="false" OnGridFocusedRowChanged="Grid_FocusedRowChanged"> + @* CustomizeGroupValueDisplayText="Grid_CustomizeGroupValueDisplayText"> *@ - + - - - @(((StockTakingItem)context.DataItem)?.StockTaking?.StartDateTime.ToString("g") ?? "") - - - - + + + + - + - - + + @@ -41,8 +39,8 @@ - - + + @@ -103,22 +101,14 @@ EditItemsEnabled = true; } - // void Grid_CustomGroup(GridCustomGroupEventArgs e) + // static void Grid_CustomizeGroupValueDisplayText(GridCustomizeGroupValueDisplayTextEventArgs e) // { - // if (e.FieldName != "StockTaking.StartDateTime") return; + // if (e.FieldName != nameof(StockTakingItem.StockTakingId)) return; - // e.SameGroup = ((StockTakingItem)e.DataItem1).StockTakingId == ((StockTakingItem)e.DataItem2).StockTakingId; - // e.Handled = true; - // } - - // void Grid_CustomizeGroupValueDisplayText(GridCustomizeGroupValueDisplayTextEventArgs e) - // { - // return; - - // if (e.FieldName != "StockTaking.StartDateTime") return; - - // var startDate = (DateTime)e.Value; - // e.DisplayText = startDate.ToString("g"); + // if (e.GetRowValue("StockTaking.StartDateTime") is DateTime startDateTime) + // { + // e.DisplayText = startDateTime.ToString("g"); + // } // } } diff --git a/FruitBankHybrid.Shared/Pages/MeasuringOut.razor b/FruitBankHybrid.Shared/Pages/MeasuringOut.razor index 65ec04bc..302ac3fe 100644 --- a/FruitBankHybrid.Shared/Pages/MeasuringOut.razor +++ b/FruitBankHybrid.Shared/Pages/MeasuringOut.razor @@ -67,6 +67,13 @@ CssClass="cw-480" Context="ctxOrder" DropDownBodyCssClass="dd-body-class" + SearchMode="ListSearchMode.AutoSearch" + SearchFilterCondition="ListSearchFilterCondition.Contains" + SearchTextParseMode="ListSearchTextParseMode.GroupWordsByAnd" + ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto" + DropDownTriggerMode="DropDownTriggerMode.Click" + ListRenderMode="ListRenderMode.Entire" + ShowDropDownButton="false" SelectedDataItemChanged="@((SelectedDataItemChangedEventArgs args) => OnSelectedOrderChanged(args))" InputId="cbOrders"> From 84f4990ff4c52a7d64dbbc706502ee37efd78c0d Mon Sep 17 00:00:00 2001 From: Loretta Date: Sun, 22 Mar 2026 20:01:49 +0100 Subject: [PATCH 2/5] Add MgCardView component & refactor MeasuringOut to tabs Introduced a reusable, responsive MgCardView component for displaying data as cards with optional filtering and paging. Refactored the MeasuringOut page to use a tabbed interface: daily tasks are now shown as filterable cards, and measuring details are separated into a dedicated tab. Improved UI clarity, code organization, and maintainability. --- .../Pages/MeasuringIn.razor | 2 +- .../Pages/MeasuringOut.razor | 460 ++++++++++-------- .../Pages/MeasuringOut.razor.cs | 119 +++-- FruitBankHybrid.Shared/wwwroot/app.css | 11 +- 4 files changed, 343 insertions(+), 249 deletions(-) diff --git a/FruitBankHybrid.Shared/Pages/MeasuringIn.razor b/FruitBankHybrid.Shared/Pages/MeasuringIn.razor index 08dfd587..63124bf9 100644 --- a/FruitBankHybrid.Shared/Pages/MeasuringIn.razor +++ b/FruitBankHybrid.Shared/Pages/MeasuringIn.razor @@ -10,7 +10,7 @@

Áru bevételezés

-
+
Áru kiadás -
- +
- - + -
-
-
- - - @{ - var cssClass = GetMeasuringDateCssClassNames(ctxOrderDate); - if (!cssClass.IsNullOrWhiteSpace()) - { - @ctxOrderDate.Day.ToString() - } - else - { - @ctxOrderDate.Day.ToString() - } - } - - -
-
- -
-
-
+ + + @{ + var cssClass = GetMeasuringDateCssClassNames(ctxOrderDate); + if (!cssClass.IsNullOrWhiteSpace()) + { + @ctxOrderDate.Day.ToString() + } + else + { + @ctxOrderDate.Day.ToString() + } + } + +
- - - - - @ctxOrder.DisplayText - - - - - + + - - @if (SelectedOrder == null) - { - - } - else - { - if (SelectedOrder is { MeasurementOwnerId: 0, IsComplete: false } && HasMeasuringAccess) - { - - - - } - else - { - - - - } - } - - @* *@ - @if (SelectedOrder != null && LoggedInModel.IsRevisor) - { - var isCompleteOrder = SelectedOrder.IsComplete; - - - - - }
- @if (SelectedOrder == null || LoadingPanelVisible) - { - } - else if (!HasMeasuringAccess) - { -
-

Mások végzik a mérést!

-
- } - else - { - string? orderNote; - if (!(orderNote = SelectedOrder?.OrderNotes.LastOrDefault(x => x.Note.StartsWith('*'))?.Note).IsNullOrWhiteSpace()) - { -
- Megjegyzés: @(orderNote) + + +
+ + + + + +
+
#@context.CustomOrderNumber
+ @GetOrderStatusText(context.MeasuringStatus) +
+
+ @context.DateOfReceiptOrCreated.ToString("H:mm") — @context.Customer.Company +
+ @foreach (var item in context.OrderItemDtos) + { +
+ @item.ProductName — @item.TrayQuantity/@item.Quantity rekesz +
+ } +
+
- } +
+ +
+ + + + + @ctxOrder.DisplayText + + + + + + -
-

- Rendelés azonosító: #@(SelectedOrder?.CustomOrderNumber) -

- - - - - @* *@ - - - @{ - if (context.Level == 0) + @if (SelectedOrder == null) + { + + + + } + else + { + if (SelectedOrder is { MeasurementOwnerId: 0, IsComplete: false } && HasMeasuringAccess) { - var cssClass = "text-danger"; - - var selectedOrderItemDto = (OrderItemDto)(context.DataItem); - var trayQuantity = selectedOrderItemDto.TrayQuantity; //selectedOrderItemDto.OrderItemPallets.Where(x => x.IsMeasured).Sum(x => x.TrayQuantity); - - var isValid = selectedOrderItemDto.IsValidMeasuringValues(); - var isValidAndMeasured = isValid && selectedOrderItemDto.IsMeasuredAndValid(); // && selectedOrderItemDto.; - - if (isValid && !selectedOrderItemDto.AverageWeightIsValid) cssClass = "text-warning"; - else if (isValidAndMeasured) cssClass = "text-success"; - else if (isValid) cssClass = string.Empty; - - var displayText = $"{selectedOrderItemDto.ProductName} - [{trayQuantity}/{selectedOrderItemDto.Quantity} rekesz, {(selectedOrderItemDto.IsMeasurable ? "net.súly: " + selectedOrderItemDto.NetWeight + "kg." : "nem mérendő!")}]"; - if (selectedOrderItemDto.MeasuringStatus == MeasuringStatus.Audited) displayText = $"[{selectedOrderItemDto.MeasuringStatus}] " + displayText; - -
@(displayText)
+ + + + } + else + { + + + } } -
- - @{ - if (context.Level == 0) - { - var selectedOrderItem = (OrderItemDto)(context.DataItem); - - - @for (var index = 0; index < (selectedOrderItem?.OrderItemPallets?.Count ?? 0); index++) + @if (SelectedOrder != null && LoggedInModel.IsRevisor) + { + var isCompleteOrder = SelectedOrder.IsComplete; + + + + + } + + + @if (SelectedOrder == null || LoadingPanelVisible) + { + } + else if (!HasMeasuringAccess) + { + + } + else + { + string? orderNote; + if (!(orderNote = SelectedOrder?.OrderNotes.LastOrDefault(x => x.Note.StartsWith('*'))?.Note).IsNullOrWhiteSpace()) + { + + } + +
+

+ Rendelés azonosító: #@(SelectedOrder?.CustomOrderNumber) +

+ + + + + + + @{ + if (context.Level == 0) { - var localI = index + 1; - var currentOrderItemPallet = selectedOrderItem!.OrderItemPallets![index]; + var cssClass = "text-danger"; - - + var selectedOrderItemDto = (OrderItemDto)(context.DataItem); + var trayQuantity = selectedOrderItemDto.TrayQuantity; //selectedOrderItemDto.OrderItemPallets.Where(x => x.IsMeasured).Sum(x => x.TrayQuantity); + + var isValid = selectedOrderItemDto.IsValidMeasuringValues(); + var isValidAndMeasured = isValid && selectedOrderItemDto.IsMeasuredAndValid(); // && selectedOrderItemDto.; + + if (isValid && !selectedOrderItemDto.AverageWeightIsValid) cssClass = "text-warning"; + else if (isValidAndMeasured) cssClass = "text-success"; + else if (isValid) cssClass = string.Empty; + + var displayText = $"{selectedOrderItemDto.ProductName} - [{trayQuantity}/{selectedOrderItemDto.Quantity} rekesz, {(selectedOrderItemDto.IsMeasurable ? "net.súly: " + selectedOrderItemDto.NetWeight + "kg." : "nem mérendő!")}]"; + if (selectedOrderItemDto.MeasuringStatus == MeasuringStatus.Audited) displayText = $"[{selectedOrderItemDto.MeasuringStatus}] " + displayText; + +
@(displayText)
} - - - - - - - - - - - - - - TOTAL: - - - - Rekesz: @(selectedOrderItem.TrayQuantity) db - Br: @(selectedOrderItem.GrossWeight) kg - Net: @(selectedOrderItem.NetWeight) kg - - - - - @if (!_errorText.IsNullOrWhiteSpace()) - { - - HIBA! @_errorText - - //_errorText = string.Empty; } - -
- } - } - -
-
- } + + + @{ + if (context.Level == 0) + { + var selectedOrderItem = (OrderItemDto)(context.DataItem); + + + + @for (var index = 0; index < (selectedOrderItem?.OrderItemPallets?.Count ?? 0); index++) + { + var localI = index + 1; + var currentOrderItemPallet = selectedOrderItem!.OrderItemPallets![index]; + + + + } + + + + + + + + + + + + + + TOTAL: + + + + Rekesz: @(selectedOrderItem.TrayQuantity) db + Br: @(selectedOrderItem.GrossWeight) kg + Net: @(selectedOrderItem.NetWeight) kg + + + + + @if (!_errorText.IsNullOrWhiteSpace()) + { + + + + } + +
+ } + } +
+
+
+ } +
+
+
diff --git a/FruitBankHybrid.Shared/Pages/MeasuringOut.razor.cs b/FruitBankHybrid.Shared/Pages/MeasuringOut.razor.cs index 85858aa0..23d5f0dd 100644 --- a/FruitBankHybrid.Shared/Pages/MeasuringOut.razor.cs +++ b/FruitBankHybrid.Shared/Pages/MeasuringOut.razor.cs @@ -4,6 +4,7 @@ using AyCode.Services.SignalRs; using DevExpress.Blazor; using FruitBank.Common.Dtos; using FruitBank.Common.Entities; +using FruitBank.Common.Enums; using FruitBank.Common.Models; using FruitBank.Common.SignalRs; using FruitBankHybrid.Shared.Extensions; @@ -30,8 +31,16 @@ namespace FruitBankHybrid.Shared.Pages private LoggerClient _logger = null!; private string _errorText; + private enum MeasuringTab + { + DailyTasks = 0, + Measuring = 1 + } + private bool _enablePalletItems = true; private int _lastDaysCount = 1; + private MeasuringTab _activeTab = MeasuringTab.DailyTasks; + private IEnumerable _statusFilter = [MeasuringStatus.NotStarted, MeasuringStatus.Started, MeasuringStatus.Finnished]; public bool HasMeasuringAccess; public bool LoadingPanelVisible { get; set; } = true; public bool IsAllOrderItemPalletAudited => SelectedOrder?.IsAllOrderItemAudited ?? false; @@ -42,6 +51,12 @@ namespace FruitBankHybrid.Shared.Pages private List _measuringDates = null!; + private IReadOnlyList FilteredOrders => SelectedDayOrders is null + ? [] + : _statusFilter.Any() + ? SelectedDayOrders.Where(o => _statusFilter.Contains(o.MeasuringStatus)).ToList() + : SelectedDayOrders; + protected override async Task OnInitializedAsync() { LoadingPanelVisible = true; @@ -187,7 +202,7 @@ namespace FruitBankHybrid.Shared.Pages SelectedOrder ??= SelectedDayOrders.FirstOrDefault(); } - LoadingPanelVisible = SelectedOrder != null; //Lefut a change és ott lesz false! - J. + LoadingPanelVisible = false; } private async Task OnMeasuringDateChanged(DateTime selectedDateTime) => await RefreshOrdersFromDb(selectedDateTime, _lastDaysCount); @@ -206,42 +221,11 @@ namespace FruitBankHybrid.Shared.Pages private async Task OnSelectedOrderChanged(SelectedDataItemChangedEventArgs eventArgs) { - //var orderDtosFromDb = await FruitBankSignalRClient.GetPendingOrderDtos(); - //if (orderDtosFromDb != null) RefreshOrderGenericAttributes(orderDtosFromDb, SelectedDayOrders); - //else MessageBox.ShowMessageBox("Hiba", "Az adatok letöltése sikertelen!", MessageBoxRenderStyle.Danger); - var orderDto = eventArgs.DataItem; if (orderDto != null && !LoadingPanelVisible) - { - //LoadingPanelVisible = true; - var orderFromDb = await FruitBankSignalRClient.GetOrderDtoById(orderDto.Id); + await RefreshOrderStatusFromDbAsync(orderDto); - if (orderFromDb != null) - { - orderDto.OrderStatus = orderFromDb.OrderStatus; - orderDto.GenericAttributes.UpdateBaseEntityCollection(orderFromDb.GenericAttributes, false); - - //if (LoggedInModel.IsRevisor) - //{ - // orderDto.OrderItemDtos.UpdateCollection(orderFromDb.OrderItemDtos, false); - - // var orderItemPalletsByOrderId = orderFromDb.OrderItemDtos.Where(o => o.OrderItemPallets.Count > 0).ToDictionary(k => k.Id, v => v.OrderItemPallets); - // foreach (var orderItemDto in orderDto.OrderItemDtos) - // { - // if (orderItemPalletsByOrderId.TryGetValue(orderDto.Id, out var orderItemPallets)) - // orderItemDto.OrderItemPallets.UpdateCollection(orderItemPallets, false); - // } - //} - } - } - - LoadingPanelVisible = false; - HasMeasuringAccess = orderDto?.HasMeasuringAccess(LoggedInModel.CustomerDto?.Id, LoggedInModel.IsRevisor) ?? false; - - StateHasChanged(); - - if (!HasMeasuringAccess && orderDto != null) - await DialogService.ShowMessageBoxAsync("Információ", "A mérés már folyamatban, válasszon másik rendelést!", MessageBoxRenderStyle.Info); + await ApplyMeasuringAccessAsync(orderDto); } private Task OnOrderItemPalletValueChanged(OrderItemPallet orderItemPallet, OrderItemDto selectedOrderItemDto) @@ -383,6 +367,73 @@ namespace FruitBankHybrid.Shared.Pages } } + /// + /// Navigates from a card click (Napi feladatok tab) to the Mérés tab with the selected order. + /// Loads the full order from DB and switches the active tab. + /// + private async Task NavigateToMeasuringTab(OrderDto order) + { + LoadingPanelVisible = true; + SelectedOrder = order; + + await RefreshOrderStatusFromDbAsync(order); + + _activeTab = MeasuringTab.Measuring; + + await ApplyMeasuringAccessAsync(order); + } + + /// + /// Fetches the latest order data from DB and updates status and generic attributes. + /// + private async Task RefreshOrderStatusFromDbAsync(OrderDto order) + { + var orderFromDb = await FruitBankSignalRClient.GetOrderDtoById(order.Id); + if (orderFromDb != null) + { + order.OrderStatus = orderFromDb.OrderStatus; + order.GenericAttributes.UpdateBaseEntityCollection(orderFromDb.GenericAttributes, false); + } + } + + /// + /// Sets measuring access, hides loading panel, refreshes UI, and shows info dialog if access is denied. + /// + private async Task ApplyMeasuringAccessAsync(OrderDto? order) + { + HasMeasuringAccess = order?.HasMeasuringAccess(LoggedInModel.CustomerDto?.Id, LoggedInModel.IsRevisor) ?? false; + LoadingPanelVisible = false; + + StateHasChanged(); + + if (!HasMeasuringAccess && order != null) + await DialogService.ShowMessageBoxAsync("Információ", "A mérés már folyamatban, válasszon másik rendelést!", MessageBoxRenderStyle.Info); + } + + private static string GetOrderStatusCssClass(MeasuringStatus status) => status switch + { + MeasuringStatus.Audited => "text-success", + MeasuringStatus.Finnished => "text-primary", + MeasuringStatus.Started => "text-warning", + _ => "text-muted" + }; + + private static string GetOrderStatusBadgeCssClass(MeasuringStatus status) => status switch + { + MeasuringStatus.Audited => "bg-success", + MeasuringStatus.Finnished => "bg-primary", + MeasuringStatus.Started => "bg-warning text-dark", + _ => "bg-secondary" + }; + + private static string GetOrderStatusText(MeasuringStatus status) => status switch + { + MeasuringStatus.Audited => "Lezárva", + MeasuringStatus.Finnished => "Kész", + MeasuringStatus.Started => "Folyamatban", + _ => "Nem kezdett" + }; + public void Dispose() { FruitBankSignalRClient.OnMessageReceived -= SignalRClientOnMessageReceived; diff --git a/FruitBankHybrid.Shared/wwwroot/app.css b/FruitBankHybrid.Shared/wwwroot/app.css index 7f7ea73c..71e41aa0 100644 --- a/FruitBankHybrid.Shared/wwwroot/app.css +++ b/FruitBankHybrid.Shared/wwwroot/app.css @@ -79,12 +79,19 @@ h1:focus { .measuring-form-layout { margin-top: 15px; - margin-bottom: 25px; + margin-bottom: 10px; padding: 8px; - background-color: lightgrey; + background-color: #e9eff5; + border: 1px solid #d5dde6; border-radius: 8px; } +.measuring-tabs { + border: 1px solid #d5dde6; + border-radius: 8px; + padding: 0; +} + .dd-body-class, .dd-body-class .dxbl-list-box-render-container { max-height: 600px !important; From e22b6f304e3e7d76ee0b58d0c92f89b19d3ca36a Mon Sep 17 00:00:00 2001 From: Loretta Date: Mon, 23 Mar 2026 05:32:15 +0100 Subject: [PATCH 3/5] Add scroll-to-item support to MgCardView component - Introduced `Height`, `ScrollToItem`, and `ItemKeySelector` parameters to MgCardView for scroll targeting and internal scroll area. - Cards now have stable DOM ids for precise JS-based scrolling. - Added mgCardView.js with smooth scroll logic; included script in app. - Updated CSS for scrollable card area. - Updated MeasuringOut.razor to use new scroll features and fixed time format. --- FruitBankHybrid.Shared/Pages/MeasuringOut.razor | 9 ++++++--- FruitBankHybrid.Web/Components/App.razor | 1 + FruitBankHybrid/wwwroot/index.html | 5 +++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/FruitBankHybrid.Shared/Pages/MeasuringOut.razor b/FruitBankHybrid.Shared/Pages/MeasuringOut.razor index fffed34e..74d9231a 100644 --- a/FruitBankHybrid.Shared/Pages/MeasuringOut.razor +++ b/FruitBankHybrid.Shared/Pages/MeasuringOut.razor @@ -58,8 +58,11 @@
+ ShowFilterPanel="true" + OnCardClick="NavigateToMeasuringTab" + ScrollToItem="@SelectedOrder" + ItemKeySelector="o => o.Id" + Height="calc(100vh - 220px)"> @GetOrderStatusText(context.MeasuringStatus)
- @context.DateOfReceiptOrCreated.ToString("H:mm") — @context.Customer.Company + @context.DateOfReceiptOrCreated.ToString("HH:mm") — @context.Customer.Company
@foreach (var item in context.OrderItemDtos) { diff --git a/FruitBankHybrid.Web/Components/App.razor b/FruitBankHybrid.Web/Components/App.razor index 354fe3a0..65f5e74c 100644 --- a/FruitBankHybrid.Web/Components/App.razor +++ b/FruitBankHybrid.Web/Components/App.razor @@ -26,6 +26,7 @@ + - - + + +