This commit is contained in:
Adam 2024-11-12 16:18:53 +01:00
parent c471294871
commit 1f3a3e4a00
26 changed files with 4552 additions and 259 deletions

View File

@ -0,0 +1,39 @@
using Microsoft.AspNetCore.Mvc;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Mvc.Filters;
using Nop.Web.Framework;
using Nop.Plugin.Misc.AuctionPlugin.Services;
using Nop.Plugin.Misc.AuctionPlugin.Models;
[AutoValidateAntiforgeryToken]
[AuthorizeAdmin]
[Area(AreaNames.ADMIN)]
public class AuctionPluginAdminController : BasePluginController
{
//private readonly SignalRservice _signalRservice;
//public AuctionPluginAdminController(SignalRservice signalRservice)
public AuctionPluginAdminController()
{
//_signalRservice = signalRservice;
}
public async Task<IActionResult> Configure(bool showtour = false)
{
return View("~/Plugins/Misc.AuctionPlugin/Views/Configure.cshtml", new ConfigurationModel());
}
//[HttpPost]
//public async Task<IActionResult> TestHubConnection(string message)
//{
// try
// {
// await _signalRservice.TestHub();
// return Json(new { success = true, message = $"Hub successfully called - {message}" });
// }
// catch (Exception ex)
// {
// return Json(new { success = false, message = $"Error: {ex.Message}" });
// }
//}
}

View File

@ -3,114 +3,71 @@
@using Nop.Web.Framework
@{
var defaultGridPageSize = EngineContext.Current.Resolve<Nop.Core.Domain.Common.AdminAreaSettings>().DefaultGridPageSize;
var gridPageSizes = EngineContext.Current.Resolve<Nop.Core.Domain.Common.AdminAreaSettings>().GridPageSizes;
Layout = "_AdminLayout";
//page title
ViewBag.Title = T("Admin.Plugins.HomePageProduct").Text;
}
@using (Html.BeginForm())
{
<div class="content-header clearfix">
<h1 class="pull-left">
Create Announcement
</h1>
<div class="pull-right">
<button type="submit" class="btn bg-purple">
<i class="fa fa-file-pdf-o"></i>
Create Announcement
</button>
<a href="/Admin/LiveAnnouncement/AnnouncementList" class="btn bg-olive">LiveAnnouncement</a>
</div>
</div>
<div class="content">
<div class="form-horizontal">
<div class="panel-group">
<div class="panel panel-default panel-search">
<div class="panel-body">
<div class="row">
<div class="form-group">
<div class="col-md-3" style="text-align:center;">
@Html.LabelFor(model => model.Name)
</div>
<div class="col-md-8">
@Html.EditorFor(model => model.Name)
</div>
<div class="col-md-1">
&nbsp;
</div>
</div>
<div class="form-group">
<div class="col-md-3" style="text-align:center;">
@Html.LabelFor(model => model.Body)
</div>
<div class="col-md-8">
<nop-editor asp-for="@Model.Body" asp-template="RichEditor" />
</div>
<div class="col-md-1">
&nbsp;
</div>
</div>
<div class="form-group">
<div class="col-md-3" style="text-align:center;">
@Html.LabelFor(model => model.IsActive)
</div>
<div class="col-md-8">
@Html.EditorFor(model => model.IsActive)
@ -118,23 +75,14 @@
</div>
<div class="col-md-1">
&nbsp;
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
}

View File

