refresh ViewComponent via SignalR

This commit is contained in:
Adam 2024-11-16 18:28:13 +01:00
parent 7f39d3dcdd
commit ddbfbc60c2
14 changed files with 470 additions and 19 deletions

View File

@ -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";
/// <summary>
/// Gets the name of autosuggest component

View File

@ -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<IList<string>>(new List<string>
{
PublicWidgetZones.ProductPriceTop,
PublicWidgetZones.ProductDetailsInsideOverviewButtonsBefore,
PublicWidgetZones.ProductDetailsBottom,
PublicWidgetZones.HeaderAfter

View File

@ -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
/// </returns>
public async Task<IViewComponentResult> 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

View File

@ -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);

View File

@ -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<IActionResult> 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<IActionResult> 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 });
}
}

View File

@ -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; }
}
}

View File

@ -59,6 +59,8 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Infrastructure
services.AddScoped<AuctionDbTable>();
services.AddScoped<AuctionBidDbTable>();
services.AddScoped<AuctionDbContext>();
services.AddScoped<MyProductModelFactory>();
}
/// <summary>

View File

@ -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" });
}

View File

@ -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; }
}
}

View File

@ -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
}
}

View File

@ -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; }
}
}

View File

@ -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<ProductDetailsModel> PrepareProductDetailsModelAsync(Product product,
ShoppingCartItem updatecartitem = null, bool isAssociatedProduct = false)
{
return await base.PrepareProductDetailsModelAsync(product, updatecartitem, isAssociatedProduct);
}
}
}

View File

@ -1,12 +1,96 @@
@model string
@model ProductBidBoxViewModel
@inject IJsonHelper JsonHelper;
<div class="bg-dark">
<h3>Auction Public Viewcomponent</h3>
<span class="label">
@T("Plugins.Misc.AuctionPlugin.BidBox.Field.Label"):
</span>
<span class="value">
@(Model)
</span>
@* @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(myObj) as String) *@
<div class="bg-dark p-3">
<h3>Auction Public View Component</h3>
<p>@T("Plugins.Misc.AuctionPlugin.BidBox.Field.Label"):</p>
<p>DEBUG DATA:</p>
<p>@Model.ProductId</p>
<p>@Model.CustomerId</p>
<p>@Model.BasePrice</p>
<div class="d-flex justify-content-between mb-3">
<div>
<strong>Current Price:</strong>
<span class="value">@Model.CurrentPrice</span>
</div>
<div>
<strong>Bid Step:</strong>
<span class="value">@Model.LicitStep</span>
</div>
</div>
<div class="d-flex align-items-center mb-3">
<label for="bidPrice" class="me-2">Your Bid Price:</label>
<input type="number" id="bidPrice" class="form-control w-25" value="@Model.BidPrice" min="@(Model.CurrentPrice + Model.LicitStep)">
</div>
<button id="bidButton" class="btn btn-success">
Place Bid
</button>
<button id="testButton" class="btn btn-success">
TestButton
</button>
<div id="bidFeedback" class="mt-3"></div>
</div>
<script>
$(document).ready(function () {
var pageViewModel = @Html.Raw(Json.Serialize(Model));
console.log(pageViewModel);
console.log(pageViewModel.WidgetZone);
$('#bidButton').click(function () {
const bidPrice = $('#bidPrice').val();
// Validate bid price
if (!bidPrice || parseInt(bidPrice) < @Model.CurrentPrice + @Model.LicitStep) {
$('#bidFeedback').text('Bid price must be at least the current price plus the licit step.')
.addClass('text-danger');
return;
}
// AJAX POST to send the bid
$.ajax({
url: '/Auction/PlaceBid', // Ensure this endpoint exists in your controller
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
bidPrice: bidPrice,
customerId: dataObject.CustomerId,
productId: dataObject.ProductId
}),
success: function (response) {
$('#bidFeedback').text(response.message).removeClass('text-danger').addClass('text-success');
},
error: function (xhr) {
$('#bidFeedback').text('Failed to place bid: ' + xhr.responseText)
.addClass('text-danger');
}
});
});
$('#testButton').click(function ()
{
$.ajax({
url: '/Auction/RefreshAuctionWidget', // Controller endpoint
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
WidgetZone: pageViewModel.WidgetZone,
ProductId: pageViewModel.ProductId
}),
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);
}
});
});
});
</script>

View File

@ -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