AIPLugin
This commit is contained in:
parent
4b2cd32870
commit
e03a9cf6db
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Main plugin class
|
||||
/// </summary>
|
||||
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<OpenAiSettings>();
|
||||
await base.UninstallAsync();
|
||||
}
|
||||
|
||||
// --- WIDGETS ---
|
||||
public bool HideInWidgetList => false;
|
||||
|
||||
public Task<IList<string>> GetWidgetZonesAsync()
|
||||
{
|
||||
return Task.FromResult<IList<string>>(new List<string> { 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<AdminMenuItem>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<IActionResult> Configure(ConfigureModel model)
|
||||
{
|
||||
_settings.ApiKey = model.ApiKey;
|
||||
await _settingService.SaveSettingAsync(_settings);
|
||||
_notificationService.SuccessNotification("Beállítások mentve.");
|
||||
return RedirectToAction("Configure");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
@model Nop.Plugin.Misc.AIPlugin.Models.ConfigureModel
|
||||
|
||||
@{
|
||||
Layout = "_AdminLayout";
|
||||
Html.SetActiveMenuItemSystemName("AiAssistant.Configure");
|
||||
}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>AI Assistant Plugin - Beállítások</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form asp-controller="AiAssistantAdmin" asp-action="Configure" method="post">
|
||||
<div class="form-group">
|
||||
<label asp-for="ApiKey"></label>
|
||||
<input asp-for="ApiKey" class="form-control" />
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Mentés</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
@inherits Nop.Web.Framework.Mvc.Razor.NopRazorPage<TModel>
|
||||
@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
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
using Nop.Core;
|
||||
|
||||
namespace Nop.Plugin.Misc.AIPlugin.Domains;
|
||||
|
||||
public partial class CustomTable : BaseEntity
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Add and configure any of the middleware
|
||||
/// </summary>
|
||||
/// <param name="services">Collection of service descriptors</param>
|
||||
/// <param name="configuration">Configuration of the application</param>
|
||||
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.Configure<RazorViewEngineOptions>(options =>
|
||||
{
|
||||
options.ViewLocationExpanders.Add(new ViewLocationExpander());
|
||||
});
|
||||
|
||||
//register services and interfaces
|
||||
//services.AddScoped<CustomModelFactory, ICustomerModelFactory>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the using of added middleware
|
||||
/// </summary>
|
||||
/// <param name="application">Builder for configuring an application's request pipeline</param>
|
||||
public void Configure(IApplicationBuilder application)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets order of this startup configuration implementation
|
||||
/// </summary>
|
||||
public int Order => 3000;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using Microsoft.AspNetCore.Routing;
|
||||
using Nop.Web.Framework.Mvc.Routing;
|
||||
|
||||
namespace Nop.Plugin.Misc.AIPlugin.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Represents plugin route provider
|
||||
/// </summary>
|
||||
public class RouteProvider : IRouteProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Register routes
|
||||
/// </summary>
|
||||
/// <param name="endpointRouteBuilder">Route builder</param>
|
||||
public void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a priority of route provider
|
||||
/// </summary>
|
||||
public int Priority => 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
|
||||
namespace Nop.Plugin.Misc.AIPlugin.Infrastructure;
|
||||
|
||||
public class ViewLocationExpander : IViewLocationExpander
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked by a <see cref="T:Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine" /> to determine the values that would be consumed by this instance
|
||||
/// of <see cref="T:Microsoft.AspNetCore.Mvc.Razor.IViewLocationExpander" />. The calculated values are used to determine if the view location
|
||||
/// has changed since the last time it was located.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="T:Microsoft.AspNetCore.Mvc.Razor.ViewLocationExpanderContext" /> for the current view location
|
||||
/// expansion operation.</param>
|
||||
public void PopulateValues(ViewLocationExpanderContext context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked by a <see cref="T:Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine" /> to determine potential locations for a view.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="T:Microsoft.AspNetCore.Mvc.Razor.ViewLocationExpanderContext" /> for the current view location
|
||||
/// expansion operation.</param>
|
||||
/// <param name="viewLocations">The sequence of view locations to expand.</param>
|
||||
/// <returns>A list of expanded view locations.</returns>
|
||||
public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CustomTable>
|
||||
{
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Apply entity configuration
|
||||
/// </summary>
|
||||
/// <param name="table">Create table expression builder</param>
|
||||
public override void MapEntity(CreateTableExpressionBuilder table)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
using Nop.Data.Mapping;
|
||||
|
||||
namespace Nop.Plugin.Misc.AIPlugin.Mapping;
|
||||
|
||||
public partial class NameCompatibility : INameCompatibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets table name for mapping with the type
|
||||
/// </summary>
|
||||
public Dictionary<Type, string> TableNames => new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets column name for mapping with the entity's property and type
|
||||
/// </summary>
|
||||
public Dictionary<(Type, string), string> ColumnName => new();
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Collect the UP migration expressions
|
||||
/// </summary>
|
||||
public override void Up()
|
||||
{
|
||||
Create.TableFor<CustomTable>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<OutputPath>$(SolutionDir)\Presentation\Nop.Web\Plugins\Misc.AIPlugin</OutputPath>
|
||||
<OutDir>$(OutputPath)</OutDir>
|
||||
<!--Set this parameter to true to get the dlls copied from the NuGet cache to the output of your project.
|
||||
You need to set this parameter to true if your plugin has a nuget package
|
||||
to ensure that the dlls copied from the NuGet cache to the output of your project-->
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="logo.jpg" />
|
||||
<None Remove="plugin.json" />
|
||||
<None Remove="Views\_ViewImports.cshtml" />
|
||||
<None Remove="Areas\Admin\Views\_ViewImports.cshtml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="logo.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="plugin.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Views\_ViewImports.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Areas\Admin\Views\_ViewImports.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Areas\Admin\Views\_ViewImports.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Views\_ViewImports.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(SolutionDir)\Presentation\Nop.Web\Nop.Web.csproj" />
|
||||
<ClearPluginAssemblies Include="$(SolutionDir)\Build\ClearPluginAssemblies.proj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Areas\Admin\Components\" />
|
||||
<Folder Include="Areas\Admin\Extensions\" />
|
||||
<Folder Include="Areas\Admin\Factories\" />
|
||||
<Folder Include="Areas\Admin\Validators\" />
|
||||
<Folder Include="Controllers\" />
|
||||
<Folder Include="Extensions\" />
|
||||
<Folder Include="Factories\" />
|
||||
<Folder Include="Models\" />
|
||||
<Folder Include="Validators\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Views\ProductAIListWidget.cshtml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Views\ProductAIWidget.cshtml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- This target execute after "Build" target -->
|
||||
<Target Name="NopTarget" AfterTargets="Build">
|
||||
<MSBuild Projects="@(ClearPluginAssemblies)" Properties="PluginPath=$(OutDir)" Targets="NopClear" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using global::Nop.Core.Configuration;
|
||||
|
||||
|
||||
namespace Nop.Plugin.Misc.AIPlugin
|
||||
{
|
||||
public class OpenAiSettings : ISettings
|
||||
{
|
||||
public string ApiKey { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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<string> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
@model Nop.Web.Models.Catalog.ProductOverviewModel
|
||||
|
||||
@if (Model != null)
|
||||
{
|
||||
<div class="ai-question-box">
|
||||
<h5>Kérdezz a termékről!</h5>
|
||||
<textarea id="ai-question"></textarea>
|
||||
<button id="ask-ai-btn" class="btn btn-primary">Kérdezz</button>
|
||||
<div id="ai-answer"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$("#ask-ai-btn").click(function() {
|
||||
$.post("/AiAssistant/Ask", { productId: "@Model.Id", question: $("#ai-question").val() }, function (data) {
|
||||
$("#ai-answer").html(data);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
@model Nop.Web.Models.Catalog.ProductDetailsModel
|
||||
|
||||
@if (Model != null)
|
||||
{
|
||||
<div class="ai-question-box">
|
||||
<h5>Kérdezz a termékről!</h5>
|
||||
<textarea id="ai-question"></textarea>
|
||||
<button id="ask-ai-btn" class="btn btn-primary">Kérdezz</button>
|
||||
<div id="ai-answer"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$("#ask-ai-btn").click(function() {
|
||||
$.post("/AiAssistant/Ask", { productId: "@Model.Id", question: $("#ai-question").val() }, function (data) {
|
||||
$("#ai-answer").html(data);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
@inherits Nop.Web.Framework.Mvc.Razor.NopRazorPage<TModel>
|
||||
@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 *@
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
|
|
@ -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": ""
|
||||
}
|
||||
Loading…
Reference in New Issue