@ -0,0 +1,114 @@
@model Nop.Plugin.Widget.LiveAnnouncement.Models.AnnouncementModel
@using Nop.Core.Infrastructure
@using Nop.Web.Framework
@{
var defaultGridPageSize = EngineContext.Current.Resolve<Nop.Core.Domain.Common.AdminAreaSettings>().DefaultGridPageSize;
var gridPageSizes = EngineContext.Current.Resolve<Nop.Core.Domain.Common.AdminAreaSettings>().GridPageSizes;
Layout = "_AdminLayout";
//page title
ViewBag.Title = T("Admin.Plugins.HomePageProduct").Text;
}
<div class="content-header clearfix">
<div class="pull-right">
<a href="../LiveAnnouncement/Announcement" class="btn bg-blue">
<i class="fa fa-floppy-o"></i>
Add
</a>
</div>
</div>
<div class="content">
<div class="form-horizontal">
<div class="panel-group">
<div class="panel panel-default">
<div class="panel-body">
<div id="Announcement-grid"></div>
<script>
$(document).ready(function () {
$("#Announcement-grid").kendoGrid({
dataSource: {
type: "json",
transport: {
read: {
url: "@Html.Raw(Url.Action("AnnouncementList", "LiveAnnouncement"))",
type: "POST",
dataType: "json",
data: addAntiForgeryToken
},
destroy: {
url: "@Html.Raw(Url.Action("Delete", "LiveAnnouncement"))",
type: "POST",
dataType: "json",
data: addAntiForgeryToken
}
},
schema: {
data: "Data",
total: "Total",
errors: "Errors",
model: {
id: "Id"
}
},
error: function (e) {
display_kendoui_grid_error(e);
// Cancel the changes
this.cancelChanges();
},
pageSize: @(defaultGridPageSize),
serverPaging: true,
serverFiltering: true,
serverSorting: true
},
pageable: {
refresh: true,
pageSizes: [@(gridPageSizes)]
},
editable: {
confirmation: false,
mode: "inline"
},
scrollable: false,
columns: [{
field: "Name",
title: "Name",
width: 100
}, {
field: "Body",
title: "Body",
width: 100,
headerAttributes: { style: "text-align:center" },
attributes: { style: "text-align:center" }
},
{
field: "IsActive",
title: "IsActive",
width: 100,
headerAttributes: { style: "text-align:center" },
attributes: { style: "text-align:center" }
},
{
title: "Edite",
width: 100,
template: '<a href="Edit/#=Id#">@T("Admin.Common.Edit")</a>'
}, {
command: { name: "destroy", text: "@T("Admin.Common.Delete")" },
title: "@T("Admin.Common.Delete")",
width: 100,
headerAttributes: { style: "text-align:center" },
attributes: { style: "text-align:center" }
}]
});
});
</script>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,110 @@
@model ConfigurationModel
@{
Layout = "_ConfigurePlugin";
NopHtml.SetActiveMenuItemSystemName("API plugins");
}
<form asp-controller="SignalRApiAdmin" asp-action="Configure" method="post" id="configuration-form">
<div class="cards-group">
<div class="card card-default no-margin">
<div class="card-header">
TITLE comes here
</div>
<div class="card-body">
<div class="form-group row">
<div class="col-md-3">
<div class="label-wrapper">
<label class="col-form-label">
Connection Status
</label>
</div>
</div>
<div class="col-md-3">
<div class="form-text-row" id="connection-status">@Model.Test</div>
</div>
</div>
<!-- New button to call TestHub method -->
<!--nop-antiforgery-token /-->
<div class="form-group row">
<div class="col-md-3 offset-md-3">
<button type="button" class="btn btn-primary" id="test-hub-button">Test Hub Connection</button>
</div>
</div>
<div class="form-group row">
<div class="col-md-9 offset-md-3">
<button type="submit" name="credentials" class="btn btn-primary">@T("Admin.Common.Save")</button>
</div>
</div>
<!-- Result display area -->
<div class="form-group row">
<div class="col-md-3 offset-md-3">
<div id="test-hub-result"></div>
</div>
</div>
</div>
</div>
</div>
</form>
<script>
document.getElementById("test-hub-button").addEventListener("click", function () {
// Clear previous result
document.getElementById("test-hub-result").innerText = "Testing connection...";
var postData = {
message: 'hello'
};
addAntiForgeryToken(postData);
$.ajax({
cache: false,
type: 'POST',
url: '@Url.Action("TestHubConnection", "SignalRApi")',
data: postData,
traditional: true,
success: function (data, textStatus, jqXHR) {
if (data.message) {
document.getElementById("test-hub-result").innerText = data.message;
} else {
//display errors if returned
document.getElementById("test-hub-result").innerText = data.message;
display_nop_error(data);
}
},
complete: function (jqXHR, textStatus) {
console.log('Complete');
}
});
// // Send AJAX request to TestHubConnection action
// fetch('@Url.Action("TestHubConnection", "SignalRApi")', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// 'RequestVerificationToken': 'addAntiForgeryToken(postData);'
// }
// })
// .then(response => response.json())
// .then(data => {
// // Update the result based on response
// if (data.success) {
// document.getElementById("test-hub-result").innerText = data.message;
// } else {
// document.getElementById("test-hub-result").innerText = data.message;
// }
// })
// .catch(error => {
// document.getElementById("test-hub-result").innerText = "Error: " + error;
// });
});
</script>

View File

@ -8,3 +8,4 @@
@using System.Text.Encodings.Web
@using Nop.Services.Events
@using Nop.Web.Framework.Events
@using Nop.Core.Infrastructure

View File

@ -22,6 +22,11 @@ public static class AuctionDefaults
/// </summary>
public static string ConfigurationRouteName => "Plugin.Misc.AuctionPlugin.Configure";
/// <summary>
/// Gets the hub route name
/// </summary>
public static string AnnouncementRouteName => "Plugin.Misc.AuctionPlugin.Announcement";
/// <summary>
/// Gets the name of autosuggest component
/// </summary>

View File

@ -1,24 +0,0 @@
using System;
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace Nop.Plugin.Misc.AuctionPlugin
{
public class AuctionHub : Hub
{
public Task Send(string announcement)
{
return Clients.All.SendAsync("Send", announcement);
}
}
}

View File

