From ddbfbc60c201e83fe5edbcefc38443e8ac15354e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 Nov 2024 18:28:13 +0100 Subject: [PATCH] refresh ViewComponent via SignalR --- .../AuctionDefaults.cs | 1 + .../AuctionPlugin.cs | 4 +- .../Components/AuctionPublicViewComponent.cs | 20 +- .../Content/Js/MgMessageHandler.js | 17 +- .../Controllers/AuctionController.cs | 59 ++++- .../Messages/RefreshAuctionWidgetMessage.cs | 13 ++ .../Infrastructure/PluginNopStartup.cs | 2 + .../Infrastructure/RouteProvider.cs | 4 + .../Models/AuctionBidRequest.cs | 16 ++ .../Models/ProductBidBoxViewModel.cs | 32 +++ .../Models/RefreshWidgetRequest.cs | 14 ++ .../Services/MyProductModelFactory.cs | 204 ++++++++++++++++++ .../Views/PublicProductBidBox.cshtml | 102 ++++++++- .../Views/_ViewImports.cshtml | 1 + 14 files changed, 470 insertions(+), 19 deletions(-) create mode 100644 Nop.Plugin.Misc.AuctionPlugin/Hubs/Messages/RefreshAuctionWidgetMessage.cs create mode 100644 Nop.Plugin.Misc.AuctionPlugin/Models/AuctionBidRequest.cs create mode 100644 Nop.Plugin.Misc.AuctionPlugin/Models/ProductBidBoxViewModel.cs create mode 100644 Nop.Plugin.Misc.AuctionPlugin/Models/RefreshWidgetRequest.cs create mode 100644 Nop.Plugin.Misc.AuctionPlugin/Services/MyProductModelFactory.cs diff --git a/Nop.Plugin.Misc.AuctionPlugin/AuctionDefaults.cs b/Nop.Plugin.Misc.AuctionPlugin/AuctionDefaults.cs index aa2629a..ca87974 100644 --- a/Nop.Plugin.Misc.AuctionPlugin/AuctionDefaults.cs +++ b/Nop.Plugin.Misc.AuctionPlugin/AuctionDefaults.cs @@ -30,6 +30,7 @@ public static class AuctionDefaults public static string AuctionRouteName => "Plugin.Misc.AuctionPlugin.Auction"; public static string TestPageRouteName => "Plugin.Misc.AuctionPlugin.TestPage"; public static string BidNotificationRouteName => "Plugin.Misc.AuctionPlugin.BidNotification"; + public static string RefreshAuctionWidgetRouteName => "Plugin.Misc.AuctionPlugin.RefreshAuctionWidget"; /// /// Gets the name of autosuggest component diff --git a/Nop.Plugin.Misc.AuctionPlugin/AuctionPlugin.cs b/Nop.Plugin.Misc.AuctionPlugin/AuctionPlugin.cs index 19b5294..989391b 100644 --- a/Nop.Plugin.Misc.AuctionPlugin/AuctionPlugin.cs +++ b/Nop.Plugin.Misc.AuctionPlugin/AuctionPlugin.cs @@ -100,7 +100,7 @@ namespace Nop.Plugin.Misc.AuctionPlugin { ArgumentNullException.ThrowIfNull(widgetZone); - if (widgetZone.Equals(PublicWidgetZones.ProductPriceTop)) + if (widgetZone.Equals(PublicWidgetZones.ProductDetailsInsideOverviewButtonsBefore)) { return typeof(AuctionPublicViewComponent); } @@ -122,7 +122,7 @@ namespace Nop.Plugin.Misc.AuctionPlugin { return Task.FromResult>(new List { - PublicWidgetZones.ProductPriceTop, + PublicWidgetZones.ProductDetailsInsideOverviewButtonsBefore, PublicWidgetZones.ProductDetailsBottom, PublicWidgetZones.HeaderAfter diff --git a/Nop.Plugin.Misc.AuctionPlugin/Components/AuctionPublicViewComponent.cs b/Nop.Plugin.Misc.AuctionPlugin/Components/AuctionPublicViewComponent.cs index 284f3a2..36c9769 100644 --- a/Nop.Plugin.Misc.AuctionPlugin/Components/AuctionPublicViewComponent.cs +++ b/Nop.Plugin.Misc.AuctionPlugin/Components/AuctionPublicViewComponent.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Nop.Core; using Nop.Plugin.Misc.AuctionPlugin; +using Nop.Plugin.Misc.AuctionPlugin.Models; using Nop.Services.Cms; using Nop.Services.Common; using Nop.Services.Logging; @@ -57,9 +58,11 @@ public class AuctionPublicViewComponent : NopViewComponent /// public async Task InvokeAsync(string widgetZone, object additionalData) { + ProductBidBoxViewModel productBidBoxViewModel = new ProductBidBoxViewModel(); + await _logger.InformationAsync("WidgetViewComponent called"); - //ensure that what3words widget is active and enabled + //ensure that widget is active and enabled var customer = await _workContext.GetCurrentCustomerAsync(); await _logger.InformationAsync($"WidgetViewComponent customer: {customer.Email}"); @@ -82,16 +85,23 @@ public class AuctionPublicViewComponent : NopViewComponent // return Content(string.Empty); //} - if (!widgetZone.Equals(PublicWidgetZones.ProductPriceTop)) + if (!widgetZone.Equals(PublicWidgetZones.ProductDetailsInsideOverviewButtonsBefore)) { await _logger.InformationAsync($"WidgetViewComponent is NOT in ProductDetailsTop now {widgetZone}"); return Content(string.Empty); } await _logger.InformationAsync("WidgetViewComponent called II"); - - - return View("~/Plugins/Misc.AuctionPlugin/Views/PublicProductBidBox.cshtml", productDetailsModel.Id.ToString()); + productBidBoxViewModel.WidgetZone = widgetZone; + productBidBoxViewModel.BasePrice = productDetailsModel.ProductPrice.OldPriceValue; + productBidBoxViewModel.CurrentPrice = productDetailsModel.ProductPrice.PriceValue; + productBidBoxViewModel.CustomerId = customer.Id; + productBidBoxViewModel.ProductId = productDetailsModel.Id; + productBidBoxViewModel.LicitStep = 50000; //add calculation + productBidBoxViewModel.BidPrice = productDetailsModel.ProductPrice.PriceValue + productBidBoxViewModel.LicitStep; + + + return View("~/Plugins/Misc.AuctionPlugin/Views/PublicProductBidBox.cshtml", productBidBoxViewModel); } #endregion diff --git a/Nop.Plugin.Misc.AuctionPlugin/Content/Js/MgMessageHandler.js b/Nop.Plugin.Misc.AuctionPlugin/Content/Js/MgMessageHandler.js index 414e29f..226cb05 100644 --- a/Nop.Plugin.Misc.AuctionPlugin/Content/Js/MgMessageHandler.js +++ b/Nop.Plugin.Misc.AuctionPlugin/Content/Js/MgMessageHandler.js @@ -40,7 +40,7 @@ }); $('.toast-success').css("background-color", "#4caf50"); }, - auctionUpdate: function (data) { + auctionUpdateAlternate: function (data) { const widgetPriceElement = document.getElementById("WidgetPrice"); if (widgetPriceElement) { widgetPriceElement.textContent = data.bidPrice; // Update the price @@ -49,6 +49,21 @@ console.warn("Element with ID 'WidgetPrice' not found in the DOM."); } }, + auctionUpdate: function (data) { + // Refresh the ViewComponent using AJAX + $.ajax({ + url: '/Auction/RefreshAuctionWidget', // Controller endpoint + method: 'GET', + success: function (response) { + //$('#auction-widget').html(response); // Update the DOM element + console.log("Auction widget refreshed!"); + }, + error: function (error) { + console.error("Error refreshing auction widget:", error); + } + }); + }, + // Add more handlers as needed default: function (data) { console.warn("Unhandled message type:", data); diff --git a/Nop.Plugin.Misc.AuctionPlugin/Controllers/AuctionController.cs b/Nop.Plugin.Misc.AuctionPlugin/Controllers/AuctionController.cs index 3553fd8..cb8b475 100644 --- a/Nop.Plugin.Misc.AuctionPlugin/Controllers/AuctionController.cs +++ b/Nop.Plugin.Misc.AuctionPlugin/Controllers/AuctionController.cs @@ -1,17 +1,72 @@ using AyCode.Core.Extensions; using Microsoft.AspNetCore.Mvc; +using Nop.Data; using Nop.Plugin.Misc.AuctionPlugin.Domains.Entities; +using Nop.Plugin.Misc.AuctionPlugin.Models; using Nop.Plugin.Misc.AuctionPlugin.Services; -using Nop.Web.Areas.Admin.Controllers; +using Nop.Services.Catalog; +using Nop.Services.Logging; +using Nop.Web.Areas.Admin.Factories; +using Nop.Web.Framework.Controllers; +using Nop.Web.Models.Catalog; namespace Nop.Plugin.Misc.AuctionPlugin.Controllers; -public class AuctionController(AuctionService _auctionService) : BaseAdminController +public class AuctionController : BasePluginController { + + protected readonly IAuctionService _auctionService; + protected readonly ILogger _logger; + protected readonly IProductService _productService; + protected readonly MyProductModelFactory _productModelFactory; + + public AuctionController(IAuctionService auctionService, ILogger logger, IProductService productService, MyProductModelFactory productModelFactory) + { + _auctionService = auctionService; + _logger = logger; + _productService = productService; + _productModelFactory = productModelFactory; + } + // GET public IActionResult Index() { //var a = new Auction(); return View(); } + + [HttpPost] + public async Task PlaceBid([FromBody] AuctionBid model) + { + + await _logger.InformationAsync("PlaceBid called"); + + //if (model.BidPrice < CurrentPrice + LicitStep) + //{ + // return BadRequest("Bid price is too low."); + //} + + // kéne valami visszajelzés + await _auctionService.InsertBidAsync(model); + bool bidSuccess = true; + if (bidSuccess) + { + return Ok(new { message = "Your bid was successfully placed!" }); + } + else + { + return BadRequest("Failed to place bid. Please try again."); + } + } + + [HttpPost] + public async Task RefreshAuctionWidget([FromBody] RefreshWidgetRequest request) + { + await _logger.InformationAsync($"Refresh auction widget called from {request.WidgetZone} with data of {request.ProductId}"); + + var product = await _productService.GetProductByIdAsync(request.ProductId); + ProductDetailsModel detailsModel = await _productModelFactory.PrepareProductDetailsModelAsync(product); + + return ViewComponent("AuctionPublic", new { widgetZone = request.WidgetZone, additionalData = detailsModel }); + } } \ No newline at end of file diff --git a/Nop.Plugin.Misc.AuctionPlugin/Hubs/Messages/RefreshAuctionWidgetMessage.cs b/Nop.Plugin.Misc.AuctionPlugin/Hubs/Messages/RefreshAuctionWidgetMessage.cs new file mode 100644 index 0000000..558a614 --- /dev/null +++ b/Nop.Plugin.Misc.AuctionPlugin/Hubs/Messages/RefreshAuctionWidgetMessage.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nop.Plugin.Misc.AuctionPlugin.Hubs.Messages +{ + public class RefreshAuctionWidgetMessage + { + public string Reason { get; set; } + } +} diff --git a/Nop.Plugin.Misc.AuctionPlugin/Infrastructure/PluginNopStartup.cs b/Nop.Plugin.Misc.AuctionPlugin/Infrastructure/PluginNopStartup.cs index 25085c9..8b50a2a 100644 --- a/Nop.Plugin.Misc.AuctionPlugin/Infrastructure/PluginNopStartup.cs +++ b/Nop.Plugin.Misc.AuctionPlugin/Infrastructure/PluginNopStartup.cs @@ -59,6 +59,8 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Infrastructure services.AddScoped(); services.AddScoped(); services.AddScoped(); + + services.AddScoped(); } /// diff --git a/Nop.Plugin.Misc.AuctionPlugin/Infrastructure/RouteProvider.cs b/Nop.Plugin.Misc.AuctionPlugin/Infrastructure/RouteProvider.cs index 64eeb18..ab6bcd6 100644 --- a/Nop.Plugin.Misc.AuctionPlugin/Infrastructure/RouteProvider.cs +++ b/Nop.Plugin.Misc.AuctionPlugin/Infrastructure/RouteProvider.cs @@ -41,6 +41,10 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Infrastructure endpointRouteBuilder.MapControllerRoute(name: AuctionDefaults.BidNotificationRouteName, pattern: "Admin/Announcement/SendBidNotificationViewModel", defaults: new { controller = "Announcement", action = "SendBidNotificationViewModel" }); + + endpointRouteBuilder.MapControllerRoute(name: AuctionDefaults.RefreshAuctionWidgetRouteName, + pattern: "Auction/RefreshAuctionWidget", + defaults: new { controller = "Auction", action = "RefreshAuctionWidget" }); } diff --git a/Nop.Plugin.Misc.AuctionPlugin/Models/AuctionBidRequest.cs b/Nop.Plugin.Misc.AuctionPlugin/Models/AuctionBidRequest.cs new file mode 100644 index 0000000..295b41e --- /dev/null +++ b/Nop.Plugin.Misc.AuctionPlugin/Models/AuctionBidRequest.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nop.Plugin.Misc.AuctionPlugin.Models +{ + internal class AuctionBidRequest + { + public decimal BidPrice { get; set; } + public int ProductId { get; set; } + public int CustomerId { get; set; } + + } +} diff --git a/Nop.Plugin.Misc.AuctionPlugin/Models/ProductBidBoxViewModel.cs b/Nop.Plugin.Misc.AuctionPlugin/Models/ProductBidBoxViewModel.cs new file mode 100644 index 0000000..a63414b --- /dev/null +++ b/Nop.Plugin.Misc.AuctionPlugin/Models/ProductBidBoxViewModel.cs @@ -0,0 +1,32 @@ +using Nop.Web.Framework.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nop.Plugin.Misc.AuctionPlugin.Models +{ + public record ProductBidBoxViewModel: BaseNopModel + { + public int ProductId { get; set; } + public int CustomerId { get; set; } + + public string WidgetZone { get; set; } + + + #region debug fields to be removed + + public decimal? BasePrice { get; set; } + + #endregion + + #region visible + public decimal LicitStep { get; set; } + public decimal? CurrentPrice { get; set; } + public decimal BidPrice { get; set; } + + #endregion visible + + } +} diff --git a/Nop.Plugin.Misc.AuctionPlugin/Models/RefreshWidgetRequest.cs b/Nop.Plugin.Misc.AuctionPlugin/Models/RefreshWidgetRequest.cs new file mode 100644 index 0000000..4df7bac --- /dev/null +++ b/Nop.Plugin.Misc.AuctionPlugin/Models/RefreshWidgetRequest.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nop.Plugin.Misc.AuctionPlugin.Models +{ + public class RefreshWidgetRequest + { + public string WidgetZone { get; set; } + public int ProductId { get; set; } + } +} diff --git a/Nop.Plugin.Misc.AuctionPlugin/Services/MyProductModelFactory.cs b/Nop.Plugin.Misc.AuctionPlugin/Services/MyProductModelFactory.cs new file mode 100644 index 0000000..b54193c --- /dev/null +++ b/Nop.Plugin.Misc.AuctionPlugin/Services/MyProductModelFactory.cs @@ -0,0 +1,204 @@ +using Nop.Core.Caching; +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Customers; +using Nop.Core.Domain.Media; +using Nop.Core.Domain.Orders; +using Nop.Core.Domain.Security; +using Nop.Core.Domain.Seo; +using Nop.Core.Domain.Shipping; +using Nop.Core.Domain.Vendors; +using Nop.Core; +using Nop.Services.Catalog; +using Nop.Services.Common; +using Nop.Services.Customers; +using Nop.Services.Directory; +using Nop.Services.Helpers; +using Nop.Services.Localization; +using Nop.Services.Media; +using Nop.Services.Orders; +using Nop.Services.Security; +using Nop.Services.Seo; +using Nop.Services.Shipping.Date; +using Nop.Services.Stores; +using Nop.Services.Tax; +using Nop.Services.Vendors; +using Nop.Web.Factories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Nop.Web.Models.Catalog; + +namespace Nop.Plugin.Misc.AuctionPlugin.Services +{ + public class MyProductModelFactory : ProductModelFactory + { + protected readonly CaptchaSettings _captchaSettings; + protected readonly CatalogSettings _catalogSettings; + protected readonly CustomerSettings _customerSettings; + protected readonly ICategoryService _categoryService; + protected readonly ICurrencyService _currencyService; + protected readonly ICustomerService _customerService; + protected readonly IDateRangeService _dateRangeService; + protected readonly IDateTimeHelper _dateTimeHelper; + protected readonly IDownloadService _downloadService; + protected readonly IGenericAttributeService _genericAttributeService; + protected readonly IJsonLdModelFactory _jsonLdModelFactory; + protected readonly ILocalizationService _localizationService; + protected readonly IManufacturerService _manufacturerService; + protected readonly IPermissionService _permissionService; + protected readonly IPictureService _pictureService; + protected readonly IPriceCalculationService _priceCalculationService; + protected readonly IPriceFormatter _priceFormatter; + protected readonly IProductAttributeParser _productAttributeParser; + protected readonly IProductAttributeService _productAttributeService; + protected readonly IProductService _productService; + protected readonly IProductTagService _productTagService; + protected readonly IProductTemplateService _productTemplateService; + protected readonly IReviewTypeService _reviewTypeService; + protected readonly IShoppingCartService _shoppingCartService; + protected readonly ISpecificationAttributeService _specificationAttributeService; + protected readonly IStaticCacheManager _staticCacheManager; + protected readonly IStoreContext _storeContext; + protected readonly IStoreService _storeService; + protected readonly IShoppingCartModelFactory _shoppingCartModelFactory; + protected readonly ITaxService _taxService; + protected readonly IUrlRecordService _urlRecordService; + protected readonly IVendorService _vendorService; + protected readonly IVideoService _videoService; + protected readonly IWebHelper _webHelper; + protected readonly IWorkContext _workContext; + protected readonly MediaSettings _mediaSettings; + protected readonly OrderSettings _orderSettings; + protected readonly SeoSettings _seoSettings; + protected readonly ShippingSettings _shippingSettings; + protected readonly VendorSettings _vendorSettings; + private static readonly char[] _separator = [',']; + + public MyProductModelFactory(CaptchaSettings captchaSettings, + CatalogSettings catalogSettings, + CustomerSettings customerSettings, + ICategoryService categoryService, + ICurrencyService currencyService, + ICustomerService customerService, + IDateRangeService dateRangeService, + IDateTimeHelper dateTimeHelper, + IDownloadService downloadService, + IGenericAttributeService genericAttributeService, + IJsonLdModelFactory jsonLdModelFactory, + ILocalizationService localizationService, + IManufacturerService manufacturerService, + IPermissionService permissionService, + IPictureService pictureService, + IPriceCalculationService priceCalculationService, + IPriceFormatter priceFormatter, + IProductAttributeParser productAttributeParser, + IProductAttributeService productAttributeService, + IProductService productService, + IProductTagService productTagService, + IProductTemplateService productTemplateService, + IReviewTypeService reviewTypeService, + IShoppingCartService shoppingCartService, + ISpecificationAttributeService specificationAttributeService, + IStaticCacheManager staticCacheManager, + IStoreContext storeContext, + IStoreService storeService, + IShoppingCartModelFactory shoppingCartModelFactory, + ITaxService taxService, + IUrlRecordService urlRecordService, + IVendorService vendorService, + IVideoService videoService, + IWebHelper webHelper, + IWorkContext workContext, + MediaSettings mediaSettings, + OrderSettings orderSettings, + SeoSettings seoSettings, + ShippingSettings shippingSettings, + VendorSettings vendorSettings) : base(captchaSettings, catalogSettings, customerSettings, categoryService, currencyService, customerService, dateRangeService, + dateTimeHelper, + downloadService, + genericAttributeService, + jsonLdModelFactory, + localizationService, + manufacturerService, + permissionService, + pictureService, + priceCalculationService, + priceFormatter, + productAttributeParser, + productAttributeService, + productService, + productTagService, + productTemplateService, + reviewTypeService, + shoppingCartService, + specificationAttributeService, + staticCacheManager, + storeContext, + storeService, + shoppingCartModelFactory, + taxService, + urlRecordService, + vendorService, + videoService, + webHelper, + workContext, + mediaSettings, + orderSettings, + seoSettings, + shippingSettings, + vendorSettings + ) + { + _captchaSettings = captchaSettings; + _catalogSettings = catalogSettings; + _customerSettings = customerSettings; + _categoryService = categoryService; + _currencyService = currencyService; + _customerService = customerService; + _dateRangeService = dateRangeService; + _dateTimeHelper = dateTimeHelper; + _downloadService = downloadService; + _genericAttributeService = genericAttributeService; + _jsonLdModelFactory = jsonLdModelFactory; + _localizationService = localizationService; + _manufacturerService = manufacturerService; + _permissionService = permissionService; + _pictureService = pictureService; + _priceCalculationService = priceCalculationService; + _priceFormatter = priceFormatter; + _productAttributeParser = productAttributeParser; + _productAttributeService = productAttributeService; + _productService = productService; + _productTagService = productTagService; + _productTemplateService = productTemplateService; + _reviewTypeService = reviewTypeService; + _shoppingCartService = shoppingCartService; + _specificationAttributeService = specificationAttributeService; + _staticCacheManager = staticCacheManager; + _storeContext = storeContext; + _storeService = storeService; + _shoppingCartModelFactory = shoppingCartModelFactory; + _taxService = taxService; + _urlRecordService = urlRecordService; + _vendorService = vendorService; + _webHelper = webHelper; + _workContext = workContext; + _mediaSettings = mediaSettings; + _orderSettings = orderSettings; + _seoSettings = seoSettings; + _shippingSettings = shippingSettings; + _vendorSettings = vendorSettings; + _videoService = videoService; + } + + public override async Task PrepareProductDetailsModelAsync(Product product, + ShoppingCartItem updatecartitem = null, bool isAssociatedProduct = false) + { + return await base.PrepareProductDetailsModelAsync(product, updatecartitem, isAssociatedProduct); + } + + } + +} diff --git a/Nop.Plugin.Misc.AuctionPlugin/Views/PublicProductBidBox.cshtml b/Nop.Plugin.Misc.AuctionPlugin/Views/PublicProductBidBox.cshtml index 67e5ac5..cb8331f 100644 --- a/Nop.Plugin.Misc.AuctionPlugin/Views/PublicProductBidBox.cshtml +++ b/Nop.Plugin.Misc.AuctionPlugin/Views/PublicProductBidBox.cshtml @@ -1,12 +1,96 @@ -@model string +@model ProductBidBoxViewModel +@inject IJsonHelper JsonHelper; -
-

