This commit is contained in:
Loretta 2024-11-20 15:01:46 +01:00
commit 3cc1269bd8
11 changed files with 224 additions and 135 deletions

View File

@ -76,7 +76,7 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Areas.Admin.Controllers
{ {
Title = viewModel.Name, Title = viewModel.Name,
Message = viewModel.Body, Message = viewModel.Body,
} }.ToJson()
}; };
var jsonMessage = JsonConvert.SerializeObject(announcement, Formatting.Indented, new JsonSerializerSettings var jsonMessage = JsonConvert.SerializeObject(announcement, Formatting.Indented, new JsonSerializerSettings
@ -122,7 +122,7 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Areas.Admin.Controllers
ProductName = viewModel.ProductName, ProductName = viewModel.ProductName,
BidPrice = viewModel.BidPrice.ToString(), BidPrice = viewModel.BidPrice.ToString(),
NextStepAmount = viewModel.NextStepAmount.ToString() NextStepAmount = viewModel.NextStepAmount.ToString()
} }.ToJson()
}; };
var jsonMessage = JsonConvert.SerializeObject(bid, Formatting.Indented, var jsonMessage = JsonConvert.SerializeObject(bid, Formatting.Indented,
new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() } new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }

View File

@ -7,6 +7,7 @@ using Nop.Data;
using Nop.Plugin.Misc.AuctionPlugin.Areas.Admin.Components; using Nop.Plugin.Misc.AuctionPlugin.Areas.Admin.Components;
using Nop.Plugin.Misc.AuctionPlugin.Components; using Nop.Plugin.Misc.AuctionPlugin.Components;
//using Nop.Plugin.Misc.AuctionPlugin.Components; //using Nop.Plugin.Misc.AuctionPlugin.Components;
using Nop.Services.Catalog; using Nop.Services.Catalog;
using Nop.Services.Cms; using Nop.Services.Cms;

View File