@ -1,3 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Data;
@ -22,16 +25,12 @@ namespace Nop.Plugin.Misc.AuctionPlugin
#region Fields
private readonly IWorkContext _context;
private readonly ILocalizationService _localizationService;
private readonly ISettingService _settingService;
private readonly IProductAttributeService _productAttributeService;
protected readonly IUrlHelperFactory _urlHelperFactory;
protected readonly IActionContextAccessor _actionContextAccessor;
#endregion
@ -39,25 +38,29 @@ namespace Nop.Plugin.Misc.AuctionPlugin
#region Ctr
public AuctionPlugin(IWorkContext context, ILocalizationService localizationService, ISettingService settingService, IProductAttributeService productAttributeService)
public AuctionPlugin(IWorkContext context,
ILocalizationService localizationService,
ISettingService settingService,
IProductAttributeService productAttributeService,
UrlHelperFactory urlHelperFactory,
IActionContextAccessor actionContextAccessor)
{
_context = context;
_localizationService = localizationService;
_settingService = settingService;
_productAttributeService = productAttributeService;
_urlHelperFactory = urlHelperFactory;
_actionContextAccessor = actionContextAccessor;
}
#endregion
public override string GetConfigurationPageUrl()
{
return _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext).RouteUrl("Plugin.Misc.SignalRApi.Configure");
}
public override async Task InstallAsync()
{
await _settingService.SaveSettingAsync(new AuctionSettings
@ -101,8 +104,7 @@ namespace Nop.Plugin.Misc.AuctionPlugin
return typeof(AuctionPublicViewComponent);
}
if (widgetZone.Equals(AdminWidgetZones.OrderBillingAddressDetailsBottom) ||
widgetZone.Equals(AdminWidgetZones.OrderShippingAddressDetailsBottom))
if (widgetZone.Equals(AdminWidgetZones.ProductDetailsBlock))
{
return typeof(AuctionAdminViewComponent);
}
@ -127,57 +129,34 @@ namespace Nop.Plugin.Misc.AuctionPlugin
var liveAnnouncementPluginNode = rootNode.ChildNodes.FirstOrDefault(x => x.SystemName == "Auction");
if (liveAnnouncementPluginNode == null)
{
liveAnnouncementPluginNode = new SiteMapNode()
{
SystemName = "Auction",
Title = "Live Auction",
Visible = true,
IconClass = "fa-gear"
};
rootNode.ChildNodes.Add(liveAnnouncementPluginNode);
}
liveAnnouncementPluginNode.ChildNodes.Add(new SiteMapNode()
{
Title = await _localizationService.GetResourceAsync("Misc.Announcement"),
Visible = true,
IconClass = "fa-dot-circle-o",
Url = "~/Admin/LiveAnnouncement/Announcement"
Url = "~/Admin/Announcement"
});
liveAnnouncementPluginNode.ChildNodes.Add(new SiteMapNode()
{
Title = await _localizationService.GetResourceAsync("Misc.AnnouncementList"),
Visible = true,
IconClass = "fa-dot-circle-o",
Url = "~/Admin/LiveAnnouncement/AnnouncementList"
});
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,83 @@
$(function () {
var connection = new signalR.HubConnectionBuilder()
.withUrl('/announcement')
.build();
connection.on('send', data => {
showannouncement(data);
});
function start() {
connection.start().catch(function (err) {
setTimeout(function () {
start();
}, 100000);
});
}
connection.onclose(function () {
start();
});
start();
});
function showannouncement(announcemant) {
if (announcemant) {
toastr.options = {
"closeButton": true,
"debug": false,
"newestOnTop": false,
"progressBar": false,
"positionClass": "toast-bottom-right",
"preventDuplicates": false,
"onclick": null,
"showDuration": 300,
"hideDuration": 10000,
"timeOut": 100000,
"extendedTimeOut": 20000,
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
};
tostView = '<div class="item announcemantToast">' + announcemant + '</div>'
toastr["info"](tostView);
$('.toast-info').css("background-color", "#008080");
toastr.options.onclick = function () {
$("html, body").animate(
{ scrollTop: 0 },
1000);
}
$(".toast").click(function () {
$("html, body").animate(
{ scrollTop: 0 },
1000);
});
$(".toast-info").click(function () {
$("html, body").animate(
{ scrollTop: 0 },
1000);
});
toastr.options = {
onclick: function () {
$("html, body").animate(
{ scrollTop: 0 },
1000);
}
}
$(".announcemantToast").on("click", function () {
$("html, body").animate(
{ scrollTop: 0 },
1000);
});
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,476 @@
/*
* Toastr
* Copyright 2012-2015
* Authors: John Papa, Hans Fjällemark, and Tim Ferrell.
* All Rights Reserved.
* Use, reproduction, distribution, and modification of this code is subject to the terms and
* conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php
*
* ARIA Support: Greta Krafsig
*
* Project: https://github.com/CodeSeven/toastr
*/
/* global define */
(function (define) {
define(['jquery'], function ($) {
return (function () {
var $container;
var listener;
var toastId = 0;
var toastType = {
error: 'error',
info: 'info',
success: 'success',
warning: 'warning'
};
var toastr = {
clear: clear,
remove: remove,
error: error,
getContainer: getContainer,
info: info,
options: {},
subscribe: subscribe,
success: success,
version: '2.1.4',
warning: warning
};
var previousToast;
return toastr;
////////////////
function error(message, title, optionsOverride) {
return notify({
type: toastType.error,
iconClass: getOptions().iconClasses.error,
message: message,
optionsOverride: optionsOverride,
title: title
});
}
function getContainer(options, create) {
if (!options) { options = getOptions(); }
$container = $('#' + options.containerId);
if ($container.length) {
return $container;
}
if (create) {
$container = createContainer(options);
}
return $container;
}
function info(message, title, optionsOverride) {
return notify({
type: toastType.info,
iconClass: getOptions().iconClasses.info,
message: message,
optionsOverride: optionsOverride,
title: title
});
}
function subscribe(callback) {
listener = callback;
}
function success(message, title, optionsOverride) {
return notify({
type: toastType.success,
iconClass: getOptions().iconClasses.success,
message: message,
optionsOverride: optionsOverride,
title: title
});
}
function warning(message, title, optionsOverride) {
return notify({
type: toastType.warning,
iconClass: getOptions().iconClasses.warning,
message: message,
optionsOverride: optionsOverride,
title: title
});
}
function clear($toastElement, clearOptions) {
var options = getOptions();
if (!$container) { getContainer(options); }
if (!clearToast($toastElement, options, clearOptions)) {
clearContainer(options);
}
}
function remove($toastElement) {
var options = getOptions();
if (!$container) { getContainer(options); }
if ($toastElement && $(':focus', $toastElement).length === 0) {
removeToast($toastElement);
return;
}
if ($container.children().length) {
$container.remove();
}
}
// internal functions
function clearContainer (options) {
var toastsToClear = $container.children();
for (var i = toastsToClear.length - 1; i >= 0; i--) {
clearToast($(toastsToClear[i]), options);
}
}
function clearToast ($toastElement, options, clearOptions) {
var force = clearOptions && clearOptions.force ? clearOptions.force : false;
if ($toastElement && (force || $(':focus', $toastElement).length === 0)) {
$toastElement[options.hideMethod]({
duration: options.hideDuration,
easing: options.hideEasing,
complete: function () { removeToast($toastElement); }
});
return true;
}
return false;
}
function createContainer(options) {
$container = $('<div/>')
.attr('id', options.containerId)
.addClass(options.positionClass);
$container.appendTo($(options.target));
return $container;
}
function getDefaults() {
return {
tapToDismiss: true,
toastClass: 'toast',
containerId: 'toast-container',
debug: false,
showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery
showDuration: 300,
showEasing: 'swing', //swing and linear are built into jQuery
onShown: undefined,
hideMethod: 'fadeOut',
hideDuration: 1000,
hideEasing: 'swing',
onHidden: undefined,
closeMethod: false,
closeDuration: false,
closeEasing: false,
closeOnHover: true,
extendedTimeOut: 1000,
iconClasses: {
error: 'toast-error',
info: 'toast-info',
success: 'toast-success',
warning: 'toast-warning'
},
iconClass: 'toast-info',
positionClass: 'toast-top-right',
timeOut: 5000, // Set timeOut and extendedTimeOut to 0 to make it sticky
titleClass: 'toast-title',
messageClass: 'toast-message',
escapeHtml: false,
target: 'body',
closeHtml: '<button type="button">&times;</button>',
closeClass: 'toast-close-button',
newestOnTop: true,
preventDuplicates: false,
progressBar: false,
progressClass: 'toast-progress',
rtl: false
};
}
function publish(args) {
if (!listener) { return; }
listener(args);
}
function notify(map) {
var options = getOptions();
var iconClass = map.iconClass || options.iconClass;
if (typeof (map.optionsOverride) !== 'undefined') {
options = $.extend(options, map.optionsOverride);
iconClass = map.optionsOverride.iconClass || iconClass;
}
if (shouldExit(options, map)) { return; }
toastId++;
$container = getContainer(options, true);
var intervalId = null;
var $toastElement = $('<div/>');
var $titleElement = $('<div/>');
var $messageElement = $('<div/>');
var $progressElement = $('<div/>');
var $closeElement = $(options.closeHtml);
var progressBar = {
intervalId: null,
hideEta: null,
maxHideTime: null
};
var response = {
toastId: toastId,
state: 'visible',
startTime: new Date(),
options: options,
map: map
};
personalizeToast();
displayToast();
handleEvents();
publish(response);
if (options.debug && console) {
console.log(response);
}
return $toastElement;
function escapeHtml(source) {
if (source == null) {
source = '';
}
return source
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
function personalizeToast() {
setIcon();
setTitle();
setMessage();
setCloseButton();
setProgressBar();
setRTL();
setSequence();
setAria();
}
function setAria() {
var ariaValue = '';
switch (map.iconClass) {
case 'toast-success':
case 'toast-info':
ariaValue = 'polite';
break;
default:
ariaValue = 'assertive';
}
$toastElement.attr('aria-live', ariaValue);
}
function handleEvents() {
if (options.closeOnHover) {
$toastElement.hover(stickAround, delayedHideToast);
}
if (!options.onclick && options.tapToDismiss) {
$toastElement.click(hideToast);
}
if (options.closeButton && $closeElement) {
$closeElement.click(function (event) {
if (event.stopPropagation) {
event.stopPropagation();
} else if (event.cancelBubble !== undefined && event.cancelBubble !== true) {
event.cancelBubble = true;
}
if (options.onCloseClick) {
options.onCloseClick(event);
}
hideToast(true);
});
}
if (options.onclick) {
$toastElement.click(function (event) {
options.onclick(event);
hideToast();
});
}
}
function displayToast() {
$toastElement.hide();
$toastElement[options.showMethod](
{duration: options.showDuration, easing: options.showEasing, complete: options.onShown}
);
if (options.timeOut > 0) {
intervalId = setTimeout(hideToast, options.timeOut);
progressBar.maxHideTime = parseFloat(options.timeOut);
progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime;
if (options.progressBar) {
progressBar.intervalId = setInterval(updateProgress, 10);
}
}
}
function setIcon() {
if (map.iconClass) {
$toastElement.addClass(options.toastClass).addClass(iconClass);
}
}
function setSequence() {
if (options.newestOnTop) {
$container.prepend($toastElement);
} else {
$container.append($toastElement);
}
}
function setTitle() {
if (map.title) {
var suffix = map.title;
if (options.escapeHtml) {
suffix = escapeHtml(map.title);
}
$titleElement.append(suffix).addClass(options.titleClass);
$toastElement.append($titleElement);
}
}
function setMessage() {
if (map.message) {
var suffix = map.message;
if (options.escapeHtml) {
suffix = escapeHtml(map.message);
}
$messageElement.append(suffix).addClass(options.messageClass);
$toastElement.append($messageElement);
}
}
function setCloseButton() {
if (options.closeButton) {
$closeElement.addClass(options.closeClass).attr('role', 'button');
$toastElement.prepend($closeElement);
}
}
function setProgressBar() {
if (options.progressBar) {
$progressElement.addClass(options.progressClass);
$toastElement.prepend($progressElement);
}
}
function setRTL() {
if (options.rtl) {
$toastElement.addClass('rtl');
}
}
function shouldExit(options, map) {
if (options.preventDuplicates) {
if (map.message === previousToast) {
return true;
} else {
previousToast = map.message;
}
}
return false;
}
function hideToast(override) {
var method = override && options.closeMethod !== false ? options.closeMethod : options.hideMethod;
var duration = override && options.closeDuration !== false ?
options.closeDuration : options.hideDuration;
var easing = override && options.closeEasing !== false ? options.closeEasing : options.hideEasing;
if ($(':focus', $toastElement).length && !override) {
return;
}
clearTimeout(progressBar.intervalId);
return $toastElement[method]({
duration: duration,
easing: easing,
complete: function () {
removeToast($toastElement);
clearTimeout(intervalId);
if (options.onHidden && response.state !== 'hidden') {
options.onHidden();
}
response.state = 'hidden';
response.endTime = new Date();
publish(response);
}
});
}
function delayedHideToast() {
if (options.timeOut > 0 || options.extendedTimeOut > 0) {
intervalId = setTimeout(hideToast, options.extendedTimeOut);
progressBar.maxHideTime = parseFloat(options.extendedTimeOut);
progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime;
}
}
function stickAround() {
clearTimeout(intervalId);
progressBar.hideEta = 0;
$toastElement.stop(true, true)[options.showMethod](
{duration: options.showDuration, easing: options.showEasing}
);
}
function updateProgress() {
var percentage = ((progressBar.hideEta - (new Date().getTime())) / progressBar.maxHideTime) * 100;
$progressElement.width(percentage + '%');
}
}
function getOptions() {
return $.extend({}, getDefaults(), toastr.options);
}
function removeToast($toastElement) {
if (!$container) { $container = getContainer(); }
if ($toastElement.is(':visible')) {
return;
}
$toastElement.remove();
$toastElement = null;
if ($container.children().length === 0) {
$container.remove();
previousToast = undefined;
}
}
})();
});
}(typeof define === 'function' && define.amd ? define : function (deps, factory) {
if (typeof module !== 'undefined' && module.exports) { //Node
module.exports = factory(require('jquery'));
} else {
window.toastr = factory(window.jQuery);
}
}));

View File

@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Nop.Plugin.Misc.AuctionPlugin;
using Nop.Plugin.Misc.AuctionPlugin.Domains;
using Nop.Plugin.Misc.AuctionPlugin.Hubs;
using Nop.Plugin.Misc.AuctionPlugin.Models;
using Nop.Plugin.Misc.AuctionPlugin.Services;
using Nop.Web.Areas.Admin.Controllers;
@ -13,167 +13,97 @@ using System.Linq;
namespace Nop.Plugin.Misc.AuctionPlugin.Controllers
{
public class LiveAnnouncementController : BaseAdminController
{
#region Field
private readonly IAnnouncementService _announcementService;
private IHubContext<AuctionHub> _announcementHubContext;
#endregion
#region Ctr
public LiveAnnouncementController(
IAnnouncementService announcementService,
IHubContext<AuctionHub> announcementHubContext)
IHubContext<AuctionHub> announcementHubContext)
{
_announcementService = announcementService;
_announcementHubContext = announcementHubContext;
}
#endregion
#region Methods
public IActionResult Announcement()
{
var model = new AnnouncementViewModel();
return View("~/Plugins/Misc.AuctionPlugin/Views/LiveAnnouncementView/Announcement.cshtml", model);
return View("~/Plugins/Misc.AuctionPlugin/Admin/Views/Announcement.cshtml", model);
}
[HttpPost]
public async Task<IActionResult> Announcement(AnnouncementViewModel viewModel)
{
AnnouncementEntity objOfAnnouncementDomain = new AnnouncementEntity();
objOfAnnouncementDomain.Name = viewModel.Name;
objOfAnnouncementDomain.Body = viewModel.Body;
objOfAnnouncementDomain.IsActive = viewModel.IsActive;
objOfAnnouncementDomain.CreateDate = DateTime.UtcNow;
objOfAnnouncementDomain.Created = DateTime.UtcNow;
await _announcementService.InsertAsync(objOfAnnouncementDomain);
if (viewModel.IsActive == true)
{
_announcementHubContext.Clients.All.SendAsync("send", viewModel.Body.ToString());
await _announcementHubContext.Clients.All.SendAsync("send", viewModel.Body.ToString());
}
return RedirectToAction("AnnouncementList");
}
[HttpPost]
public async Task<IActionResult> Edit(AnnouncementEntity model)
{
var entity = await _announcementService.GetAnnouncementByIdAsync(model.Id);
entity.Name = model.Name;
entity.Body = model.Body;
entity.IsActive = model.IsActive;
entity.CreateDate = DateTime.UtcNow;
entity.Created = DateTime.UtcNow;
await _announcementService.UpdateAsync(entity);
if (model.IsActive == true)
{
_announcementHubContext.Clients.All.SendAsync("send", model.Body.ToString());
}
return RedirectToAction("AnnouncementList");
}
public async Task<IActionResult> Edit(int Id)
{
var singleAnnouncement = await _announcementService.GetAnnouncementByIdAsync(Id);
var model = new AnnouncementEntity();
model.Id = singleAnnouncement.Id;
model.Name = singleAnnouncement.Name;
model.Body = singleAnnouncement.Body;
model.IsActive = singleAnnouncement.IsActive;
return View("~/Plugins/Widget.LiveAnnouncement/Views/LiveAnnouncementView/Announcement.cshtml", model);
}
public async Task<IActionResult> Delete(int Id)
{
var singleAnnouncement = await _announcementService.GetAnnouncementByIdAsync(Id);
await _announcementService.DeleteAsync(singleAnnouncement);
return new NullJsonResult();
}
public IActionResult AnnouncementList()
{
var model = new AnnouncementViewModel();
return View("~/Plugins/Widget.LiveAnnouncement/Views/LiveAnnouncementView/AnnouncementList.cshtml", model);
}
//[HttpPost]

View File

@ -13,7 +13,7 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Domains
public bool IsActive { get; set; }
public DateTime CreateDate { get; set; }
public DateTime Created { get; set; }
}

View File

@ -0,0 +1,30 @@
using System;
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace Nop.Plugin.Misc.AuctionPlugin.Hubs
{
public class AuctionHub : Hub<IAuctionHubClient>
{
public async Task ReceiveMessageFromClient(string message)
{
// Broadcast the message received from the client to all clients
Console.Write($"Received message: {message}");
await Clients.All.SendAsync("Send", message);
//await _signalRservice.TestHub();
}
public async Task Send(string announcement)
{
await Clients.All.SendAsync("Send", announcement);
}
public async Task SendPriceToUsers(string message)
{
await Clients.All.SendAsync("SendPrice", message);
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nop.Plugin.Misc.AuctionPlugin.Hubs
{
public interface IAuctionHubClient
{
Task SendAsync(string name, string message);
Task ReceiveMessageFromClient(string message);
Task SendPrice(string price);
}
}

View File

@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Nop.Core.Infrastructure;
using Nop.Plugin.Misc.AuctionPlugin.Hubs;
using Nop.Plugin.Misc.AuctionPlugin.Services;
namespace Nop.Plugin.Misc.AuctionPlugin.Infrastructure
@ -24,11 +25,8 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Infrastructure
});
services.AddSignalR(hubOptions =>
{
hubOptions.KeepAliveInterval = TimeSpan.FromMinutes(1);
});
//register services and interfaces
@ -46,7 +44,7 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Infrastructure
application.UseEndpoints(endpoints =>
{
endpoints.MapHub<AuctionHub>("/announcement");
endpoints.MapHub<AuctionHub>("/auctionhub");
});
application.UseCors(options => {
options.AllowAnyMethod().AllowAnyHeader().AllowCredentials().SetIsOriginAllowed((hosts) => true);

View File

@ -1,4 +1,6 @@
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Nop.Web.Framework;
using Nop.Web.Framework.Mvc.Routing;
namespace Nop.Plugin.Misc.AuctionPlugin.Infrastructure
@ -14,7 +16,15 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Infrastructure
/// <param name="endpointRouteBuilder">Route builder</param>
public void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
{
//config
endpointRouteBuilder.MapControllerRoute(name: AuctionDefaults.ConfigurationRouteName,
pattern: "Admin/AuctionPlugin/Configure",
defaults: new { controller = "AuctionPluginAdmin", action = "Configure", area = AreaNames.ADMIN });
//announcement admin
endpointRouteBuilder.MapControllerRoute(name: AuctionDefaults.AnnouncementRouteName,
pattern: "Admin/AuctionPluginAdmin/Announcement",
defaults: new { controller = "Announcement", action = "Announcement", area = AreaNames.ADMIN });
}
/// <summary>

View File

@ -1,8 +1,9 @@
using FluentMigrator.Builders.Create.Table;
using FluentMigrator;
using FluentMigrator.Builders.Create.Table;
using Nop.Data.Mapping.Builders;
using Nop.Plugin.Misc.AuctionPlugin.Domains;
namespace Nop.Plugin.Misc.AuctionPlugin.Mapping;
namespace Nop.Plugin.Misc.AuctionPlugin.Mapping.Builders;
/// <summary>
/// Represents a pickup point entity builder
@ -17,10 +18,7 @@ public class AnnouncementBuilder : NopEntityBuilder<AnnouncementEntity>
/// <param name="table">Create table expression builder</param>
public override void MapEntity(CreateTableExpressionBuilder table)
{
table.WithColumn(nameof(AnnouncementEntity.Id))
.AsInt16()
.NotNullable()
.WithColumn(nameof(AnnouncementEntity.Name))
table.WithColumn(nameof(AnnouncementEntity.Name))
.AsString(250)
.NotNullable()
.WithColumn(nameof(AnnouncementEntity.IsActive))
@ -28,7 +26,9 @@ public class AnnouncementBuilder : NopEntityBuilder<AnnouncementEntity>
.NotNullable().WithDefault(0)
.WithColumn(nameof(AnnouncementEntity.Body))
.AsString(500)
.NotNullable();
.NotNullable()
.WithColumn(nameof(AnnouncementEntity.Created)).AsDateTime().NotNullable().WithDefault(SystemMethods.CurrentUTCDateTime);
}
#endregion

View File

@ -1,21 +0,0 @@
using FluentMigrator.Builders.Create.Table;
using Nop.Data.Mapping.Builders;
using Nop.Plugin.Misc.AuctionPlugin.Domains;
namespace Nop.Plugin.Misc.AuctionPlugin.Mapping.Builders
{
public class PluginBuilder : NopEntityBuilder<BidEntity>
{
#region Methods
/// <summary>
/// Apply entity configuration
/// </summary>
/// <param name="table">Create table expression builder</param>
public override void MapEntity(CreateTableExpressionBuilder table)
{
}
#endregion
}
}

View File

@ -5,7 +5,7 @@ using Nop.Plugin.Misc.AuctionPlugin.Domains;
namespace Nop.Plugin.Misc.AuctionPlugin.Migrations
{
[NopMigration("11/9/2024 9:01:27 PM", "Nop.Plugin.Misc.AuctionPlugin schema", MigrationProcessType.Installation)]
[NopMigration("2024-11-09 09:01:27", "Nop.Plugin.Misc.AuctionPlugin schema", MigrationProcessType.Installation)]
public class SchemaMigration : AutoReversingMigration
{
/// <summary>

View File

@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations;
using Nop.Web.Framework.Models;
using Nop.Web.Framework.Mvc;
using Nop.Web.Framework.Mvc.ModelBinding;
namespace Nop.Plugin.Misc.AuctionPlugin.Models;
/// <summary>
/// Represents a configuration model
/// </summary>
public record ConfigurationModel : BaseNopModel
{
#region Ctor
public ConfigurationModel()
{
Test = "";
}
#endregion
#region Properties
public string Test { get; set; }
#endregion
}

View File

@ -15,8 +15,10 @@
<None Remove="logo.jpg" />
<None Remove="plugin.json" />
<None Remove="Views\AdminProductAuctionSettingsBox.cshtml" />
<None Remove="Views\AnnouncementView.cshtml" />
<None Remove="Views\LiveAnnouncementView.cshtml" />
<None Remove="Views\Announcement.cshtml" />
<None Remove="Views\AnnouncementList.cshtml" />
<None Remove="Views\Configure.cshtml" />
<None Remove="Views\LiveAnnouncement.cshtml" />
<None Remove="Views\PublicInfo.cshtml" />
<None Remove="Views\PublicProductBidBox.cshtml" />
<None Remove="Views\_ViewImports.cshtml" />
@ -31,19 +33,25 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Views\AdminProductAuctionSettingsBox.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Views\AnnouncementView.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Content Include="Areas\Admin\Views\AnnouncementList.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Views\LiveAnnouncementView.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Content Include="Areas\Admin\Views\Announcement.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Areas\Admin\Views\Configure.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Views\LiveAnnouncement.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Views\PublicInfo.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Views\PublicProductBidBox.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Views\_ViewImports.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -55,7 +63,7 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Views\_ViewImports.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
@ -66,7 +74,6 @@
<ItemGroup>
<Folder Include="Areas\Admin\Components\" />
<Folder Include="Areas\Admin\Controllers\" />
<Folder Include="Areas\Admin\Extensions\" />
<Folder Include="Areas\Admin\Factories\" />
<Folder Include="Areas\Admin\Models\" />
@ -74,6 +81,19 @@
<Folder Include="Extensions\" />
<Folder Include="Factories\" />
<Folder Include="Validators\" />
<Folder Include="Views\LiveAnnouncementView\" />
</ItemGroup>
<ItemGroup>
<None Update="Content\Js\LiveAnnouncement.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Content\Js\signalr.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Content\Js\toastr.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<!-- This target execute after "Build" target -->

View File

@ -1,13 +1,8 @@
@using Nop.Core;
@using Nop.Core.Domain.Seo;
@using Nop.Core.Infrastructure;
@using Nop.Web.Framework;
@using Nop.Web.Framework.UI;
@using Nop.Services.Configuration;
@{
@ -15,10 +10,10 @@
ISettingService _settingContext = EngineContext.Current.Resolve<ISettingService>();
IStoreContext _storeContext = EngineContext.Current.Resolve<IStoreContext>();
Html.AddScriptParts("~/Plugins/Widget.LiveAnnouncement/Scripts/signalr.js");
Html.AddScriptParts("~/Plugins/Widget.LiveAnnouncement/Scripts/LiveAnnouncement.js");
Html.AddCssFileParts("~/Plugins/Widget.LiveAnnouncement/Content/toastr.min.css");
Html.AddScriptParts("~/Plugins/Widget.LiveAnnouncement/Scripts/toastr.js");
Html.AddScriptParts("~/Plugins/Misc.AuctionPlugin/Content/Js/signalr.js");
Html.AddScriptParts("~/Plugins/Misc.AuctionPlugin/Content/Js/LiveAnnouncement.js");
Html.AddCssFileParts("~/Plugins/Misc.AuctionPlugin/Content/Css/toastr.min.css");
Html.AddScriptParts("~/Plugins/Misc.AuctionPlugin/Content/Js/toastr.js");
}

View File

@ -2,27 +2,27 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Nop.Web.Framework
@inject INopHtmlHelper NopHtml
@using Microsoft.AspNetCore.Mvc.ViewFeatures
@using Nop.Web.Framework.UI
@using Nop.Web.Framework.Extensions
@using System.Text.Encodings.Web
@using Nop.Services.Events
@using Nop.Web.Framework.Events
@using Nop.Web.Framework.Infrastructure
@using Nop.Core
@using Nop.Core.Infrastructure
@using Nop.Core.Domain.Catalog
@using Nop.Web.Areas.Admin.Models.Catalog
@using Nop.Web.Extensions
@using Nop.Core.Domain.Seo;
@using Nop.Services.Events
@using Nop.Web.Framework
@using Nop.Web.Framework.Extensions
@using Nop.Web.Framework.Events
@using Nop.Web.Framework.Infrastructure
@using Nop.Web.Extensions
@using Nop.Web.Framework.Extensions
@using Nop.Web.Framework.Models
@using Nop.Web.Framework.Models.DataTables
@using Nop.Web.Framework.Security.Captcha
@using Nop.Web.Framework.Security.Honeypot
@using Nop.Web.Framework.Themes
@using Nop.Web.Framework.UI
@using Nop.Web.Areas.Admin.Models.Catalog
@using Nop.Plugin.Misc.AuctionPlugin
@using Nop.Plugin.Misc.AuctionPlugin.Models
@using Nop.Plugin.Misc.AuctionPlugin.Services