diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomDashboardController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomDashboardController.cs new file mode 100644 index 0000000..eea1d99 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomDashboardController.cs @@ -0,0 +1,150 @@ +using ExCSS; +using Microsoft.AspNetCore.Mvc; +using Nop.Core; +using Nop.Core.Domain.Common; +using Nop.Core.Domain.Customers; +using Nop.Services.Common; +using Nop.Services.Configuration; +using Nop.Services.Localization; +using Nop.Services.Messages; +using Nop.Services.Security; +using Nop.Web.Areas.Admin.Controllers; +using Nop.Web.Areas.Admin.Factories; +using Nop.Web.Areas.Admin.Models.Common; +using Nop.Web.Areas.Admin.Models.Home; +using Nop.Web.Framework.Models.DataTables; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers +{ + public partial class CustomDashboardController : BaseAdminController + { + #region Fields + + protected readonly AdminAreaSettings _adminAreaSettings; + protected readonly ICommonModelFactory _commonModelFactory; + protected readonly IHomeModelFactory _homeModelFactory; + protected readonly ILocalizationService _localizationService; + protected readonly INotificationService _notificationService; + protected readonly IPermissionService _permissionService; + protected readonly ISettingService _settingService; + protected readonly IGenericAttributeService _genericAttributeService; + protected readonly IWorkContext _workContext; + + #endregion + + #region Ctor + + public CustomDashboardController(AdminAreaSettings adminAreaSettings, + ICommonModelFactory commonModelFactory, + IHomeModelFactory homeModelFactory, + ILocalizationService localizationService, + INotificationService notificationService, + IPermissionService permissionService, + ISettingService settingService, + IGenericAttributeService genericAttributeService, + IWorkContext workContext) + { + _adminAreaSettings = adminAreaSettings; + _commonModelFactory = commonModelFactory; + _homeModelFactory = homeModelFactory; + _localizationService = localizationService; + _notificationService = notificationService; + _permissionService = permissionService; + _settingService = settingService; + _workContext = workContext; + _genericAttributeService = genericAttributeService; + } + + #endregion + + #region Methods + + public virtual async Task Index() + { + //display a warning to a store owner if there are some error + var customer = await _workContext.GetCurrentCustomerAsync(); + var hideCard = await _genericAttributeService.GetAttributeAsync(customer, NopCustomerDefaults.HideConfigurationStepsAttribute); + var closeCard = await _genericAttributeService.GetAttributeAsync(customer, NopCustomerDefaults.CloseConfigurationStepsAttribute); + + if ((hideCard || closeCard) && await _permissionService.AuthorizeAsync(StandardPermission.System.MANAGE_MAINTENANCE)) + { + var warnings = await _commonModelFactory.PrepareSystemWarningModelsAsync(); + if (warnings.Any(warning => warning.Level == SystemWarningLevel.Fail || warning.Level == SystemWarningLevel.Warning)) + { + var locale = await _localizationService.GetResourceAsync("Admin.System.Warnings.Errors"); + _notificationService.WarningNotification(string.Format(locale, Url.Action("Warnings", "Common")), false); //do not encode URLs + } + } + + //progress of localization + var currentLanguage = await _workContext.GetWorkingLanguageAsync(); + var progress = await _genericAttributeService.GetAttributeAsync(currentLanguage, NopCommonDefaults.LanguagePackProgressAttribute); + if (!string.IsNullOrEmpty(progress)) + { + var locale = await _localizationService.GetResourceAsync("Admin.Configuration.LanguagePackProgressMessage"); + _notificationService.SuccessNotification(string.Format(locale, progress, NopLinksDefaults.OfficialSite.Translations), false); + await _genericAttributeService.SaveAttributeAsync(currentLanguage, NopCommonDefaults.LanguagePackProgressAttribute, string.Empty); + } + + //prepare model + var model = await _homeModelFactory.PrepareDashboardModelAsync(new DashboardModel()); + + //return View(model); + return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Index.cshtml", model); + } + + [HttpPost] + public virtual async Task NopCommerceNewsHideAdv() + { + _adminAreaSettings.HideAdvertisementsOnAdminArea = !_adminAreaSettings.HideAdvertisementsOnAdminArea; + await _settingService.SaveSettingAsync(_adminAreaSettings); + + return Content("Setting changed"); + } + + public virtual async Task GetPopularSearchTerm() + { + var model = new DataTablesModel(); + model = await _homeModelFactory.PreparePopularSearchTermReportModelAsync(model); + return PartialView("Table", model); + } + + public virtual async Task GetBestsellersBriefReportByAmount() + { + var model = new DataTablesModel(); + model = await _homeModelFactory.PrepareBestsellersBriefReportByAmountModelAsync(model); + return PartialView("Table", model); + } + + public virtual async Task GetBestsellersBriefReportByQuantity() + { + var model = new DataTablesModel(); + model = await _homeModelFactory.PrepareBestsellersBriefReportByQuantityModelAsync(model); + return PartialView("Table", model); + } + + public virtual async Task GetLatestOrders() + { + var model = new DataTablesModel(); + model = await _homeModelFactory.PrepareLatestOrdersModelAsync(model); + return PartialView("Table", model); + } + + public virtual async Task GetOrderIncomplete() + { + var model = new DataTablesModel(); + model = await _homeModelFactory.PrepareOrderIncompleteModelAsync(model); + return PartialView("Table", model); + } + + public virtual async Task GetOrderAverage() + { + var model = new DataTablesModel(); + model = await _homeModelFactory.PrepareOrderAverageModelAsync(model); + return PartialView("Table", model); + } + + #endregion + } +} + diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs new file mode 100644 index 0000000..ba30f4f --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs @@ -0,0 +1,82 @@ +using Microsoft.AspNetCore.Mvc; +using Nop.Services.Orders; +using Nop.Web.Areas.Admin.Controllers; +using Nop.Web.Framework.Mvc.Filters; +using Nop.Web.Framework; +using Nop.Web.Areas.Admin.Models.Orders; +using Nop.Services.Security; +using Nop.Plugin.Misc.FruitBankPlugin.Factories; +using Nop.Web.Areas.Admin.Factories; +using Nop.Plugin.Misc.FruitBankPlugin.Models; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers +{ + [Area(AreaNames.ADMIN)] + [AuthorizeAdmin] + public class CustomOrderController : BaseAdminController + { + private readonly IOrderService _orderService; + private readonly IOrderModelFactory _orderModelFactory; + private readonly IPermissionService _permissionService; + // ... other dependencies + + public CustomOrderController(IOrderService orderService, IOrderModelFactory orderModelFactory, IPermissionService permissionService) + { + _orderService = orderService; + _orderModelFactory = orderModelFactory; + _permissionService = permissionService; + // ... initialize other deps + } + + [CheckPermission(StandardPermission.Orders.ORDERS_VIEW)] + public virtual async Task List(List orderStatuses = null, List paymentStatuses = null, List shippingStatuses = null) + { + //prepare model + var model = await _orderModelFactory.PrepareOrderSearchModelAsync(new OrderSearchModel + { + OrderStatusIds = orderStatuses, + PaymentStatusIds = paymentStatuses, + ShippingStatusIds = shippingStatuses, + }); + + return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Order/List.cshtml", model); + } + + [HttpPost] + [CheckPermission(StandardPermission.Orders.ORDERS_VIEW)] + public virtual async Task OrderList(OrderSearchModel searchModel) + { + //prepare model + var model = await _orderModelFactory.PrepareOrderListModelAsync(searchModel); + + + Console.WriteLine($"Total: {model.RecordsTotal}, Data Count: {model.Data.Count()}"); + foreach (var item in model.Data.Take(3)) + { + Console.WriteLine($"Order: {item.Id}, {item.CustomOrderNumber}"); + } + var valami = Json(model); + Console.WriteLine(valami); + return valami; + } + + public virtual IActionResult Test() + { + // Your custom logic here + // This will use your custom List.cshtml view + return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Order/Test.cshtml"); + } + + //[HttpPost] + //[CheckPermission(Nop.Services.Security.StandardPermission.Orders.ORDERS_VIEW)] + //public virtual async Task OrderList(OrderSearchModel searchModel) + //{ + // //prepare model + // var model = await _orderModelFactory.PrepareOrderListModelAsync(searchModel); + + // return Json(model); + //} + } +} + + diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InvoiceController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InvoiceController.cs new file mode 100644 index 0000000..0273ff2 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InvoiceController.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Mvc; +using Nop.Web.Areas.Admin.Controllers; +using Nop.Web.Framework.Mvc.Filters; +using Nop.Web.Framework; +using Nop.Services.Security; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers +{ + [Area(AreaNames.ADMIN)] + [AuthorizeAdmin] + public class InvoiceController : BaseAdminController + { + private readonly IPermissionService _permissionService; + + public InvoiceController(IPermissionService permissionService) + { + _permissionService = permissionService; + } + + public async Task List() + { + if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL)) + return AccessDeniedView(); + + return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Invoice/List.cshtml"); + } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/ShipmentController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/ShipmentController.cs new file mode 100644 index 0000000..324d67d --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/ShipmentController.cs @@ -0,0 +1,149 @@ +using Microsoft.AspNetCore.Mvc; +using Nop.Web.Areas.Admin.Controllers; +using Nop.Web.Framework.Mvc.Filters; +using Nop.Web.Framework; +using Nop.Services.Security; +using Microsoft.AspNetCore.Http; +using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models; +using Nop.Services.Messages; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers +{ + [Area(AreaNames.ADMIN)] + [AuthorizeAdmin] + public class ShipmentController : BaseAdminController + { + private readonly IPermissionService _permissionService; + protected readonly INotificationService _notificationService; + + public ShipmentController(IPermissionService permissionService, INotificationService notificationService) + { + _permissionService = permissionService; + _notificationService = notificationService; + } + + public async Task List() + { + if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL)) + return AccessDeniedView(); + + return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Shipment/List.cshtml"); + } + + [HttpGet] + public async Task Create() + { + if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL)) + return AccessDeniedView(); + + var model = new CreateShipmentModel + { + ShipmentDate = DateTime.Now + }; + + return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Shipment/Create.cshtml", model); + } + + [HttpPost] + public async Task Create(CreateShipmentModel model) + { + if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL)) + return AccessDeniedView(); + + if (!ModelState.IsValid) + { + return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Shipment/Create.cshtml", model); + } + + try + { + // TODO: Save shipment to database + // var shipment = new Shipment + // { + // Name = model.ShipmentName, + // Description = model.Description, + // ShipmentDate = model.ShipmentDate, + // TrackingNumber = model.TrackingNumber, + // CreatedOnUtc = DateTime.UtcNow + // }; + // await _shipmentService.InsertShipmentAsync(shipment); + + _notificationService.SuccessNotification("Shipment created successfully"); + return RedirectToAction("List"); + } + catch (Exception ex) + { + _notificationService.ErrorNotification($"Error creating shipment: {ex.Message}"); + return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Shipment/Create.cshtml", model); + } + } + + [HttpPost] + public async Task UploadFile(IFormFile file) + { + try + { + if (file == null || file.Length == 0) + return Json(new FileUploadResult { Success = false, ErrorMessage = "No file selected" }); + + // Validate file type (PDF only) + if (!file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase)) + return Json(new FileUploadResult { Success = false, ErrorMessage = "Only PDF files are allowed" }); + + // Validate file size (e.g., max 10MB) + if (file.Length > 10 * 1024 * 1024) + return Json(new FileUploadResult { Success = false, ErrorMessage = "File size must be less than 10MB" }); + + // Create upload directory if it doesn't exist + var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads", "shipments"); + Directory.CreateDirectory(uploadsPath); + + // Generate unique filename + var fileName = $"{Guid.NewGuid()}_{file.FileName}"; + var filePath = Path.Combine(uploadsPath, fileName); + + // Save file + using (var stream = new FileStream(filePath, FileMode.Create)) + { + await file.CopyToAsync(stream); + } + + return Json(new FileUploadResult + { + Success = true, + FileName = file.FileName, + FilePath = $"/uploads/shipments/{fileName}" + }); + } + catch (Exception ex) + { + return Json(new FileUploadResult { Success = false, ErrorMessage = ex.Message }); + } + } + + [HttpPost] + public IActionResult DeleteUploadedFile(string filePath) + { + try + { + if (string.IsNullOrEmpty(filePath)) + return Json(new { success = false, message = "Invalid file path" }); + + var fullPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", filePath.TrimStart('/')); + + if (System.IO.File.Exists(fullPath)) + { + System.IO.File.Delete(fullPath); + return Json(new { success = true }); + } + + return Json(new { success = false, message = "File not found" }); + } + catch (Exception ex) + { + return Json(new { success = false, message = ex.Message }); + } + } + + } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/CreateShipmentModel.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/CreateShipmentModel.cs new file mode 100644 index 0000000..d4ccfd8 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/CreateShipmentModel.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Http; +using Nop.Web.Framework.Models; +using System.ComponentModel.DataAnnotations; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models +{ + public record CreateShipmentModel : BaseNopModel + { + [Required] + public string ShipmentName { get; set; } + + public string Description { get; set; } + + [Required] + public DateTime ShipmentDate { get; set; } = DateTime.Now; + + public string TrackingNumber { get; set; } + + public List UploadedFiles { get; set; } = new(); + } + + public class FileUploadResult + { + public bool Success { get; set; } + public string FileName { get; set; } + public string FilePath { get; set; } + public string ErrorMessage { get; set; } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/DashboardModel.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/DashboardModel.cs new file mode 100644 index 0000000..1273ecf --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/DashboardModel.cs @@ -0,0 +1,15 @@ +using Nop.Web.Framework.Models; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models; + +/// +/// Represents a dashboard model +/// +public partial record DashboardModel : BaseNopModel +{ + #region Properties + + public bool IsLoggedInAsVendor { get; set; } + + #endregion +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Index.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Index.cshtml new file mode 100644 index 0000000..959a392 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Index.cshtml @@ -0,0 +1,129 @@ +@model DashboardModel +@inject IPermissionService permissionService +@using Nop.Services.Security +@{ + //page title + ViewBag.PageTitle = T("Admin.Dashboard").Text; + + var canManageOrders = await permissionService.AuthorizeAsync(StandardPermission.Orders.ORDERS_VIEW); + var canManageCustomers = await permissionService.AuthorizeAsync(StandardPermission.Customers.CUSTOMERS_VIEW); + var canManageProducts = await permissionService.AuthorizeAsync(StandardPermission.Catalog.PRODUCTS_VIEW); + var canManageReturnRequests = await permissionService.AuthorizeAsync(StandardPermission.Orders.RETURN_REQUESTS_VIEW); + + //close configuration steps value + const string closeCardAttributeName = "CloseConfigurationSteps"; + var closeConfigurationStepsCard = await genericAttributeService.GetAttributeAsync(await workContext.GetCurrentCustomerAsync(), closeCardAttributeName); + + //active menu item (system name) + NopHtml.SetActiveMenuItemSystemName("Dashboard"); +} + + + + +
+

+ Fruit Bank @T("Admin.Dashboard") +

+
+
+
+
+
+
+ @if (!closeConfigurationStepsCard) + { +
+
+ @await Html.PartialAsync("~/Areas/Admin/Views/Home/_ConfigurationSteps.cshtml") +
+
+ } + + @await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.DashboardTop, additionalData = Model }) + @if (!Model.IsLoggedInAsVendor) + { +
+
+ @await Component.InvokeAsync(typeof(NopCommerceNewsViewComponent)) +
+
+ } + + @await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.DashboardNewsAfter, additionalData = Model }) + @if (!Model.IsLoggedInAsVendor && canManageOrders && canManageCustomers && canManageProducts && canManageReturnRequests) + { +
+
+ @await Component.InvokeAsync(typeof(CommonStatisticsViewComponent)) +
+
+ } + @await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.DashboardCommonstatisticsAfter, additionalData = Model }) + @if (!Model.IsLoggedInAsVendor && (canManageOrders || canManageCustomers)) + { +
+ @if (!Model.IsLoggedInAsVendor && canManageOrders) + { +
+ @await Html.PartialAsync("~/Areas/Admin/Views/Home/_OrderStatistics.cshtml") +
+ } + @if (!Model.IsLoggedInAsVendor && canManageCustomers) + { +
+ @await Html.PartialAsync("~/Areas/Admin/Views/Home/_CustomerStatistics.cshtml") +
+ } +
+ } + @await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.DashboardCustomerorderchartsAfter, additionalData = Model }) + @if (!Model.IsLoggedInAsVendor && canManageOrders) + { +
+
+ @await Html.PartialAsync("~/Areas/Admin/Views/Home/_OrderAverageReport.cshtml") +
+
+ @await Html.PartialAsync("~/Areas/Admin/Views/Home/_OrderIncompleteReport.cshtml") +
+
+ } + @await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.DashboardOrderreportsAfter, additionalData = Model }) + @if (!Model.IsLoggedInAsVendor && (canManageOrders || canManageProducts)) + { +
+ @if (canManageOrders) + { +
+ @await Html.PartialAsync("~/Areas/Admin/Views/Home/_LatestOrders.cshtml") +
+ } +
+ @if (canManageProducts) + { + @await Html.PartialAsync("~/Areas/Admin/Views/Home/_PopularSearchTermsReport.cshtml") + } +
+
+ } + @await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.DashboardLatestordersSearchtermsAfter, additionalData = Model }) + @if (canManageOrders) + { +
+
+ @await Html.PartialAsync("~/Areas/Admin/Views/Home/_BestsellersBriefReportByQuantity.cshtml") +
+
+ @await Html.PartialAsync("~/Areas/Admin/Views/Home/_BestsellersBriefReportByAmount.cshtml") +
+
+ } + @await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.DashboardBottom, additionalData = Model }) +
+
+
+
+
+ + diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Invoice/List.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Invoice/List.cshtml new file mode 100644 index 0000000..0b8e403 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Invoice/List.cshtml @@ -0,0 +1,32 @@ +@{ + // Layout = "_AdminLayout"; + ViewBag.PageTitle = "Invoice"; + NopHtml.SetActiveMenuItemSystemName("Invoices"); +} + +
+

+ + Invoices +

+
+ +
+
+
+
+
+
+

+ Shipment Management +

+
+
+

This is our custom Invoices page.

+

Add shipment functionality here.

+
+
+
+
+
+
\ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml index bbaab15..ebc6b19 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml @@ -1,7 +1,13 @@ @model OrderSearchModel @inject IStoreService storeService +@using Nop.Plugin.Misc.FruitBankPlugin.Models @using Nop.Services.Stores +@using Nop.Web.Areas.Admin.Components +@using Nop.Web.Areas.Admin.Models.Orders +@using Nop.Web.Framework.Infrastructure +@using Nop.Web.Framework.Models.DataTables +@using static Nop.Services.Common.NopLinksDefaults @{ //page title @@ -284,6 +290,9 @@
+
+

RTTTTTTTTTT

+
@@ -291,7 +300,7 @@ var gridModel = new DataTablesModel { Name = "orders-grid", - UrlRead = new DataUrl("OrderList", "Order", null), + UrlRead = new DataUrl("OrderList", "CustomOrder", null), SearchButtonId = "search-orders", Length = Model.PageSize, LengthMenu = Model.AvailablePageSizes, @@ -332,12 +341,12 @@ } }; gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModelExtended.NeedsMeasurement)) - { - Title = "Needs Measurement", - Width = "100", - Render = new RenderCustom("renderColumnNeedsMeasurement"), - ClassName = NopColumnClassDefaults.CenterAll - }); + { + Title = "Needs Measurement", + Width = "100", + Render = new RenderCustom("renderColumnNeedsMeasurement"), + ClassName = NopColumnClassDefaults.CenterAll + }); //a vendor does not have access to this functionality if (!Model.IsLoggedInAsVendor) @@ -397,31 +406,36 @@ Render = new RenderButtonView(new DataUrl("~/Admin/Order/Edit")) }); var orderSummaryColumnNumber = 8; - } - + } @await Html.PartialAsync("Table", gridModel) \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Shipment/List.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Shipment/List.cshtml new file mode 100644 index 0000000..86e4c05 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Shipment/List.cshtml @@ -0,0 +1,32 @@ +@{ + // Layout = "_AdminLayout"; + ViewBag.PageTitle = "Shipments"; + NopHtml.SetActiveMenuItemSystemName("Shipments"); +} + +
+

