diff --git a/Nop.Plugin.Misc.AIPlugin/AIPlugin.cs b/Nop.Plugin.Misc.AIPlugin/AIPlugin.cs
new file mode 100644
index 0000000..9998ffa
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/AIPlugin.cs
@@ -0,0 +1,164 @@
+
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Infrastructure;
+using Microsoft.AspNetCore.Mvc.Routing;
+using Nop.Plugin.Misc.AIPlugin.Components;
+using Nop.Services.Cms;
+using Nop.Services.Configuration;
+using Nop.Services.Localization;
+using Nop.Services.Plugins;
+using Nop.Services.Security;
+using Nop.Web.Framework.Infrastructure;
+using Nop.Web.Framework.Menu;
+
+
+namespace Nop.Plugin.Misc.AIPlugin
+{
+ ///
+ /// Main plugin class
+ ///
+ public class AIPlugin : BasePlugin, IWidgetPlugin
+ {
+ protected readonly IActionContextAccessor _actionContextAccessor;
+ private readonly ISettingService _settingService;
+ //private readonly IWebHelper _webHelper;
+ protected readonly IPermissionService _permissionService;
+ protected readonly ILocalizationService _localizationService;
+ protected readonly IUrlHelperFactory _urlHelperFactory;
+ private readonly IAdminMenu _adminMenu;
+
+ //handle AdminMenuCreatedEvent
+
+
+ public AIPlugin(IActionContextAccessor actionContextAccessor,
+ ISettingService settingService,
+ //IWebHelper webHelper,
+ ILocalizationService localizationService,
+ IPermissionService permissionService,
+ IUrlHelperFactory urlHelperFactory,
+ IAdminMenu adminMenu)
+ {
+ _actionContextAccessor = actionContextAccessor;
+ _settingService = settingService;
+ //_webHelper = webHelper;
+ _localizationService = localizationService;
+ _urlHelperFactory = urlHelperFactory;
+ _adminMenu = adminMenu;
+ _permissionService = permissionService;
+ }
+
+ // --- INSTALL ---
+ public override async Task InstallAsync()
+ {
+ // Default settings
+ var settings = new OpenAiSettings
+ {
+ ApiKey = string.Empty
+ };
+ await _settingService.SaveSettingAsync(settings);
+
+ await base.InstallAsync();
+ }
+
+ // --- UNINSTALL ---
+ public override async Task UninstallAsync()
+ {
+ await _settingService.DeleteSettingAsync();
+ await base.UninstallAsync();
+ }
+
+ // --- WIDGETS ---
+ public bool HideInWidgetList => false;
+
+ public Task> GetWidgetZonesAsync()
+ {
+ return Task.FromResult>(new List { PublicWidgetZones.ProductBoxAddinfoBefore, PublicWidgetZones.ProductDetailsBottom });
+ }
+
+ //public string GetWidgetViewComponentName(string widgetZone)
+ //{
+ // return "ProductAIWidget"; // A ViewComponent neve
+ //}
+
+ // --- ADMIN MENÜ ---
+ //public async Task ManageSiteMapAsync(AdminMenuItem rootNode)
+ //{
+ // if (!await _permissionService.AuthorizeAsync(StandardPermission.Configuration.MANAGE_PLUGINS))
+ // return;
+
+ // var pluginNode = new AdminMenuItem
+ // {
+ // SystemName = "AIPlugin.Configure",
+ // Title = "AI Assistant",
+ // Url = $"{_webHelper.GetStoreLocation()}Admin/AIPluginAdmin/Configure",
+ // Visible = true
+ // };
+ // rootNode.ChildNodes.Add(pluginNode);
+ // //return Task.CompletedTask;
+ //}
+
+ public async Task ManageSiteMapAsync(AdminMenuItem rootNode)
+ {
+ 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 = "AIPlugin.Configure",
+ // Title = "AI Assistant",
+ // Url = $"{_webHelper.GetStoreLocation()}Admin/AIPluginAdmin/Configure",
+ // Visible = true
+ Visible = true,
+ SystemName = PluginDescriptor.SystemName,
+ Title = PluginDescriptor.FriendlyName,
+ IconClass = "far fa-circle",
+ Url = _adminMenu.GetMenuItemUrl("AIPlugin", "Configure"),
+ //Url = "Admin/SignalRApi/Configure",
+ //ControllerName = "SignalRApi",
+ //ActionName = "Configure",
+ //RouteValues = new RouteValueDictionary { { "area", AreaNames.ADMIN } }
+ }
+ }
+ });
+ }
+
+ public override string GetConfigurationPageUrl()
+ {
+ return _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext).RouteUrl("Plugin.Misc.AIPlugin.Configure");
+ }
+
+ public Type GetWidgetViewComponent(string widgetZone)
+ {
+ if (widgetZone is null)
+ throw new ArgumentNullException(nameof(widgetZone));
+
+ var zones = GetWidgetZonesAsync().Result;
+
+ return zones.Any(widgetZone.Equals) ? typeof(ProductAIWidgetViewComponent) : null;
+ }
+ }
+}
diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/AIPluginAdminController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/AIPluginAdminController.cs
new file mode 100644
index 0000000..6cba1d1
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/AIPluginAdminController.cs
@@ -0,0 +1,45 @@
+using Microsoft.AspNetCore.Mvc;
+using Nop.Plugin.Misc.AIPlugin.Areas.Admin.Models;
+using Nop.Plugin.Misc.AIPlugin;
+//using Nop.Plugin.Misc.AIPlugin;
+using Nop.Services.Configuration;
+using Nop.Web.Framework.Controllers;
+using Nop.Services.Messages;
+
+namespace Nop.Plugin.Misc.AIPlugin.Controllers
+{
+ [Area("Admin")]
+ public class AIPluginAdminController : BasePluginController
+ {
+ private readonly INotificationService _notificationService;
+ private readonly ISettingService _settingService;
+ private readonly OpenAiSettings _settings;
+
+ public AIPluginAdminController(INotificationService notificationService, ISettingService settingService, OpenAiSettings settings)
+ {
+ _notificationService = notificationService;
+ _settingService = settingService;
+ _settings = settings;
+ }
+
+ [HttpGet]
+ public IActionResult Configure()
+ {
+ var model = new ConfigureModel
+ {
+ ApiKey = _settings.ApiKey
+ };
+ return View("~/Plugins/Misc.AIPlugin/Views/Configure/Configure.cshtml", model);
+ }
+
+ [HttpPost]
+ public async Task Configure(ConfigureModel model)
+ {
+ _settings.ApiKey = model.ApiKey;
+ await _settingService.SaveSettingAsync(_settings);
+ _notificationService.SuccessNotification("BeállÃtások mentve.");
+ return RedirectToAction("Configure");
+ }
+ }
+}
+
diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/ConfigureModel.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/ConfigureModel.cs
new file mode 100644
index 0000000..d74e084
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/ConfigureModel.cs
@@ -0,0 +1,16 @@
+using Nop.Web.Framework.Mvc.ModelBinding;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Nop.Plugin.Misc.AIPlugin.Areas.Admin.Models
+{
+ public record ConfigureModel
+ {
+ [NopResourceDisplayName("Plugins.AIPlugin.Fields.ApiKey")]
+ public string ApiKey { get; set; }
+ }
+}
+
diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Configure/Configure.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Configure/Configure.cshtml
new file mode 100644
index 0000000..3abc8da
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Configure/Configure.cshtml
@@ -0,0 +1,22 @@
+@model Nop.Plugin.Misc.AIPlugin.Models.ConfigureModel
+
+@{
+ Layout = "_AdminLayout";
+ Html.SetActiveMenuItemSystemName("AiAssistant.Configure");
+}
+
+
+
diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/_ViewImports.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/_ViewImports.cshtml
new file mode 100644
index 0000000..270744e
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/_ViewImports.cshtml
@@ -0,0 +1,10 @@
+@inherits Nop.Web.Framework.Mvc.Razor.NopRazorPage
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+@addTagHelper *, Nop.Web.Framework
+
+@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
\ No newline at end of file
diff --git a/Nop.Plugin.Misc.AIPlugin/Components/CustomViewComponent.cs b/Nop.Plugin.Misc.AIPlugin/Components/CustomViewComponent.cs
new file mode 100644
index 0000000..1e2453a
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Components/CustomViewComponent.cs
@@ -0,0 +1,18 @@
+using Microsoft.AspNetCore.Mvc;
+using Nop.Web.Framework.Components;
+
+namespace Nop.Plugin.Misc.AIPlugin.Components;
+
+[ViewComponent(Name = "Custom")]
+public class CustomViewComponent : NopViewComponent
+{
+ public CustomViewComponent()
+ {
+
+ }
+
+ public IViewComponentResult Invoke(int productId)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/Nop.Plugin.Misc.AIPlugin/Components/ProductAIWidgetViewComponent.cs b/Nop.Plugin.Misc.AIPlugin/Components/ProductAIWidgetViewComponent.cs
new file mode 100644
index 0000000..7ee276e
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Components/ProductAIWidgetViewComponent.cs
@@ -0,0 +1,39 @@
+using Microsoft.AspNetCore.Mvc;
+using Nop.Web.Framework.Components;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Nop.Plugin.Misc.AIPlugin.Components
+{
+
+ [ViewComponent(Name = "ProductAIWidget")]
+ public class ProductAIWidgetViewComponent : NopViewComponent
+ {
+ public IViewComponentResult Invoke(string widgetZone, object additionalData)
+ {
+ if(additionalData is Nop.Web.Models.Catalog.ProductOverviewModel)
+ {
+ var product = additionalData as Nop.Web.Models.Catalog.ProductOverviewModel;
+
+ if (product == null)
+ return Content(""); // ne rendereljen semmit, ha nincs product
+ return View("~/Plugins/Misc.AIPlugin/Views/ProductAIListWidget.cshtml", product);
+ }
+ else if (additionalData is Nop.Web.Models.Catalog.ProductDetailsModel)
+ {
+ var product = additionalData as Nop.Web.Models.Catalog.ProductDetailsModel;
+
+ if (product == null)
+ return Content(""); // ne rendereljen semmit, ha nincs product
+ return View("~/Plugins/Misc.AIPlugin/Views/ProductAIWidget.cshtml", product);
+ }
+ else {
+ return Content(""); // ne rendereljen semmit, ha nem productDetailModel vagy productOverviewModel
+ }
+
+ }
+ }
+}
diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/CustomTable.cs b/Nop.Plugin.Misc.AIPlugin/Domains/CustomTable.cs
new file mode 100644
index 0000000..6628c61
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Domains/CustomTable.cs
@@ -0,0 +1,8 @@
+using Nop.Core;
+
+namespace Nop.Plugin.Misc.AIPlugin.Domains;
+
+public partial class CustomTable : BaseEntity
+{
+
+}
\ No newline at end of file
diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs
new file mode 100644
index 0000000..5fb9632
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs
@@ -0,0 +1,39 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Mvc.Razor;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Nop.Core.Infrastructure;
+
+namespace Nop.Plugin.Misc.AIPlugin.Infrastructure;
+
+public class PluginNopStartup : INopStartup
+{
+ ///
+ /// Add and configure any of the middleware
+ ///
+ /// Collection of service descriptors
+ /// Configuration of the application
+ public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
+ {
+ services.Configure(options =>
+ {
+ options.ViewLocationExpanders.Add(new ViewLocationExpander());
+ });
+
+ //register services and interfaces
+ //services.AddScoped();
+ }
+
+ ///
+ /// Configure the using of added middleware
+ ///
+ /// Builder for configuring an application's request pipeline
+ public void Configure(IApplicationBuilder application)
+ {
+ }
+
+ ///
+ /// Gets order of this startup configuration implementation
+ ///
+ public int Order => 3000;
+}
\ No newline at end of file
diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs
new file mode 100644
index 0000000..6bd7294
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs
@@ -0,0 +1,24 @@
+using Microsoft.AspNetCore.Routing;
+using Nop.Web.Framework.Mvc.Routing;
+
+namespace Nop.Plugin.Misc.AIPlugin.Infrastructure;
+
+///
+/// Represents plugin route provider
+///
+public class RouteProvider : IRouteProvider
+{
+ ///
+ /// Register routes
+ ///
+ /// Route builder
+ public void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
+ {
+
+ }
+
+ ///
+ /// Gets a priority of route provider
+ ///
+ public int Priority => 0;
+}
\ No newline at end of file
diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/ViewLocationExpander.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/ViewLocationExpander.cs
new file mode 100644
index 0000000..c4266bf
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/ViewLocationExpander.cs
@@ -0,0 +1,38 @@
+using Microsoft.AspNetCore.Mvc.Razor;
+
+namespace Nop.Plugin.Misc.AIPlugin.Infrastructure;
+
+public class ViewLocationExpander : IViewLocationExpander
+{
+ ///
+ /// Invoked by a to determine the values that would be consumed by this instance
+ /// of . The calculated values are used to determine if the view location
+ /// has changed since the last time it was located.
+ ///
+ /// The for the current view location
+ /// expansion operation.
+ public void PopulateValues(ViewLocationExpanderContext context)
+ {
+ }
+
+ ///
+ /// Invoked by a to determine potential locations for a view.
+ ///
+ /// The for the current view location
+ /// expansion operation.
+ /// The sequence of view locations to expand.
+ /// A list of expanded view locations.
+ public IEnumerable ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable viewLocations)
+ {
+ if (context.AreaName == "Admin")
+ {
+ viewLocations = new[] { $"/Plugins/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/{context.ControllerName}/{context.ViewName}.cshtml" }.Concat(viewLocations);
+ }
+ else
+ {
+ viewLocations = new[] { $"/Plugins/Nop.Plugin.Misc.AIPlugin/Views/{context.ControllerName}/{context.ViewName}.cshtml" }.Concat(viewLocations);
+ }
+
+ return viewLocations;
+ }
+}
\ No newline at end of file
diff --git a/Nop.Plugin.Misc.AIPlugin/Mapping/Builders/PluginBuilder.cs b/Nop.Plugin.Misc.AIPlugin/Mapping/Builders/PluginBuilder.cs
new file mode 100644
index 0000000..18e17fd
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Mapping/Builders/PluginBuilder.cs
@@ -0,0 +1,20 @@
+using FluentMigrator.Builders.Create.Table;
+using Nop.Data.Mapping.Builders;
+using Nop.Plugin.Misc.AIPlugin.Domains;
+
+namespace Nop.Plugin.Misc.AIPlugin.Mapping.Builders;
+
+public class PluginBuilder : NopEntityBuilder
+{
+ #region Methods
+
+ ///
+ /// Apply entity configuration
+ ///
+ /// Create table expression builder
+ public override void MapEntity(CreateTableExpressionBuilder table)
+ {
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/Nop.Plugin.Misc.AIPlugin/Mapping/NameCompatibility.cs b/Nop.Plugin.Misc.AIPlugin/Mapping/NameCompatibility.cs
new file mode 100644
index 0000000..f4ab688
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Mapping/NameCompatibility.cs
@@ -0,0 +1,16 @@
+using Nop.Data.Mapping;
+
+namespace Nop.Plugin.Misc.AIPlugin.Mapping;
+
+public partial class NameCompatibility : INameCompatibility
+{
+ ///
+ /// Gets table name for mapping with the type
+ ///
+ public Dictionary TableNames => new();
+
+ ///
+ /// Gets column name for mapping with the entity's property and type
+ ///
+ public Dictionary<(Type, string), string> ColumnName => new();
+}
\ No newline at end of file
diff --git a/Nop.Plugin.Misc.AIPlugin/Migrations/SchemaMigration.cs b/Nop.Plugin.Misc.AIPlugin/Migrations/SchemaMigration.cs
new file mode 100644
index 0000000..8df4da1
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Migrations/SchemaMigration.cs
@@ -0,0 +1,19 @@
+using FluentMigrator;
+using Nop.Data.Extensions;
+using Nop.Data.Migrations;
+using Nop.Plugin.Misc.AIPlugin.Domains;
+
+namespace Nop.Plugin.Misc.AIPlugin.Migrations;
+
+//2024-10-31
+[NopMigration("2025-08-29 12:07:22", "Nop.Plugin.Misc.AIPlugin schema", MigrationProcessType.Installation)]
+public class SchemaMigration : AutoReversingMigration
+{
+ ///
+ /// Collect the UP migration expressions
+ ///
+ public override void Up()
+ {
+ Create.TableFor();
+ }
+}
\ No newline at end of file
diff --git a/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.AIPlugin.csproj b/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.AIPlugin.csproj
new file mode 100644
index 0000000..8a0f3af
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.AIPlugin.csproj
@@ -0,0 +1,73 @@
+
+
+
+ net9.0
+ $(SolutionDir)\Presentation\Nop.Web\Plugins\Misc.AIPlugin
+ $(OutputPath)
+
+ false
+ enable
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+
+
+
+
+
+
+
diff --git a/Nop.Plugin.Misc.AIPlugin/OpenAISettings.cs b/Nop.Plugin.Misc.AIPlugin/OpenAISettings.cs
new file mode 100644
index 0000000..2b2d7dc
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/OpenAISettings.cs
@@ -0,0 +1,10 @@
+using global::Nop.Core.Configuration;
+
+
+namespace Nop.Plugin.Misc.AIPlugin
+{
+ public class OpenAiSettings : ISettings
+ {
+ public string ApiKey { get; set; }
+ }
+}
diff --git a/Nop.Plugin.Misc.AIPlugin/Services/OpenAIService.cs b/Nop.Plugin.Misc.AIPlugin/Services/OpenAIService.cs
new file mode 100644
index 0000000..407b069
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Services/OpenAIService.cs
@@ -0,0 +1,45 @@
+using Nop.Plugin.Misc.AIPlugin;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Nop.Services.Configuration;
+
+namespace Nop.Plugin.Misc.AIPlugin.Services
+{
+
+ public class OpenAiService
+ {
+ private readonly OpenAiSettings _settings;
+
+ public OpenAiService(OpenAiSettings settings)
+ {
+ _settings = settings;
+ }
+
+ public async Task AskAsync(string prompt)
+ {
+ using var client = new HttpClient();
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _settings.ApiKey);
+
+ var body = new
+ {
+ model = "gpt-4o-mini",
+ messages = new[]
+ {
+ new { role = "system", content = "You are a helpful assistant." },
+ new { role = "user", content = prompt }
+ }
+ };
+
+ var content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json");
+ var response = await client.PostAsync("https://api.openai.com/v1/chat/completions", content);
+
+ var json = await response.Content.ReadAsStringAsync();
+ using var doc = JsonDocument.Parse(json);
+
+ return doc.RootElement.GetProperty("choices")[0].GetProperty("message").GetProperty("content").GetString();
+ }
+ }
+}
diff --git a/Nop.Plugin.Misc.AIPlugin/Views/ProductAIListWidget.cshtml b/Nop.Plugin.Misc.AIPlugin/Views/ProductAIListWidget.cshtml
new file mode 100644
index 0000000..d78592d
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Views/ProductAIListWidget.cshtml
@@ -0,0 +1,20 @@
+@model Nop.Web.Models.Catalog.ProductOverviewModel
+
+@if (Model != null)
+{
+
+
Kérdezz a termékről!
+
+
+
+
+
+
+}
+
diff --git a/Nop.Plugin.Misc.AIPlugin/Views/ProductAIWidget.cshtml b/Nop.Plugin.Misc.AIPlugin/Views/ProductAIWidget.cshtml
new file mode 100644
index 0000000..0af8412
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Views/ProductAIWidget.cshtml
@@ -0,0 +1,20 @@
+@model Nop.Web.Models.Catalog.ProductDetailsModel
+
+@if (Model != null)
+{
+
+
Kérdezz a termékről!
+
+
+
+
+
+
+}
+
diff --git a/Nop.Plugin.Misc.AIPlugin/Views/_ViewImports.cshtml b/Nop.Plugin.Misc.AIPlugin/Views/_ViewImports.cshtml
new file mode 100644
index 0000000..248fb2b
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/Views/_ViewImports.cshtml
@@ -0,0 +1,15 @@
+@inherits Nop.Web.Framework.Mvc.Razor.NopRazorPage
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+@addTagHelper *, Nop.Web.Framework
+
+@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.Plugin.Misc.AIPlugin
+@* @using Nop.Plugin.Misc.AIPlugin.Models *@
+@using Nop.Plugin.Misc.AIPlugin.Services
+@* @using Nop.Plugin.Misc.AIPlugin.Hubs *@
\ No newline at end of file
diff --git a/Nop.Plugin.Misc.AIPlugin/logo.jpg b/Nop.Plugin.Misc.AIPlugin/logo.jpg
new file mode 100644
index 0000000..cae3ce3
Binary files /dev/null and b/Nop.Plugin.Misc.AIPlugin/logo.jpg differ
diff --git a/Nop.Plugin.Misc.AIPlugin/plugin.json b/Nop.Plugin.Misc.AIPlugin/plugin.json
new file mode 100644
index 0000000..b56ed48
--- /dev/null
+++ b/Nop.Plugin.Misc.AIPlugin/plugin.json
@@ -0,0 +1,13 @@
+{
+ "Group": "Misc",
+ "FriendlyName": "AIPlugin",
+ "SystemName": "Misc.AIPlugin",
+ "Version": "1.00",
+ "SupportedVersions": [
+ "4.80"
+ ],
+ "Author": "Adam Gelencser",
+ "DisplayOrder": 1,
+ "FileName": "Nop.Plugin.Misc.AIPlugin.dll",
+ "Description": ""
+}
\ No newline at end of file