From c9affe62bd4a32b4ced9c82878d98af116286efd Mon Sep 17 00:00:00 2001 From: masoodafar-web Date: Wed, 18 Feb 2026 00:43:12 +0330 Subject: [PATCH] feat: smart product autocomplete for inventory dialogs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DiscountProductsAutoComplete: new autocomplete component for discount products with search/debounce (mirrors ProductsAutoComplete pattern) - AddStockDialog: replace manual product ID input with smart autocomplete - When 'regular product' selected → shows ProductsAutoComplete - When 'discount product' selected → shows DiscountProductsAutoComplete - Admin never has to type product IDs manually - Product type selector controls which autocomplete is shown - Validates product selection before submit --- .../DiscountProductsAutoComplete.razor | 14 ++++ .../DiscountProductsAutoComplete.razor.cs | 83 +++++++++++++++++++ .../Inventory/Components/AddStockDialog.razor | 39 ++++++--- 3 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 src/BackOffice/Pages/AutoComplete/DiscountProductsAutoComplete.razor create mode 100644 src/BackOffice/Pages/AutoComplete/DiscountProductsAutoComplete.razor.cs diff --git a/src/BackOffice/Pages/AutoComplete/DiscountProductsAutoComplete.razor b/src/BackOffice/Pages/AutoComplete/DiscountProductsAutoComplete.razor new file mode 100644 index 0000000..70b2886 --- /dev/null +++ b/src/BackOffice/Pages/AutoComplete/DiscountProductsAutoComplete.razor @@ -0,0 +1,14 @@ +@using CMSMicroservice.Protobuf.Protos.DiscountProduct + + diff --git a/src/BackOffice/Pages/AutoComplete/DiscountProductsAutoComplete.razor.cs b/src/BackOffice/Pages/AutoComplete/DiscountProductsAutoComplete.razor.cs new file mode 100644 index 0000000..3853fed --- /dev/null +++ b/src/BackOffice/Pages/AutoComplete/DiscountProductsAutoComplete.razor.cs @@ -0,0 +1,83 @@ +using CMSMicroservice.Protobuf.Protos.DiscountProduct; +using Microsoft.AspNetCore.Components; + +namespace BackOffice.Pages.AutoComplete; + +public partial class DiscountProductsAutoComplete +{ + [Inject] public DiscountProductContract.DiscountProductContractClient DiscountProductService { get; set; } = default!; + + [Parameter] public long? Value { get; set; } + [Parameter] public string? Label { get; set; } = "محصول تخفیفی"; + [Parameter] public EventCallback ValueChanged { get; set; } + + private List _items = new(); + private DiscountProductDto? _item; + + protected override async void OnParametersSet() + { + await base.OnParametersSetAsync(); + + if (Value.HasValue && Value.Value > 0 && (_item == null || _item.Id != Value.Value)) + { + try + { + var getAll = await DiscountProductService.GetDiscountProductsAsync(new GetDiscountProductsRequest + { + PageNumber = 1, + PageSize = 50 + }); + + if (getAll?.Models != null) + { + _item = getAll.Models.FirstOrDefault(x => x.Id == Value.Value); + StateHasChanged(); + } + } + catch + { + // Silently handle + } + } + } + + private async Task OnSelected(DiscountProductDto? model) + { + if (model == null) + { + _item = null; + await ValueChanged.InvokeAsync(null); + } + else + { + _item = model; + await ValueChanged.InvokeAsync(model.Id); + } + } + + private async Task> Search(string value, CancellationToken token) + { + var request = new GetDiscountProductsRequest + { + PageNumber = 1, + PageSize = 9 + }; + + if (!string.IsNullOrWhiteSpace(value)) + { + request.SearchQuery = value; + } + + try + { + var getAll = await DiscountProductService.GetDiscountProductsAsync(request); + _items = getAll?.Models?.ToList() ?? new List(); + } + catch + { + _items = new List(); + } + + return _items; + } +} diff --git a/src/BackOffice/Pages/Inventory/Components/AddStockDialog.razor b/src/BackOffice/Pages/Inventory/Components/AddStockDialog.razor index 08e7f37..c51c21e 100644 --- a/src/BackOffice/Pages/Inventory/Components/AddStockDialog.razor +++ b/src/BackOffice/Pages/Inventory/Components/AddStockDialog.razor @@ -1,6 +1,7 @@ @using CMSMicroservice.Protobuf.Protos.Inventory @using Google.Protobuf.WellKnownTypes @using InventoryProductType = CMSMicroservice.Protobuf.Protos.Inventory.ProductType +@using BackOffice.Pages.AutoComplete @@ -19,12 +20,16 @@ - + @if (_productType == InventoryProductType.RegularProduct) + { + + } + else + { + + } } else @@ -103,7 +108,8 @@ private bool _isSubmitting; private InventoryProductType _productType = InventoryProductType.RegularProduct; - private long _productId; + private long? _productId; + private long? _discountProductId; private long? _warehouseId; private int _quantity = 1; private string _referenceNumber = string.Empty; @@ -111,9 +117,12 @@ protected override void OnInitialized() { - if (ProductIdParam.HasValue) + if (ProductIdParam.HasValue && ProductTypeParam.HasValue) { - _productId = ProductIdParam.Value; + if (ProductTypeParam.Value == InventoryProductType.DiscountProduct) + _discountProductId = ProductIdParam.Value; + else + _productId = ProductIdParam.Value; } if (ProductTypeParam.HasValue) { @@ -144,9 +153,19 @@ _isSubmitting = true; try { + var selectedProductId = ProductIdParam + ?? (_productType == InventoryProductType.DiscountProduct ? _discountProductId : _productId); + + if (!selectedProductId.HasValue || selectedProductId.Value <= 0) + { + Snackbar.Add("لطفاً یک محصول انتخاب کنید", Severity.Warning); + _isSubmitting = false; + return; + } + var request = new AddStockRequest { - ProductId = ProductIdParam ?? _productId, + ProductId = selectedProductId.Value, ProductType = ProductTypeParam ?? _productType, Quantity = _quantity, ReferenceNumber = _referenceNumber ?? string.Empty,