@ -2,10 +2,12 @@
using Nop.Core; using Nop.Core;
using Nop.Plugin.Misc.AuctionPlugin.Domains.Dtos; using Nop.Plugin.Misc.AuctionPlugin.Domains.Dtos;
using Nop.Plugin.Misc.AuctionPlugin.Domains.Entities; using Nop.Plugin.Misc.AuctionPlugin.Domains.Entities;
using Nop.Plugin.Misc.AuctionPlugin.Domains.Enums;
using Nop.Plugin.Misc.AuctionPlugin.Models; using Nop.Plugin.Misc.AuctionPlugin.Models;
using Nop.Plugin.Misc.AuctionPlugin.Services; using Nop.Plugin.Misc.AuctionPlugin.Services;
using Nop.Services.Cms; using Nop.Services.Cms;
using Nop.Services.Common; using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Logging; using Nop.Services.Logging;
using Nop.Web.Framework.Components; using Nop.Web.Framework.Components;
using Nop.Web.Framework.Infrastructure; using Nop.Web.Framework.Infrastructure;
@ -23,6 +25,7 @@ public class AuctionPublicViewComponent : NopViewComponent
protected readonly IWorkContext _workContext; protected readonly IWorkContext _workContext;
protected readonly AuctionService _auctionService; protected readonly AuctionService _auctionService;
protected readonly AuctionSettings _auctionSettings; protected readonly AuctionSettings _auctionSettings;
protected readonly ICustomerService _customerService;
protected readonly ILogger _logger; protected readonly ILogger _logger;
#endregion #endregion
@ -35,6 +38,7 @@ public class AuctionPublicViewComponent : NopViewComponent
IWorkContext workContext, IWorkContext workContext,
AuctionService auctionService, AuctionService auctionService,
AuctionSettings auctionSettings, AuctionSettings auctionSettings,
ICustomerService customerService,
ILogger logger) ILogger logger)
{ {
_addressService = addressService; _addressService = addressService;
@ -43,6 +47,7 @@ public class AuctionPublicViewComponent : NopViewComponent
_workContext = workContext; _workContext = workContext;
_auctionService = auctionService; _auctionService = auctionService;
_auctionSettings = auctionSettings; _auctionSettings = auctionSettings;
_customerService = customerService;
_logger = logger; _logger = logger;
} }
@ -100,6 +105,7 @@ public class AuctionPublicViewComponent : NopViewComponent
List<ProductToAuctionMapping> auctionIds = await _auctionService.GetProductToAuctionsByProductIdAsync(productDetailsModel.Id); List<ProductToAuctionMapping> auctionIds = await _auctionService.GetProductToAuctionsByProductIdAsync(productDetailsModel.Id);
int auctionId = 0; int auctionId = 0;
AuctionDto auction;
if(auctionIds == null || auctionIds.Count == 0) if(auctionIds == null || auctionIds.Count == 0)
{ {
return Content(string.Empty); return Content(string.Empty);
@ -112,27 +118,32 @@ public class AuctionPublicViewComponent : NopViewComponent
//we have more auctions so we return the closest open //we have more auctions so we return the closest open
foreach (var auctionMapping in auctionIds) foreach (var auctionMapping in auctionIds)
{ {
var auction = await _auctionService.GetAuctionDtoByIdAsync(auctionMapping.AuctionId); var auctionItem = await _auctionService.GetAuctionDtoByIdAsync(auctionMapping.AuctionId);
if (!auction.Closed) if (!auctionItem.Closed)
{ {
openAuctionList.Add(auction); openAuctionList.Add(auctionItem);
} }
} }
//first auction that started or strats not earlier than yesterday //first auction that started or strats not earlier than yesterday
auctionId = openAuctionList.Where(x => x.StartDateUtc > DateTime.UtcNow.AddDays(-1)).OrderBy(x => x.StartDateUtc).FirstOrDefault().Id; auction = openAuctionList.Where(x => x.StartDateUtc > DateTime.UtcNow.AddDays(-1)).OrderBy(x => x.StartDateUtc).FirstOrDefault();
auctionId = auction.Id;
} }
else else
{ {
var valami = await _auctionService.GetAuctionDtoByIdAsync(auctionIds.FirstOrDefault().AuctionId); auction = await _auctionService.GetAuctionDtoByIdAsync(auctionIds.FirstOrDefault().AuctionId);
auctionId = valami.Id; auctionId = auction.Id;
} }
var productToAuctionId = await _auctionService.GetProductToAuctionByAuctionIdAndProductIdAsync(auctionId, productDetailsModel.Id); var productToAuctionId = await _auctionService.GetProductToAuctionByAuctionIdAndProductIdAsync(auctionId, productDetailsModel.Id);
productBidBoxViewModel.IsAdmin = await _customerService.IsAdminAsync(customer);
productBidBoxViewModel.IsGuest = await _customerService.IsGuestAsync(customer);
productBidBoxViewModel.AuctionClosed = auction.Closed;
productBidBoxViewModel.WidgetZone = widgetZone; productBidBoxViewModel.WidgetZone = widgetZone;
productBidBoxViewModel.BasePrice = productDetailsModel.ProductPrice.OldPriceValue; productBidBoxViewModel.BasePrice = productDetailsModel.ProductPrice.OldPriceValue;
productBidBoxViewModel.CurrentPrice = productDetailsModel.ProductPrice.PriceValue; productBidBoxViewModel.CurrentPrice = productDetailsModel.ProductPrice.PriceValue;
productBidBoxViewModel.ProductToAuctionId = productToAuctionId.FirstOrDefault().Id; productBidBoxViewModel.ProductToAuctionsId = productToAuctionId.FirstOrDefault().Id;
productBidBoxViewModel.AuctionId = auctionId; productBidBoxViewModel.AuctionId = auctionId;
productBidBoxViewModel.CustomerId = customer.Id; productBidBoxViewModel.CustomerId = customer.Id;
productBidBoxViewModel.ProductId = productDetailsModel.Id; productBidBoxViewModel.ProductId = productDetailsModel.Id;

View File

