Refactor measuring UI, centralize status logic
- Reworked MeasuringIn to use tabbed interface with card view and improved form layout - Unified status badge/text/color logic via new MeasurementService helpers - Updated MeasuringOut to use centralized status display and improved order note handling - Added shipping date column to GridShippingDocument - Improved link styling in MgGridDataColumn - Removed redundant code and applied minor UI/layout tweaks for consistency and maintainability
This commit is contained in:
parent
e22b6f304e
commit
fcd7866d09
|
|
@ -43,6 +43,7 @@
|
|||
</DxComboBoxSettings>
|
||||
</EditSettings>
|
||||
</DxGridDataColumn>
|
||||
<DxGridDataColumn FieldName="Shipping.ShippingDate" Caption="Beérkezés" ReadOnly="true" />
|
||||
<DxGridDataColumn FieldName="ShippingId" Caption="Shipping" Visible="@(!ParentDataItemIsShipping)" ReadOnly="@ParentDataItemIsShipping">
|
||||
<EditSettings>
|
||||
<DxComboBoxSettings Data="Shippings"
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
@using DevExpress.Blazor
|
||||
@using DevExpress.Blazor.Internal
|
||||
@using FruitBank.Common.Entities
|
||||
@using FruitBank.Common.Enums
|
||||
@using FruitBank.Common.SignalRs
|
||||
@using FruitBankHybrid.Shared.Components
|
||||
@using FruitBankHybrid.Shared.Services
|
||||
@using AyCode.Blazor.Components.Components.CardViews
|
||||
@using Mango.Nop.Core.Dtos
|
||||
|
||||
<h3>Áru bevételezés</h3>
|
||||
|
|
@ -19,8 +21,9 @@
|
|||
IndicatorAnimationType="WaitIndicatorAnimationType.Spin"
|
||||
Text="Adatok szinkronizálása folyamatban...">
|
||||
|
||||
<DxFormLayout CaptionPosition="CaptionPosition.Vertical" CssClass="w-100">
|
||||
<DxFormLayoutItem Caption="Dátum" ColSpanMd="2" CaptionCssClass="@(SelectedShipping != null && _measuringDates.Where(x => MeasurementService.DaysEqual(x.DateTime, SelectedShipping.ShippingDate)).All(x => x.IsMeasured) ? "text-success" : "")">
|
||||
<DxFormLayout CaptionPosition="CaptionPosition.Vertical" CssClass="w-100 measuring-form-layout">
|
||||
<DxFormLayoutItem Caption="Dátum" ColSpanXs="8" ColSpanSm="8" ColSpanMd="2"
|
||||
CaptionCssClass="@(SelectedShipping != null && _measuringDates.Where(x => MeasurementService.DaysEqual(x.DateTime, SelectedShipping.ShippingDate)).All(x => x.IsMeasured) ? "text-success" : "")">
|
||||
<DxDateEdit CssClass="cw-320"
|
||||
DisplayFormat="m"
|
||||
Format="m"
|
||||
|
|
@ -46,7 +49,63 @@
|
|||
</DayCellTemplate>
|
||||
</DxDateEdit>
|
||||
</DxFormLayoutItem>
|
||||
</DxFormLayout>
|
||||
|
||||
<DxTabs ActiveTabIndex="(int)_activeTab" ActiveTabIndexChanged="i => _activeTab = (MeasuringTab)i" RenderMode="TabsRenderMode.OnDemand" CssClass="measuring-tabs">
|
||||
<DxTabPage Text="Napi feladatok">
|
||||
<div class="p-3">
|
||||
<MgCardView TItem="Shipping" Data="@FilteredShippings"
|
||||
ShowFilterPanel="true"
|
||||
OnCardClick="NavigateToMeasuringTab"
|
||||
ScrollToItem="@SelectedShipping"
|
||||
ItemKeySelector="s => s.Id"
|
||||
Height="calc(100vh - 220px)">
|
||||
<FilterPanel>
|
||||
<DxTagBox Data="@(new[] { MeasuringStatus.NotStarted, MeasuringStatus.Started, MeasuringStatus.Finnished })"
|
||||
NullText="Összes státusz"
|
||||
@bind-Values="_statusFilter"
|
||||
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto"
|
||||
CssClass="cw-480" />
|
||||
</FilterPanel>
|
||||
<CardTemplate>
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<h6 class="mb-0">@context.LicencePlate</h6>
|
||||
<span class="badge @(MeasurementService.GetMeasuringStatusBadgeCssClass(MeasurementService.GetShippingMeasuringStatus(context), hasAuditedStage: false))">@(MeasurementService.GetMeasuringStatusText(MeasurementService.GetShippingMeasuringStatus(context)))</span>
|
||||
</div>
|
||||
<div class="text-muted small mb-2">
|
||||
<strong>@context.ShippingDate.ToString("yyyy.MM.dd")</strong>
|
||||
@if (!string.IsNullOrWhiteSpace(context.CargoCompany))
|
||||
{
|
||||
<span> — @context.CargoCompany</span>
|
||||
}
|
||||
</div>
|
||||
@{
|
||||
var allItems = context.ShippingDocuments?
|
||||
.Where(sd => sd.ShippingItems is not null)
|
||||
.SelectMany(sd => sd.ShippingItems!);
|
||||
}
|
||||
@if (allItems is not null)
|
||||
{
|
||||
@foreach (var item in allItems)
|
||||
{
|
||||
<div class="@(MeasurementService.GetMeasuringStatusCssClass(item.MeasuringStatus, hasAuditedStage: false)) small">
|
||||
@item.ProductName — @item.MeasuredQuantity/@item.QuantityOnDocument rekesz
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@if (!string.IsNullOrWhiteSpace(context.Comment))
|
||||
{
|
||||
<div class="text-muted small mt-2 fst-italic">
|
||||
📝 @context.Comment
|
||||
</div>
|
||||
}
|
||||
</CardTemplate>
|
||||
</MgCardView>
|
||||
</div>
|
||||
</DxTabPage>
|
||||
<DxTabPage Text="Mérés">
|
||||
<div class="p-3">
|
||||
<DxFormLayout CaptionPosition="CaptionPosition.Vertical" CssClass="w-100">
|
||||
<DxFormLayoutItem Caption="Rendszám:" ColSpanMd="2" CaptionCssClass="@(SelectedShipping?.IsAllMeasured == true ? "text-success" : "")">
|
||||
<DxComboBox Data="@NotMeasuredShippings"
|
||||
@bind-Value="@SelectedShipping"
|
||||
|
|
@ -79,8 +138,8 @@
|
|||
<span class="@(ctxShippingDocument.DataItem.IsAllMeasured ? "text-success" : "")">@ctxShippingDocument.DisplayText</span>
|
||||
</ItemDisplayTemplate>
|
||||
</DxComboBox>
|
||||
</DxFormLayoutItem>
|
||||
*@
|
||||
</DxFormLayoutItem> *@
|
||||
|
||||
<DxFormLayoutItem Caption="Termék:" ColSpanMd="5" CaptionCssClass="@(SelectedShippingItem?.IsMeasured == true ? "text-success" : "")">
|
||||
<DxComboBox Data="@_shippingItemsDataSource"
|
||||
@bind-Value="@SelectedShippingItem"
|
||||
|
|
@ -106,15 +165,13 @@
|
|||
<div class="combobox-item-template-text">
|
||||
<span>@ctxShippingitem.DisplayText</span>
|
||||
</div>
|
||||
</div>
|
||||
*@
|
||||
</div> *@
|
||||
<span class="@(ctxShippingitem.DataItem.IsMeasured ? "text-success" : "")">@ctxShippingitem.DisplayText)</span>
|
||||
</ItemDisplayTemplate>
|
||||
<Buttons>
|
||||
<DxEditorButton IconCssClass="editor-icon editor-icon-add" Text="R" Tooltip="Adatok frissítése..."
|
||||
Click="() => OnOrdersRefreshClick()" />
|
||||
</Buttons>
|
||||
|
||||
</DxComboBox>
|
||||
</DxFormLayoutItem>
|
||||
|
||||
|
|
@ -130,7 +187,6 @@
|
|||
</span>
|
||||
}
|
||||
}
|
||||
|
||||
</DxFormLayoutItem>
|
||||
</DxFormLayout>
|
||||
|
||||
|
|
@ -185,5 +241,11 @@
|
|||
</DxFormLayout>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</DxTabPage>
|
||||
</DxTabs>
|
||||
</DxLoadingPanel>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using AyCode.Core.Loggers;
|
|||
using DevExpress.Blazor;
|
||||
using FruitBank.Common.Dtos;
|
||||
using FruitBank.Common.Entities;
|
||||
using FruitBank.Common.Enums;
|
||||
using FruitBank.Common.Helpers;
|
||||
using FruitBank.Common.Interfaces;
|
||||
using FruitBank.Common.Models;
|
||||
|
|
@ -26,6 +27,15 @@ namespace FruitBankHybrid.Shared.Pages
|
|||
private ILogger _logger = null!;
|
||||
private string _errorText;
|
||||
|
||||
private enum MeasuringTab
|
||||
{
|
||||
DailyTasks = 0,
|
||||
Measuring = 1
|
||||
}
|
||||
|
||||
private MeasuringTab _activeTab = MeasuringTab.DailyTasks;
|
||||
private IEnumerable<MeasuringStatus> _statusFilter = [];
|
||||
|
||||
private List<Shipping> NotMeasuredShippings { get; set; } = null!;
|
||||
private Shipping? SelectedShipping { get; set; }
|
||||
//private ShippingDocument? SelectedShippingDocument { get; set; }
|
||||
|
|
@ -37,6 +47,12 @@ namespace FruitBankHybrid.Shared.Pages
|
|||
private List<ShippingItem>? _shippingItemsDataSource;
|
||||
private List<MeasuringDateSelectorModel> _measuringDates = null!;
|
||||
|
||||
private IReadOnlyList<Shipping> FilteredShippings => NotMeasuredShippings is null
|
||||
? []
|
||||
: _statusFilter.Any()
|
||||
? NotMeasuredShippings.Where(s => _statusFilter.Contains(MeasurementService.GetShippingMeasuringStatus(s))).ToList()
|
||||
: NotMeasuredShippings;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
LoadingPanelVisible = true;
|
||||
|
|
@ -181,6 +197,20 @@ namespace FruitBankHybrid.Shared.Pages
|
|||
//Nem végezhető el a mérés, nincs megadva a ProductId! Jelezze a vezetőségnek...
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates from a card click (Napi feladatok tab) to the Mérés tab with the selected shipping.
|
||||
/// </summary>
|
||||
private void NavigateToMeasuringTab(Shipping shipping)
|
||||
{
|
||||
SelectedShipping = shipping;
|
||||
|
||||
PrepareShippingItems(shipping);
|
||||
_shippingItemsDataSource = GetShippingItemsDataSource(shipping);
|
||||
SelectedShippingItem = _shippingItemsDataSource?.FirstOrDefault();
|
||||
|
||||
_activeTab = MeasuringTab.Measuring;
|
||||
}
|
||||
|
||||
private void PrepareShippingItems(Shipping? shipping)
|
||||
{
|
||||
if (shipping?.ShippingDocuments == null) return;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
</DayCellTemplate>
|
||||
</DxDateEdit>
|
||||
</DxFormLayoutItem>
|
||||
<DxFormLayoutItem Caption="Napok száma" ColSpanXs="4" ColSpanSm="4" ColSpanMd="1">
|
||||
<DxFormLayoutItem Caption="Napok száma" ColSpanXs="4" ColSpanSm="4" ColSpanMd="2">
|
||||
<DxSpinEdit T="int" Value="1" Increment="1" MinValue="1"
|
||||
ValueChanged="async i => await RefreshOrdersFromDb(DateTime.Now, i)" />
|
||||
</DxFormLayoutItem>
|
||||
|
|
@ -73,17 +73,26 @@
|
|||
<CardTemplate>
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<h6 class="mb-0">#@context.CustomOrderNumber</h6>
|
||||
<span class="badge @GetOrderStatusBadgeCssClass(context.MeasuringStatus)">@GetOrderStatusText(context.MeasuringStatus)</span>
|
||||
<span class="badge @(MeasurementService.GetMeasuringStatusBadgeCssClass(context.MeasuringStatus))">@(MeasurementService.GetMeasuringStatusText(context.MeasuringStatus))</span>
|
||||
</div>
|
||||
<div class="text-muted small mb-2">
|
||||
<strong>@context.DateOfReceiptOrCreated.ToString("HH:mm")</strong> — @context.Customer.Company
|
||||
</div>
|
||||
@foreach (var item in context.OrderItemDtos)
|
||||
{
|
||||
<div class="@GetOrderStatusCssClass(item.MeasuringStatus) small">
|
||||
<div class="@(MeasurementService.GetMeasuringStatusCssClass(item.MeasuringStatus)) small">
|
||||
@item.ProductName — @item.TrayQuantity/@item.Quantity rekesz
|
||||
</div>
|
||||
}
|
||||
@{
|
||||
var cardOrderNote = MeasurementService.GetOrderNote(context.OrderNotes);
|
||||
}
|
||||
@if (!string.IsNullOrWhiteSpace(cardOrderNote))
|
||||
{
|
||||
<div class="text-muted small mt-2 fst-italic">
|
||||
📝 @cardOrderNote
|
||||
</div>
|
||||
}
|
||||
</CardTemplate>
|
||||
</MgCardView>
|
||||
</div>
|
||||
|
|
@ -169,8 +178,8 @@
|
|||
}
|
||||
else
|
||||
{
|
||||
string? orderNote;
|
||||
if (!(orderNote = SelectedOrder?.OrderNotes.LastOrDefault(x => x.Note.StartsWith('*'))?.Note).IsNullOrWhiteSpace())
|
||||
var orderNote = MeasurementService.GetOrderNote(SelectedOrder?.OrderNotes);
|
||||
if (!string.IsNullOrWhiteSpace(orderNote))
|
||||
{
|
||||
<div class="alert alert-info mt-3" role="alert">
|
||||
<strong>📝 Megjegyzés:</strong> @(orderNote)
|
||||
|
|
|
|||
|
|
@ -410,30 +410,6 @@ namespace FruitBankHybrid.Shared.Pages
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@
|
|||
using DevExpress.Blazor;
|
||||
using FruitBank.Common.Dtos;
|
||||
using FruitBank.Common.Entities;
|
||||
using FruitBank.Common.Enums;
|
||||
using FruitBank.Common.Interfaces;
|
||||
using FruitBank.Common.Services;
|
||||
using FruitBankHybrid.Shared.Models;
|
||||
using FruitBankHybrid.Shared.Services.Loggers;
|
||||
using Mango.Nop.Core.Dtos;
|
||||
using Mango.Nop.Core.Loggers;
|
||||
using Nop.Core.Domain.Orders;
|
||||
|
||||
namespace FruitBankHybrid.Shared.Services;
|
||||
|
||||
|
|
@ -109,4 +111,66 @@ public class MeasurementService(IEnumerable<IAcLogWriterClientBase> logWriters)
|
|||
|
||||
public static bool IsCustomItemPalletMeasuredAndValid(IMeasuringItemPalletBase customItemPallet, bool isMeasurable)
|
||||
=> customItemPallet.IsMeasuredAndValid(isMeasurable);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the last order note starting with '*', or null if none exists.
|
||||
/// </summary>
|
||||
public static string? GetOrderNote(IEnumerable<OrderNote>? orderNotes)
|
||||
=> orderNotes?.LastOrDefault(x => x.Note.StartsWith('*'))?.Note;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Bootstrap badge CSS class for the given measuring status.
|
||||
/// When <paramref name="hasAuditedStage"/> is false, Finnished is treated as the final state (success/green).
|
||||
/// </summary>
|
||||
public static string GetMeasuringStatusBadgeCssClass(MeasuringStatus status, bool hasAuditedStage = true) => status switch
|
||||
{
|
||||
MeasuringStatus.Audited => "bg-success",
|
||||
MeasuringStatus.Finnished => hasAuditedStage ? "bg-primary" : "bg-success",
|
||||
MeasuringStatus.Started => "bg-warning text-dark",
|
||||
_ => "bg-secondary"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Returns a localized display text for the given measuring status.
|
||||
/// </summary>
|
||||
public static string GetMeasuringStatusText(MeasuringStatus status) => status switch
|
||||
{
|
||||
MeasuringStatus.Audited => "Lezárva",
|
||||
MeasuringStatus.Finnished => "Kész",
|
||||
MeasuringStatus.Started => "Folyamatban",
|
||||
_ => "Nem kezdett"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Returns a text color CSS class for the given measuring status.
|
||||
/// When <paramref name="hasAuditedStage"/> is false, Finnished is treated as the final state (success/green).
|
||||
/// </summary>
|
||||
public static string GetMeasuringStatusCssClass(MeasuringStatus status, bool hasAuditedStage = true) => status switch
|
||||
{
|
||||
MeasuringStatus.Audited => "text-success",
|
||||
MeasuringStatus.Finnished => hasAuditedStage ? "text-primary" : "text-success",
|
||||
MeasuringStatus.Started => "text-warning",
|
||||
_ => "text-muted"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Computes a shipping-level MeasuringStatus from its items.
|
||||
/// </summary>
|
||||
public static MeasuringStatus GetShippingMeasuringStatus(Shipping shipping)
|
||||
{
|
||||
var items = shipping.ShippingDocuments?
|
||||
.Where(sd => sd.ShippingItems is not null)
|
||||
.SelectMany(sd => sd.ShippingItems!);
|
||||
|
||||
if (items is null || !items.Any())
|
||||
return MeasuringStatus.NotStarted;
|
||||
|
||||
if (items.All(si => si.IsMeasured))
|
||||
return MeasuringStatus.Finnished;
|
||||
|
||||
if (items.Any(si => si.MeasuringStatus == MeasuringStatus.Started || si.IsMeasured))
|
||||
return MeasuringStatus.Started;
|
||||
|
||||
return MeasuringStatus.NotStarted;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue