From b132061986fc1e7928582a7c1812725669438425 Mon Sep 17 00:00:00 2001 From: Chris Ainsley Date: Fri, 24 Jan 2025 19:03:43 -0500 Subject: [PATCH 1/4] Add product attribute name filtering functionality - Updated `IProductAttributeService` to include an optional `name` parameter in `GetAllProductAttributesAsync`. - Modified `ProductAttributeService` to filter product attributes by name using the new parameter. - Added a new locale resource for product attribute name search in the XML resource file. - Enhanced `ProductAttributeSearchModel` with a `SearchProductAttributeName` property. - Updated `List.cshtml` to include a search block for filtering product attributes by name. - Made minor formatting adjustments for improved code readability. --- .../Catalog/IProductAttributeService.cs | 3 +- .../Catalog/ProductAttributeService.cs | 35 +++++++------- .../UpgradeTo490/LocalizationMigration.cs | 3 ++ .../Localization/defaultResources.nopres.xml | 3 ++ .../Controllers/ProductAttributeController.cs | 2 +- .../Factories/ProductAttributeModelFactory.cs | 2 +- .../Catalog/ProductAttributeSearchModel.cs | 3 ++ .../Admin/Views/ProductAttribute/List.cshtml | 47 ++++++++++++++++++- 8 files changed, 75 insertions(+), 23 deletions(-) diff --git a/src/Libraries/Nop.Services/Catalog/IProductAttributeService.cs b/src/Libraries/Nop.Services/Catalog/IProductAttributeService.cs index bf5650ea504..ae91013effc 100644 --- a/src/Libraries/Nop.Services/Catalog/IProductAttributeService.cs +++ b/src/Libraries/Nop.Services/Catalog/IProductAttributeService.cs @@ -29,11 +29,12 @@ public partial interface IProductAttributeService /// /// Page index /// Page size + /// Filter by name /// /// A task that represents the asynchronous operation /// The task result contains the product attributes /// - Task> GetAllProductAttributesAsync(int pageIndex = 0, int pageSize = int.MaxValue); + Task> GetAllProductAttributesAsync(int pageIndex = 0, int pageSize = int.MaxValue, string name = ""); /// /// Gets a product attribute diff --git a/src/Libraries/Nop.Services/Catalog/ProductAttributeService.cs b/src/Libraries/Nop.Services/Catalog/ProductAttributeService.cs index 16055e6880b..016fc924174 100644 --- a/src/Libraries/Nop.Services/Catalog/ProductAttributeService.cs +++ b/src/Libraries/Nop.Services/Catalog/ProductAttributeService.cs @@ -88,21 +88,20 @@ public virtual async Task DeleteProductAttributesAsync(IList p /// /// Page index /// Page size + /// Filter by name /// /// A task that represents the asynchronous operation /// The task result contains the product attributes /// public virtual async Task> GetAllProductAttributesAsync(int pageIndex = 0, - int pageSize = int.MaxValue) + int pageSize = int.MaxValue, string name = "") { - var productAttributes = await _productAttributeRepository.GetAllPagedAsync(query => - { - return from pa in query - orderby pa.Name - select pa; - }, pageIndex, pageSize); + var query = _productAttributeRepository.Table; - return productAttributes; + if (!string.IsNullOrWhiteSpace(name)) + query = query.Where(pa => pa.Name.Contains(name)); + + return await query.OrderBy(x => x.Name).ToPagedListAsync(pageIndex, pageSize); } /// @@ -124,7 +123,7 @@ public virtual async Task GetProductAttributeByIdAsync(int pro /// Product attribute identifiers /// /// A task that represents the asynchronous operation - /// The task result contains the product attributes + /// The task result contains the product attributes /// public virtual async Task> GetProductAttributeByIdsAsync(int[] productAttributeIds) { @@ -199,9 +198,9 @@ public virtual async Task> GetProductAttributeMap var allCacheKey = _staticCacheManager.PrepareKeyForDefaultCache(NopCatalogDefaults.ProductAttributeMappingsByProductCacheKey, productId); var query = from pam in _productAttributeMappingRepository.Table - orderby pam.DisplayOrder, pam.Id - where pam.ProductId == productId - select pam; + orderby pam.DisplayOrder, pam.Id + where pam.ProductId == productId + select pam; var attributes = await _staticCacheManager.GetAsync(allCacheKey, async () => await query.ToListAsync()) ?? new List(); @@ -268,9 +267,9 @@ public virtual async Task> GetProductAttributeValue var key = _staticCacheManager.PrepareKeyForDefaultCache(NopCatalogDefaults.ProductAttributeValuesByAttributeCacheKey, productAttributeMappingId); var query = from pav in _productAttributeValueRepository.Table - orderby pav.DisplayOrder, pav.Id - where pav.ProductAttributeMappingId == productAttributeMappingId - select pav; + orderby pav.DisplayOrder, pav.Id + where pav.ProductAttributeMappingId == productAttributeMappingId + select pav; var productAttributeValues = await _staticCacheManager.GetAsync(key, async () => await query.ToListAsync()); return productAttributeValues; @@ -411,9 +410,9 @@ public virtual async Task> GetPredefinedP var key = _staticCacheManager.PrepareKeyForDefaultCache(NopCatalogDefaults.PredefinedProductAttributeValuesByAttributeCacheKey, productAttributeId); var query = from ppav in _predefinedProductAttributeValueRepository.Table - orderby ppav.DisplayOrder, ppav.Id - where ppav.ProductAttributeId == productAttributeId - select ppav; + orderby ppav.DisplayOrder, ppav.Id + where ppav.ProductAttributeId == productAttributeId + select ppav; var values = await _staticCacheManager.GetAsync(key, async () => await query.ToListAsync()); diff --git a/src/Presentation/Nop.Web.Framework/Migrations/UpgradeTo490/LocalizationMigration.cs b/src/Presentation/Nop.Web.Framework/Migrations/UpgradeTo490/LocalizationMigration.cs index cdeee681c83..7faf677e93a 100644 --- a/src/Presentation/Nop.Web.Framework/Migrations/UpgradeTo490/LocalizationMigration.cs +++ b/src/Presentation/Nop.Web.Framework/Migrations/UpgradeTo490/LocalizationMigration.cs @@ -57,6 +57,9 @@ public override void Up() //#7398 ["Admin.ConfigurationSteps.Product.Details.Text"] = "Enter the relevant product details in these fields. The screenshot below shows how they will be displayed on the product page with the default nopCommerce theme:
", ["Admin.ConfigurationSteps.PaymentPayPal.ApiCredentials.Text"] = "If you already have an app created in your PayPal account, follow these steps.", + + //#7515 + ["Admin.Catalog.Attributes.ProductAttributes.List.SearchProductAttributeName"] = "Product attribute name", }, languageId); #endregion diff --git a/src/Presentation/Nop.Web/App_Data/Localization/defaultResources.nopres.xml b/src/Presentation/Nop.Web/App_Data/Localization/defaultResources.nopres.xml index e3a3125bbff..613e9bc52eb 100644 --- a/src/Presentation/Nop.Web/App_Data/Localization/defaultResources.nopres.xml +++ b/src/Presentation/Nop.Web/App_Data/Localization/defaultResources.nopres.xml @@ -1956,6 +1956,9 @@ Published + + Product attribute name + Specification attributes diff --git a/src/Presentation/Nop.Web/Areas/Admin/Controllers/ProductAttributeController.cs b/src/Presentation/Nop.Web/Areas/Admin/Controllers/ProductAttributeController.cs index 42d83b1d703..422d15b2f8d 100644 --- a/src/Presentation/Nop.Web/Areas/Admin/Controllers/ProductAttributeController.cs +++ b/src/Presentation/Nop.Web/Areas/Admin/Controllers/ProductAttributeController.cs @@ -25,7 +25,7 @@ public partial class ProductAttributeController : BaseAdminController protected readonly IProductAttributeModelFactory _productAttributeModelFactory; protected readonly IProductAttributeService _productAttributeService; - #endregion Fields + #endregion Fields #region Ctor diff --git a/src/Presentation/Nop.Web/Areas/Admin/Factories/ProductAttributeModelFactory.cs b/src/Presentation/Nop.Web/Areas/Admin/Factories/ProductAttributeModelFactory.cs index e2ef1f6fa12..2c1f1657366 100644 --- a/src/Presentation/Nop.Web/Areas/Admin/Factories/ProductAttributeModelFactory.cs +++ b/src/Presentation/Nop.Web/Areas/Admin/Factories/ProductAttributeModelFactory.cs @@ -117,7 +117,7 @@ public virtual async Task PrepareProductAttributeList //get product attributes var productAttributes = await _productAttributeService - .GetAllProductAttributesAsync(pageIndex: searchModel.Page - 1, pageSize: searchModel.PageSize); + .GetAllProductAttributesAsync(pageIndex: searchModel.Page - 1, pageSize: searchModel.PageSize, name: searchModel.SearchProductAttributeName); //prepare list model var model = new ProductAttributeListModel().PrepareToGrid(searchModel, productAttributes, () => diff --git a/src/Presentation/Nop.Web/Areas/Admin/Models/Catalog/ProductAttributeSearchModel.cs b/src/Presentation/Nop.Web/Areas/Admin/Models/Catalog/ProductAttributeSearchModel.cs index b35b3142d3e..d22a2a50655 100644 --- a/src/Presentation/Nop.Web/Areas/Admin/Models/Catalog/ProductAttributeSearchModel.cs +++ b/src/Presentation/Nop.Web/Areas/Admin/Models/Catalog/ProductAttributeSearchModel.cs @@ -1,4 +1,5 @@ using Nop.Web.Framework.Models; +using Nop.Web.Framework.Mvc.ModelBinding; namespace Nop.Web.Areas.Admin.Models.Catalog; @@ -7,4 +8,6 @@ namespace Nop.Web.Areas.Admin.Models.Catalog; ///
public partial record ProductAttributeSearchModel : BaseSearchModel { + [NopResourceDisplayName("Admin.Catalog.Attributes.ProductAttributes.List.SearchProductAttributeName")] + public string SearchProductAttributeName { get; set; } } \ No newline at end of file diff --git a/src/Presentation/Nop.Web/Areas/Admin/Views/ProductAttribute/List.cshtml b/src/Presentation/Nop.Web/Areas/Admin/Views/ProductAttribute/List.cshtml index f13c40e1c87..0f0b9f9b1a7 100644 --- a/src/Presentation/Nop.Web/Areas/Admin/Views/ProductAttribute/List.cshtml +++ b/src/Presentation/Nop.Web/Areas/Admin/Views/ProductAttribute/List.cshtml @@ -7,7 +7,11 @@ NopHtml.SetActiveMenuItemSystemName("Product attributes"); } - +@{ + const string hideSearchBlockAttributeName = "ProductAttributeListPage.HideSearchBlock"; + var hideSearchBlock = await genericAttributeService.GetAttributeAsync(await workContext.GetCurrentCustomerAsync(), hideSearchBlockAttributeName); +} +