@ -24,9 +24,10 @@ $(function () {
start(); start();
}); });
window.sendMessageToServer = function (messageType, data) { window.sendMessageToServer = function (messageType, senderId, data) {
var messageWrapper = { var messageWrapper = {
MessageType: messageType, MessageType: messageType,
SenderId: senderId,
Data: data Data: data
}; };

View File

@ -3,7 +3,8 @@
let animation = "slideDown"; let animation = "slideDown";
const handlers = { const handlers = {
announcement: function (data) { announcement: function (data) {
toastr.info(`<div class="item announcemantToast">${data.message}</div>`, data.title, { var myObject = JSON.parse(data);
toastr.info(`<div class="item announcemantToast">${myObject.message}</div>`, myObject.title, {
"closeButton": true, "closeButton": true,
"positionClass": "toast-bottom-right", "positionClass": "toast-bottom-right",
"newestOnTop": true, "newestOnTop": true,
@ -22,7 +23,9 @@
$('.toast-info').css("background-color", "#008080"); $('.toast-info').css("background-color", "#008080");
}, },
bidNotification: function (data) { bidNotification: function (data) {
toastr.success(`<div class="item bidToast"><p>${data.bidPrice}</p><p>${data.productName}</p></div>`, "New bid arrived", { console.log(data);
var myObject = JSON.parse(data);
toastr.success(`<div class="item bidToast"><p>${myObject.bidPrice}</p><p>${myObject.productName}</p></div>`, "New bid arrived", {
"closeButton": true, "closeButton": true,
"positionClass": "toast-bottom-right", "positionClass": "toast-bottom-right",
"newestOnTop": true, "newestOnTop": true,
@ -43,39 +46,36 @@
var publicProductBidBox = document.getElementById("publicProductBidBox"); var publicProductBidBox = document.getElementById("publicProductBidBox");
if (publicProductBidBox) if (publicProductBidBox)
{ {
refreshPublicBidBox(data); refreshPublicBidBox(myObject);
} }
}, },
auctionUpdateAlternate: function (data) { openItemMessage: function (data) {
const widgetPriceElement = document.getElementById("WidgetPrice"); var myObject = JSON.parse(data);
toastr.success(`<div class="item bidToast"><p>${myObject.bidPrice}</p><p>${myObject.productName}</p></div>`, "Item auction is OPENED!", {
"closeButton": true,
"positionClass": "toast-bottom-right",
"newestOnTop": true,
"progressBar": true,
"preventDuplicates": false,
"onclick": null,
"showDuration": "30000",
"hideDuration": "1000",
"timeOut": "5000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": animation,
"hideMethod": "fadeOut"
});
$('.toast-success').css("background-color", "#4caf50");
if (widgetPriceElement) { var publicProductBidBox = document.getElementById("publicProductBidBox");
widgetPriceElement.textContent = data.bidPrice; // Update the price if (publicProductBidBox) {
console.log(`WidgetPrice updated to: ${data.bidPrice}`); refreshPublicBidBox(myObject);
} else {
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
// type: 'POST',
// contentType: 'application/json',
// data: JSON.stringify({
// WidgetZone: data.WidgetZone,
// ProductId: data.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);
// }
// });
//},
// Add more handlers as needed // Add more handlers as needed
default: function (data) { default: function (data) {
@ -87,10 +87,13 @@
function messageRouter(message) { function messageRouter(message) {
// Parse the JSON message // Parse the JSON message
try { try {
const parsedMessage = JSON.parse(message); var parsedMessage = JSON.parse(message);
const messageType = parsedMessage.messageType; var messageType = parsedMessage.messageType;
const messageData = parsedMessage.data; var senderId = parsedMessage.senderId;
var messageData = parsedMessage.data;
console.log("Message type:" + messageType); console.log("Message type:" + messageType);
console.log("Message sender:" + senderId);
console.log("Message content" + parsedMessage.data);
// Route to appropriate handler, default if no match // Route to appropriate handler, default if no match
(handlers[messageType] || handlers.default)(messageData); (handlers[messageType] || handlers.default)(messageData);
} catch (e) { } catch (e) {

View File

@ -9,6 +9,7 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs.Messages
public class MessageWrapper public class MessageWrapper
{ {
public string MessageType { get; set; } public string MessageType { get; set; }
public object Data { get; set; } public int SenderId { get; set; }
public string Data { get; set; }
} }
} }

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nop.Plugin.Misc.AuctionPlugin.Domains.Dtos;
namespace Nop.Plugin.Misc.AuctionPlugin.Models
{
public class OpenItemRequest : AuctionBidDto
{
}
}

View File

@ -52,7 +52,11 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs
switch (message.MessageType) switch (message.MessageType)
{ {
case "BidRequestMessage": case "BidRequestMessage":
await HandleBidRequest(message.Data.ToString()?.JsonTo<AuctionBidRequest>()); await HandleBidRequest(message.SenderId, message.Data.JsonTo<AuctionBidRequest>());
break;
case "OpenItemRequestMessage":
await HandleOpenItemMessageRequest(message.SenderId, message.Data.JsonTo<OpenItemRequest>());
break; break;
// Add other message types here // Add other message types here
@ -62,7 +66,37 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs
} }
} }
private async Task HandleBidRequest(AuctionBidRequest bidRequestMessage) private async Task HandleOpenItemMessageRequest(int SenderId, OpenItemRequest openItemMessage)
{
if (openItemMessage == null)
{
_logger.Error($"SignalRMessageHandler.HandleOpenItemMessageRequest(); openItemRequestMessage == null");
return;
}
try
{
await _logger.InformationAsync($"SignalRMessageHandler.HandleOpenItemMessageRequest(); Item to open: - ProductToAuctionMappingId: {openItemMessage.ProductAuctionMappingId}");
//get auction
var auction = await _auctionService.GetAuctionDtoByProductToAuctionIdAsync(openItemMessage.ProductAuctionMappingId);
//get productToAuction
var productToAuction = await _auctionService.GetProductToAuctionDtoByIdAsync(openItemMessage.ProductAuctionMappingId);
auction.Closed = false;
await _logger.InformationAsync($"SignalRMessageHandler.HandleOpenItemMessageRequest(); Auction {auction.Id}, ProducToAuction {productToAuction.Id}");
//_auctionService.UpdateAuctionAsync(auction);
}
catch (Exception ex)
{
_logger.Error($"Error {ex.Message}");
}
}
private async Task HandleBidRequest(int senderId, AuctionBidRequest bidRequestMessage)
{ {
if (bidRequestMessage == null) if (bidRequestMessage == null)
{ {
@ -128,12 +162,13 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs
var bid = new MessageWrapper var bid = new MessageWrapper
{ {
MessageType = "bidNotification", MessageType = "bidNotification",
SenderId = senderId,
Data = new BidNotificationMessage(await _auctionService.GetAuctionDtoWithProductByIdAsync(auction.Id, activeProductAuction.ProductId, true)) Data = new BidNotificationMessage(await _auctionService.GetAuctionDtoWithProductByIdAsync(auction.Id, activeProductAuction.ProductId, true))
{ {
ProductName = auctionBid.ProductId.ToString(), ProductName = auctionBid.ProductId.ToString(),
BidPrice = auctionBid.BidPrice.ToString(CultureInfo.InvariantCulture), BidPrice = auctionBid.BidPrice.ToString(CultureInfo.InvariantCulture),
NextStepAmount = "50000" NextStepAmount = "50000"
} }.ToJson()
}; };
await _hubContext.Clients.All.SendAsync("send", bid.ToJson()); await _hubContext.Clients.All.SendAsync("send", bid.ToJson());

View File

@ -1,4 +1,5 @@
using Nop.Web.Framework.Models; using Nop.Plugin.Misc.AuctionPlugin.Domains.Enums;
using Nop.Web.Framework.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -9,8 +10,11 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Models
{ {
public record ProductBidBoxViewModel: BaseNopModel public record ProductBidBoxViewModel: BaseNopModel
{ {
public int ProductToAuctionId { get; set; } public int ProductToAuctionsId { get; set; }
public bool IsAdmin { get; set; }
public bool IsGuest { get; set; }
public int AuctionId { get; set; } public int AuctionId { get; set; }
public bool AuctionClosed { get; set; }
public int ProductId { get; set; } public int ProductId { get; set; }
public int CustomerId { get; set; } public int CustomerId { get; set; }

View File

@ -3,12 +3,13 @@
@* @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(myObj) as String) *@ @* @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(myObj) as String) *@
<style>
</style> @{
<div id="publicProductBidBox" class="bg-dark p-3"> if (!Model.IsGuest)
{
<div class="d-flex justify-content-between mb-3"> <div id="publicProductBidBox" class="p-3 bg-primary text-white">
<h4>This item is under auction!</h4>
<div class="d-flex justify-content-between">
<div> <div>
<strong>Base Price:</strong> <strong>Base Price:</strong>
<span class="value"> <span class="value">
@ -21,7 +22,7 @@
<span class="value">@String.Format("{0:c}", Model.LicitStep)</span> <span class="value">@String.Format("{0:c}", Model.LicitStep)</span>
</div> </div>
<div> <div>
<button id="signalRBidButton" class="btn btn-success" type="button"> <button id="signalRBidButton" class="btn btn-success" style="text-transform: uppercase;" type="button">
Bid @String.Format("{0:c}", Model.BidPrice) Bid @String.Format("{0:c}", Model.BidPrice)
</button> </button>
@* <button id="bidButton" class="btn btn-success"> @* <button id="bidButton" class="btn btn-success">
@ -37,7 +38,41 @@
</button> *@ </button> *@
<div id="bidFeedback" class="mt-3"></div> <div id="bidFeedback" class="mt-3"></div>
</div> </div>
if (Model.IsAdmin)
{
<div id="publicProductBidBoxAdmin" class="p-3 bg-primary text-white">
<h4>Manage auction!</h4>
<div id="bidBoxAdminButtons" class="d-flex justify-content-between mb-3">
<button id="signalROpenItemButton" class="btn btn-secondary" style="text-transform: uppercase;" type="button">
Activate item
</button>
<button id="signalRFirstWarningButton" class="btn btn-warning" style="text-transform: uppercase;" type="button">
First warning
</button>
</div>
</div>
}
else
{
<p>No access to admin level buttons</p>
}
}
else
{
<div id="publicProductBidBoxGuest" class="p-3 bg-primary text-white">
<h4>This item is under auction!</h4>
<div id="bidBoxGuestMessage" class="d-flex justify-content-between mb-3">
<p>Please log in or register to participate!</p>
</div>
</div>
}
}
<script> <script>
var pageViewModel = undefined; var pageViewModel = undefined;
@ -48,77 +83,61 @@
console.log(pageViewModel.WidgetZone); console.log(pageViewModel.WidgetZone);
console.log(typeof sendMessageToServer); console.log(typeof sendMessageToServer);
// $('#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({
// auctionId: pageViewModel.AuctionId,
// 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');
// }
// });
// });
$("#signalRBidButton").on("click", function () { $("#signalRBidButton").on("click", function () {
document.getElementById("signalRBidButton").disabled = true; document.getElementById("signalRBidButton").disabled = true;
event.preventDefault(); event.preventDefault();
var bidMessage = { var bidMessage = {
AuctionId: pageViewModel.AuctionId.toFixed(), ProductAuctionMappingId: pageViewModel.ProductToAuctions,
BidPrice: pageViewModel.BidPrice.toFixed(2), AuctionId: pageViewModel.AuctionId,
ProductId: pageViewModel.ProductId.toFixed(), BidPrice: pageViewModel.BidPrice,
CustomerId: pageViewModel.CustomerId.toFixed() ProductId: pageViewModel.ProductId,
CustomerId: pageViewModel.CustomerId
}; };
var content = JSON.stringify(bidMessage);
sendMessageToServer("BidRequestMessage", bidMessage); sendMessageToServer("BidRequestMessage", pageViewModel.CustomerId, content);
return false; return false;
}); });
$("#signalROpenItemButton").on("click", function () {
document.getElementById("signalROpenItemButton").disabled = true;
event.preventDefault();
var openItemMessage = {
ProductAuctionMappingId: pageViewModel.ProductToAuctions,
AdminId: pageViewModel.CustomerId
};
var content = JSON.stringify(openItemMessage);
console.log(content);
sendMessageToServer("OpenItemRequestMessage", pageViewModel.CustomerId, content);
return false;
});
$("#signalRFirstWarningButton").on("click", function () {
document.getElementById("signalRFirstWarningButton").disabled = true;
event.preventDefault();
var itemFirstWarningMessage = {
ProductAuctionMappingId: pageViewModel.ProductToAuctions,
AdminId: pageViewModel.CustomerId
};
var content = JSON.stringify(itemFirstWarningMessage);
console.log(content);
sendMessageToServer("FirstWarningMessage", pageViewModel.CustomerId, content);
return false;
});
}); });
function refreshPublicBidBox(data) { function refreshPublicBidBox(data) {
// $.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);
// }
// });
var widgetPriceElement = document.getElementById("price-value-"+pageViewModel.ProductId);
var widgetPriceElement = document.getElementById("price-value-" + pageViewModel.ProductId);
var budButtonElement = document.getElementById("signalRBidButton"); var budButtonElement = document.getElementById("signalRBidButton");
if (widgetPriceElement) { if (widgetPriceElement) {