+ + Shipments +

+
+ +
+
+
+
+
+
+

+ Shipment Management +

+
+
+

This is our custom Shipments page.

+

Add shipment functionality here.

+
+
+
+
+
+
\ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/_ViewImports.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/_ViewImports.cshtml index 47e0479..ad5d40c 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/_ViewImports.cshtml +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/_ViewImports.cshtml @@ -2,11 +2,72 @@ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, Nop.Web.Framework -@inject INopHtmlHelper NopHtml +@inject INopHtmlHelper NopHtml +@inject IGenericAttributeService genericAttributeService +@inject IWorkContext workContext @using Microsoft.AspNetCore.Mvc.ViewFeatures +@using Nop.Core +@using Nop.Services.Common @using Nop.Web.Framework.UI @using Nop.Web.Framework.Extensions @using System.Text.Encodings.Web @using Nop.Services.Events -@using Nop.Web.Framework.Events \ No newline at end of file +@using Nop.Web.Framework.Events + + + + +@using System.Globalization; +@using System.Net; +@using Microsoft.AspNetCore.Routing +@using Microsoft.AspNetCore.StaticFiles +@using Microsoft.Extensions.Primitives +@using Nop.Core.Domain.Common +@using Nop.Core.Events +@using Nop.Core.Infrastructure +@using static Nop.Services.Common.NopLinksDefaults +@using Nop.Web.Areas.Admin.Components +@using Nop.Web.Areas.Admin.Models.Affiliates +@using Nop.Web.Areas.Admin.Models.Blogs +@using Nop.Web.Areas.Admin.Models.Catalog +@using Nop.Web.Areas.Admin.Models.Cms +@using Nop.Web.Areas.Admin.Models.Common +@using Nop.Web.Areas.Admin.Models.Customers +@using Nop.Web.Areas.Admin.Models.Directory +@using Nop.Web.Areas.Admin.Models.Discounts +@using Nop.Web.Areas.Admin.Models.ExternalAuthentication +@using Nop.Web.Areas.Admin.Models.Forums +@using Nop.Web.Areas.Admin.Models.Home +@using Nop.Web.Areas.Admin.Models.Localization +@using Nop.Web.Areas.Admin.Models.Logging +@using Nop.Web.Areas.Admin.Models.Messages +@using Nop.Web.Areas.Admin.Models.MultiFactorAuthentication +@using Nop.Web.Areas.Admin.Models.News +@using Nop.Web.Areas.Admin.Models.Orders +@using Nop.Web.Areas.Admin.Models.Payments +@using Nop.Web.Areas.Admin.Models.Plugins +@using Nop.Web.Areas.Admin.Models.Plugins.Marketplace +@using Nop.Web.Areas.Admin.Models.Polls +@using Nop.Web.Areas.Admin.Models.Reports +@using Nop.Web.Areas.Admin.Models.Security +@using Nop.Web.Areas.Admin.Models.Settings +@using Nop.Web.Areas.Admin.Models.Shipping +@using Nop.Web.Areas.Admin.Models.ShoppingCart +@using Nop.Web.Areas.Admin.Models.Stores +@using Nop.Web.Areas.Admin.Models.Tasks +@using Nop.Web.Areas.Admin.Models.Tax +@using Nop.Web.Areas.Admin.Models.Templates +@using Nop.Web.Areas.Admin.Models.Topics +@using Nop.Web.Areas.Admin.Models.Vendors +@using Nop.Web.Components +@using Nop.Web.Extensions +@using Nop.Web.Framework +@using Nop.Web.Framework.Infrastructure +@using Nop.Web.Framework.Models +@using Nop.Web.Framework.Models.Cms +@using Nop.Web.Framework.Models.DataTables +@using Nop.Web.Framework.Mvc.Routing +@using Nop.Web.Framework.Security.Captcha +@using Nop.Web.Framework.Security.Honeypot +@using Nop.Web.Framework.Themes \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/_ViewStart.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/_ViewStart.cshtml new file mode 100644 index 0000000..0297c66 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_AdminLayout"; +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Factories/CustomOrderModelFactory.cs b/Nop.Plugin.Misc.AIPlugin/Factories/CustomOrderModelFactory.cs index 0f0f8f0..280c2c5 100644 --- a/Nop.Plugin.Misc.AIPlugin/Factories/CustomOrderModelFactory.cs +++ b/Nop.Plugin.Misc.AIPlugin/Factories/CustomOrderModelFactory.cs @@ -31,6 +31,7 @@ using Nop.Web.Areas.Admin.Models.Orders; using Nop.Plugin.Misc.FruitBankPlugin.Models; using System.Reflection; using Nop.Plugin.Misc.FruitBankPlugin.Helpers; +using Microsoft.AspNetCore.Mvc.TagHelpers; namespace Nop.Plugin.Misc.FruitBankPlugin.Factories { @@ -140,24 +141,28 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Factories // let base prepare default model first var baseModel = await base.PrepareOrderSearchModelAsync(searchModel); + //foreach (var order in baseModel.order) + + // create derived/extended instance - var extended = new OrderSearchModelExtended(); + //var extended = new OrderSearchModelExtended(); // copy all public instance properties from baseModel to extended - CopyModelHelper.CopyPublicProperties(baseModel, extended); + //CopyModelHelper.CopyPublicProperties(baseModel, extended); // try to obtain NeedsMeasurement from the incoming searchModel (if it's extended) - bool? needsMeasurement = null; - var prop = searchModel?.GetType().GetProperty("NeedsMeasurement", BindingFlags.Public | BindingFlags.Instance); - if (prop != null && prop.PropertyType == typeof(bool?)) - { - needsMeasurement = (bool?)prop.GetValue(searchModel); - } + //bool? needsMeasurement = null; + //var prop = searchModel?.GetType().GetProperty("NeedsMeasurement", BindingFlags.Public | BindingFlags.Instance); + //if (prop != null && prop.PropertyType == typeof(bool?)) + //{ + // needsMeasurement = (bool?)prop.GetValue(searchModel); + //} - extended.NeedsMeasurement = needsMeasurement; + //extended.NeedsMeasurement = needsMeasurement; // return the extended object (it's assignable to OrderSearchModel) - return extended; + //return extended; + return baseModel; } public override async Task PrepareOrderListModelAsync(OrderSearchModel searchModel) @@ -165,29 +170,26 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Factories // get the default model first var baseModel = await base.PrepareOrderListModelAsync(searchModel); + var extendedRows = new List(); - - // project the rows into your extended row type - var extendedRows = baseModel.Data.Select(async order => new OrderModelExtended + foreach (var order in baseModel.Data) { - Id = order.Id, - CustomOrderNumber = order.CustomOrderNumber, - OrderStatus = order.OrderStatus, - PaymentStatus = order.PaymentStatus, - ShippingStatus = order.ShippingStatus, - OrderTotal = order.OrderTotal, - CreatedOn = order.CreatedOn, + var extendedOrder = new OrderModelExtended(); + CopyModelHelper.CopyPublicProperties(order, extendedOrder); + extendedOrder.NeedsMeasurement = await ShouldMarkAsNeedsMeasurementAsync(order); + Console.WriteLine(extendedOrder.Id); + extendedRows.Add(extendedOrder); + } - // custom field - NeedsMeasurement = await ShouldMarkAsNeedsMeasurementAsync(order) - }).ToList(); + //var model = new OrderListModel + //{ + // Data = extendedRows.Cast().ToList(), + // RecordsTotal = baseModel.RecordsTotal + //}; - // build a new OrderListModel but replace Data with the extended items - var model = new OrderListModel - { - Data = extendedRows.Cast().ToList(), // still satisfies the grid - //Total = baseModel.Total - }; + var model = new OrderListModel(); + CopyModelHelper.CopyPublicProperties(baseModel, model); + model.Data = extendedRows.ToList(); // Different cast approach return model; } diff --git a/Nop.Plugin.Misc.AIPlugin/Filters/PendingMeasurementCheckoutFilter.cs b/Nop.Plugin.Misc.AIPlugin/Filters/PendingMeasurementCheckoutFilter.cs index 2547364..b0fa135 100644 --- a/Nop.Plugin.Misc.AIPlugin/Filters/PendingMeasurementCheckoutFilter.cs +++ b/Nop.Plugin.Misc.AIPlugin/Filters/PendingMeasurementCheckoutFilter.cs @@ -40,6 +40,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Filters if (order != null && await _orderMeasurementService.IsPendingMeasurementAsync(order)) { order.OrderStatus = OrderStatus.Processing; + order.PaymentStatus = Core.Domain.Payments.PaymentStatus.Pending; context.Result = new RedirectToRouteResult(new { controller = "Checkout", diff --git a/Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs b/Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs index 98a1d9b..52de11e 100644 --- a/Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs +++ b/Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs @@ -60,7 +60,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin ApiKey = string.Empty }; await _settingService.SaveSettingAsync(settings); - + await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Menu.ShipmentsList", "Shipment", "EN"); + await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Menu.ShipmentsList", "Shipment", "HU"); await base.InstallAsync(); } @@ -106,48 +107,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin if (!await _permissionService.AuthorizeAsync(StandardPermission.Configuration.MANAGE_PLUGINS)) return; - var configurationItem = rootNode.ChildNodes.FirstOrDefault(node => node.SystemName.Equals("Configuration")); - if (configurationItem is null) - return; - - var shippingItem = configurationItem.ChildNodes.FirstOrDefault(node => node.SystemName.Equals("Shipping")); - var widgetsItem = configurationItem.ChildNodes.FirstOrDefault(node => node.SystemName.Equals("Widgets")); - if (shippingItem is null && widgetsItem is null) - return; - - var index = shippingItem is not null ? configurationItem.ChildNodes.IndexOf(shippingItem) : -1; - if (index < 0) - index = widgetsItem is not null ? configurationItem.ChildNodes.IndexOf(widgetsItem) : -1; - if (index < 0) - return; - - configurationItem.ChildNodes.Insert(index + 1, new AdminMenuItem - { - - Visible = true, - SystemName = "AI plugins", - Title = await _localizationService.GetResourceAsync("Plugins.Misc.SignalRApi.Menu.AI"), - IconClass = "far fa-dot-circle", - ChildNodes = new List - { - new() - { - // SystemName = "FruitBankPlugin.Configure", - // Title = "AI Assistant", - // Url = $"{_webHelper.GetStoreLocation()}Admin/FruitBankPluginAdmin/Configure", - // Visible = true - Visible = true, - SystemName = PluginDescriptor.SystemName, - Title = PluginDescriptor.FriendlyName, - IconClass = "far fa-circle", - Url = _adminMenu.GetMenuItemUrl("FruitBankPlugin", "Configure"), - //Url = "Admin/SignalRApi/Configure", - //ControllerName = "SignalRApi", - //ActionName = "Configure", - //RouteValues = new RouteValueDictionary { { "area", AreaNames.ADMIN } } - } - } - }); } public override string GetConfigurationPageUrl() diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs index 711e0ea..33497f8 100644 --- a/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs +++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Nop.Core.Domain.Orders; using Nop.Core.Infrastructure; using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; using Nop.Plugin.Misc.FruitBankPlugin.Factories; @@ -18,6 +19,8 @@ using Nop.Plugin.Misc.FruitBankPlugin.Filters; using Nop.Plugin.Misc.FruitBankPlugin.Models; using Nop.Plugin.Misc.FruitBankPlugin.Services; using Nop.Services.Catalog; +using Nop.Services.Common; +using Nop.Services.Events; using Nop.Web.Areas.Admin.Factories; using Nop.Web.Areas.Admin.Models.Catalog; using Nop.Web.Areas.Admin.Models.Orders; @@ -58,12 +61,14 @@ public class PluginNopStartup : INopStartup //services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped, EventConsumer>(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddControllersWithViews(options => { options.Filters.AddService(); diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs index 9cb3d70..e308580 100644 --- a/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs +++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs @@ -22,10 +22,58 @@ public class RouteProvider : IRouteProvider defaults: new { controller = "FruitBankPluginAdmin", action = "Configure", area = AreaNames.ADMIN }); //endpointRouteBuilder.MapHub("/fbhub");//.RequireCors("AllowBlazorClient"); + + endpointRouteBuilder.MapControllerRoute( + name: "Plugin.FruitBank.Admin.Order.List", + pattern: "Admin/Order/List", + defaults: new { controller = "CustomOrder", action = "List", area = AreaNames.ADMIN } + //constraints: new { area = AreaNames.ADMIN } + ); + + endpointRouteBuilder.MapControllerRoute( + name: "Plugin.FruitBank.Admin.Order.OrderList", + pattern: "Admin/Order/OrderList", + defaults: new { controller = "CustomOrder", action = "OrderList", area = AreaNames.ADMIN }); + + endpointRouteBuilder.MapControllerRoute( + name: "Plugin.FruitBank.Admin.Order.Test", + pattern: "Admin/Order/Test", + defaults: new { controller = "CustomOrder", action = "Test", area = AreaNames.ADMIN } + //constraints: new { area = AreaNames.ADMIN } + ); + + endpointRouteBuilder.MapControllerRoute( + name: "Plugin.FruitBank.Admin.Index", + pattern: "Admin", + defaults: new { controller = "CustomDashboard", action = "Index", area = AreaNames.ADMIN } + //constraints: new { area = AreaNames.ADMIN } + ); + + endpointRouteBuilder.MapControllerRoute( + name: "Plugin.FruitBank.Admin.Shipment.List", + pattern: "Admin/Shipment/List", + defaults: new { controller = "Shipment", action = "List", area = AreaNames.ADMIN } + ); + + endpointRouteBuilder.MapControllerRoute( + name: "Plugin.FruitBank.Admin.Invoices.List", + pattern: "Admin/Invoices/List", + defaults: new { controller = "Invoice", action = "List", area = AreaNames.ADMIN } + ); + + endpointRouteBuilder.MapControllerRoute( + name: "Plugin.FruitBank.Admin.Shipment.Create", + pattern: "Admin/Shipment/Create", + defaults: new { controller = "Shipment", action = "Create", area = AreaNames.ADMIN }); + + endpointRouteBuilder.MapControllerRoute( + name: "Plugin.FruitBank.Admin.Shipment.UploadFile", + pattern: "Admin/Shipment/UploadFile", + defaults: new { controller = "Shipment", action = "UploadFile", area = AreaNames.ADMIN }); } /// /// Gets a priority of route provider /// - public int Priority => 0; + public int Priority => 3000; } \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj b/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj index 12ab433..4dfd746 100644 --- a/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj +++ b/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj @@ -20,6 +20,16 @@ + + true + PreserveNewest + Always + + + true + PreserveNewest + Always + PreserveNewest @@ -33,7 +43,7 @@ PreserveNewest - PreserveNewest + Always PreserveNewest @@ -55,7 +65,8 @@ - + + @@ -133,16 +144,22 @@ Always + + Always + Always Always - + Always - + + Always + + Always diff --git a/Nop.Plugin.Misc.AIPlugin/Services/EventConsumer.cs b/Nop.Plugin.Misc.AIPlugin/Services/EventConsumer.cs index f75d45a..c124fd2 100644 --- a/Nop.Plugin.Misc.AIPlugin/Services/EventConsumer.cs +++ b/Nop.Plugin.Misc.AIPlugin/Services/EventConsumer.cs @@ -1,32 +1,56 @@ using System.Linq; +using Nop.Core; using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Orders; using Nop.Services.Catalog; using Nop.Services.Common; using Nop.Services.Events; +using Nop.Services.Localization; using Nop.Services.Orders; +using Nop.Services.Plugins; +using Nop.Web.Framework.Events; +using Nop.Web.Framework.Menu; +using Nop.Web.Models.Sitemap; -namespace Nop.Plugin.YourCompany.Measurement.Services +namespace Nop.Plugin.Misc.FruitBankPlugin.Services { - public class OrderPlacedConsumer : IConsumer + public class EventConsumer : BaseAdminMenuCreatedEventConsumer, IConsumer, IConsumer { private readonly IGenericAttributeService _genericAttributeService; private readonly IProductService _productService; + private readonly ISpecificationAttributeService _specificationAttributeService; private readonly IOrderService _orderService; private readonly IProductAttributeService _productAttributeService; + private readonly IWorkContext _workContext; + private readonly IStoreContext _storeContext; + private readonly IAdminMenu _adminMenu; + private readonly ILocalizationService _localizationService; - public OrderPlacedConsumer( + public EventConsumer( IGenericAttributeService genericAttributeService, IProductService productService, - IOrderService orderService, - IProductAttributeService productAttributeService) + ISpecificationAttributeService specificationAttributeService, + IOrderService orderService, + IProductAttributeService productAttributeService, + IPluginManager pluginManager, + IWorkContext workContext, + IStoreContext storeContext, + IAdminMenu adminMenu, + ILocalizationService localizationService) : base(pluginManager) { _genericAttributeService = genericAttributeService; _productService = productService; + _specificationAttributeService = specificationAttributeService; _orderService = orderService; _productAttributeService = productAttributeService; + _workContext = workContext; + _storeContext = storeContext; + _adminMenu = adminMenu; + _localizationService = localizationService; } + protected override string PluginSystemName => "Misc.FruitBankPlugin"; + public async Task HandleEventAsync(OrderPlacedEvent eventMessage) { var order = eventMessage.Order; @@ -41,31 +65,159 @@ namespace Nop.Plugin.YourCompany.Measurement.Services // akár egy product attribute is lehet, vagy egy saját extension metódus if (product != null) { - var productAttributeMappings = await _productAttributeService.GetProductAttributeMappingsByProductIdAsync(product.Id); - //Product Attributes - foreach (var pam in productAttributeMappings) + //var productAttributeMappings = await _productAttributeService.GetProductAttributeMappingsByProductIdAsync(product.Id); + ////Product Attributes + //foreach (var pam in productAttributeMappings) + //{ + // var attributes = await _productAttributeService.GetProductAttributeValuesAsync(pam.Id); + // foreach (var attr in attributes) + // { + // // you can check for specific attribute by its name or id + // if (attr.Name == "NeedsToBeMeasured" && attr.IsPreSelected) + // { + // requiresMeasurement = true; + // break; + // } + // } + //} + + var productSpecAttributes = await _specificationAttributeService.GetProductSpecificationAttributesAsync(product.Id); + + foreach (var specAttribute in productSpecAttributes) { - var attributes = await _productAttributeService.GetProductAttributeValuesAsync(pam.Id); - foreach (var attr in attributes) + // Get the specification attribute + var specificationAttribute = await _specificationAttributeService + .GetSpecificationAttributeByIdAsync(specAttribute.Id); + + // Get the specification attribute option + var specificationAttributeOption = await _specificationAttributeService + .GetSpecificationAttributeOptionByIdAsync(specAttribute.SpecificationAttributeOptionId); + + System.Diagnostics.Debug.WriteLine($"Spec Attribute: {specificationAttribute.Name}, Option: {specificationAttributeOption.Name}"); + + // Check if this is your "NeedsToBeMeasured" specification attribute + if (specificationAttribute.Name == "Measureable" && + specificationAttributeOption.Name == "Yes") // or whatever value you set { - // you can check for specific attribute by its name or id - if (attr.Name == "NeedsToBeMeasured" && attr.IsPreSelected) - { - requiresMeasurement = true; - break; - } + requiresMeasurement = true; + break; } } - + } } if (requiresMeasurement) { + var store = await _storeContext.GetCurrentStoreAsync(); // itt adjuk hozzá a GenericAttribute flag-et az orderhez await _genericAttributeService.SaveAttributeAsync(order, - "PendingMeasurement", true); + "PendingMeasurement", true, store.Id); + // status pending + // paymentstatus pending } } + + public async Task HandleEventAsync(AdminMenuCreatedEvent eventMessage) + { + var rootNode = eventMessage.RootMenuItem; + + + var shipmentsListMenuItem = new AdminMenuItem + { + Visible = true, + SystemName = "FruitBank", + Title = await _localizationService.GetResourceAsync("Plugins.Misc.FruitBankPlugin.Menu.ShipmentsList"), // You can localize this with await _localizationService.GetResourceAsync("...") + IconClass = "fas fa-shipping-fast", + Url = _adminMenu.GetMenuItemUrl("Shipment", "List") + }; + + + var createShipmentMenuItem = new AdminMenuItem + { + Visible = true, + SystemName = "Shipments.Create", + Title = "Create Shipment", + IconClass = "far fa-circle", + Url = _adminMenu.GetMenuItemUrl("Shipment", "Create") + }; + + // Create a new top-level menu item + var shipmentsMenuItem = new AdminMenuItem + { + Visible = true, + SystemName = "FruitBank", + Title = await _localizationService.GetResourceAsync("Plugins.Misc.FruitBankPlugin.Menu.Shipments"), // You can localize this with await _localizationService.GetResourceAsync("...") + IconClass = "fas fa-shipping-fast", + //Url = _adminMenu.GetMenuItemUrl("Shipment", "List") + ChildNodes = new[] { shipmentsListMenuItem, createShipmentMenuItem } + }; + + var shipmentConfigurationItem = rootNode; + shipmentConfigurationItem.ChildNodes.Insert(2, shipmentsMenuItem); + + + var invoiceListMenuItem = new AdminMenuItem + { + Visible = true, + SystemName = "FruitBank", + Title = await _localizationService.GetResourceAsync("Plugins.Misc.FruitBankPlugin.Menu.InvoicesList"), // You can localize this with await _localizationService.GetResourceAsync("...") + IconClass = "fas fa-file-invoice", + Url = _adminMenu.GetMenuItemUrl("Invoices", "List") + }; + + // Create a new top-level menu item + var invoicesMenuItem = new AdminMenuItem + { + Visible = true, + SystemName = "FruitBank", + Title = await _localizationService.GetResourceAsync("Plugins.Misc.FruitBankPlugin.Menu.Invoices"), // You can localize this with await _localizationService.GetResourceAsync("...") + IconClass = "fas fa-file-invoice", + //Url = _adminMenu.GetMenuItemUrl("Shipment", "List") + ChildNodes = new[] { invoiceListMenuItem } + }; + + var invoiceConfigurationItem = rootNode; + invoiceConfigurationItem.ChildNodes.Insert(2, invoicesMenuItem); + + + var configurationItem = rootNode.ChildNodes.FirstOrDefault(node => node.SystemName.Equals("Configuration")); + if (configurationItem is null) + return; + + var shippingItem = configurationItem.ChildNodes.FirstOrDefault(node => node.SystemName.Equals("Shipping")); + var widgetsItem = configurationItem.ChildNodes.FirstOrDefault(node => node.SystemName.Equals("Widgets")); + if (shippingItem is null && widgetsItem is null) + return; + + var index = shippingItem is not null ? configurationItem.ChildNodes.IndexOf(shippingItem) : -1; + if (index < 0) + index = widgetsItem is not null ? configurationItem.ChildNodes.IndexOf(widgetsItem) : -1; + if (index < 0) + return; + + configurationItem.ChildNodes.Insert(index + 1, new AdminMenuItem + { + + Visible = true, + SystemName = "FruitBank", + Title = await _localizationService.GetResourceAsync("Plugins.Misc.FruitBankPlugin.Menu.AI"), + IconClass = "far fa-dot-circle", + ChildNodes = new List + { + new() + { + + Visible = true, + SystemName = "FruitBank", + Title = await _localizationService.GetResourceAsync("Plugins.Misc.FruitBankPlugin.Menu.Configure"), + IconClass = "far fa-circle", + Url = _adminMenu.GetMenuItemUrl("FruitBankPlugin", "Configure"), + + } + } + }); + + } } } diff --git a/Nop.Plugin.Misc.AIPlugin/Views/Admin/Order/List.cshtml b/Nop.Plugin.Misc.AIPlugin/Views/Admin/Order/List.cshtml deleted file mode 100644 index f93834b..0000000 --- a/Nop.Plugin.Misc.AIPlugin/Views/Admin/Order/List.cshtml +++ /dev/null @@ -1,657 +0,0 @@ -@model OrderSearchModel - -@inject IStoreService storeService -@using Nop.Services.Stores - -@{ - //page title - ViewBag.PageTitle = T("Admin.Orders").Text; - //active menu item (system name) - NopHtml.SetActiveMenuItemSystemName("Orders"); -} - -@{ - const string hideSearchBlockAttributeName = "OrdersPage.HideSearchBlock"; - var hideSearchBlock = await genericAttributeService.GetAttributeAsync(await workContext.GetCurrentCustomerAsync(), hideSearchBlockAttributeName); -} - -@if (Model.LicenseCheckModel.BlockPages != true) -{ -
-
-

- @T("Admin.Orders") -

-
-
- - - -
- -
- - - -
- @await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.OrderListButtons, additionalData = Model }) -
-
-
-
-
-
- - -
-

- khfdsjklfkl fklsh fjsklhfjskl fhsjklhf sjklhfdsklhf djkfldsh fjklsdjkl -

-
- - - @{ - var gridModel = new DataTablesModel - { - Name = "orders-grid", - UrlRead = new DataUrl("OrderList", "Order", null), - SearchButtonId = "search-orders", - Length = Model.PageSize, - LengthMenu = Model.AvailablePageSizes, - FooterCallback = !Model.IsLoggedInAsVendor ? "ordersfootercallback" : null, - FooterColumns = !Model.IsLoggedInAsVendor ? 10 : 0, - Filters = new List - { - new FilterParameter(nameof(Model.StartDate), typeof(DateTime?)), - new FilterParameter(nameof(Model.EndDate), typeof(DateTime?)), - new FilterParameter(nameof(Model.OrderStatusIds)), - new FilterParameter(nameof(Model.PaymentStatusIds)), - new FilterParameter(nameof(Model.ShippingStatusIds)), - new FilterParameter(nameof(Model.StoreId)), - new FilterParameter(nameof(Model.VendorId)), - new FilterParameter(nameof(Model.WarehouseId)), - new FilterParameter(nameof(Model.BillingEmail)), - new FilterParameter(nameof(Model.BillingPhone)), - new FilterParameter(nameof(Model.BillingLastName)), - new FilterParameter(nameof(Model.BillingCountryId)), - new FilterParameter(nameof(Model.PaymentMethodSystemName)), - new FilterParameter(nameof(Model.ProductId)), - new FilterParameter(nameof(Model.OrderNotes)) - } - }; - gridModel.ColumnCollection = new List - { - new ColumnProperty(nameof(OrderModel.Id)) - { - IsMasterCheckBox = true, - Render = new RenderCheckBox("checkbox_orders"), - ClassName = NopColumnClassDefaults.CenterAll, - Width = "50" - }, - new ColumnProperty(nameof(OrderModel.CustomOrderNumber)) - { - Title = T("Admin.Orders.Fields.CustomOrderNumber").Text, - Width = "80" - } - }; - gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModelExtended.NeedsMeasurement)) - { - Title = "Needs Measurement", - Width = "100", - Render = new RenderCustom("renderColumnNeedsMeasurement"), - ClassName = NopColumnClassDefaults.CenterAll - }); - - //a vendor does not have access to this functionality - if (!Model.IsLoggedInAsVendor) - { - gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.OrderStatus)) - { - Title = T("Admin.Orders.Fields.OrderStatus").Text, - Width = "100", - Render = new RenderCustom("renderColumnOrderStatus") - }); - } - gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.PaymentStatus)) - { - Title = T("Admin.Orders.Fields.PaymentStatus").Text, - Width = "150" - }); - //a vendor does not have access to this functionality - if (!Model.IsLoggedInAsVendor) - { - gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.ShippingStatus)) - { - Title = T("Admin.Orders.Fields.ShippingStatus").Text, - Width = "150" - }); - } - gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.CustomerEmail)) - { - Title = T("Admin.Orders.Fields.Customer").Text, - Render = new RenderCustom("renderColumnCustomer") - }); - gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.StoreName)) - { - Title = T("Admin.Orders.Fields.Store").Text, - Width = "100", - Visible = (await storeService.GetAllStoresAsync()).Count > 1 - }); - gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.CreatedOn)) - { - Title = T("Admin.Orders.Fields.CreatedOn").Text, - Width = "120", - Render = new RenderDate() - }); - //a vendor does not have access to this functionality - if (!Model.IsLoggedInAsVendor) - { - gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.OrderTotal)) - { - Title = T("Admin.Orders.Fields.OrderTotal").Text, - Width = "100", - }); - } - gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.Id)) - { - Title = T("Admin.Common.View").Text, - Width = "50", - ClassName = NopColumnClassDefaults.Button, - Render = new RenderButtonView(new DataUrl("~/Admin/Order/Edit")) - }); - var orderSummaryColumnNumber = 8; - } - - @await Html.PartialAsync("Table", gridModel) - - -
-
-
-
-
-
-
-} - - - - -@*export selected (XML). We don't use GET approach because it's limited to 2K-4K chars and won't work for large number of entities*@ -
- -
- - - - -@*export selected (Excel). We don't use GET approach because it's limited to 2K-4K chars and won't work for large number of entities*@ -
- -
- - - - -@*Print packaging slips selected (XML). We don't use GET approach because it's limited to 2K-4K chars and won't work for large number of entities*@ -
- -
- - - - -@*import orders form*@ - -