Auction Public Viewcomponent

- - @T("Plugins.Misc.AuctionPlugin.BidBox.Field.Label"): - - - @(Model) - +@* @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(myObj) as String) *@ +
+

Auction Public View Component

+

@T("Plugins.Misc.AuctionPlugin.BidBox.Field.Label"):

+

DEBUG DATA:

+

@Model.ProductId

+

@Model.CustomerId

+

@Model.BasePrice

+
+
+ Current Price: + @Model.CurrentPrice +
+
+ Bid Step: + @Model.LicitStep +
+
+ +
+ + +
+ + + + + +
+ \ No newline at end of file diff --git a/Nop.Plugin.Misc.AuctionPlugin/Views/_ViewImports.cshtml b/Nop.Plugin.Misc.AuctionPlugin/Views/_ViewImports.cshtml index fedd360..0e71861 100644 --- a/Nop.Plugin.Misc.AuctionPlugin/Views/_ViewImports.cshtml +++ b/Nop.Plugin.Misc.AuctionPlugin/Views/_ViewImports.cshtml @@ -6,6 +6,7 @@ @using Microsoft.AspNetCore.Mvc.ViewFeatures @using System.Text.Encodings.Web +@using Newtonsoft.Json @using Nop.Core @using Nop.Core.Infrastructure @using Nop.Core.Domain.Catalog