@T("Admin.Catalog.Attributes.ProductAttributes") @@ -29,6 +33,39 @@
+ +

@@ -39,8 +76,13 @@ { Name = "products-grid", UrlRead = new DataUrl("List", "ProductAttribute", null), + SearchButtonId = "search-productattributes", Length = Model.PageSize, LengthMenu = Model.AvailablePageSizes, + Filters = new List + { + new FilterParameter(nameof(Model.SearchProductAttributeName)) + }, ColumnCollection = new List { new ColumnProperty(nameof(ProductAttributeModel.Id)) @@ -99,4 +141,5 @@

-
\ No newline at end of file + + \ No newline at end of file From 5e0c99e59dedd7af7e6c7748428ab6b8594c533c Mon Sep 17 00:00:00 2001 From: Chris Ainsley Date: Fri, 24 Jan 2025 19:58:36 -0500 Subject: [PATCH 2/4] Refactor ProductAttributeService for improved readability Updated LINQ query formatting in the ProductAttributeService class to enhance code readability. Adjusted indentation for `orderby`, `where`, and `select` clauses. Also, modified a comment in the `GetProductAttributeByIdsAsync` method for consistent formatting. --- .../Catalog/ProductAttributeService.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Libraries/Nop.Services/Catalog/ProductAttributeService.cs b/src/Libraries/Nop.Services/Catalog/ProductAttributeService.cs index 016fc924174..d072ab3174a 100644 --- a/src/Libraries/Nop.Services/Catalog/ProductAttributeService.cs +++ b/src/Libraries/Nop.Services/Catalog/ProductAttributeService.cs @@ -123,7 +123,7 @@ public virtual async Task GetProductAttributeByIdAsync(int pro /// Product attribute identifiers /// /// A task that represents the asynchronous operation - /// The task result contains the product attributes + /// The task result contains the product attributes /// public virtual async Task> GetProductAttributeByIdsAsync(int[] productAttributeIds) { @@ -198,9 +198,9 @@ public virtual async Task> GetProductAttributeMap var allCacheKey = _staticCacheManager.PrepareKeyForDefaultCache(NopCatalogDefaults.ProductAttributeMappingsByProductCacheKey, productId); var query = from pam in _productAttributeMappingRepository.Table - orderby pam.DisplayOrder, pam.Id - where pam.ProductId == productId - select pam; + orderby pam.DisplayOrder, pam.Id + where pam.ProductId == productId + select pam; var attributes = await _staticCacheManager.GetAsync(allCacheKey, async () => await query.ToListAsync()) ?? new List(); @@ -267,9 +267,9 @@ public virtual async Task> GetProductAttributeValue var key = _staticCacheManager.PrepareKeyForDefaultCache(NopCatalogDefaults.ProductAttributeValuesByAttributeCacheKey, productAttributeMappingId); var query = from pav in _productAttributeValueRepository.Table - orderby pav.DisplayOrder, pav.Id - where pav.ProductAttributeMappingId == productAttributeMappingId - select pav; + orderby pav.DisplayOrder, pav.Id + where pav.ProductAttributeMappingId == productAttributeMappingId + select pav; var productAttributeValues = await _staticCacheManager.GetAsync(key, async () => await query.ToListAsync()); return productAttributeValues; @@ -410,9 +410,9 @@ public virtual async Task> GetPredefinedP var key = _staticCacheManager.PrepareKeyForDefaultCache(NopCatalogDefaults.PredefinedProductAttributeValuesByAttributeCacheKey, productAttributeId); var query = from ppav in _predefinedProductAttributeValueRepository.Table - orderby ppav.DisplayOrder, ppav.Id - where ppav.ProductAttributeId == productAttributeId - select ppav; + orderby ppav.DisplayOrder, ppav.Id + where ppav.ProductAttributeId == productAttributeId + select ppav; var values = await _staticCacheManager.GetAsync(key, async () => await query.ToListAsync()); From 96e2f3393f4d2313e09fd4cde7165147cfccc61d Mon Sep 17 00:00:00 2001 From: Chris Ainsley Date: Sun, 26 Jan 2025 11:57:32 -0500 Subject: [PATCH 3/4] Enhance product attribute search and localization Added a hint for "SearchProductAttributeName" in `LocalizationMigration.cs` and created a new locale resource entry in `defaultResources.nopres.xml`. Updated the HTML structure in `List.cshtml` for better layout and user interaction, including a restructured search block and improved button placements. JavaScript functionality for deleting selected attributes remains intact and has been reformatted for clarity. --- .../UpgradeTo490/LocalizationMigration.cs | 1 + .../Localization/defaultResources.nopres.xml | 3 + .../Admin/Views/ProductAttribute/List.cshtml | 220 +++++++++--------- 3 files changed, 115 insertions(+), 109 deletions(-) diff --git a/src/Presentation/Nop.Web.Framework/Migrations/UpgradeTo490/LocalizationMigration.cs b/src/Presentation/Nop.Web.Framework/Migrations/UpgradeTo490/LocalizationMigration.cs index 7faf677e93a..4d1896dd860 100644 --- a/src/Presentation/Nop.Web.Framework/Migrations/UpgradeTo490/LocalizationMigration.cs +++ b/src/Presentation/Nop.Web.Framework/Migrations/UpgradeTo490/LocalizationMigration.cs @@ -60,6 +60,7 @@ public override void Up() //#7515 ["Admin.Catalog.Attributes.ProductAttributes.List.SearchProductAttributeName"] = "Product attribute name", + ["Admin.Catalog.Attributes.ProductAttributes.List.SearchProductAttributeName.Hint"] = "A Product attribute name.", }, languageId); #endregion diff --git a/src/Presentation/Nop.Web/App_Data/Localization/defaultResources.nopres.xml b/src/Presentation/Nop.Web/App_Data/Localization/defaultResources.nopres.xml index 613e9bc52eb..df53e0fbe58 100644 --- a/src/Presentation/Nop.Web/App_Data/Localization/defaultResources.nopres.xml +++ b/src/Presentation/Nop.Web/App_Data/Localization/defaultResources.nopres.xml @@ -1959,6 +1959,9 @@ Product attribute name + + A Product attribute name. + Specification attributes diff --git a/src/Presentation/Nop.Web/Areas/Admin/Views/ProductAttribute/List.cshtml b/src/Presentation/Nop.Web/Areas/Admin/Views/ProductAttribute/List.cshtml index 0f0b9f9b1a7..16289e0b8e2 100644 --- a/src/Presentation/Nop.Web/Areas/Admin/Views/ProductAttribute/List.cshtml +++ b/src/Presentation/Nop.Web/Areas/Admin/Views/ProductAttribute/List.cshtml @@ -12,134 +12,136 @@ var hideSearchBlock = await genericAttributeService.GetAttributeAsync(await workContext.GetCurrentCustomerAsync(), hideSearchBlockAttributeName); }
-
-

- @T("Admin.Catalog.Attributes.ProductAttributes") -

-
- - - @T("Admin.Common.AddNew") - - @await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.ProductAttributeListButtons, additionalData = Model }) - - +
+

+ @T("Admin.Catalog.Attributes.ProductAttributes") +

+
+ + + @T("Admin.Common.AddNew") + + @await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.ProductAttributeListButtons, additionalData = Model }) + + +
-
-
-
-
-
\ No newline at end of file From 6eb89bf48b96783fa51593cf3cd024858b0fa297 Mon Sep 17 00:00:00 2001 From: Chris Ainsley Date: Wed, 29 Jan 2025 16:33:27 -0500 Subject: [PATCH 4/4] Fixed Hint --- .../Nop.Web/App_Data/Localization/defaultResources.nopres.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Presentation/Nop.Web/App_Data/Localization/defaultResources.nopres.xml b/src/Presentation/Nop.Web/App_Data/Localization/defaultResources.nopres.xml index df53e0fbe58..2d6d119cda0 100644 --- a/src/Presentation/Nop.Web/App_Data/Localization/defaultResources.nopres.xml +++ b/src/Presentation/Nop.Web/App_Data/Localization/defaultResources.nopres.xml @@ -1959,7 +1959,7 @@ Product attribute name - + A Product attribute name.